[
  {
    "path": ".clang-format",
    "content": "﻿---\nAccessModifierOffset: '-4'\nAllowShortFunctionsOnASingleLine: Empty\nAlwaysBreakTemplateDeclarations: 'true'\nBreakBeforeBraces: Allman\nColumnLimit: '80'\nConstructorInitializerAllOnOneLineOrOnePerLine: 'true'\nForEachMacros: [ foreach, BOOST_FOREACH ]\nIndentWidth: '4'\nMacroBlockBegin: \"^[A-Z_]+_BEGIN$\"\nMacroBlockEnd: \"^[A-Z_]+_END(?)?$\"\nMaxEmptyLinesToKeep: '1'\nNamespaceIndentation: None\nPointerAlignment: Left\nSpaceAfterControlStatementKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpacesInAngles:  false\nSpacesInParentheses: false\nUseTab: Never\n\n...\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.sh crlf=input"
  },
  {
    "path": ".tx/config",
    "content": "[main]\nhost = https://www.transifex.com\n\n[swish.swish]\nfile_filter = po\\<lang>\\swish.po\nsource_file = po\\template\\swish.pot\nsource_lang = en_GB\ntype = PO\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software; you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation; either version 2 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin\n# Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\ncmake_minimum_required(VERSION 3.1)\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)\n\n# Package management ###########################################################\n\ninclude(HunterGate)\n\nHunterGate(\n    URL \"https://github.com/alamaison/hunter/archive/ee83a15960b5d74098d8704bc9cf82ad8ec1734f.tar.gz\"\n    SHA1 \"d02301fee68f00198e7941129da446f80189017e\"\n    LOCAL\n    )\n\n################################################################################\n\nproject(swish VERSION 0.8.3)\n\nset(SWISH_FRIENDLY_NAME \"Swish\")\nset(SWISH_VENDOR \"swish-sftp.org\")\nset(SWISH_DESCRIPTION \"Easy SFTP for Windows Explorer\")\nset(SWISH_COPYRIGHT \"Copyright (C) 2006-2015  Alexander Lamaison and contributors\")\n\n#include(max_warnings)\n\ninclude_directories(\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/taskdialog98)\n\n# Prevent Winsock errors and gives quicker builds\nadd_definitions(-DWIN32_LEAN_AND_MEAN)\n\n# Silence bogus MSVC warnings\nadd_definitions(-D_SCL_SECURE_NO_WARNINGS)\n\n# Currently required because not all Win32 calls use A or W form explicitly\nadd_definitions(-DUNICODE -D_UNICODE)\n\nhunter_add_package(Boost\n  COMPONENTS filesystem system test date_time regex signals locale)\n\nset(Boost_USE_STATIC_LIBS ON)\nfind_package(Boost 1.49 REQUIRED\n  COMPONENTS filesystem date_time system regex signals thread locale)\nif(MSVC)\n  add_definitions(-DBOOST_ALL_NO_LIB=1)\nendif()\ninclude_directories(${Boost_INCLUDE_DIRS})\n\nadd_subdirectory(ezel)\nadd_subdirectory(ssh)\nadd_subdirectory(swish)\nadd_subdirectory(po)\n\noption(BUILD_TESTING \"Build test suite\" ON)\nif(BUILD_TESTING)\n  enable_testing()\n  add_subdirectory(test)\nendif()\n\nset(INSTALLER_URL\n  \"https://sourceforge.net/projects/swish/files/swish/swish-${swish_VERSION}/swish-${swish_VERSION}.exe\")\nstring(TIMESTAMP PUBLICATION_DATE UTC)\n\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/appcast.xml.in\n  ${CMAKE_CURRENT_BINARY_DIR}/appcast.xml\n  @ONLY)\n\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/setup_conf.xml.in\n  ${CMAKE_CURRENT_BINARY_DIR}/setup_conf.xml\n  @ONLY)\n\nfile(DOWNLOAD \"http://the.earth.li/~sgtatham/putty/0.64/x86/pageant.exe\"\n  \"${CMAKE_CURRENT_BINARY_DIR}/pageant.exe\"\n  SHOW_PROGRESS\n  EXPECTED_HASH SHA1=4f7ec7e53b7dd557603c2447fd177d85f14006ad)\n\ninstall(FILES\n  \"${CMAKE_CURRENT_BINARY_DIR}/pageant.exe\" NEWS LICENSE.txt README.md\n  DESTINATION .)\n\ninclude(InstallRequiredSystemLibraries)\n\nset(CPACK_GENERATOR WIX)\nset(CPACK_PACKAGE_NAME \"${SWISH_FRIENDLY_NAME}\")\nset(CPACK_PACKAGE_VENDOR \"${SWISH_VENDOR}\")\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"${SWISH_DESCRIPTION}\")\n\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt\")\nset(CPACK_RESOURCE_FILE_README \"${CMAKE_CURRENT_SOURCE_DIR}/README.md\")\n\nset(CPACK_WIX_UPGRADE_GUID \"97CF376F-FFDE-472A-946B-E3F5D45229DA\")\nset(CPACK_WIX_PATCH_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/cpack_wix_patch.xml\")\n\nset(CPACK_PACKAGE_VERSION \"${PROJECT_VERSION}\")\nset(CPACK_PACKAGE_INSTALL_DIRECTORY \"${SWISH_FRIENDLY_NAME}\")\nset(CMAKE_WIX_PROPERTY_ARPCONTACT swish@lammy.co.uk)\nset(CMAKE_WIX_PROPERTY_ARPHELPLINK http://www.swish-sftp.org)\nset(CMAKE_WIX_PROPERTY_ARPURLINFOABOUT http://www.swish-sftp.org)\nset(CMAKE_WIX_PROPERTY_ARPURLUPDATEINFO http://sourceforge.net/projects/swish/)\n\n#set(CPACK_WIX_UI_REF WixUI_Minimal)\n\ninclude(CPack)"
  },
  {
    "path": "COPYING.rtf",
    "content": "{\\rtf1\\ansi\\ansicpg1252\\deff0{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}}\r\n{\\colortbl ;\\red0\\green0\\blue0;}\r\n{\\*\\generator Msftedit 5.41.15.1507;}\\viewkind4\\uc1\\pard\\cf1\\lang2057\\f0\\fs18\\tab\\tab     GNU GENERAL PUBLIC LICENSE\\par\r\n\\tab\\tab        Version 2, June 1991\\par\r\n\\par\r\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\\par\r\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\\par\r\n Everyone is permitted to copy and distribute verbatim copies\\par\r\n of this license document, but changing it is not allowed.\\par\r\n\\par\r\n\\tab\\tab\\tab     Preamble\\par\r\n\\par\r\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.\\par\r\n\\par\r\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\\par\r\n\\par\r\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\\par\r\n\\par\r\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\\par\r\n\\par\r\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\\par\r\n\\par\r\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.\\par\r\n\\par\r\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.\\par\r\n\\par\r\nThe precise terms and conditions for copying, distribution and modification follow. \\par\r\n\\par\r\n\\tab\\tab     GNU GENERAL PUBLIC LICENSE\\par\r\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\\par\r\n\\par\r\n0.  This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\".\\par\r\n\\par\r\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\\par\r\n\\par\r\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\\par\r\n\\par\r\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\\par\r\n\\par\r\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\\par\r\n\\par\r\n    a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. \\par\r\n    b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. \\par\r\n    c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) \\par\r\n\\par\r\nThese requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\\par\r\n\\par\r\nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\\par\r\n\\par\r\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\\par\r\n\\par\r\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\\par\r\n\\par\r\n    a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \\par\r\n    b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \\par\r\n    c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) \\par\r\n\\par\r\nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\\par\r\n\\par\r\nIf distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\\par\r\n\\par\r\n4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\\par\r\n\\par\r\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\\par\r\n\\par\r\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\\par\r\n\\par\r\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\\par\r\n\\par\r\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\\par\r\n\\par\r\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\\par\r\n\\par\r\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\\par\r\n\\par\r\n8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\\par\r\n\\par\r\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\\par\r\n\\par\r\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\\par\r\n\\par\r\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \\par\r\n\\par\r\n\\tab\\tab\\tab     NO WARRANTY\\par\r\n\\par\r\n11.  BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\\par\r\n\\par\r\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \\par\r\n\\par\r\n\\tab\\tab      END OF TERMS AND CONDITIONS\\par\r\n\\par\r\n\\tab     How to Apply These Terms to Your New Programs\\par\r\n\\par\r\nIf you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\\par\r\n\\par\r\nTo do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the \"copyright\" line and a pointer to where the full notice is found. \\par\r\n\\par\r\n    <one line to give the program's name and a brief idea of what it does.>\\par\r\n    Copyright (C) <year>  <name of author>\\par\r\n\\par\r\n    This program is free software; you can redistribute it and/or modify\\par\r\n    it under the terms of the GNU General Public License as published by\\par\r\n    the Free Software Foundation; either version 2 of the License, or\\par\r\n    (at your option) any later version.\\par\r\n\\par\r\n    This program is distributed in the hope that it will be useful,\\par\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\\par\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\\par\r\n    GNU General Public License for more details.\\par\r\n\\par\r\n    You should have received a copy of the GNU General Public License along\\par\r\n    with this program; if not, write to the Free Software Foundation, Inc.,\\par\r\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\\par\r\n\\par\r\nAlso add information on how to contact you by electronic and paper mail.\\par\r\n\\par\r\nIf the program is interactive, make it output a short notice like this when it starts in an interactive mode: \\par\r\n\\par\r\n    Gnomovision version 69, Copyright (C) year name of author\\par\r\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\\par\r\n    This is free software, and you are welcome to redistribute it\\par\r\n    under certain conditions; type `show c' for details.\\par\r\n\\par\r\nThe hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.\\par\r\n\\par\r\nYou should also get your employer (if you work as a programmer) or your school, if any, to sign a \"copyright disclaimer\" for the program, if necessary. Here is a sample; alter the names: \\par\r\n\\par\r\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\\par\r\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\\par\r\n\\par\r\n  <signature of Ty Coon>, 1 April 1989\\par\r\n  Ty Coon, President of Vice\\par\r\n\\par\r\nThis General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.\\par\r\n\\par\r\n}\r\n\u0000"
  },
  {
    "path": "LICENSE.txt",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       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 Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  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\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n\t\t    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 contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these 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\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the 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 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\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions 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\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the 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\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\n\t    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\nconvey 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 General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "NEWS",
    "content": "swish-0.8.2\n-Fixed error when known_hosts contained unrecognised key types.\n\nswish-0.8.1\n-Fix crash in 32-bit version when showing dialogues.\n-Updated OpenSSL to 1.0.0l.\n-Updated Hebrew translation.\n\nswish-0.8.0\n-Added option to log out of server manually.\n-Fix file extension behaviour to match Windows folder options.\n-Fixed bug where timestamps were adjusted to local time twice.\n-New Latvian and Catalan translations.\n-Updated Hungarian, Japanese and Portuguese translations.\n\nswish-0.7.4\n-Restored copy progress bar which had disappeared in 0.7.2.\n-Updated Welsh translation.\n\nswish-0.7.3\n-Fixed drag-and-drop crash on Windows 8.\n-Fixed timestamps so they are in user's timezone.\n-New Romanian translation.\n-Updated Korean translation.\n\nswish-0.7.2\n-Added direct downloading and installation of updates.\n-More detailed error messages.\n-Fixed bug with non-ASCII characters in usernames and passwords.\n-New Welsh, Danish, Greek, Estonian, Finnish and Korean translations.\n-Updated Bulgarian, Czech, German, Spanish, French, Hebrew, Italian, Japanese,\n Dutch, Polish, Russian, Swedish and Chinese (Simplified) translations.\n\nswish-0.7.0\n-Added support for public key authentication through Pageant key agent.\n-Updated Czech, Spanish, Japanese, Dutch and Portuguese (Brazil) translations.\n\nswish-0.6.3\n-Fixed errors with Windows 8.\n-Disabled compression to support a greater range of SSH servers.\n-Updated German, Italian, Japanese, Portuguese (Brazil), Chinese (Simplified),\n Chinese (Traditional) and French translations.\n\nswish-0.6.2\n-Fixed performance problems with small files.\n-Fixed bug causing ZIP files to be unpacked as the were copied.\n-New Brazilian Portuguese translation.\n-Updated Dutch, German, Chinese (Simplified), Italian and Portuguese\n translations.\n\nswish-0.6.1\n-Significant speed improvement.\n-Installer supports Windows 8.\n-Better error reporting when copying to a remote server.\n-Fixed bug where renaming a folder did not change the name displayed.\n-Fixed bug where files did not appear after copying to remote server.\n-Hindi, Bulgarian, Portuguese, Swedish and Chinese (Simplified) translations.\n\nswish-0.6.0\n-Added double-click file opening.\n-Downgraded to libssh2 1.2.7 to fix incomplete file transfers.\n\nswish-0.5.4\n-Added support for symlinks.\n-Display error message if drag-and-drop fails.\n-Japanese, Polish, Slovak, Hungarian and Chinese translations.\n\nswish-0.4.6\n-Fixed major installer problem which broke all new installations as it failed\n to register an interface.\n-Fixed problem that left assertion checks in release builds.\n\nswish-0.4.5\n-Added ability to create new folders.\n-Added auto-updater.\n-More useful error messages.\n-Better error dialogues.\n-Fixed pasting files to the server.  This may have been broken for a long time!\n-Fixed problem with port numbers above 999 in some locales.\n-Fixed bug where pressing Cancel showed an error message.\n-Fixed bug causing Explorer Window to flash when navigating between folders.\n-Italian translation.\n\nswish-0.4.4\n-64-bit support.\n-Windows XP 'webtask' pane.\n-Hebrew, Czech, Spanish, French and Turkish translations.\n\nswish-0.4.2\n-Explicitly prevent 64-bit installation.\n-Fix bug where sessions couldn't be restored until Explorer restarted.\n-Fix bug with non-ASCII Windows usernames.\n\nswish-0.4.1\n-Write speeds 4x faster.\n-Huge reduction in GUI memory usage.\n-Confirmation before overwriting existing files.\n-Improved write progress indicator.\n-Better error reporting.\n-Fix bug where 'Remove' option missing from host menu.\n-German translation.\n\nswish-0.4.0\n-Full support for files and directories with non-Latin Unicode names.\n\nswish-0.3.5\n-Display Unicode filenames.\n\nswish-0.3.4\n-Fixed hostname resolution for Windows versions earlier than Vista\n\nswish-0.3.3\n-IPv6 support.\n-Window 7 fixes.\n-Fix overwriting larger file with smaller one.\n-Error messages when negotiating a connection fails.\n-Support for most characters (e.g. @) in user names.\n\nswish-0.3.2.0\n-Internationalisation (English, Dutch, Russian)\n\nswish-0.3.1.0\n-Added host-key verification.\n-New WIX-based installer\n\nswish-0.3.0.1\n-Added command buttons to add and remove hosts (Windows Vista and above).\n-Allow the user to cancel a file transfer mid-file rather than only between\n files.\n-Fix bug where password dialogues appear behind the Explorer window.\n-Fix missing menu options bug in Windows 98.\n\nswish-0.3.0.0\n-Full drag-and-drop support in Windows XP and above.  Drag-and-drop files and\n folders to and from the remote server.\n\nswish-0.2.1.13\n-Enable removal of hosts from Swish folder.\n\nswish-0.2.1.12\n-Added 'Type' property to Explorer view.\n-Fixed incorrect column witdths (ticket #2).\n-Display user name and group name rather than the UID and GID.\n-Added 'Owner ID' and 'Group ID' columns to Explorer view to display the\n numerical UID and GID.\n-Added 'Date Accessed' column to Explorer view.\n\nswish-0.2.1.11\n-Read-only file transfer support added.  Files and folders can be copied from a\n remote server to the local computer using cut&paste or drag&drop.\n\nswish-0.2.1.10\n-Now supports Windows Vista.\n\nswish-0.2.1.9\n-Added support for the keyboard-interactive authentication scheme:\n (http://www.ietf.org/rfc/rfc4256.txt).\n-Improved robustness of the low-level backend session handling in the face\n of errors.\n\nswish-0.2.1.8\n-Fixed bug when overwriting existing directories.\n\nswish-0.2.1.7\n-Files and folders can be renamed.  If an item with the chosen name already\n exists, we offer to overwrite it.  Due to the limitations of SFTP versions 4\n and below, we do this non-atomically which is not ideal.  Time will tell\n whether this is more trouble than it's worth.\n\nswish-0.2.1.6\n-Files and folders can be deleted from the Explorer window.  If a directory\n contains other items, we deleted these recursively using separate calls to the\n server.  This is a bit clunky but we are limited by what SFTP supports.\n\nswish-0.2.1.5\n-SFTP sessions (connections) are now pooled in the COM ROT so that subsequent\n requests can reuse them without prompting the user for a password or having to\n renegotiate with the server.\n\nswish-0.2.1.4\n-Replace the PuTTY-based dataprovider component with one based in libssh2. This\n is vastly more stable. The new backend has all the abilities of the of the\n old one with a few exceptions, namely, host-verification (aka known_hosts),\n key-interactive authentication and public-key authentication. Replacing these\n is a priority.\n-The frontend has been integrate much more closely with Explorer.\n-Connections are added with a dialog invoked from the Tools menu and are stored\n in the registry.\n-Explorer displays the default icon for files based on their extension (this\n doesn't work in Windows 98 yet).\n-Subfolder can be navigated.\n-The default explorer context menu is displayed when right-clicking a file.\n\nswish-0.2.1\n-Created a data provider component to wrap PuTTY's SFTP client (psftp.exe)\n permitting listing of files in a directory\n-Linked data provider to front end using simple dialog box for requesting\n host information from the user\n\nswish-0.2\n-All previous prototype code discarded\n-Created Explorer namespace extension with window explorer window showing\n three dummy host connection items\n-Used SHCreateShellFolderView to create default shell view window giving\n best possible Explorer integration\n-Created PIDL Manager to handle host (connection) folder item\n-Added conection item details to Details and Tiles view\n-Created Remote Folder displaying dummy file listing which is displayed by\n double-clicking (or otherwise opening) host connection in initial folder\n\nswish-0.1.1\n-Experimentation with Visual Studio and linking with gcc-compiled libssh\n\nswish-0.1-gcc\n-Experimentation using gcc, wxWidgets and libssh-0.11"
  },
  {
    "path": "README.md",
    "content": "Swish\n=====\n\nWhat is Swish?\n--------------\n\nSwish is a plugin for Microsoft Windows Explorer that adds support for SFTP.\n\nIf you've used Explorer's built-in FTP support, Swish is that but for SFTP\nover SSH.\n\nSupported Operating Systems\n---------------------------\n  * Windows 8     (most tested)\n  * Windows 7     (occasionally tested)\n  * Windows Vista (rarely tested)\n\nSwish may also work on Windows XP, but we haven't tested that in a while.\n\nBinaries\n--------\nBinary installers are [on our website](http://www.swish-sftp.org).\n\nGetting involved\n----------------\nWe welcome patches to help improve Swish.\n\nSwish fetches almost all its dependencies when you configure it with [CMake].\nThat magic happens thanks to the [Hunter package manager].  However, you will\nneed Perl installed. [Strawberry Perl] is good, and available on [Chocolatey].\n\nYou'll also need a compiler (obviously), a recent version of the Windows SDK\nand CMake.\n\n[CMake]:                  https://cmake.org/\n[Hunter package manager]: https://github.com/ruslo/hunter/\n[Strawberry Perl]:        http://strawberryperl.com/\n[Chocolatey]:             https://chocolatey.org/packages/StrawberryPerl\n\nLicensing\n---------\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at\nyour option) any later version.\n\nThis program is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nIf you modify this Program, or any covered work, by linking or\ncombining it with the OpenSSL project's OpenSSL library (or a modified\nversion of that library), containing parts covered by the terms of the\nOpenSSL or SSLeay licenses, the licensors of this Program grant you\nadditional permission to convey the resulting work.\n\n### Why have an exception for OpenSSL?\n\nThe [OpenSSL] library is incompatible with the GPL license because it\ncontains an advertising clause.  However lots of useful, open source\nsoftware (including our own projects) need to use it and currently the\nalternatives aren't quite up to scratch.  As we want these projects to\nbe able to reuse Washer, we have added this exception to the GPL - a\ncommon technique used by other projects such as [wget].\n\nIf [GnuTLS] improves to the point where OpenSSL is no longer\nnecessary, we may remove this exception.\n\n[OpenSSL]: http://www.openssl.org/\n[wget]:    http://www.gnu.org/software/wget/\n[GnuTLS]:  http://www.gnu.org/software/gnutls/\n"
  },
  {
    "path": "appcast.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">\n  <channel>\n    <title>Swish updates</title>\n    <link>http://www.swish-sftp.org/autoupdate/appcast.xml</link>\n    <description>Appcast for Swish updates.</description>\n    <language>en</language>\n    <item>\n      <title>Version @swish_VERSION@</title>\n      <sparkle:releaseNotesLink>\n\thttp://www.swish-sftp.org/autoupdate/NEWS-@swish_VERSION@.html\n      </sparkle:releaseNotesLink>\n      <pubDate>@PUBLICATION_DATE@</pubDate>\n      <enclosure url=\"@INSTALLER_URL@\"\n                 sparkle:version=\"@swish_VERSION@\"\n                 type=\"application/octet-stream\"/>\n    </item>\n  </channel>\n</rss>\n"
  },
  {
    "path": "build/making_a_release.txt",
    "content": "Update version number in:\n- top-level CMakeLists.txt\n- wix/swish.wxs\n- wix/wix.wixproj\n\nAdd notable changes to NEWS.\n\nMake sure .po files are compiled to .mo files (po/compile_mo.sh).\n\nTo build the final multi-architecture EXE, run this command in the top level:\n\"C:\\Program Files (x86)\\dotNetInstaller\\bin\\InstallerLinker.exe\" /Output:swish.exe /Template:\"C:\\Program Files (x86)\\dotNetInstaller\\bin\\dotNetInstaller.exe\" /Configuration:setup_conf.xml\n\nAdd version to Trac.\n"
  },
  {
    "path": "build/news_html.py",
    "content": "\"\"\"Convert Swish NEWS file to HTML.\r\n\"\"\"\r\n\r\n_NEWS = '../NEWS'\r\n_VERSIONED_NEWS = '../NEWS-%s.html'\r\n\r\ndef main():\r\n\r\n    with open(_NEWS, 'r') as news_in:\r\n        html = ('''<html style=\"font-size:x-small; font-family : sans-serif\">\r\n<body>\r\n%s\r\n</body>\r\n</html>''') % read_news(news_in, 1)\r\n\r\n    with open(_NEWS, 'r') as news_in:\r\n        version = extract_version(news_in)\r\n    with open(_VERSIONED_NEWS % version, 'w') as html_open:\r\n        html_open.write(html)\r\n\r\ndef extract_version(news):\r\n    return news.readline().strip('\\n\\r').replace('swish-', '')\r\n\r\ndef read_news(news, indent):\r\n    entry_html = '''<h1 style=\"font-size:small; margin-bottom:0ex; color:brown\">%s</h1>\r\n<p style=\"margin-top:0ex\">\r\n<ul style=\"margin-left:3ex; margin-top:1ex\">\r\n%s\r\n</ul>\r\n</p>'''\r\n    html = []\r\n    while True:\r\n        entry = read_entry(news)\r\n        if not entry:\r\n            break\r\n        lines = []\r\n        for item in entry['items']:\r\n            lines.append(create_list_item(item, indent + 1))\r\n        html.append(entry_html % (entry['heading'], '\\n'.join(lines)))\r\n\r\n    return '\\n'.join(html)\r\n\r\ndef create_list_item(item, indent):\r\n    return '%s<li>%s</li>' % (('\\t' * indent), item)\r\n\r\ndef read_entry(news):\r\n    entry = { 'heading' : news.readline().strip(), 'items' : [] }\r\n\r\n    while True:\r\n        line = news.readline().strip('\\n\\r')\r\n        print line\r\n        if not line:\r\n            break\r\n        if line[0] == ' ':\r\n            entry['items'][-1] += line\r\n            continue\r\n        if line[0] != '-':\r\n            raise Exception('Incorrect format: %s' % line)\r\n        entry['items'].append(line[1:])\r\n\r\n    if not entry['heading']:\r\n        return None\r\n\r\n    return entry\r\n\r\nif __name__ == '__main__':\r\n    main()\r\n\r\n"
  },
  {
    "path": "cmake/GetGitRevisionDescription.cmake",
    "content": "# - Returns a version string from Git\n#\n# These functions force a re-configure on each git commit so that you can\n# trust the values of the variables in your build system.\n#\n#  get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])\n#\n# Returns the refspec and sha hash of the current head revision\n#\n#  git_describe(<var> [<additional arguments to git describe> ...])\n#\n# Returns the results of git describe on the source tree, and adjusting\n# the output so that it tests false if an error occurs.\n#\n#  git_get_exact_tag(<var> [<additional arguments to git describe> ...])\n#\n# Returns the results of git describe --exact-match on the source tree,\n# and adjusting the output so that it tests false if there was no exact\n# matching tag.\n#\n# Requires CMake 2.6 or newer (uses the 'function' command)\n#\n# Original Author:\n# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>\n# http://academic.cleardefinition.com\n# Iowa State University HCI Graduate Program/VRAC\n#\n# Copyright Iowa State University 2009-2010.\n# Distributed under the Boost Software License, Version 1.0.\n# (See accompanying file LICENSE_1_0.txt or copy at\n# http://www.boost.org/LICENSE_1_0.txt)\n\nif(__get_git_revision_description)\n\treturn()\nendif()\nset(__get_git_revision_description YES)\n\n# We must run the following at \"include\" time, not at function call time,\n# to find the path to this module rather than the path to a calling list file\nget_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)\n\nfunction(get_git_head_revision _refspecvar _hashvar)\n\tset(GIT_PARENT_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\tset(GIT_DIR \"${GIT_PARENT_DIR}/.git\")\n\twhile(NOT EXISTS \"${GIT_DIR}\")\t# .git dir not found, search parent directories\n\t\tset(GIT_PREVIOUS_PARENT \"${GIT_PARENT_DIR}\")\n\t\tget_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)\n\t\tif(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)\n\t\t\t# We have reached the root directory, we are not in git\n\t\t\tset(${_refspecvar} \"GITDIR-NOTFOUND\" PARENT_SCOPE)\n\t\t\tset(${_hashvar} \"GITDIR-NOTFOUND\" PARENT_SCOPE)\n\t\t\treturn()\n\t\tendif()\n\t\tset(GIT_DIR \"${GIT_PARENT_DIR}/.git\")\n\tendwhile()\n\t# check if this is a submodule\n\tif(NOT IS_DIRECTORY ${GIT_DIR})\n\t\tfile(READ ${GIT_DIR} submodule)\n\t\tstring(REGEX REPLACE \"gitdir: (.*)\\n$\" \"\\\\1\" GIT_DIR_RELATIVE ${submodule})\n\t\tget_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)\n\t\tget_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)\n\tendif()\n\tset(GIT_DATA \"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data\")\n\tif(NOT EXISTS \"${GIT_DATA}\")\n\t\tfile(MAKE_DIRECTORY \"${GIT_DATA}\")\n\tendif()\n\n\tif(NOT EXISTS \"${GIT_DIR}/HEAD\")\n\t\treturn()\n\tendif()\n\tset(HEAD_FILE \"${GIT_DATA}/HEAD\")\n\tconfigure_file(\"${GIT_DIR}/HEAD\" \"${HEAD_FILE}\" COPYONLY)\n\n\tconfigure_file(\"${_gitdescmoddir}/GetGitRevisionDescription.cmake.in\"\n\t\t\"${GIT_DATA}/grabRef.cmake\"\n\t\t@ONLY)\n\tinclude(\"${GIT_DATA}/grabRef.cmake\")\n\n\tset(${_refspecvar} \"${HEAD_REF}\" PARENT_SCOPE)\n\tset(${_hashvar} \"${HEAD_HASH}\" PARENT_SCOPE)\nendfunction()\n\nfunction(git_describe _var)\n\tif(NOT GIT_FOUND)\n\t\tfind_package(Git QUIET)\n\tendif()\n\tget_git_head_revision(refspec hash)\n\tif(NOT GIT_FOUND)\n\t\tset(${_var} \"GIT-NOTFOUND\" PARENT_SCOPE)\n\t\treturn()\n\tendif()\n\tif(NOT hash)\n\t\tset(${_var} \"HEAD-HASH-NOTFOUND\" PARENT_SCOPE)\n\t\treturn()\n\tendif()\n\n\t# TODO sanitize\n\t#if((${ARGN}\" MATCHES \"&&\") OR\n\t#\t(ARGN MATCHES \"||\") OR\n\t#\t(ARGN MATCHES \"\\\\;\"))\n\t#\tmessage(\"Please report the following error to the project!\")\n\t#\tmessage(FATAL_ERROR \"Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}\")\n\t#endif()\n\n\t#message(STATUS \"Arguments to execute_process: ${ARGN}\")\n\n\texecute_process(COMMAND\n\t\t\"${GIT_EXECUTABLE}\"\n\t\tdescribe\n\t\t${hash}\n\t\t${ARGN}\n\t\tWORKING_DIRECTORY\n\t\t\"${CMAKE_CURRENT_SOURCE_DIR}\"\n\t\tRESULT_VARIABLE\n\t\tres\n\t\tOUTPUT_VARIABLE\n\t\tout\n\t\tERROR_QUIET\n\t\tOUTPUT_STRIP_TRAILING_WHITESPACE)\n\tif(NOT res EQUAL 0)\n\t\tset(out \"${out}-${res}-NOTFOUND\")\n\tendif()\n\n\tset(${_var} \"${out}\" PARENT_SCOPE)\nendfunction()\n\nfunction(git_get_exact_tag _var)\n\tgit_describe(out --exact-match ${ARGN})\n\tset(${_var} \"${out}\" PARENT_SCOPE)\nendfunction()\n"
  },
  {
    "path": "cmake/GetGitRevisionDescription.cmake.in",
    "content": "#\n# Internal file for GetGitRevisionDescription.cmake\n#\n# Requires CMake 2.6 or newer (uses the 'function' command)\n#\n# Original Author:\n# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>\n# http://academic.cleardefinition.com\n# Iowa State University HCI Graduate Program/VRAC\n#\n# Copyright Iowa State University 2009-2010.\n# Distributed under the Boost Software License, Version 1.0.\n# (See accompanying file LICENSE_1_0.txt or copy at\n# http://www.boost.org/LICENSE_1_0.txt)\n\nset(HEAD_HASH)\n\nfile(READ \"@HEAD_FILE@\" HEAD_CONTENTS LIMIT 1024)\n\nstring(STRIP \"${HEAD_CONTENTS}\" HEAD_CONTENTS)\nif(HEAD_CONTENTS MATCHES \"ref\")\n\t# named branch\n\tstring(REPLACE \"ref: \" \"\" HEAD_REF \"${HEAD_CONTENTS}\")\n\tif(EXISTS \"@GIT_DIR@/${HEAD_REF}\")\n\t\tconfigure_file(\"@GIT_DIR@/${HEAD_REF}\" \"@GIT_DATA@/head-ref\" COPYONLY)\n\telse()\n\t\tconfigure_file(\"@GIT_DIR@/packed-refs\" \"@GIT_DATA@/packed-refs\" COPYONLY)\n\t\tfile(READ \"@GIT_DATA@/packed-refs\" PACKED_REFS)\n\t\tif(${PACKED_REFS} MATCHES \"([0-9a-z]*) ${HEAD_REF}\")\n\t\t\tset(HEAD_HASH \"${CMAKE_MATCH_1}\")\n\t\tendif()\n\tendif()\nelse()\n\t# detached HEAD\n\tconfigure_file(\"@GIT_DIR@/HEAD\" \"@GIT_DATA@/head-ref\" COPYONLY)\nendif()\n\nif(NOT HEAD_HASH)\n\tfile(READ \"@GIT_DATA@/head-ref\" HEAD_HASH LIMIT 1024)\n\tstring(STRIP \"${HEAD_HASH}\" HEAD_HASH)\nendif()\n"
  },
  {
    "path": "cmake/Hunter/config.cmake",
    "content": "hunter_config(Boost VERSION 1.49.0)"
  },
  {
    "path": "cmake/HunterGate.cmake",
    "content": "# Copyright (c) 2013-2015, Ruslan Baratov\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#\n# * Redistributions of source code must retain the above copyright notice, this\n#   list of conditions and the following disclaimer.\n#\n# * Redistributions in binary form must reproduce the above copyright notice,\n#   this list of conditions and the following disclaimer in the documentation\n#   and/or other materials provided with the distribution.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n# This is a gate file to Hunter package manager.\n# Include this file using `include` command and add package you need, example:\n#\n#     cmake_minimum_required(VERSION 3.0)\n#\n#     include(\"cmake/HunterGate.cmake\")\n#     HunterGate(\n#         URL \"https://github.com/path/to/hunter/archive.tar.gz\"\n#         SHA1 \"798501e983f14b28b10cda16afa4de69eee1da1d\"\n#     )\n#\n#     project(MyProject)\n#\n#     hunter_add_package(Foo)\n#     hunter_add_package(Boo COMPONENTS Bar Baz)\n#\n# Projects:\n#     * https://github.com/hunter-packages/gate/\n#     * https://github.com/ruslo/hunter\n\ncmake_minimum_required(VERSION 3.0) # Minimum for Hunter\ninclude(CMakeParseArguments) # cmake_parse_arguments\n\noption(HUNTER_ENABLED \"Enable Hunter package manager support\" ON)\noption(HUNTER_STATUS_PRINT \"Print working status\" ON)\noption(HUNTER_STATUS_DEBUG \"Print a lot info\" OFF)\n\nset(HUNTER_WIKI \"https://github.com/ruslo/hunter/wiki\")\n\nfunction(hunter_gate_status_print)\n  foreach(print_message ${ARGV})\n    if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)\n      message(STATUS \"[hunter] ${print_message}\")\n    endif()\n  endforeach()\nendfunction()\n\nfunction(hunter_gate_status_debug)\n  foreach(print_message ${ARGV})\n    if(HUNTER_STATUS_DEBUG)\n      string(TIMESTAMP timestamp)\n      message(STATUS \"[hunter *** DEBUG *** ${timestamp}] ${print_message}\")\n    endif()\n  endforeach()\nendfunction()\n\nfunction(hunter_gate_wiki wiki_page)\n  message(\"------------------------------ WIKI -------------------------------\")\n  message(\"    ${HUNTER_WIKI}/${wiki_page}\")\n  message(\"-------------------------------------------------------------------\")\n  message(\"\")\n  message(FATAL_ERROR \"\")\nendfunction()\n\nfunction(hunter_gate_internal_error)\n  message(\"\")\n  foreach(print_message ${ARGV})\n    message(\"[hunter ** INTERNAL **] ${print_message}\")\n  endforeach()\n  message(\"[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]\")\n  message(\"\")\n  hunter_gate_wiki(\"error.internal\")\nendfunction()\n\nfunction(hunter_gate_fatal_error)\n  cmake_parse_arguments(hunter \"\" \"WIKI\" \"\" \"${ARGV}\")\n  string(COMPARE EQUAL \"${hunter_WIKI}\" \"\" have_no_wiki)\n  if(have_no_wiki)\n    hunter_gate_internal_error(\"Expected wiki\")\n  endif()\n  message(\"\")\n  foreach(x ${hunter_UNPARSED_ARGUMENTS})\n    message(\"[hunter ** FATAL ERROR **] ${x}\")\n  endforeach()\n  message(\"[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]\")\n  message(\"\")\n  hunter_gate_wiki(\"${hunter_WIKI}\")\nendfunction()\n\nfunction(hunter_gate_user_error)\n  hunter_gate_fatal_error(${ARGV} WIKI \"error.incorrect.input.data\")\nendfunction()\n\nfunction(hunter_gate_self root version sha1 result)\n  string(COMPARE EQUAL \"${root}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"root is empty\")\n  endif()\n\n  string(COMPARE EQUAL \"${version}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"version is empty\")\n  endif()\n\n  string(COMPARE EQUAL \"${sha1}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"sha1 is empty\")\n  endif()\n\n  string(SUBSTRING \"${sha1}\" 0 7 archive_id)\n\n  if(EXISTS \"${root}/cmake/Hunter\")\n    set(hunter_self \"${root}\")\n  else()\n    set(\n        hunter_self\n        \"${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked\"\n    )\n  endif()\n\n  set(\"${result}\" \"${hunter_self}\" PARENT_SCOPE)\nendfunction()\n\n# Set HUNTER_GATE_ROOT cmake variable to suitable value.\nfunction(hunter_gate_detect_root)\n  # Check CMake variable\n  string(COMPARE NOTEQUAL \"${HUNTER_ROOT}\" \"\" not_empty)\n  if(not_empty)\n    set(HUNTER_GATE_ROOT \"${HUNTER_ROOT}\" PARENT_SCOPE)\n    hunter_gate_status_debug(\"HUNTER_ROOT detected by cmake variable\")\n    return()\n  endif()\n\n  # Check environment variable\n  string(COMPARE NOTEQUAL \"$ENV{HUNTER_ROOT}\" \"\" not_empty)\n  if(not_empty)\n    set(HUNTER_GATE_ROOT \"$ENV{HUNTER_ROOT}\" PARENT_SCOPE)\n    hunter_gate_status_debug(\"HUNTER_ROOT detected by environment variable\")\n    return()\n  endif()\n\n  # Check HOME environment variable\n  string(COMPARE NOTEQUAL \"$ENV{HOME}\" \"\" result)\n  if(result)\n    set(HUNTER_GATE_ROOT \"$ENV{HOME}/.hunter\" PARENT_SCOPE)\n    hunter_gate_status_debug(\"HUNTER_ROOT set using HOME environment variable\")\n    return()\n  endif()\n\n  # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)\n  if(WIN32)\n    string(COMPARE NOTEQUAL \"$ENV{SYSTEMDRIVE}\" \"\" result)\n    if(result)\n      set(HUNTER_GATE_ROOT \"$ENV{SYSTEMDRIVE}/.hunter\" PARENT_SCOPE)\n      hunter_gate_status_debug(\n          \"HUNTER_ROOT set using SYSTEMDRIVE environment variable\"\n      )\n      return()\n    endif()\n\n    string(COMPARE NOTEQUAL \"$ENV{USERPROFILE}\" \"\" result)\n    if(result)\n      set(HUNTER_GATE_ROOT \"$ENV{USERPROFILE}/.hunter\" PARENT_SCOPE)\n      hunter_gate_status_debug(\n          \"HUNTER_ROOT set using USERPROFILE environment variable\"\n      )\n      return()\n    endif()\n  endif()\n\n  hunter_gate_fatal_error(\n      \"Can't detect HUNTER_ROOT\"\n      WIKI \"error.detect.hunter.root\"\n  )\nendfunction()\n\nmacro(hunter_gate_lock dir)\n  if(NOT HUNTER_SKIP_LOCK)\n    if(\"${CMAKE_VERSION}\" VERSION_LESS \"3.2\")\n      hunter_gate_fatal_error(\n          \"Can't lock, upgrade to CMake 3.2 or use HUNTER_SKIP_LOCK\"\n          WIKI \"error.can.not.lock\"\n      )\n    endif()\n    hunter_gate_status_debug(\"Locking directory: ${dir}\")\n    file(LOCK \"${dir}\" DIRECTORY GUARD FUNCTION)\n    hunter_gate_status_debug(\"Lock done\")\n  endif()\nendmacro()\n\nfunction(hunter_gate_download dir)\n  string(\n      COMPARE\n      NOTEQUAL\n      \"$ENV{HUNTER_DISABLE_AUTOINSTALL}\"\n      \"\"\n      disable_autoinstall\n  )\n  if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)\n    hunter_gate_fatal_error(\n        \"Hunter not found in '${dir}'\"\n        \"Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'\"\n        \"Settings:\"\n        \"  HUNTER_ROOT: ${HUNTER_GATE_ROOT}\"\n        \"  HUNTER_SHA1: ${HUNTER_GATE_SHA1}\"\n        WIKI \"error.run.install\"\n    )\n  endif()\n  string(COMPARE EQUAL \"${dir}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"Empty 'dir' argument\")\n  endif()\n\n  string(COMPARE EQUAL \"${HUNTER_GATE_SHA1}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"HUNTER_GATE_SHA1 empty\")\n  endif()\n\n  string(COMPARE EQUAL \"${HUNTER_GATE_URL}\" \"\" is_bad)\n  if(is_bad)\n    hunter_gate_internal_error(\"HUNTER_GATE_URL empty\")\n  endif()\n\n  set(done_location \"${dir}/DONE\")\n  set(sha1_location \"${dir}/SHA1\")\n\n  set(build_dir \"${dir}/Build\")\n  set(cmakelists \"${dir}/CMakeLists.txt\")\n\n  hunter_gate_lock(\"${dir}\")\n  if(EXISTS \"${done_location}\")\n    # while waiting for lock other instance can do all the job\n    hunter_gate_status_debug(\"File '${done_location}' found, skip install\")\n    return()\n  endif()\n\n  file(REMOVE_RECURSE \"${build_dir}\")\n  file(REMOVE_RECURSE \"${cmakelists}\")\n\n  file(MAKE_DIRECTORY \"${build_dir}\") # check directory permissions\n\n  # Disabling languages speeds up a little bit, reduces noise in the output\n  # and avoids path too long windows error\n  file(\n      WRITE\n      \"${cmakelists}\"\n      \"cmake_minimum_required(VERSION 3.0)\\n\"\n      \"project(HunterDownload LANGUAGES NONE)\\n\"\n      \"include(ExternalProject)\\n\"\n      \"ExternalProject_Add(\\n\"\n      \"    Hunter\\n\"\n      \"    URL\\n\"\n      \"    \\\"${HUNTER_GATE_URL}\\\"\\n\"\n      \"    URL_HASH\\n\"\n      \"    SHA1=${HUNTER_GATE_SHA1}\\n\"\n      \"    DOWNLOAD_DIR\\n\"\n      \"    \\\"${dir}\\\"\\n\"\n      \"    SOURCE_DIR\\n\"\n      \"    \\\"${dir}/Unpacked\\\"\\n\"\n      \"    CONFIGURE_COMMAND\\n\"\n      \"    \\\"\\\"\\n\"\n      \"    BUILD_COMMAND\\n\"\n      \"    \\\"\\\"\\n\"\n      \"    INSTALL_COMMAND\\n\"\n      \"    \\\"\\\"\\n\"\n      \")\\n\"\n  )\n\n  if(HUNTER_STATUS_DEBUG)\n    set(logging_params \"\")\n  else()\n    set(logging_params OUTPUT_QUIET)\n  endif()\n\n  hunter_gate_status_debug(\"Run generate\")\n  execute_process(\n      COMMAND \"${CMAKE_COMMAND}\" \"-H${dir}\" \"-B${build_dir}\"\n      WORKING_DIRECTORY \"${dir}\"\n      RESULT_VARIABLE download_result\n      ${logging_params}\n  )\n\n  if(NOT download_result EQUAL 0)\n    hunter_gate_internal_error(\"Configure project failed\")\n  endif()\n\n  hunter_gate_status_print(\n      \"Initializing Hunter workspace (${HUNTER_GATE_SHA1})\"\n      \"  ${HUNTER_GATE_URL}\"\n      \"  -> ${dir}\"\n  )\n  execute_process(\n      COMMAND \"${CMAKE_COMMAND}\" --build \"${build_dir}\"\n      WORKING_DIRECTORY \"${dir}\"\n      RESULT_VARIABLE download_result\n      ${logging_params}\n  )\n\n  if(NOT download_result EQUAL 0)\n    hunter_gate_internal_error(\"Build project failed\")\n  endif()\n\n  file(REMOVE_RECURSE \"${build_dir}\")\n  file(REMOVE_RECURSE \"${cmakelists}\")\n\n  file(WRITE \"${sha1_location}\" \"${HUNTER_GATE_SHA1}\")\n  file(WRITE \"${done_location}\" \"DONE\")\n\n  hunter_gate_status_debug(\"Finished\")\nendfunction()\n\n# Must be a macro so master file 'cmake/Hunter' can\n# apply all variables easily just by 'include' command\n# (otherwise PARENT_SCOPE magic needed)\nmacro(HunterGate)\n  if(HUNTER_GATE_DONE)\n    # variable HUNTER_GATE_DONE set explicitly for external project\n    # (see `hunter_download`)\n    set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)\n  endif()\n\n  # First HunterGate command will init Hunter, others will be ignored\n  get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)\n\n  if(NOT HUNTER_ENABLED)\n    # Empty function to avoid error \"unknown function\"\n    function(hunter_add_package)\n    endfunction()\n  elseif(_hunter_gate_done)\n    hunter_gate_status_debug(\"Secondary HunterGate (use old settings)\")\n    hunter_gate_self(\n        \"${HUNTER_CACHED_ROOT}\"\n        \"${HUNTER_VERSION}\"\n        \"${HUNTER_SHA1}\"\n        _hunter_self\n    )\n    include(\"${_hunter_self}/cmake/Hunter\")\n  else()\n    set(HUNTER_GATE_LOCATION \"${CMAKE_CURRENT_LIST_DIR}\")\n\n    string(COMPARE NOTEQUAL \"${PROJECT_NAME}\" \"\" _have_project_name)\n    if(_have_project_name)\n      hunter_gate_fatal_error(\n          \"Please set HunterGate *before* 'project' command. \"\n          \"Detected project: ${PROJECT_NAME}\"\n          WIKI \"error.huntergate.before.project\"\n      )\n    endif()\n\n    cmake_parse_arguments(\n        HUNTER_GATE \"LOCAL\" \"URL;SHA1;GLOBAL;FILEPATH\" \"\" ${ARGV}\n    )\n\n    string(COMPARE EQUAL \"${HUNTER_GATE_SHA1}\" \"\" _empty_sha1)\n    string(COMPARE EQUAL \"${HUNTER_GATE_URL}\" \"\" _empty_url)\n    string(\n        COMPARE\n        NOTEQUAL\n        \"${HUNTER_GATE_UNPARSED_ARGUMENTS}\"\n        \"\"\n        _have_unparsed\n    )\n    string(COMPARE NOTEQUAL \"${HUNTER_GATE_GLOBAL}\" \"\" _have_global)\n    string(COMPARE NOTEQUAL \"${HUNTER_GATE_FILEPATH}\" \"\" _have_filepath)\n\n    if(_empty_sha1)\n      hunter_gate_user_error(\"SHA1 suboption of HunterGate is mandatory\")\n    endif()\n    if(_empty_url)\n      hunter_gate_user_error(\"URL suboption of HunterGate is mandatory\")\n    endif()\n    if(_have_unparsed)\n      hunter_gate_user_error(\n          \"HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}\"\n      )\n    endif()\n    if(_have_global)\n      if(HUNTER_GATE_LOCAL)\n        hunter_gate_user_error(\"Unexpected LOCAL (already has GLOBAL)\")\n      endif()\n      if(_have_filepath)\n        hunter_gate_user_error(\"Unexpected FILEPATH (already has GLOBAL)\")\n      endif()\n    endif()\n    if(HUNTER_GATE_LOCAL)\n      if(_have_global)\n        hunter_gate_user_error(\"Unexpected GLOBAL (already has LOCAL)\")\n      endif()\n      if(_have_filepath)\n        hunter_gate_user_error(\"Unexpected FILEPATH (already has LOCAL)\")\n      endif()\n    endif()\n    if(_have_filepath)\n      if(_have_global)\n        hunter_gate_user_error(\"Unexpected GLOBAL (already has FILEPATH)\")\n      endif()\n      if(HUNTER_GATE_LOCAL)\n        hunter_gate_user_error(\"Unexpected LOCAL (already has FILEPATH)\")\n      endif()\n    endif()\n\n    hunter_gate_detect_root() # set HUNTER_GATE_ROOT\n\n    # Beautify path, fix probable problems with windows path slashes\n    get_filename_component(\n        HUNTER_GATE_ROOT \"${HUNTER_GATE_ROOT}\" ABSOLUTE\n    )\n    hunter_gate_status_debug(\"HUNTER_ROOT: ${HUNTER_GATE_ROOT}\")\n    if(NOT HUNTER_ALLOW_SPACES_IN_PATH)\n      string(FIND \"${HUNTER_GATE_ROOT}\" \" \" _contain_spaces)\n      if(NOT _contain_spaces EQUAL -1)\n        hunter_gate_fatal_error(\n            \"HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces.\"\n            \"Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error\"\n            \"(Use at your own risk!)\"\n            WIKI \"error.spaces.in.hunter.root\"\n        )\n      endif()\n    endif()\n\n    string(\n        REGEX\n        MATCH\n        \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+[-_a-z0-9]*\"\n        HUNTER_GATE_VERSION\n        \"${HUNTER_GATE_URL}\"\n    )\n    string(COMPARE EQUAL \"${HUNTER_GATE_VERSION}\" \"\" _is_empty)\n    if(_is_empty)\n      set(HUNTER_GATE_VERSION \"unknown\")\n    endif()\n\n    hunter_gate_self(\n        \"${HUNTER_GATE_ROOT}\"\n        \"${HUNTER_GATE_VERSION}\"\n        \"${HUNTER_GATE_SHA1}\"\n        hunter_self_\n    )\n\n    set(_master_location \"${hunter_self_}/cmake/Hunter\")\n    if(EXISTS \"${HUNTER_GATE_ROOT}/cmake/Hunter\")\n      # Hunter downloaded manually (e.g. by 'git clone')\n      set(_unused \"xxxxxxxxxx\")\n      set(HUNTER_GATE_SHA1 \"${_unused}\")\n      set(HUNTER_GATE_VERSION \"${_unused}\")\n    else()\n      get_filename_component(_archive_id_location \"${hunter_self_}/..\" ABSOLUTE)\n      set(_done_location \"${_archive_id_location}/DONE\")\n      set(_sha1_location \"${_archive_id_location}/SHA1\")\n\n      # Check Hunter already downloaded by HunterGate\n      if(NOT EXISTS \"${_done_location}\")\n        hunter_gate_download(\"${_archive_id_location}\")\n      endif()\n\n      if(NOT EXISTS \"${_done_location}\")\n        hunter_gate_internal_error(\"hunter_gate_download failed\")\n      endif()\n\n      if(NOT EXISTS \"${_sha1_location}\")\n        hunter_gate_internal_error(\"${_sha1_location} not found\")\n      endif()\n      file(READ \"${_sha1_location}\" _sha1_value)\n      string(COMPARE EQUAL \"${_sha1_value}\" \"${HUNTER_GATE_SHA1}\" _is_equal)\n      if(NOT _is_equal)\n        hunter_gate_internal_error(\n            \"Short SHA1 collision:\"\n            \"  ${_sha1_value} (from ${_sha1_location})\"\n            \"  ${HUNTER_GATE_SHA1} (HunterGate)\"\n        )\n      endif()\n      if(NOT EXISTS \"${_master_location}\")\n        hunter_gate_user_error(\n            \"Master file not found:\"\n            \"  ${_master_location}\"\n            \"try to update Hunter/HunterGate\"\n        )\n      endif()\n    endif()\n    include(\"${_master_location}\")\n    set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)\n  endif()\nendmacro()\n"
  },
  {
    "path": "cpack_wix_patch.xml",
    "content": "<CPackWiXPatch>\n  <!-- When #PRODUCT is supported (CMake 3.3?) we can yse this to remove the\n       license page -->\n  <!-- <CPackWiXFragment Id=\"#PRODUCT\"> -->\n  <!--   <UI> -->\n  <!--     <UIRef Id=\"WixUI_InstallDir\" /> -->\n\n  <!--     <Publish Dialog=\"WelcomeDlg\" -->\n  <!--              Control=\"Next\" -->\n  <!--              Event=\"NewDialog\" -->\n  <!--              Value=\"InstallDirDlg\" -->\n  <!--              Order=\"2\">1</Publish> -->\n  <!--     <Publish Dialog=\"InstallDirDlg\" -->\n  <!--              Control=\"Back\" -->\n  <!--              Event=\"NewDialog\" -->\n  <!--              Value=\"WelcomeDlg\" -->\n  <!--              Order=\"2\">1</Publish> -->\n  <!--   </UI> -->\n  <!-- </CPackWiXFragment> -->\n\n  <CPackWiXFragment Id=\"CM_FP_shell_folder_com_dlld.dll\">\n\n    <TypeLib Id=\"B816A838-5022-11DC-9153-0090F5284F85\"\n\t     Description=\"Swish Type Library\" Language=\"0\" MajorVersion=\"0\"\n\t     MinorVersion=\"3\">\n      <AppId Description=\"Swish\"\n\t     Id=\"B816A838-5022-11DC-9153-0090F5284F85\">\n        <Class Id=\"B816A83A-5022-11DC-9153-0090F5284F85\"\n\t       Context=\"InprocServer32\" Description=\"Swish\"\n\t       ThreadingModel=\"apartment\">\n          <ProgId Id=\"Swish.HostFolder.1\" Description=\"CHostFolder Class\">\n            <ProgId Id=\"Swish.HostFolder\" Description=\"CHostFolder Class\" />\n          </ProgId>\n        </Class>\n        <Class Id=\"B816A83C-5022-11DC-9153-0090F5284F85\"\n\t       Context=\"InprocServer32\" Description=\"CRemoteFolder Class\"\n\t       ThreadingModel=\"apartment\">\n          <ProgId Id=\"Swish.RemoteFolder.1\" Description=\"CRemoteFolder Class\">\n            <ProgId Id=\"Swish.RemoteFolder\" Description=\"CRemoteFolder Class\" />\n          </ProgId>\n        </Class>\n      </AppId>\n    </TypeLib>\n\n  </CPackWiXFragment>\n\n  <CPackWiXFragment Id=\"CM_CP_shell_folder_com_dlld.dll\">\n\n    <RegistryValue Root=\"HKCR\" Key=\"AppID\\Swish.DLL\" Name=\"AppID\" Value=\"{b816a838-5022-11dc-9153-0090f5284f85}\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83a-5022-11dc-9153-0090f5284f85}\\DefaultIcon\" Value=\"shell32.dll,9\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83a-5022-11dc-9153-0090f5284f85}\\ShellFolder\" Name=\"Attributes\" Value=\"-1610612736\" Type=\"integer\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83a-5022-11dc-9153-0090f5284f85}\" Name=\"InfoTip\" Value=\"Remote file-system access via SFTP\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83a-5022-11dc-9153-0090f5284f85}\" Name=\"TileInfo\" Value=\"prop:{28636AA6-953D-11D2-B5D6-00C04FD918D0} 5;{b816a850-5022-11dc-9153-0090f5284f85} 2;{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 7\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83c-5022-11dc-9153-0090f5284f85}\\DefaultIcon\" Value=\"shell32.dll,9\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83c-5022-11dc-9153-0090f5284f85}\\ShellFolder\" Name=\"Attributes\" Value=\"-1610612736\" Type=\"integer\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83c-5022-11dc-9153-0090f5284f85}\" Name=\"InfoTip\" Value=\"Remote file-system access via SFTP\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"CLSID\\{b816a83c-5022-11dc-9153-0090f5284f85}\" Name=\"TileInfo\" Value=\"prop:{B725F130-47EF-101A-A5F1-02608C9EEBAC}, 12;{B725F130-47EF-101A-A5F1-02608C9EEBAC, 14}\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKCR\" Key=\"Interface\" Value=\"\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKLM\" Key=\"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\NameSpace\\{b816a83a-5022-11dc-9153-0090f5284f85}\" Value=\"Swish\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKLM\" Key=\"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MyComputer\\NameSpace\\{b816a83a-5022-11dc-9153-0090f5284f85}\" Name=\"Removal Message\" Value=\"Please don't remove Swish this way - uninstall it.\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKLM\" Key=\"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved\" Name=\"{b816a83a-5022-11dc-9153-0090f5284f85}\" Value=\"Swish HostFolder\" Type=\"string\" Action=\"write\" />\n    <RegistryValue Root=\"HKLM\" Key=\"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved\" Name=\"{b816a83c-5022-11dc-9153-0090f5284f85}\" Value=\"Swish SFTP Folder\" Type=\"string\" Action=\"write\" />\n\n  </CPackWiXFragment>\n</CPackWiXPatch>\n"
  },
  {
    "path": "ezel/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  detail/command_dispatch.hpp\n  detail/dialog_template.hpp\n  detail/hooks.hpp\n  detail/hwnd_linking.hpp\n  detail/message_dispatch.hpp\n  detail/window_impl.hpp\n  detail/window_link.hpp\n  detail/window_proc.hpp\n  detail/window_proxy.hpp\n  controls/button.hpp\n  controls/checkbox.hpp\n  controls/edit.hpp\n  controls/icon.hpp\n  controls/label.hpp\n  controls/line.hpp\n  controls/spinner.hpp\n  control.hpp\n  control_parent_impl.hpp\n  form.hpp\n  window.hpp)\n\nadd_custom_target(ezel-src SOURCES ${SOURCES})\nadd_library(ezel INTERFACE)\ntarget_include_directories(ezel INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})\ntarget_link_libraries(ezel INTERFACE ${Boost_LIBRARIES})\n"
  },
  {
    "path": "ezel/control.hpp",
    "content": "/**\n    @file\n\n    GUI control base.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROL_HPP\n#define EZEL_CONTROL_HPP\n#pragma once\n\n#include <ezel/window.hpp> // window\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nnamespace ezel {\n\nclass form;\n\n/**\n * Base-class for form control facades.\n *\n * All controls that can be added to forms are added as an instance of a\n * subclass of this this template. \n * This allows form to access the impl pointer but nothing else.\n *\n * @param T  Type of implementation class (pimpl)\n */\ntemplate<typename T>\nclass control : public window<T>\n{\npublic:\n\n    control(boost::shared_ptr<T> impl) : window<T>(impl) {}\n    virtual ~control() {}\n\nprotected:\n    \n    friend class form; // form need a p-impl from controls\n};\n\n} // namespace ezel\n\n#endif\n"
  },
  {
    "path": "ezel/control_parent_impl.hpp",
    "content": "/**\n    @file\n\n    Compound window parent.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROL_PARENT_IMPL_HPP\n#define EZEL_CONTROL_PARENT_IMPL_HPP\n#pragma once\n\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <washer/gui/messages.hpp> // message\n\n#include <cassert> // assert\n\nnamespace ezel {\nnamespace detail {\n\n/**\n * Parent of any window that receive WM_COMMAND message from one or more\n * children.\n */\nclass control_parent_impl : public window_impl\n{\npublic:\n    typedef window_impl super;\n\n    typedef message_map<WM_COMMAND> messages;\n\n    virtual LRESULT handle_message(\n        UINT message, WPARAM wparam, LPARAM lparam)\n    {\n        return dispatch_message(this, message, wparam, lparam);\n    }\n\n    /**\n     * What to do if this window is sent a command message by a child window.\n     *\n     * We reflect the command back to the control that sent it in case\n     * wants to react to it.\n     */\n    LRESULT on(message<WM_COMMAND> m)\n    {\n        window_impl* w = window_from_hwnd(m.control_hwnd());\n        assert(w != this);\n        \n        w->handle_command(m.command_code(), m.wparam(), m.lparam());\n        \n        return default_message_handler(m);\n    }\n\nprotected:\n\n    control_parent_impl(\n        const std::wstring& title, short left, short top, short width,\n        short height)\n        :\n        window_impl(title, left, top, width, height) {}\n};\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/controls/button.hpp",
    "content": "/**\n    @file\n\n    GUI button control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_BUTTON_HPP\n#define EZEL_CONTROLS_BUTTON_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/command_dispatch.hpp> // command_map\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <washer/gui/commands.hpp> // command\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/signal.hpp> // signal\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\nclass button_impl : public ezel::detail::window_impl\n{\npublic:\n    typedef ezel::detail::window_impl super;\n\n    typedef ezel::detail::command_map<BN_CLICKED> commands;\n\n    virtual void handle_command(\n        WORD command_id, WPARAM wparam, LPARAM lparam)\n    {\n        dispatch_command(this, command_id, wparam, lparam);\n    }\n\n    button_impl(\n        const std::wstring& title, short left, short top, short width,\n        short height, bool default)\n        :\n        ezel::detail::window_impl(title, left, top, width, height),\n        m_default(default) {}\n\n    std::wstring window_class() const { return L\"button\"; }\n\n    DWORD style() const\n    {\n        DWORD style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;\n        \n        style |= (m_default) ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;\n\n        return style;\n    }\n\n    boost::signal<void ()>& on_click() { return m_on_click; }\n\n    void on(command<BN_CLICKED>) { m_on_click(); }\n\nprivate:\n\n    boost::signal<void ()> m_on_click;\n    bool m_default;\n};\n\nclass button : public ezel::control<button_impl>\n{\npublic:\n    button(\n        const std::wstring& title, short left, short top, short width,\n        short height, bool default=false)\n        :\n        control<button_impl>(\n            boost::shared_ptr<button_impl>(\n                new button_impl(title, left, top, width, height, default))) {}\n\n    boost::signal<void ()>& on_click() { return impl()->on_click(); }\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/checkbox.hpp",
    "content": "/**\n    @file\n\n    GUI check-box control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_CHECKBOX_HPP\n#define EZEL_CONTROLS_CHECKBOX_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <boost/function.hpp> // function\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\ntypedef boost::function<void (void)> on_click_callback;\n\nclass checkbox_impl : public ezel::detail::window_impl\n{\npublic:\n    typedef ezel::detail::window_impl super;\n\n    typedef ezel::detail::command_map<BN_CLICKED> commands;\n\n    virtual void handle_command(\n        WORD command_id, WPARAM wparam, LPARAM lparam)\n    {\n        dispatch_command(this, command_id, wparam, lparam);\n    }\n\n    checkbox_impl(\n        const std::wstring& text, short left, short top, short width,\n        short height)\n        :\n        ezel::detail::window_impl(text, left, top, width, height)\n        {}\n\n    std::wstring window_class() const { return L\"button\"; }\n\n    DWORD style() const\n    {\n        return WS_CHILD | WS_VISIBLE | BS_CHECKBOX | WS_TABSTOP;\n    }\n\n    boost::signal<void ()>& on_click() { return m_on_click; }\n\n    void on(command<BN_CLICKED>) { m_on_click(); }\n\nprivate:\n\n    boost::signal<void ()> m_on_click;\n    bool m_default;\n};\n\nclass checkbox : public ezel::control<checkbox_impl>\n{\npublic:\n    checkbox(\n        const std::wstring& text, short left, short top, short width,\n        short height)\n        :\n        control<checkbox_impl>(\n            boost::shared_ptr<checkbox_impl>(\n                new checkbox_impl(text, left, top, width, height))) {}\n\n    std::wstring text() const { return impl()->text(); }\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/edit.hpp",
    "content": "/**\n    @file\n\n    GUI edit (text) control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_EDIT_HPP\n#define EZEL_CONTROLS_EDIT_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/signal.hpp> // signal\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\nclass edit_impl : public ezel::detail::window_impl\n{\npublic:\n    typedef ezel::detail::window_impl super;\n\n    typedef ezel::detail::command_map<EN_CHANGE, EN_UPDATE> commands;\n\n    virtual void handle_command(\n        WORD command_id, WPARAM wparam, LPARAM lparam)\n    {\n        dispatch_command(this, command_id, wparam, lparam);\n    }\n\n    edit_impl(\n        const std::wstring& text, short left, short top, short width,\n        short height, DWORD custom_style)\n        :\n        ezel::detail::window_impl(text, left, top, width, height),\n        m_custom_style(custom_style) {}\n\n    std::wstring window_class() const { return L\"Edit\"; }\n\n    DWORD style() const\n    {\n        DWORD style = ezel::detail::window_impl::style() |\n            WS_CHILD | ES_LEFT | WS_BORDER | ES_AUTOHSCROLL;\n        \n        style |= m_custom_style;\n\n        return style;\n    }\n\n    boost::signal<void ()>& on_change() { return m_on_change; }\n    boost::signal<void ()>& on_update() { return m_on_update; }\n\n    void on(command<EN_CHANGE>) { m_on_change(); }\n    void on(command<EN_UPDATE>) { m_on_update(); }\n\nprivate:\n    boost::signal<void ()> m_on_change;\n    boost::signal<void ()> m_on_update;\n    DWORD m_custom_style;\n};\n\nclass edit : public ezel::control<edit_impl>\n{\npublic:\n\n    struct style\n    {\n        enum value\n        {\n            default = 0,\n            password = ES_PASSWORD,\n            force_lowercase = ES_LOWERCASE,\n            only_allow_numbers = ES_NUMBER\n        };\n    };\n\n    edit(\n        const std::wstring& text, short left, short top, short width,\n        short height, style::value custom_style=style::default)\n        :\n        control<edit_impl>(\n            boost::shared_ptr<edit_impl>(\n                new edit_impl(\n                    text, left, top, width, height, custom_style))) {}\n\n    boost::signal<void ()>& on_change() { return impl()->on_change(); }\n    boost::signal<void ()>& on_update() { return impl()->on_update(); }\n    \n    boost::signal<void (const wchar_t*)>& on_text_change()\n    { return impl()->on_text_change(); }\n    boost::signal<void ()>& on_text_changed()\n    { return impl()->on_text_changed(); }\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/icon.hpp",
    "content": "/**\n    @file\n\n    GUI icon control.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_ICON_HPP\n#define EZEL_CONTROLS_ICON_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <washer/window/icon.hpp> // icon_window\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\nclass icon_impl : public ezel::detail::window_impl\n{\nprotected:\n    typedef ezel::detail::window_impl super;\n\npublic:\n\n    icon_impl(short left, short top, short width, short height)\n        :\n        ezel::detail::window_impl(L\"\", left, top, width, height),\n        m_icon(NULL) {}\n\n    std::wstring window_class() const { return L\"static\"; }\n    DWORD style() const\n    {\n        DWORD style = ezel::detail::window_impl::style() |\n            WS_CHILD | SS_ICON | WS_GROUP | SS_NOTIFY | SS_CENTERIMAGE;\n\n        return style;\n    }\n\n    HICON change_icon(HICON new_icon)\n    {\n        if (!is_active())\n        {\n            HICON previous = m_icon;\n            m_icon = new_icon;\n            return previous;\n        }\n        else\n        {\n            washer::window::icon_window<wchar_t> icon(\n                washer::window::window_handle::foster_handle(hwnd()));\n            return icon.change_icon(new_icon);\n        }\n    }\n\nprotected:\n\n    /**\n     * Set the source of the icon to whatever the user set via change_icon.\n     */\n    virtual void push()\n    {\n        super::push();\n\n        washer::send_message<wchar_t>(\n            hwnd(), STM_SETIMAGE, IMAGE_ICON, m_icon);\n    }\n\nprivate:\n    HICON m_icon;\n};\n\nclass icon : public ezel::control<icon_impl>\n{\npublic:\n\n    struct style\n    {\n        enum value\n        {\n            default = 0,\n            ampersand_not_special = SS_NOPREFIX\n        };\n    };\n\n    icon(short left, short top, short width, short height)\n        :\n        control<icon_impl>(\n            boost::shared_ptr<icon_impl>(\n                new icon_impl(left, top, width, height))) {}\n\n    HICON change_icon(HICON new_icon) { return impl()->change_icon(new_icon); }\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/label.hpp",
    "content": "/**\n    @file\n\n    GUI label (static text) control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_LABEL_HPP\n#define EZEL_CONTROLS_LABEL_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\nclass label_impl : public ezel::detail::window_impl\n{\npublic:\n\n    label_impl(\n        const std::wstring& text, short left, short top, short width,\n        short height, DWORD custom_style)\n        :\n        ezel::detail::window_impl(text, left, top, width, height),\n        m_custom_style(custom_style) {}\n\n    std::wstring window_class() const { return L\"static\"; }\n    DWORD style() const\n    {\n        DWORD style = ezel::detail::window_impl::style() |\n            WS_CHILD | SS_LEFT | WS_GROUP | SS_NOTIFY;\n        \n        style &= ~WS_TABSTOP;\n        style |= m_custom_style;\n\n        return style;\n    }\n\nprivate:\n    DWORD m_custom_style;\n};\n\nclass label : public ezel::control<label_impl>\n{\npublic:\n\n    struct style\n    {\n        enum value\n        {\n            default = 0,\n            ampersand_not_special = SS_NOPREFIX\n        };\n    };\n\n    label(\n        const std::wstring& text, short left, short top, short width,\n        short height, style::value custom_style=style::default)\n        :\n        control<label_impl>(\n            boost::shared_ptr<label_impl>(\n                new label_impl(\n                    text, left, top, width, height, custom_style))) {}\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/line.hpp",
    "content": "/**\n    @file\n\n    GUI horizontal line control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_LINE_HPP\n#define EZEL_CONTROLS_LINE_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nnamespace ezel {\nnamespace controls {\n\nclass line_impl : public ezel::detail::window_impl\n{\npublic:\n\n    line_impl(short left, short top, short width)\n        :\n        ezel::detail::window_impl(L\"\", left, top, width, 1) {}\n\n    std::wstring window_class() const { return L\"static\"; }\n    DWORD style() const\n    {\n        return WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ | WS_GROUP | SS_NOTIFY;\n    }\n};\n\nclass line : public ezel::control<line_impl>\n{\npublic:\n    line(\n        short left, short top, short width)\n        :\n        control<line_impl>(\n            boost::shared_ptr<line_impl>(new line_impl(left, top, width)))\n        {}\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/controls/spinner.hpp",
    "content": "/**\n    @file\n\n    GUI spinner control.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_CONTROLS_SPINNER_HPP\n#define EZEL_CONTROLS_SPINNER_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control base class\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <washer/message.hpp> // send_message\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/cstdint.hpp> // int32_t\n\n#include <string>\n\n#include <Commctrl.h> // UDS_*, UDM_*\n\nnamespace ezel {\nnamespace controls {\n\nclass spinner_impl : public ezel::detail::window_impl\n{\nprotected:\n    typedef ezel::detail::window_impl super;\n\npublic:\n\n    spinner_impl(\n        short left, short top, short width, short height,\n        boost::int32_t minimum, boost::int32_t maximum,\n        boost::int32_t initial_value, DWORD custom_style)\n        :\n        ezel::detail::window_impl(L\"\", left, top, width, height),\n        m_min(minimum), m_max(maximum), m_value(initial_value),\n        m_custom_style(custom_style) {}\n\n    std::wstring window_class() const { return L\"msctls_updown32\"; }\n    DWORD style() const\n    {\n        DWORD style = ezel::detail::window_impl::style() |\n            WS_CHILD | UDS_ARROWKEYS | UDS_SETBUDDYINT | UDS_AUTOBUDDY;\n        style |= m_custom_style;\n\n        return style;\n    }\n\n    void range(boost::int32_t minimum, boost::int32_t maximum)\n    {\n        if (!is_active())\n        {\n            m_min = minimum;\n            m_max = maximum;\n        }\n        else\n        {\n            washer::send_message<wchar_t>(\n                hwnd(), UDM_SETRANGE32, minimum, maximum);\n        }\n    }\n\n    boost::int32_t value(boost::int32_t v)\n    {\n        if (!is_active())\n            m_value = v;\n        else\n            return washer::send_message_return<wchar_t, boost::int32_t>(\n                hwnd(), UDM_SETPOS32, 0, v);\n    }\n\nprotected:\n\n    /**\n     * Set the initial range of the spinner.\n     */\n    virtual void push()\n    {\n        super::push();\n\n        washer::send_message<wchar_t>(hwnd(), UDM_SETRANGE32, m_min, m_max);\n        washer::send_message<wchar_t>(hwnd(), UDM_SETPOS32, 0, m_value);\n    }\n\nprivate:\n\n    boost::int32_t m_min;\n    boost::int32_t m_max;\n    boost::int32_t m_value;\n    \n    DWORD m_custom_style;\n};\n\nclass spinner : public ezel::control<spinner_impl>\n{\npublic:\n\n    struct style\n    {\n        enum value\n        {\n            default = 0,\n            no_thousand_separator = UDS_NOTHOUSANDS,\n            wrap_sequence = UDS_WRAP\n        };\n    };\n\n    spinner(\n        short left, short top, short width, short height,\n        boost::int32_t minimum, boost::int32_t maximum,\n        boost::int32_t initial_value,\n        style::value custom_style=style::default)\n        :\n        control<spinner_impl>(\n            boost::shared_ptr<spinner_impl>(\n                new spinner_impl(\n                left, top, width, height, minimum, maximum, initial_value,\n                custom_style)))\n        {}\n\n    void range(boost::int32_t minimum, boost::int32_t maximum)\n    { impl()->range(minimum, maximum); }\n\n    boost::int32_t value(boost::int32_t new_value)\n    { return impl()->value(new_value); }\n\n    short left() const { return impl()->left(); }\n    short top() const { return impl()->top(); }\n    short width() const { return impl()->width(); }\n    short height() const { return impl()->height(); }\n};\n\n}} // namespace ezel::controls\n\n#endif\n"
  },
  {
    "path": "ezel/detail/command_dispatch.hpp",
    "content": "/**\n    @file\n\n    Command message dispatch.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_COMMAND_DISPATCH_HPP\n#define EZEL_COMMAND_DISPATCH_HPP\n#pragma once\n\n#include <washer/gui/commands.hpp> // command\n\n#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE\n#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS\n#endif\n\n#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE\n#define BOOST_MPL_LIMIT_VECTOR_SIZE 50\n#endif\n\n#include <boost/mpl/begin_end.hpp> // begin, end\n#include <boost/mpl/copy_if.hpp> // copy_if\n#include <boost/mpl/not_equal_to.hpp> // not_equal_to\n#include <boost/mpl/vector_c.hpp> // vector_c\n#include <boost/type_traits/is_same.hpp> // is_same\n\n#define EZEL_COMMAND_MAP_TEMPLATE \\\n    int EZC0=-1, int EZC1=-1, int EZC2=-1, int EZC3=-1, int EZC4=-1, \\\n    int EZC5=-1, int EZC6=-1, int EZC7=-1, int EZC8=-1, int EZC9=-1, \\\n    int EZC10=-1, int EZC11=-1, int EZC12=-1, int EZC13=-1, int EZC14=-1, \\\n    int EZC15=-1, int EZC16=-1, int EZC17=-1, int EZC18=-1, int EZC19=-1, \\\n    int EZC20=-1, int EZC21=-1, int EZC22=-1, int EZC23=-1, int EZC24=-1, \\\n    int EZC25=-1, int EZC26=-1, int EZC27=-1, int EZC28=-1, int EZC29=-1, \\\n    int EZC30=-1, int EZC31=-1, int EZC32=-1, int EZC33=-1, int EZC34=-1, \\\n    int EZC35=-1, int EZC36=-1, int EZC37=-1, int EZC38=-1, int EZC39=-1, \\\n    int EZC40=-1, int EZC41=-1, int EZC42=-1, int EZC43=-1, int EZC44=-1, \\\n    int EZC45=-1, int EZC46=-1, int EZC47=-1, int EZC48=-1, int EZC49=-1\n\n#define EZEL_COMMAND_ARG \\\n    EZC0, EZC1, EZC2, EZC3, EZC4, \\\n    EZC5, EZC6, EZC7, EZC8, EZC9, \\\n    EZC10, EZC11, EZC12, EZC13, EZC14, \\\n    EZC15, EZC16, EZC17, EZC18, EZC19, \\\n    EZC20, EZC21, EZC22, EZC23, EZC24, \\\n    EZC25, EZC26, EZC27, EZC28, EZC29, \\\n    EZC30, EZC31, EZC32, EZC33, EZC34, \\\n    EZC35, EZC36, EZC37, EZC38, EZC39, \\\n    EZC40, EZC41, EZC42, EZC43, EZC44, \\\n    EZC45, EZC46, EZC47, EZC48, EZC49\n\nnamespace ezel {\nnamespace detail {\n\ntemplate<typename T>\ninline void dispatch_command(T* obj, WORD id, WPARAM wparam, LPARAM lparam);\n\ntemplate<typename It, typename End, typename T>\ninline void dispatch_command_next(\n    T* obj, WORD /*id*/, WPARAM wparam, LPARAM lparam, boost::mpl::true_)\n{\n    obj->on(washer::gui::command_base(wparam, lparam));\n}\n\ntemplate<typename It, typename End, typename T>\ninline void dispatch_command_next(\n    T* obj, WORD id, WPARAM wparam, LPARAM lparam, boost::mpl::false_)\n{\n    dispatch_command<T::super>(obj, id, wparam, lparam);\n}\n\n/**\n * Dispatch conditionally based on whether we're at end of superclass chain.\n *\n * If we are, this command goes to the default command handler.  Otherwise,\n * we create a dispatch for the superclasses command map and delegate\n * dispatch there.\n */\ntemplate<typename It, typename End, typename T>\ninline void dispatch_command(\n    T* obj, WORD command_id, WPARAM wparam, LPARAM lparam, boost::mpl::true_)\n{\n    return dispatch_command_next<It, End>(\n        obj, command_id, wparam, lparam, boost::is_same<T, T::super>::type());\n}\n\ntemplate<typename It, typename End, typename T>\ninline void dispatch_command(\n    T* obj, WORD command_id, WPARAM wparam, LPARAM lparam, boost::mpl::false_)\n{\n    typedef boost::mpl::deref<It>::type Front;\n    typedef boost::mpl::next<It>::type Next;\n\n    if(command_id == Front::value)\n    {\n        return obj->on(command<Front::value>(wparam, lparam));\n    }\n    else\n    {\n        return dispatch_command<Next, End>(\n            obj, command_id, wparam, lparam,\n            typename boost::is_same<Next, End>::type());\n    }\n}\n\n/**\n * Command dispatcher.\n *\n * Messages are dispatched to the superclasses of T one at a time until one is\n * found whose command map contains the current command.  Then its handler\n * for that meassage is invoked.\n * If we reach the end of the chain without finding a matching map entry,\n * the command is delivered to the default command handler.\n */\ntemplate<typename T>\ninline void dispatch_command(\n    T* obj, WORD command_id, WPARAM wparam, LPARAM lparam)\n{\n    typedef boost::mpl::begin<T::commands::commands>::type Begin;\n    typedef boost::mpl::end<T::commands::commands>::type End;\n\n    dispatch_command<Begin, End>(\n        obj, command_id, wparam, lparam,\n        typename boost::is_same<Begin, End>::type());\n}\n\ntemplate<EZEL_COMMAND_MAP_TEMPLATE>\nclass command_map\n{\npublic:\n\n    // -1 is used to indicate an unused template parameter.  To\n    // prevent us having to treat -1 as a special case, we filter it out of\n    // the command map immediately here\n    typedef typename boost::mpl::copy_if<\n        boost::mpl::vector_c<int, EZEL_COMMAND_ARG>,\n        boost::mpl::not_equal_to< boost::mpl::_1, boost::mpl::int_<-1> >,\n        boost::mpl::back_inserter< boost::mpl::vector_c<WORD> >\n    >::type commands;\n};\n\n}} // namespace washer::gui\n\n#endif\n"
  },
  {
    "path": "ezel/detail/dialog_template.hpp",
    "content": "/**\n    @file\n\n    Windows dialog in-memory template helpers\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_DETAIL_DIALOG_TEMPLATE_HPP\n#define EZEL_DETAIL_DIALOG_TEMPLATE_HPP\n#pragma once\n\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/static_assert.hpp> // BOOST_STATIC_ASSERT\n\n#include <algorithm> // copy\n#include <string>\n#include <vector>\n\nnamespace ezel {\nnamespace detail {\n\n#pragma warning(push)\n#pragma warning(disable: 4996) // std::copy: Function call that may be unsafe\n\n// the calculations rely on pointer-arithmetic on wchar_t pointers\n// skipping 16-bit (WORD-sized) fields\nBOOST_STATIC_ASSERT(sizeof(wchar_t) == 2); // 16-bits\nBOOST_STATIC_ASSERT(sizeof(wchar_t) == sizeof(WORD));\n\n/**\n * If the pointer is already DWORD-aligned return it unchanged otherwise\n * advance to the next DWORD boundary.\n */\ntemplate<typename T>\ninline T* next_double_word(T* p)\n{\n    return reinterpret_cast<T*>((reinterpret_cast<size_t>(p) + 3) & ~3);\n}\n\n/**\n * If the pointer is already WORD-aligned return it unchanged otherwise\n * advance to the next WORD boundary.\n */\ntemplate<typename T>\ninline T* next_word(T* p)\n{\n    return reinterpret_cast<T*>((reinterpret_cast<size_t>(p) + 1) & ~1);\n}\n\n/**\n * Calculate the necessary buffer size for a dialog template.\n *\n * Apparently all the fields are naturally WORD-aligned so we don't need to \n * force it explicitly\n * (@see http://msdn.microsoft.com/en-us/library/ms644996%28VS.85%29.aspx in\n * the user contrib comments) but we do so anyway just to be on the safe side.\n */\ninline size_t calculate_template_size(\n    const std::wstring& title, const std::wstring& font)\n{\n    DLGTEMPLATE* dlg = 0;\n                              // template\n    wchar_t* pos = reinterpret_cast<wchar_t*>(dlg + 1);\n\n    pos = next_word(pos) + 1; // menu (1)\n    pos = next_word(pos) + 1; // window class (1)\n\n                              // title (?) + terminator (1)\n    wchar_t* title_array = next_word(pos);\n    title_array = title_array + title.size() + 1;\n\n                              // fontsize (1)\n    pos = next_word(title_array);\n    pos = pos + 1;\n\n                              // font (?) + terminator (1)\n    wchar_t* font_array = next_word(pos);\n    font_array = next_word(font_array) + font.size() + 1;\n\n                              // padding\n    return reinterpret_cast<size_t>(next_double_word(font_array));\n}\n\n/**\n * Write the DLGTEMPLATE part of a Dialog template.\n *\n * Assumes the buffer is big enough to fit the data.  Use\n * calculate_template_size to calculate the nescessary size.\n */\ninline DLGITEMTEMPLATE* write_template_to_buffer(\n    const std::wstring& title, short font_size, const std::wstring& font,\n    short left, short top, short width, short height, size_t control_count,\n    DLGTEMPLATE* dlg)\n{\n    // dlg buffer must already be DWORD aligned\n    assert((((int)dlg) % sizeof(DWORD)) == 0);\n    \n    dlg->style = DS_SETFONT | WS_VISIBLE | WS_POPUPWINDOW | DS_MODALFRAME;\n    if (!title.empty())\n        dlg->style |= WS_CAPTION;\n\n    dlg->cx = width;\n    dlg->cy = height;\n    dlg->x = left;\n    dlg->y = top;\n\n    dlg->cdit = boost::numeric_cast<WORD>(control_count);\n\n    wchar_t* pos = reinterpret_cast<wchar_t*>(dlg + 1);\n    pos = next_word(pos);\n    *pos++ = 0; // no menu\n\n    pos = next_word(pos);\n    *pos++ = 0; // default dialog window class\n\n    // caption\n    wchar_t* title_array = next_word(pos);\n    title_array = std::copy(title.begin(), title.end(), title_array);\n    *title_array++ = 0; // null-terminate\n\n    // font size\n    pos = next_word(title_array);\n    *pos++ = font_size;\n\n    // font name\n    wchar_t* font_array = next_word(pos);\n    font_array = std::copy(font.begin(), font.end(), font_array);\n    *font_array++ = 0; // null-terminate\n\n    return reinterpret_cast<DLGITEMTEMPLATE*>(next_double_word(font_array));\n}\n\n/**\n * Calculate the necessary buffer size for an item template.\n *\n * Apparently all the fields are naturally WORD-aligned so we don't need to \n * force it explicitly\n * (@see http://msdn.microsoft.com/en-us/library/ms644996%28VS.85%29.aspx in\n * the user contrib comments) but we do so anyway just to be on the safe side.\n *\n * After the custom data field we have to add en extra WORD of buffer *not*\n * including any extra needed for DWORD-alignment.  This does not match the\n * MSDN documentation but is required.\n */\ntemplate<typename Customdata>\ninline size_t calculate_control_template_size(\n    const std::wstring& window_class, const std::wstring& title,\n    size_t current_buffer_size)\n{\n                              // item template\n    DLGITEMTEMPLATE* item =\n        reinterpret_cast<DLGITEMTEMPLATE*>(current_buffer_size);\n    item = next_double_word(item) + 1;\n\n                              // class (?) + terminator (1)\n    wchar_t* class_array = reinterpret_cast<wchar_t*>(item);\n    class_array = next_word(class_array) + window_class.size() + 1;\n\n                              // title (?) + terminator (1)\n    wchar_t* title_array = class_array + 1;\n    title_array = next_word(title_array) + title.size() + 1;\n\n                     // custom data size (1) + custom data (?) + extra WORD (1)\n    wchar_t* custom_data_size = title_array + 1;\n    custom_data_size = next_word(custom_data_size);\n    Customdata* data = reinterpret_cast<Customdata*>(custom_data_size + 1);\n    data++;\n    wchar_t* mystery_extra = reinterpret_cast<wchar_t*>(data);\n    mystery_extra++;\n\n    return reinterpret_cast<size_t>(next_double_word(mystery_extra));\n}\n\n/**\n * Write a DLGITEMTEMPLATE entry for a dialog control.\n *\n * Assumes the buffer is big enough to fit the data.  Use\n * calculate_item_template_size to calculate the nescessary size.\n */\ntemplate<typename CustomData>\ninline DLGITEMTEMPLATE* write_control_to_buffer(\n    const std::wstring& window_class, const std::wstring& title,\n    unsigned short id, DWORD style,\n    short width, short height, short left, short top, \n    const CustomData& custom_data, DLGITEMTEMPLATE* buffer)\n{\n    DLGITEMTEMPLATE* item = next_double_word(buffer);\n\n    item->style = style;\n\n    item->id = id;\n\n    item->cx = width;\n    item->cy = height;\n    item->x = left;\n    item->y = top;\n\n    item = item + 1; // skip over template\n\n    // control window class name\n    wchar_t* class_array = reinterpret_cast<wchar_t*>(next_word(item));\n    class_array = std::copy(\n        window_class.begin(), window_class.end(), class_array);\n    *class_array++ = 0; // null-terminate\n\n    // title\n    wchar_t* title_array = next_word(class_array);\n    title_array = std::copy(title.begin(), title.end(), title_array);\n    *title_array++ = 0; // null-terminate\n\n    // custom data size must include its own memory in the size value\n    wchar_t* custom_data_size = next_word(title_array);\n    *custom_data_size++ = sizeof(wchar_t) + sizeof(CustomData);\n\n    CustomData* data = reinterpret_cast<CustomData*>(custom_data_size);\n    *data++ = custom_data;\n\n    wchar_t* mystery_extra = reinterpret_cast<wchar_t*>(data);\n    mystery_extra++;\n\n    return reinterpret_cast<DLGITEMTEMPLATE*>(next_double_word(mystery_extra));\n}\n\n/**\n * The required buffer size to store the window as a control in a\n * dialog template.\n */\ninline size_t increment_required_buffer_size(\n    const window_impl* w, size_t current_buffer_size)\n{\n    return calculate_control_template_size<window_impl*>(\n        w->window_class(), w->text(), current_buffer_size);\n}\n\n/**\n * Write the window to a byte buffer as a control in a dialog template.\n */\ninline DLGITEMTEMPLATE* to_buffer(\n    window_impl* w, unsigned short id, DLGITEMTEMPLATE* buffer)\n{\n    return write_control_to_buffer(\n        w->window_class(), w->text(), id, w->style(),\n        w->width(), w->height(), w->left(), w->top(),\n        w, buffer);\n}\n\nconst unsigned short BUTTON_ID_OFFSET = 100;\n\n/**\n * Build a dialog resource template in memory.\n */\ninline std::vector<unsigned char> build_dialog_template_in_memory(\n    const std::wstring& font, short font_size, const std::wstring& title,\n    short width, short height, short left, short top,\n    const std::vector<boost::shared_ptr<window_impl> >& controls)\n{\n    size_t buffer_len = calculate_template_size(title, font);\n    BOOST_FOREACH(boost::shared_ptr<window_impl> w, controls)\n    {\n        buffer_len = increment_required_buffer_size(w.get(), buffer_len);\n    }\n\n    std::vector<unsigned char> buffer(buffer_len, 0);\n\n    DLGITEMTEMPLATE* pos = write_template_to_buffer(\n        title, font_size, font, width, height, left, top,\n        controls.size(), reinterpret_cast<DLGTEMPLATE*>(&buffer[0]));\n\n    for (size_t i = 0; i < controls.size(); ++i)\n    {\n        window_impl* w = controls[i].get();\n\n        // offset ID to avoid collision with dialog manager's \n        // 'special' button IDs\n        unsigned short id = boost::numeric_cast<unsigned short>(\n            i + BUTTON_ID_OFFSET);\n\n        pos = to_buffer(\n            w, id, reinterpret_cast<DLGITEMTEMPLATE*>(pos));\n    }\n\n    return buffer;\n}\n\n#pragma warning(pop)\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/hooks.hpp",
    "content": "/**\n    @file\n\n    Window creation hooks\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_HOOKS_HPP\n#define EZEL_HOOKS_HPP\n#pragma once\n\n#include <ezel/detail/window_impl.hpp> // window_impl\n\n#include <washer/hook.hpp> // windows_hook\n\n#include <iostream> // cerr\n\n#include <Winuser.h> // CallNextHookEx\n\nnamespace ezel {\nnamespace detail {\n\nnamespace native {\n\n    /// @name CREATESTRUCT\n    // @{\n    template<typename T> struct create_struct;\n\n    template<> struct create_struct<char>\n    { typedef CREATESTRUCTA type; };\n\n    template<> struct create_struct<wchar_t>\n    { typedef CREATESTRUCTW type; };\n    // @}\n\n    /// @name CBT_CREATEWND\n    // @{\n    template<typename T> struct cbt_createwnd;\n\n    template<> struct cbt_createwnd<char>\n    { typedef CBT_CREATEWNDA type; };\n\n    template<> struct cbt_createwnd<wchar_t>\n    { typedef CBT_CREATEWNDW type; };\n    // @}\n\n}\n\ntemplate<typename T>\ninline void handle_create(\n    HWND hwnd, HWND /*insert_after*/,\n    typename native::create_struct<T>::type& create_info)\n{\n    UNALIGNED wchar_t* data =\n        static_cast<UNALIGNED wchar_t*>(create_info.lpCreateParams);\n\n    if (data)\n    {\n        assert(*data == sizeof(wchar_t) + sizeof(window_impl*));\n        data++; // skip size\n\n        window_impl* w = *reinterpret_cast<window_impl**>(data);\n        w->attach(hwnd);\n    }\n}\n\ntemplate<typename T>\ninline void handle_destroy(HWND hwnd)\n{\n    window_impl* this_window =\n        fetch_user_window_data<T, window_impl*>(hwnd);\n    this_window->detach();\n}\n\n/**\n * Hooking procedure called by Windows any time a GUI event happens.\n *\n * This function captures window creation and establishes a two-way link\n * between the Win32 window object and the C++ wrapper object.\n *\n * @todo  Pass *this* hook as first param for Windows 9x.\n */\ntemplate<typename T>\ninline LRESULT CALLBACK cbt_hook_function(\n    int code, WPARAM wparam, LPARAM lparam)\n{\n    try\n    {\n        if (code == HCBT_CREATEWND)\n        {\n            HWND hwnd = reinterpret_cast<HWND>(wparam);\n\n            typedef typename native::cbt_createwnd<T>::type* cbt_param;\n\n            cbt_param cbt_info = reinterpret_cast<cbt_param>(lparam);\n\n            handle_create<T>(\n                hwnd, cbt_info->hwndInsertAfter, *(cbt_info->lpcs));\n        }\n        else if (code == HCBT_DESTROYWND)\n        {\n            //handle_destroy<T>(reinterpret_cast<HWND>(wparam));\n        }\n    }\n    catch (std::exception& e)\n    {\n        std::cerr << e.what();\n    }\n\n    // TODO: pass *this* hook as first param for Windows 9x\n    LRESULT rc = ::CallNextHookEx(NULL, code, wparam, lparam);\n    return rc;\n}\n\n/**\n * Sets up and tears down window event hooks.\n *\n * Declare one static instance of this class in order to register window\n * creation.\n */\ntemplate<typename T>\nclass creation_hooks\n{\npublic:\n    creation_hooks()\n        :\n        m_cbt_hook(washer::windows_hook(WH_CBT, &cbt_hook_function<T>)) {}\n\nprivate:\n    washer::hhook m_cbt_hook;\n};\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/hwnd_linking.hpp",
    "content": "/**\n    @file\n\n    Low-level HWND manipulation.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_DETAIL_WINDOW_HPP\n#define EZEL_DETAIL_WINDOW_HPP\n#pragma once\n\n#include <washer/error.hpp> // last_error\n#include <washer/gui/hwnd.hpp> // set_window_field, window_field\n\nnamespace ezel {\nnamespace detail {\n\n/**\n * Store a value in the GWLP_USERDATA segment of the window descriptor.\n *\n * The value type must be no bigger than a LONG_PTR.\n */\ntemplate<typename T, typename U>\ninline void store_user_window_data(HWND hwnd, const U& data)\n{\n    washer::gui::set_window_field<T>(hwnd, GWLP_USERDATA, data);\n}\n\n/**\n * Store a value in the DWLP_USER segment of the window descriptor.\n *\n * The value type must be no bigger than a LONG_PTR.\n */\ntemplate<typename T, typename U>\ninline void store_dialog_window_data(HWND hwnd, const U& data)\n{\n    washer::gui::set_window_field<T>(hwnd, DWLP_USER, data);\n}\n\n/**\n * Get a value previously stored in the GWLP_USERDATA segment of the\n * window descriptor.\n *\n * The value type must be no bigger than a LONG_PTR.\n *\n * @throws system_error if there is an error or if no value has yet been\n *         stored.\n * @note  Storing 0 will count as not having a previous value so will\n *        throw an exception.\n */\ntemplate<typename T, typename U>\ninline U fetch_user_window_data(HWND hwnd)\n{\n    return washer::gui::window_field<T, U>(hwnd, GWLP_USERDATA);\n}\n\n/**\n * Get a value previously stored in the DWLP_USER segment of the\n * window descriptor.\n *\n * The value type must be no bigger than a LONG_PTR.\n *\n * @throws system_error if there is an error or if no value has yet been\n *         stored.\n * @note  Storing 0 will count as not having a previous value so will\n *        throw an exception.\n */\ntemplate<typename T, typename U>\ninline U fetch_dialog_window_data(HWND hwnd)\n{\n    return washer::gui::window_field<T, U>(hwnd, DWLP_USER);\n}\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/message_dispatch.hpp",
    "content": "/**\n    @file\n\n    Message dispatch.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_MESSAGE_DISPATCH_HPP\n#define EZEL_MESSAGE_DISPATCH_HPP\n#pragma once\n\n#include <washer/gui/messages.hpp> // message\n\n#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE\n#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS\n#endif\n\n#ifndef BOOST_MPL_LIMIT_VECTOR_SIZE\n#define BOOST_MPL_LIMIT_VECTOR_SIZE 50\n#endif\n\n#include <boost/mpl/begin_end.hpp> // begin, end\n#include <boost/mpl/copy_if.hpp> // copy_if\n#include <boost/mpl/not_equal_to.hpp> // not_equal_to\n#include <boost/mpl/vector_c.hpp> // vector_c\n#include <boost/type_traits/is_same.hpp> // is_same\n\n#define EZEL_MESSAGE_MAP_TEMPLATE \\\n    UINT EZM0=0, UINT EZM1=0, UINT EZM2=0, UINT EZM3=0, UINT EZM4=0, \\\n    UINT EZM5=0, UINT EZM6=0, UINT EZM7=0, UINT EZM8=0, UINT EZM9=0, \\\n    UINT EZM10=0, UINT EZM11=0, UINT EZM12=0, UINT EZM13=0, UINT EZM14=0, \\\n    UINT EZM15=0, UINT EZM16=0, UINT EZM17=0, UINT EZM18=0, UINT EZM19=0, \\\n    UINT EZM20=0, UINT EZM21=0, UINT EZM22=0, UINT EZM23=0, UINT EZM24=0, \\\n    UINT EZM25=0, UINT EZM26=0, UINT EZM27=0, UINT EZM28=0, UINT EZM29=0, \\\n    UINT EZM30=0, UINT EZM31=0, UINT EZM32=0, UINT EZM33=0, UINT EZM34=0, \\\n    UINT EZM35=0, UINT EZM36=0, UINT EZM37=0, UINT EZM38=0, UINT EZM39=0, \\\n    UINT EZM40=0, UINT EZM41=0, UINT EZM42=0, UINT EZM43=0, UINT EZM44=0, \\\n    UINT EZM45=0, UINT EZM46=0, UINT EZM47=0, UINT EZM48=0, UINT EZM49=0\n\n#define EZEL_MESSAGE_ARG \\\n    EZM0, EZM1, EZM2, EZM3, EZM4, \\\n    EZM5, EZM6, EZM7, EZM8, EZM9, \\\n    EZM10, EZM11, EZM12, EZM13, EZM14, \\\n    EZM15, EZM16, EZM17, EZM18, EZM19, \\\n    EZM20, EZM21, EZM22, EZM23, EZM24, \\\n    EZM25, EZM26, EZM27, EZM28, EZM29, \\\n    EZM30, EZM31, EZM32, EZM33, EZM34, \\\n    EZM35, EZM36, EZM37, EZM38, EZM39, \\\n    EZM40, EZM41, EZM42, EZM43, EZM44, \\\n    EZM45, EZM46, EZM47, EZM48, EZM49\n\nnamespace ezel {\nnamespace detail {\n\ntemplate<typename T>\ninline LRESULT dispatch_message_next(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam);\n\ntemplate<typename It, typename End, typename T>\ninline LRESULT dispatch_message_next(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam, boost::mpl::true_)\n{\n    return obj->default_message_handler(message_id, wparam, lparam);\n}\n\ntemplate<typename It, typename End, typename T>\ninline LRESULT dispatch_message_next(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam, boost::mpl::false_)\n{\n    return dispatch_message<T::super>(obj, message_id, wparam, lparam);\n}\n\n/**\n * Dispatch conditionally based on whether we're at end of superclass chain.\n *\n * If we are, this message goes to the default message handler.  Otherwise,\n * we create a dispatch for the superclasses message map and delegate\n * dispatch there.\n */\ntemplate<typename It, typename End, typename T>\ninline LRESULT dispatch_message(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam, boost::mpl::true_)\n{\n    return dispatch_message_next<It, End>(\n        obj, message_id, wparam, lparam, boost::is_same<T, T::super>::type());\n}\n\ntemplate<typename It, typename End, typename T>\ninline LRESULT dispatch_message(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam, boost::mpl::false_)\n{\n    typedef boost::mpl::deref<It>::type Front;\n    typedef boost::mpl::next<It>::type Next;\n\n    if(message_id == Front::value)\n    {\n        return obj->on(message<Front::value>(wparam, lparam));\n    }\n    else\n    {\n        return dispatch_message<Next, End>(\n            obj, message_id, wparam, lparam,\n            typename boost::is_same<Next, End>::type());\n    }\n}\n\n/**\n * Main message handler.\n *\n * Messages are dispatched to the superclasses of T one at a time until one is\n * found whose message map contains the current message.  Then its handler for\n * that meassage is invoked.  If we reach the end of the chain without finding\n * a matching map entry, the message is delivered to the default message\n * handler.\n */\ntemplate<typename T>\ninline LRESULT dispatch_message(\n    T* obj, UINT message_id, WPARAM wparam, LPARAM lparam)\n{\n    typedef boost::mpl::begin<T::messages::messages>::type Begin;\n    typedef boost::mpl::end<T::messages::messages>::type End;\n\n    return dispatch_message<Begin, End>(\n        obj, message_id, wparam, lparam,\n        typename boost::is_same<Begin, End>::type());\n}\n\ntemplate<EZEL_MESSAGE_MAP_TEMPLATE>\nclass message_map\n{\npublic:\n\n    // Zero (0) is used to indicate an unused template parameter.  To\n    // prevent us having to treat 0 as a special case, we filter it out of\n    // the message map immediately here\n    typedef typename boost::mpl::copy_if<\n        boost::mpl::vector_c<UINT, EZEL_MESSAGE_ARG>,\n        boost::mpl::not_equal_to< boost::mpl::_1, boost::mpl::int_<0> >,\n        boost::mpl::back_inserter< boost::mpl::vector_c<UINT> >\n    >::type messages;\n};\n\n}} // namespace washer::gui\n\n#endif\n"
  },
  {
    "path": "ezel/detail/window_impl.hpp",
    "content": "/**\n    @file\n\n    HWND wrapper implementation.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_DETAIL_WINDOW_IMPL_HPP\n#define EZEL_DETAIL_WINDOW_IMPL_HPP\n#pragma once\n\n#include <ezel/detail/command_dispatch.hpp> // command_map\n#include <ezel/detail/message_dispatch.hpp> // message_map\n#include <ezel/detail/window_link.hpp> // window_link\n#include <ezel/detail/window_proc.hpp> // window_proc_base, window_proc\n#include <ezel/detail/window_proxy.hpp> // window_proxy\n\n#include <washer/gui/messages.hpp> // message\n#include <washer/gui/commands.hpp> // command\n#include <washer/trace.hpp> // trace\n#include <washer/window/window.hpp>\n\n#include <boost/exception/diagnostic_information.hpp> // diagnostic_information\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/noncopyable.hpp> // noncopyable\n#include <boost/signal.hpp> // signal\n\n#include <cassert> // assert\n#include <string>\n\nnamespace ezel {\n    \n/**\n * We are EXPLICITLY bringing the washer::gui::message/command classes into\n * the ezel namespace.\n */\nusing washer::gui::message;\nusing washer::gui::command;\n\nnamespace detail {\n\nvoid catch_form_creation(HWND hwnd, unsigned int msg, LPARAM lparam);\nvoid catch_form_destruction(HWND hwnd, unsigned int msg);\n\nLRESULT CALLBACK window_impl_proc(\n    HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);\n\nclass window_impl;\n\n/**\n * Fetch C++ wrapper pointer embedded in WND data.\n */\ninline window_impl* window_from_hwnd(HWND hwnd)\n{\n    return fetch_user_window_data<wchar_t, window_impl*>(hwnd);\n}\n\n/**\n * Interface for window wrappers used internally by window_impl.\n *\n * There are two implementations of this interface.  One wraps a real Win32\n * window which window_impl uses once it's been attached to an HWND.  The\n * other only simulates a window to holds the properties that a window_impl\n * in initialised with as well as to reflect any property changes made before\n * the real window is created.\n */\ntemplate<typename T>\nclass internal_window\n{\npublic:\n    virtual std::basic_string<T> text() const = 0;\n    virtual void text(const std::basic_string<T>& new_text) = 0;\n\n    virtual bool is_visible() = 0;\n    virtual bool is_enabled() = 0;\n    virtual void visible(bool state) = 0;\n    virtual void enable(bool state) = 0;\n\n    virtual short left() const = 0;\n    virtual short top() const = 0;\n    virtual short width() const = 0;\n    virtual short height() const = 0;\n};\n\n/**\n * Fake window holding properties before real window is attached.\n *\n * The purpose of this class is to maintain any properties which are set on\n * a wrapper before it has been attached to a real Win32 window.  It\n * simulates the fields in the real window.\n */\ntemplate<typename T>\nclass fake_window : public internal_window<T>\n{\npublic:\n    fake_window(\n        bool is_enabled, bool is_visible, const std::basic_string<T>& text,\n        short left, short top, short width, short height)\n        :\n        m_enabled(is_enabled), m_visible(is_visible), m_text(text),\n        m_left(left), m_top(top), m_width(width), m_height(height) {}\n\n    std::basic_string<T> text() const { return m_text; }\n    void text(const std::basic_string<T>& new_text) { m_text = new_text; }\n\n    bool is_visible() { return m_visible; }\n    bool is_enabled() { return m_enabled; }\n    void visible(bool state) { m_visible = state; }\n    void enable(bool state) { m_enabled = state; }\n\n    short left() const { return m_left; }\n    short top() const { return m_top; }\n    short width() const { return m_width; }\n    short height() const { return m_height; }\n\nprivate:\n    std::basic_string<T> m_text;\n\n    bool m_enabled;\n    bool m_visible;\n\n    short m_left;\n    short m_top;\n    short m_width;\n    short m_height;\n};\n\n/**\n * Wrapper around a real Win32 window.\n */\ntemplate<typename T>\nclass real_window : public internal_window<T>\n{\npublic:\n    real_window(HWND hwnd) :\n        m_window(washer::window::window_handle::foster_handle(hwnd))\n    {\n        if (hwnd == NULL)\n            BOOST_THROW_EXCEPTION(std::logic_error(\"Invalid window handle\"));\n    }\n\n    /**\n     * Window text.\n     *\n     * @note  We could allow the caller to specify that they require narrow or\n     *        wide string irrespective of whether this window is narrow or\n     *        wide.  However, the fake_window doesn't support this and\n     *        this class must match its interface.\n     */\n    std::basic_string<T> text() const { return window().text<T>(); }\n\n    /**\n     * Window text.\n     *\n     * @note  We could allow the caller to pass this method a narrow or wide\n     *        string irrespective of whether this window is narrow or wide.\n     *        However, the fake_window doesn't support this and this\n     *        class must match its interface.\n     */\n    void text(const std::basic_string<T>& new_text)\n    { window().text(new_text); }\n\n    bool is_visible() { return window().is_visible(); }\n    bool is_enabled() { return window().is_enabled(); }\n\n    void visible(bool state) { window().visible(state); }\n    void enable(bool state) { window().enable(state); }\n\n    short left() const { return window().position().left(); }\n    short top() const { return window().position().top(); }\n    short width() const { return window().position().width(); }\n    short height() const { return window().position().height(); }\n\nprivate:\n\n    washer::window::window<T>& window() { return m_window; }\n    const washer::window::window<T>& window() const { return m_window; }\n\n    washer::window::window<T> m_window;\n};\n\ntemplate<typename T>\nvoid copy_fields(internal_window<T>& source, internal_window<T>& target)\n{\n    target.text(source.text());\n    target.enable(source.is_enabled());\n    target.visible(source.is_visible());\n}\n\n/**\n * Window handle (HWND) wrapper class.\n *\n * Only one instance must exist per HWND so this class in non-copyable.\n * Clients use it via facade classes that reference the single instances\n * via a shared pointer.\n *\n * The lifetime of the class has three phases:\n *\n *  - before it is connected to an HWND.\n *    The data in the fields of this class are the data that the Win32 window\n *    will be initialised with (via a dialog template) when the Window dialog\n *    manager calls CreateWindow.\n *\n *  - while connected to an HWND.\n *    Method of this class fetch their data directly from the Win32 object.\n *    Member fields are ignored.\n *\n *  - after detaching from an HWND (when the Win32 window is destroyed)\n *    The Win32 data is pulled in just before destruction and stored in the\n *    member fields.  Subsequent method calls use this data.\n */\nclass window_impl : private boost::noncopyable\n{\npublic:\n\n    typedef window_impl super; // end of dispatch chain\n\n    typedef message_map<\n        WM_CREATE, WM_DESTROY, WM_NCDESTROY, WM_SETTEXT, WM_SHOWWINDOW>\n        messages;\n    typedef command_map<-1> commands;\n\n    virtual LRESULT handle_message(\n        UINT message_id, WPARAM wparam, LPARAM lparam)\n    {\n        return dispatch_message(this, message_id, wparam, lparam);\n    }\n\n    virtual void handle_command(\n        WORD command_id, WPARAM wparam, LPARAM lparam)\n    {\n        dispatch_command(this, command_id, wparam, lparam);\n    }\n\n    window_impl(\n        const std::wstring& text, short left, short top, short width,\n        short height)\n        :\n        m_window(\n            boost::make_shared< fake_window<wchar_t> >(\n                true, true, text, left, top, width, height)) {}\n\n    virtual ~window_impl()\n    {\n        assert(!m_link.attached()); // why have we not detached?\n    }\n\n    bool is_active() const { return m_link.attached(); }\n\n    virtual std::wstring window_class() const = 0;\n    virtual DWORD style() const\n    {\n        return WS_VISIBLE | WS_TABSTOP;\n    }\n\n    short left() const { return window().left(); }\n    short top() const { return window().top(); }\n    short width() const { return window().width(); }\n    short height() const { return window().height(); }\n\n    std::wstring text() const { return window().text(); }\n    void text(const std::wstring& new_text) { window().text(new_text); }\n\n    void visible(bool state) { window().visible(state); }\n    void enable(bool state) { window().enable(state); }\n\n    /// @name Event handlers\n    // @{\n\n    /**\n     * @name Lifetime events\n     *\n     * The main purpose of these handlers is to synchronise C++ wrapper\n     * and the real Win32 window.  The wrapper's fields can be set before the\n     * real window is created and callers need access to the fields after the\n     * real window is destroyed.  Therefore we push the data out to the\n     * window when it's created and pull it back just before it's destroyed.\n     *\n     * We do this with the @c WM_CREATE and @c WM_DESTROY messages (rather\n     * than @c WM_NCCREATE and @c WM_NCDESTROY) as we can't be sure of the\n     * fields' integrity outside of this 'safe zone'.  For example, when\n     * common controls 6 is enabled setting an icon before @c WM_CREATE\n     * fails to show the icon.\n     */\n\n    LRESULT on(message<WM_CREATE> m)\n    {\n        LRESULT res = default_message_handler(m);\n        push();\n        return res;\n    }\n\n    LRESULT on(message<WM_DESTROY> m)\n    {\n        pull();\n        return default_message_handler(m);\n    }\n\n    /**\n     * To prevent capturing creation of windows not directly part of our\n     * dialog template, such as the system menu, we engage our CBT hook for\n     * as short a period as possible.  Therefore we have to detach ourselves\n     * in this function rather than from the CBT hook.\n     *\n     * @see ezel::detail::cbt_hook_function.\n     */\n    LRESULT on(message<WM_NCDESTROY> m)\n    {\n        LRESULT res = default_message_handler(m);\n        detach();\n        return res;\n    }\n\n    // @}\n\n    LRESULT on(message<WM_SETTEXT> m)\n    {\n        m_on_text_change(m.text<wchar_t>());\n        LRESULT res = default_message_handler(m);\n        m_on_text_changed();\n        return res;\n    }\n\n    LRESULT on(message<WM_SHOWWINDOW> m)\n    {\n        m_on_showing(m.state());\n        LRESULT res = default_message_handler(m);\n        m_on_show(m.state());\n        return res;\n    }\n\n    // @}\n\n    /**\n     * Perform default processing for a message.\n     *\n     * This method must call the window procedure of the wrapped window.\n     * Here it's done through a call to CallWindowProc but dialog windows\n     * must do this differently so should override this method.\n     */\n    virtual LRESULT default_message_handler(\n        UINT message_id, WPARAM wparam, LPARAM lparam)\n    {\n        return m_window_proc->do_default_handling(message_id, wparam, lparam);\n    }\n\n    template<UINT N>\n    LRESULT default_message_handler(const message<N>& m)\n    {\n        return default_message_handler(N, m.wparam(), m.lparam());\n    }\n\n    /**\n     * Default command handler.\n     *\n     * Command message that aren't handled elsewhere are dispatched to this\n     * function.  By default, it does nothing.  Override if you want to\n     * to handle unhandled command messages.\n     */\n    virtual void on(washer::gui::command_base unknown)\n    {\n        (void)unknown;\n#ifdef _DEBUG\n        window_impl* w = window_from_hwnd(unknown.control_hwnd());\n        washer::trace(\n            \"Unhandled command (code 0x%x) from window with title '%s'\")\n            % unknown.command_code() % w->text();\n#endif\n    }\n\n    /**\n     * Establish a two-way link between this C++ wrapper object and the\n     * Win32 window object.\n     *\n     * Also replace the Win32 window's message handling procedure (WNDPROC)\n     * with our own so that we can intercept any messages it is sent. This\n     * is otherwise known as subclassing.\n     *\n     * We don't push the wrapper fields out to the Win32 window yet as it's\n     * much too early.  This is called by the CBT hook and at this point the\n     * window hasn't even recieved an WM_NCCREATE message yet.\n     *\n     * @todo  What if we get a failure partway through?\n     */\n    void attach(HWND hwnd)\n    {\n        assert(!m_link.attached()); // an instance should only be attached once\n        \n        m_link = window_link<window_impl>(hwnd, this);\n        m_window.attach(hwnd);\n\n        install_window_procedure();\n    }\n\n    /// @name Event delegates\n    // @{\n\n    boost::signal<void (const wchar_t*)>& on_text_change()\n    { return m_on_text_change; }\n\n    boost::signal<void ()>& on_text_changed()\n    { return m_on_text_changed; }\n\n    boost::signal<void (bool)>& on_showing() { return m_on_showing; }\n    boost::signal<void (bool)>& on_show() { return m_on_show; }\n\n    // @}\n\nprotected:\n\n    HWND hwnd() const { return m_link.hwnd(); }\n\n    /**\n     * Suck data from real Win32 window object into the wrapper class.\n     *\n     * This method exists so that properties of the window are still available\n     * after the real window has been destroyed.\n     *\n     * Override this method when subclasses have other fields that need to\n     * be sucked out of the window.  In most cases the overriding method must\n     * call this method of the base class to synchronise all the fields.\n     */\n    virtual void pull()\n    {\n        m_window.pull();\n    }\n\n    /**\n     * Update Win32 window object from fields in this wrapper class.\n     *\n     * Fields can be set in the wrapper before the Win32 window is created.\n     * This window pushes those values out to the real window once it is\n     * created.\n     *\n     * Override this method when subclasses have other fields that need to\n     * be pushed out to the window.  In most cases the overriding method must\n     * call this method of the base class to synchronise all the fields.\n     *\n     * @todo  Some of this pushing is redundant as the values are set in\n     *        the dialogue template.  Not necessarily harmful but worth\n     *        further thought.\n     */\n    virtual void push()\n    {\n        m_window.push();\n    }\n\n    boost::shared_ptr<window_proc_base>& window_procedure()\n    { return m_window_proc; }\n\nprivate:\n\n    /**\n     * Break the two-way link between this C++ wrapper object and the\n     * Win32 window object.\n     * \n     * The fields of the Win32 must have been pulled in by our window\n     * proc when it recieved WM_DESTROY.  That message is the last point at\n     * which we can be sure of the fields' integrity.\n     *\n     * @bug  If someone has subclassed us but not removed their hook by the\n     *       time they pass us the the WM_NCDESTROY message (bad!) then we\n     *       never remove our hooks as we're not at the bottom of the\n     *       subclass chain.  The UpDown control seems to do this when\n     *       it subclasses its buddy control.\n     *\n     * @todo  Investigate SetWindowSubclass/RemoveWindowSubclass and\n     *        whether it might fix the unsubclassing bug.  Unfortunately,\n     *        it may not work on earlier Windows versions.\n     */\n    void detach()\n    {\n        assert(m_link.attached()); // why are we detaching a detached wrapper?\n\n        remove_window_procedure();\n\n        m_window.detach();\n        m_link = window_link<window_impl>();\n    }\n\n    /**\n     * Replace the window's own Window proc with ours.\n     */\n    virtual void install_window_procedure()\n    {\n        window_procedure() =\n            boost::make_shared<window_proc>(hwnd(), window_impl_proc);\n    }\n\n    /**\n     * Remove our window proc and put back the one it came with.\n     */\n    virtual void remove_window_procedure()\n    {\n        window_procedure().reset();\n    }\n\n    internal_window<wchar_t>& window() { return *m_window; }\n    const internal_window<wchar_t>& window() const { return *m_window; }\n\n    window_link<window_impl> m_link;\n    window_proxy<\n        internal_window<wchar_t>, fake_window<wchar_t>, real_window<wchar_t> >\n        m_window;\n    boost::shared_ptr<window_proc_base> m_window_proc;\n\n    /// @name Events\n    // @{\n\n    boost::signal<void (const wchar_t*)> m_on_text_change;\n    boost::signal<void ()> m_on_text_changed;\n    boost::signal<void (bool)> m_on_showing;\n    boost::signal<void (bool)> m_on_show;\n\n    // @}\n};\n\n/**\n * Custom window procedure for wrapping HWNDs and intercepting their\n * messages.\n */\ninline LRESULT CALLBACK window_impl_proc(\n    HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)\n{\n    try\n    {\n        window_impl* w = window_from_hwnd(hwnd);\n        return w->handle_message(message, wparam, lparam);\n    }\n    catch (const std::exception& e)\n    {\n        // We should always be able to get our window.  If we were able to\n        // replace the window proc with this one then we must have hooked\n        // it correctly so why can't we find it now?\n\n        washer::trace(\"window_impl_proc exception: %s\")\n            % boost::diagnostic_information(e);\n\n        assert(!\"Something went very wrong here - we couldn't get our window\");\n\n        return ::DefWindowProcW(hwnd, message, wparam, lparam);\n    }\n}\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/window_link.hpp",
    "content": "/**\n    @file\n\n    HWND/wrapper linking.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_DETAIL_WINDOW_LINK_HPP\n#define EZEL_DETAIL_WINDOW_LINK_HPP\n#pragma once\n\n#include <ezel/detail/hwnd_linking.hpp> // store_user_window_data\n\n#include <washer/trace.hpp> // trace\n\n#include <boost/exception/diagnostic_information.hpp> // diagnostic_information\n#include <boost/noncopyable.hpp> // noncopyable\n#include <boost/shared_ptr.hpp> // shared_ptr, make_shared\n\nnamespace ezel {\nnamespace detail {\n\n/**\n * Link between a real Win32 window handle and a pointer to a window wrapper.\n *\n * The purpose of this class is to establish, maintain and then destroy a\n * two-way link between an HWND and a C++ wrapper instance.  The link\n * is broken when the instance is destroyed.  To explicitly break the link,\n * assign a broken link to the existing link:\n *     @code   link = window_link()   @endcode\n *\n * Clients can query the status of the linked by calling attached().\n *\n * Stores the instance pointer in the HWND's user data field.\n * @todo  Store the pointer somewhere else to prevent issues including other\n *        uses accidentally overwriting our pointer.\n */\ntemplate<typename T>\nclass window_link_helper : private boost::noncopyable\n{\npublic:\n    typedef T* pointer_type;\n\n    /// Link HWND to wrapper.\n    window_link_helper(HWND hwnd, pointer_type wrapper) : m_hwnd(hwnd)\n    {\n        store_user_window_data<wchar_t>(hwnd, wrapper);\n    }\n\n    /// Create a broken link.\n    window_link_helper() : m_hwnd(NULL) {}\n\n    /// Break link.\n    ~window_link_helper()\n    {\n        try\n        {\n            if (attached())\n                store_user_window_data<wchar_t>(m_hwnd, pointer_type());\n        }\n        catch (const std::exception& e)\n        {\n            washer::trace(\"Unlinking window threw exception: %s\")\n                % boost::diagnostic_information(e);\n        }\n    }\n\n    HWND hwnd() const { return m_hwnd; }\n    bool attached() const { return m_hwnd != NULL; }\n\nprivate:\n    HWND m_hwnd;\n};\n\n/**\n * Copyable link between a real Win32 window handle and a pointer to a window\n * wrapper.\n *\n * The purpose of this class make the link implementation in window_link_helper\n * break the link only when the last copy goes out-of-scope.\n *\n * The methods are the same as window_link_helper.\n */\ntemplate<typename T>\nclass window_link\n{\npublic:\n    typedef T* pointer_type;\n\n    /// Link HWND to wrapper.\n    window_link(HWND hwnd, pointer_type wrapper)\n        : m_link(boost::make_shared<window_link_helper<T> >(hwnd, wrapper)) {}\n\n    /// Create a broken link.\n    window_link() : m_link(boost::make_shared<window_link_helper<T> >()) {}\n\n    HWND hwnd() const { return m_link->hwnd(); }\n    bool attached() const { return m_link->attached(); }\n\nprivate:\n    boost::shared_ptr<window_link_helper<T> > m_link;\n};\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/window_proc.hpp",
    "content": "/**\n    @file\n\n    Window procedures.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_WINDOW_PROC_HPP\n#define EZEL_WINDOW_PROC_HPP\n#pragma once\n\n#include <washer/trace.hpp> // trace\n#include <washer/window/dialog.hpp> // dialog\n#include <washer/window/window.hpp> // window\n\n#include <cassert> // assert\n#include <exception>\n\nnamespace ezel {\nnamespace detail {\n\nclass window_proc_base\n{\npublic:\n    virtual ~window_proc_base() {}\n\n    virtual LRESULT do_default_handling(\n        UINT message_id, WPARAM wparam, LPARAM lparam) = 0;\n};\n\n/**\n * Subclass a window with a standard window procdure (WNDPROC).\n */\nclass window_proc : public window_proc_base\n{\npublic:\n\n    /**\n     * Subclass window.\n     */\n    window_proc(HWND hwnd, WNDPROC new_proc) :\n        m_window(washer::window::window_handle::foster_handle(hwnd)),\n        m_proc(new_proc),\n        m_sub_proc(m_window.change_window_procedure(m_proc)) {}\n\n    /**\n     * Unsubclass window.\n     */\n    ~window_proc()\n    {\n        try\n        {\n            WNDPROC current_wndproc = m_window.window_procedure();\n            if (current_wndproc == m_proc)\n            {\n                WNDPROC proc = m_window.change_window_procedure(m_sub_proc);\n                (void)proc;\n                assert(proc == m_proc); // mustn't remove someone else's\n                                        // window procedure\n            }\n        }\n        catch (const std::exception& e)\n        {\n            washer::trace(\"window_proc destructor threw exception: %s\")\n                % boost::diagnostic_information(e);\n        }\n    }\n\n    virtual LRESULT do_default_handling(\n        UINT message_id, WPARAM wparam, LPARAM lparam)\n    {\n        return ::CallWindowProcW(\n            m_sub_proc, m_window.hwnd(), message_id, wparam, lparam);\n    }\n\nprotected:\n    washer::window::window<wchar_t>& window() { return m_window; }\n\nprivate:\n    washer::window::window<wchar_t> m_window;\n    WNDPROC m_proc;\n    WNDPROC m_sub_proc; ///< Subclassed window's default message handler\n};\n\n\n/**\n * Window proc for dialog window.\n *\n * Delegates default processing to DefDlgProc as in the alternative\n * dialog handling method explained by Raymond Chen.\n *\n * Your dialog loop will still be called but only if default processing\n * is invoked.  Generally you should just return FALSE to allow the\n * dialog manager to handle the message.\n * \n * @see http://blogs.msdn.com/b/oldnewthing/archive/2003/11/13/55662.aspx\n */\nclass dialog_proc : public window_proc\n{\npublic:\n\n    dialog_proc(HWND hwnd, WNDPROC new_proc) : window_proc(hwnd, new_proc) {}\n\n    virtual LRESULT do_default_handling(\n        UINT message_id, WPARAM wparam, LPARAM lparam)\n    {\n        return ::DefDlgProc(window().hwnd(), message_id, wparam, lparam);\n    }\n};\n\n}} // namespace ezel::detail\n\n#endif\n"
  },
  {
    "path": "ezel/detail/window_proxy.hpp",
    "content": "/**\n    @file\n\n    Window implementation switcher.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_DETAIL_WINDOW_PROXY_HPP\n#define EZEL_DETAIL_WINDOW_PROXY_HPP\n#pragma once\n\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/noncopyable.hpp> // noncopyable\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <cassert> // assert\n\nnamespace ezel {\nnamespace detail {\n\n/**\n * Switch between two window implementations.\n *\n * One implementation is a real wrapper round an HWND, the other just pretends.\n * This class serves up a window wrapper that works correctly whether or not\n * it is attached to an real Win32 window object.\n *\n * The purpose of this class is to manage the change from an artificial\n * window to an attached window and vice versa.  The idea is that an instance\n * of this class will responds correctly to property setting/getting\n * regardless of whether the window is real or fake.\n *\n * As the fake window type may have an arbitrary contructor signature, the\n * client must pass an instance to our constructor.  The real window type\n * we instantiate ourselves and must have the constructor:\n *    @code RealType(HWND hwnd) @endcode\n *\n * Field data is synchronised between the real and fake windows by calling\n * @c copy_fields.  For any type you use as the @c Interface template \n * parameter you must implement @c copy_fields in the same namespace with this\n * signature:\n *    @code copy_fields(Interface& source, Interface& target) @endcode\n * It will be found by ADL.\n */\ntemplate<typename Interface, typename FakeType, typename RealType>\nclass window_proxy\n{\npublic:\n    window_proxy(boost::shared_ptr<FakeType> fake)\n        : m_fake_window(fake), m_active_window(m_fake_window) {}\n\n    Interface* operator->() { return m_active_window; }\n    Interface& operator*() { return *m_active_window; }\n    const Interface* operator->() const { return m_active_window; }\n    const Interface& operator*() const { return *m_active_window; }\n\n    /**\n     * Switch from fake to real window.\n     */\n    void attach(HWND hwnd)\n    {\n        assert(!m_real_window); // why are we attaching twice?\n        assert(m_active_window == m_fake_window); // fake window not active one\n\n        m_real_window = boost::make_shared<RealType>(hwnd);\n        m_active_window = m_real_window;\n    }\n\n    /**\n     * Switch back to the fake window.\n     */\n    void detach()\n    {\n        assert(m_real_window); // why are not attached?\n        assert(m_active_window == m_real_window); // real window not active one\n\n        m_real_window.reset();\n        m_active_window = m_fake_window;\n    }\n\n    /**\n     * Suck data from real Win32 window object into the fake window.\n     *\n     * This method exists so that properties of the window are still available\n     * after the real window has been destroyed.\n     *\n     * Call this method after receiving the WM_CREATE message so that\n     * @c copy_fields() can rely on the integrity of the window fields.\n     */\n    void pull()\n    {\n        assert(m_real_window); // must not call this method unless attached\n\n        copy_fields(real(), fake());\n    }\n\n    /**\n     * Update Win32 window object from fields in the fake window.\n     *\n     * Fields can be set in the wrapper before the Win32 window is created.\n     * This window pushes those values out to the real window once it is\n     * created.\n     *\n     * Call this method before receiving the WM_DESTROY message so that\n     * @c copy_fields() can rely on the integrity of the window fields.\n     */\n    void push()\n    {\n        assert(m_real_window); // must not call this method unless attached\n\n        copy_fields(fake(), real());\n    }\n\nprivate:\n    Interface& real() { return *m_real_window; }\n    Interface& fake() { return *m_fake_window; }\n\n    boost::shared_ptr<FakeType> m_fake_window;\n    boost::shared_ptr<RealType> m_real_window;\n    boost::shared_ptr<Interface> m_active_window;\n};\n\n}} // namespace ezel::detail\n\n#endif"
  },
  {
    "path": "ezel/form.hpp",
    "content": "/**\n    @file\n\n    GUI forms (aka dialogs)\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_FORM_HPP\n#define EZEL_FORM_HPP\n#pragma once\n\n#include <ezel/control.hpp> // control\n#include <ezel/control_parent_impl.hpp> // control_parent_impl\n#include <ezel/detail/dialog_template.hpp>\n                                   // build_in_memory_dialog_template\n#include <ezel/detail/hooks.hpp> // creation_hooks\n#include <ezel/detail/hwnd_linking.hpp> // fetch_user_window_data\n#include <ezel/detail/window_impl.hpp> // window_impl\n#include <ezel/detail/window_proc.hpp> // dialog_proc\n#include <ezel/window.hpp> // window\n\n#include <washer/dynamic_link.hpp> // module_handle\n#include <washer/gui/commands.hpp> // command\n#include <washer/gui/messages.hpp> // message\n\n#include <boost/bind.hpp> // bind\n#include <boost/exception/errinfo_api_function.hpp> // errinfo_api_function\n#include <boost/exception/info.hpp> // errinfo\n#include <boost/function.hpp> // function\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/signal.hpp> // signal\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/weak_ptr.hpp> // weak_ptr\n\n#include <cassert> // assert\n#include <string>\n#include <vector>\n\n#include <Windows.h> // DialogBoxIndirectParam\n\nnamespace ezel {\n\nnamespace detail {\n\n    INT_PTR CALLBACK dialog_creation_handler(\n        HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);\n\n    /**\n     * Real form class implementation.\n     */\n    class form_impl : public control_parent_impl\n    {\n    public:\n        typedef control_parent_impl super;\n\n        virtual LRESULT handle_message(\n            UINT message, WPARAM wparam, LPARAM lparam)\n        {\n            return dispatch_message(this, message, wparam, lparam);\n        }\n\n        typedef message_map<WM_INITDIALOG, WM_ACTIVATE, WM_CLOSE> messages;\n\n        form_impl(\n            const std::wstring& title, short left, short top, short width,\n            short height)\n            :\n            control_parent_impl(title, left, top, width, height) {}\n\n        std::wstring window_class() const { return L\"#32770\"; }\n        DWORD style() const\n        { return DS_SETFONT | WS_VISIBLE | WS_POPUPWINDOW | DS_MODALFRAME; }\n\n        void add_control(boost::shared_ptr<window_impl> control)\n        {\n            m_controls.push_back(control);\n        }\n\n        void show(HWND hwnd_owner)\n        {\n            std::vector<unsigned char> buffer = build_dialog_template_in_memory(\n                L\"MS Shell Dlg\", 8, text(), left(), top(), width(), height(),\n                m_controls);\n\n            hook_window_creation();\n            try\n            {\n                INT_PTR rc = ::DialogBoxIndirectParamW(\n                    washer::module_handle(),\n                    (buffer.empty()) ?\n                        NULL : reinterpret_cast<DLGTEMPLATE*>(&buffer[0]),\n                    hwnd_owner, dialog_creation_handler,\n                    reinterpret_cast<LPARAM>(this));\n                if (rc < 1)\n                    BOOST_THROW_EXCEPTION(\n                        boost::enable_error_info(washer::last_error()) << \n                        boost::errinfo_api_function(\n                            \"DialogBoxIndirectParamW\"));\n            }\n            catch (...)\n            {\n                unhook_window_creation();\n                throw;\n            }\n        }\n\n        void end()\n        {\n            // set the 2nd parameter to > 0 so we can detect error case from \n            // return value of DialogBoxIndirectParam\n            if(!::EndDialog(hwnd(), 1))\n                BOOST_THROW_EXCEPTION(\n                    boost::enable_error_info(washer::last_error()) << \n                    boost::errinfo_api_function(\"EndDialog\"));\n        }\n\n        /// @name Event delegates\n        // @{\n\n        boost::signal<bool ()>& on_create() { return m_on_create; }\n\n        boost::signal<void (bool)>& on_activating() { return m_on_activating; }\n        boost::signal<void (bool)>& on_activate() { return m_on_activate; }\n\n        boost::signal<void ()>& on_deactivating() { return m_on_deactivating; }\n        boost::signal<void ()>& on_deactivate() { return m_on_deactivate; }\n\n        // @}\n\n        /// @name Message handlers\n        // @{\n\n        LRESULT on(message<WM_CLOSE> m)\n        {\n            end();\n            return default_message_handler(m);\n        }\n\n        LRESULT on(message<WM_ACTIVATE> m)\n        {\n            if (m.active())\n                m_on_activating(m.by_mouse());\n            else if (m.deactive())\n                m_on_deactivating();\n            else\n                assert(!\"Inconsistent message state\");\n\n            LRESULT res = default_message_handler(m);\n\n            if (m.active())\n                m_on_activate(m.by_mouse());\n            else if (m.deactive())\n                m_on_deactivate();\n            else\n                assert(!\"Inconsistent message state\");\n\n            return res;\n        }\n\n        LRESULT on(message<WM_INITDIALOG> /*message*/)\n        {\n            // All our controls should have been created by now so stop\n            // monitoring window creation.  This prevents problems with\n            // the system menu which is created later.\n            unhook_window_creation();\n\n            if (!m_on_create.empty())\n                return m_on_create() == TRUE;\n            else\n                return TRUE; // give default control focus\n        }\n        \n        // @}\n\n    private:\n\n        /**\n         * Replace the window's own window proc with ours.\n         */\n        virtual void install_window_procedure()\n        {\n            window_procedure() =\n                boost::make_shared<dialog_proc>(hwnd(), window_impl_proc);\n        }\n\n        friend INT_PTR CALLBACK dialog_creation_handler(\n            HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);\n\n        void hook_window_creation()\n        {\n            m_hooks = boost::make_shared<creation_hooks<wchar_t> >();\n        }\n\n        void unhook_window_creation()\n        {\n            m_hooks.reset();\n        }\n\n        /**\n         * The collection of controls on this form.\n         * They are held as smart pointers to ensure they stay alive as long\n         * as the form, regardless of how they are passed to add_control.\n         */\n        std::vector<boost::shared_ptr<window_impl> > m_controls;\n        boost::shared_ptr<creation_hooks<wchar_t> > m_hooks;\n\n\n        /// @name Events\n        // @{\n        \n        boost::signal<bool ()> m_on_create;\n\n        boost::signal<void (bool)> m_on_activating;\n        boost::signal<void (bool)> m_on_activate;\n        boost::signal<void ()> m_on_deactivating;\n        boost::signal<void ()> m_on_deactivate;\n\n        // @}\n    };\n\n    /**\n     * Dialog proc to capture WM_INITDIALOG.\n     *\n     * The dialog proc boostraps all the rest of the window and message\n     * capture.  It is the way we associate the dialog's HWND with a form_impl\n     * instance.  We can't do it with the window creation hooks used to\n     * capture the rest of the windows as that relies on a pointer stuffed\n     * in the WM_CREATE CREATESTRUCT.  DialogBoxIndirectParam doesn't give\n     * us a way to do that but it does allow us to stuff the pointer into\n     * the WM_INITDIALOG lparam which is why we cature that here.\n     *\n     * The moment we attach the instance to the HWND, our regular Ezel window\n     * procedure takes over and any subsequent message are dispatched as\n     * normal.  Therefore we ignore any other types of message.  However,\n     * we have to dispatch the WM_INITDIALOG message here as it won't be\n     * sent again.\n        */\n    inline INT_PTR CALLBACK dialog_creation_handler(\n        HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)\n    {\n        assert(msg != WM_CREATE); // apparently a dialog should never get this\n\n        if (msg != WM_INITDIALOG)\n            return FALSE;\n\n        try\n        {\n            // we stashed a pointer to our C++ form object in the creation\n            // data in the dialog template\n            // now we extract it here and use it to set up a two-way link\n            // between the C++ form object and the Win32 dialog object\n            form_impl* this_form = reinterpret_cast<form_impl*>(lparam);\n            this_form->attach(hwnd);\n\n            return this_form->handle_message(msg, wparam, lparam);\n        }\n        catch (const std::exception& e)\n        {\n            (void)e; /* ignore */\n            washer::trace(\"threw when trying to link to dialog window: %s\")\n                % e.what();\n            return FALSE;\n        }\n    }\n}\n\nclass form : public window<detail::form_impl>\n{\npublic:\n\n    form(\n        const std::wstring& title, short left, short top, short width,\n        short height)\n        : window(\n            boost::make_shared<detail::form_impl>(\n                title, left, top, width, height)) {}\n\n    template<typename T>\n    void add_control(const control<T>& control)\n    {\n        impl()->add_control(control.impl());\n    }\n\n    void show(HWND hwnd_owner=NULL)\n    {\n        impl()->show(hwnd_owner);\n    }\n\n    void end()\n    {\n        impl()->end();\n    }\n\n    /**\n     * A functor that can be called to destroy the form instance.\n     *\n     * This allows users to write :\n     * @code btn.on_click().connect(frm.killer()) @endcode\n     * instead of\n     * @code btn.on_click().connect(bind(&form::end, ref(frm))) @endcode\n     *\n     * The functor holds a *weak* reference to the form to prevent circular\n     * references.  If it held a strong reference, when the functor is passed\n     * to a control owned by the form, the form would indirectly hold a\n     * reference and would never be destroyed.\n     */\n    boost::function<void ()> killer()\n    {\n        typedef boost::weak_ptr<detail::form_impl> weak_form_reference;\n        weak_form_reference weak_ref = impl();\n\n        return boost::bind(\n            &detail::form_impl::end, boost::bind(\n                &weak_form_reference::lock, weak_ref));\n    }\n\n    /// @name  Event delegates.\n    // @{\n\n    boost::signal<bool ()>& on_create() { return impl()->on_create(); }\n    boost::signal<void (bool)>& on_activating() { return impl()->on_activating(); }\n    boost::signal<void (bool)>& on_activate() { return impl()->on_activate(); }\n\n    boost::signal<void ()>& on_deactivating() { return impl()->on_deactivating(); }\n    boost::signal<void ()>& on_deactivate() { return impl()->on_deactivate(); }\n\n    // @}\n};\n\n} // namespace ezel\n\n#endif\n"
  },
  {
    "path": "ezel/window.hpp",
    "content": "/**\n    @file\n\n    Ezel window.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef EZEL_WINDOW_HPP\n#define EZEL_WINDOW_HPP\n#pragma once\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/signal.hpp> // signal\n\n#include <string>\n\nnamespace ezel {\n\n/**\n * Base-class for window facades.\n *\n * All Ezel windows are an instance of a subclass of this this template. \n * This provides access to the methods and properties common to all windows.\n *\n * @param T  Type of implementation class (pimpl)\n */\ntemplate<typename T>\nclass window\n{\npublic:\n\n    window(boost::shared_ptr<T> impl) : m_impl(impl) {}\n    virtual ~window() {}\n\n    std::wstring text() const { return impl()->text(); }\n    void text(const std::wstring& new_text) const\n    {\n        return impl()->text(new_text);\n    }\n\n    void visible(bool visibility) { impl()->visible(visibility); }\n    void enable(bool enablement) { impl()->enable(enablement); }\n\n    /// @name Events\n    // @{\n\n    boost::signal<void (bool)>& on_showing() { return impl()->on_showing(); }\n    boost::signal<void (bool)>& on_show() { return impl()->on_show(); }\n\n    boost::signal<void (const wchar_t*)>& on_text_change()\n    { return impl()->on_text_change(); }\n\n    boost::signal<void ()>& on_text_changed()\n    { return impl()->on_text_changed(); }\n\n    // @}\n\nprotected:\n    \n    boost::shared_ptr<T> impl() const { return m_impl; }\n\nprivate:\n\n    boost::shared_ptr<T> m_impl; // pimpl\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "guids.txt",
    "content": "b816a838-5022-11dc-9153-0090f5284f85 Swish Type Library (DLL module UUID)\r\nb816a839-5022-11dc-9153-0090f5284f85   *REMOVED*  IHostFolder Interface\r\nb816a83a-5022-11dc-9153-0090f5284f85 CHostFolder Class\r\nb816a83b-5022-11dc-9153-0090f5284f85   *REMOVED*  IRemoteFolder Interface\r\nb816a83c-5022-11dc-9153-0090f5284f85 CRemoteFolder Class\r\nb816a83d-5022-11dc-9153-0090f5284f85   *REMOVED*  IRemoteEnumIDList Interface\r\nb816a83e-5022-11dc-9153-0090f5284f85   *REMOVED*  CRemoteEnumIDList Class\r\nb816a83f-5022-11dc-9153-0090f5284f85   *REMOVED*  IHostContextMenu Interface\r\nb816a840-5022-11dc-9153-0090f5284f85   *REMOVED*  CHostContextMenu Class\r\nb816a841-5022-11dc-9153-0090f5284f85   *REMOVED*  ISftpProvider Interface\r\nb816a842-5022-11dc-9153-0090f5284f85   *REMOVED*  CPuttyProvider Class\r\nb816a843-5022-11dc-9153-0090f5284f85   *CHANGED*  IEnumListing Interface\r\nb816a844-5022-11dc-9153-0090f5284f85   *REMOVED*  ISftpConsumer Interface\r\nb816a845-5022-11dc-9153-0090f5284f85   *REMOVED*  PuttyProvider Type Library\r\nb816a846-5022-11dc-9153-0090f5284f85   *CHANGED*  Libssh2Provider Type Library (DLL module UUID)\r\nb816a847-5022-11dc-9153-0090f5284f85   *CHANGED*  CLibssh2Provider Class\r\nb816a848-5022-11dc-9153-0090f5284f85   *REMOVED*  CExplorerCallback Class\r\nb816a849-5022-11dc-9153-0090f5284f85   *REMOVED*  CIconExtractor Class\r\nb816a84a-5022-11dc-9153-0090f5284f85   *REMOVED*  CUserInteraction Class\r\nb816a84b-5022-11dc-9153-0090f5284f85   *REMOVED*  CSftpDirectory Class\r\nb816a84c-5022-11dc-9153-0090f5284f85\r\nb816a84d-5022-11dc-9153-0090f5284f85\r\nb816a84e-5022-11dc-9153-0090f5284f85\r\nb816a84f-5022-11dc-9153-0090f5284f85\r\nb816a850-5022-11dc-9153-0090f5284f85 FMTID Swish host\r\nb816a851-5022-11dc-9153-0090f5284f85 FMTID Swish remote folder\r\nb816a852-5022-11dc-9153-0090f5284f85\r\nb816a853-5022-11dc-9153-0090f5284f85\r\nb816a854-5022-11dc-9153-0090f5284f85\r\nb816a855-5022-11dc-9153-0090f5284f85\r\nb816a856-5022-11dc-9153-0090f5284f85\r\nb816a857-5022-11dc-9153-0090f5284f85\r\nb816a858-5022-11dc-9153-0090f5284f85\r\nb816a859-5022-11dc-9153-0090f5284f85\r\nb816a85a-5022-11dc-9153-0090f5284f85\r\nb816a85b-5022-11dc-9153-0090f5284f85\r\nb816a85c-5022-11dc-9153-0090f5284f85\r\nb816a85d-5022-11dc-9153-0090f5284f85\r\nb816a85e-5022-11dc-9153-0090f5284f85\r\nb816a85f-5022-11dc-9153-0090f5284f85\r\nb816a860-5022-11dc-9153-0090f5284f85   *REMOVED*  Provider backend APPID\r\nb816a861-5022-11dc-9153-0090f5284f85   *REMOVED*  Provider backend Type Library\r\nb816a862-5022-11dc-9153-0090f5284f85   *REMOVED*  Provider Component\r\nb816a863-5022-11dc-9153-0090f5284f85   *REMOVED*  RealDispenser Component\r\nb816a864-5022-11dc-9153-0090f5284f85   *REMOVED*  Dispenser Component\r\nb816a865-5022-11dc-9153-0090f5284f85\r\nb816a866-5022-11dc-9153-0090f5284f85\r\nb816a867-5022-11dc-9153-0090f5284f85\r\nb816a868-5022-11dc-9153-0090f5284f85\r\nb816a869-5022-11dc-9153-0090f5284f85\r\nb816a86a-5022-11dc-9153-0090f5284f85\r\nb816a86b-5022-11dc-9153-0090f5284f85\r\nb816a86c-5022-11dc-9153-0090f5284f85\r\nb816a86d-5022-11dc-9153-0090f5284f85\r\nb816a86e-5022-11dc-9153-0090f5284f85\r\nb816a86f-5022-11dc-9153-0090f5284f85\r\nb816a870-5022-11dc-9153-0090f5284f85\r\nb816a871-5022-11dc-9153-0090f5284f85\r\nb816a872-5022-11dc-9153-0090f5284f85\r\nb816a873-5022-11dc-9153-0090f5284f85\r\nb816a874-5022-11dc-9153-0090f5284f85\r\nb816a875-5022-11dc-9153-0090f5284f85\r\nb816a876-5022-11dc-9153-0090f5284f85\r\nb816a877-5022-11dc-9153-0090f5284f85\r\nb816a878-5022-11dc-9153-0090f5284f85\r\nb816a879-5022-11dc-9153-0090f5284f85\r\nb816a87a-5022-11dc-9153-0090f5284f85\r\nb816a87b-5022-11dc-9153-0090f5284f85\r\nb816a87c-5022-11dc-9153-0090f5284f85\r\nb816a87d-5022-11dc-9153-0090f5284f85\r\nb816a87e-5022-11dc-9153-0090f5284f85\r\nb816a87f-5022-11dc-9153-0090f5284f85\r\nb816a880-5022-11dc-9153-0090f5284f85 Add SFTP command ID\r\nb816a881-5022-11dc-9153-0090f5284f85 Remove SFTP command ID\r\nb816a882-5022-11dc-9153-0090f5284f85 New folder commmand ID\r\nb816a883-5022-11dc-9153-0090f5284f85 Rename SFTP host command ID\r\nb816a884-5022-11dc-9153-0090f5284f85 Launch key agent command ID\r\nb816a885-5022-11dc-9153-0090f5284f85 About box command ID\r\nb816a886-5022-11dc-9153-0090f5284f85 Close session command ID\r\nb816a887-5022-11dc-9153-0090f5284f85\r\nb816a888-5022-11dc-9153-0090f5284f85\r\nb816a889-5022-11dc-9153-0090f5284f85\r\nb816a88a-5022-11dc-9153-0090f5284f85\r\nb816a88b-5022-11dc-9153-0090f5284f85\r\nb816a88c-5022-11dc-9153-0090f5284f85\r\nb816a88d-5022-11dc-9153-0090f5284f85\r\nb816a88e-5022-11dc-9153-0090f5284f85\r\nb816a88f-5022-11dc-9153-0090f5284f85\r\nb816a890-5022-11dc-9153-0090f5284f85\r\nb816a891-5022-11dc-9153-0090f5284f85\r\nb816a892-5022-11dc-9153-0090f5284f85\r\nb816a893-5022-11dc-9153-0090f5284f85\r\nb816a894-5022-11dc-9153-0090f5284f85\r\nb816a895-5022-11dc-9153-0090f5284f85\r\nb816a896-5022-11dc-9153-0090f5284f85\r\nb816a897-5022-11dc-9153-0090f5284f85\r\nb816a898-5022-11dc-9153-0090f5284f85\r\nb816a899-5022-11dc-9153-0090f5284f85\r\nb816a89a-5022-11dc-9153-0090f5284f85\r\nb816a89b-5022-11dc-9153-0090f5284f85\r\nb816a89c-5022-11dc-9153-0090f5284f85\r\nb816a89d-5022-11dc-9153-0090f5284f85\r\nb816a89e-5022-11dc-9153-0090f5284f85\r\nb816a89f-5022-11dc-9153-0090f5284f85\r\nb816a8a0-5022-11dc-9153-0090f5284f85\r\nb816a8a1-5022-11dc-9153-0090f5284f85\r\nb816a8a2-5022-11dc-9153-0090f5284f85\r\nb816a8a3-5022-11dc-9153-0090f5284f85\r\nb816a8a4-5022-11dc-9153-0090f5284f85\r\nb816a8a5-5022-11dc-9153-0090f5284f85\r\nb816a8a6-5022-11dc-9153-0090f5284f85\r\nb816a8a7-5022-11dc-9153-0090f5284f85\r\nb816a8a8-5022-11dc-9153-0090f5284f85\r\nb816a8a9-5022-11dc-9153-0090f5284f85\r\nb816a8aa-5022-11dc-9153-0090f5284f85\r\nb816a8ab-5022-11dc-9153-0090f5284f85\r\nb816a8ac-5022-11dc-9153-0090f5284f85\r\nb816a8ad-5022-11dc-9153-0090f5284f85\r\nb816a8ae-5022-11dc-9153-0090f5284f85\r\nb816a8af-5022-11dc-9153-0090f5284f85\r\nb816a8b0-5022-11dc-9153-0090f5284f85\r\nb816a8b1-5022-11dc-9153-0090f5284f85\r\nb816a8b2-5022-11dc-9153-0090f5284f85\r\nb816a8b3-5022-11dc-9153-0090f5284f85\r\nb816a8b4-5022-11dc-9153-0090f5284f85\r\nb816a8b5-5022-11dc-9153-0090f5284f85\r\nb816a8b6-5022-11dc-9153-0090f5284f85\r\nb816a8b7-5022-11dc-9153-0090f5284f85\r\nb816a8b8-5022-11dc-9153-0090f5284f85\r\nb816a8b9-5022-11dc-9153-0090f5284f85\r\nb816a8ba-5022-11dc-9153-0090f5284f85\r\nb816a8bb-5022-11dc-9153-0090f5284f85\r\nb816a8bc-5022-11dc-9153-0090f5284f85\r\nb816a8bd-5022-11dc-9153-0090f5284f85\r\nb816a8be-5022-11dc-9153-0090f5284f85\r\nb816a8bf-5022-11dc-9153-0090f5284f85\r\nb816a8c0-5022-11dc-9153-0090f5284f85\r\nb816a8c1-5022-11dc-9153-0090f5284f85\r\nb816a8c2-5022-11dc-9153-0090f5284f85\r\nb816a8c3-5022-11dc-9153-0090f5284f85\r\nb816a8c4-5022-11dc-9153-0090f5284f85\r\nb816a8c5-5022-11dc-9153-0090f5284f85\r\nb816a8c6-5022-11dc-9153-0090f5284f85\r\nb816a8c7-5022-11dc-9153-0090f5284f85\r\nb816a8c8-5022-11dc-9153-0090f5284f85\r\nb816a8c9-5022-11dc-9153-0090f5284f85\r\nb816a8ca-5022-11dc-9153-0090f5284f85\r\nb816a8cb-5022-11dc-9153-0090f5284f85\r\nb816a8cc-5022-11dc-9153-0090f5284f85\r\nb816a8cd-5022-11dc-9153-0090f5284f85\r\nb816a8ce-5022-11dc-9153-0090f5284f85\r\nb816a8cf-5022-11dc-9153-0090f5284f85\r\nb816a8d0-5022-11dc-9153-0090f5284f85\r\nb816a8d1-5022-11dc-9153-0090f5284f85\r\nb816a8d2-5022-11dc-9153-0090f5284f85\r\nb816a8d3-5022-11dc-9153-0090f5284f85\r\nb816a8d4-5022-11dc-9153-0090f5284f85\r\nb816a8d5-5022-11dc-9153-0090f5284f85\r\nb816a8d6-5022-11dc-9153-0090f5284f85\r\nb816a8d7-5022-11dc-9153-0090f5284f85\r\nb816a8d8-5022-11dc-9153-0090f5284f85\r\nb816a8d9-5022-11dc-9153-0090f5284f85\r\nb816a8da-5022-11dc-9153-0090f5284f85\r\nb816a8db-5022-11dc-9153-0090f5284f85\r\nb816a8dc-5022-11dc-9153-0090f5284f85\r\nb816a8dd-5022-11dc-9153-0090f5284f85\r\nb816a8de-5022-11dc-9153-0090f5284f85\r\nb816a8df-5022-11dc-9153-0090f5284f85\r\nb816a8e0-5022-11dc-9153-0090f5284f85\r\nb816a8e1-5022-11dc-9153-0090f5284f85\r\nb816a8e2-5022-11dc-9153-0090f5284f85\r\nb816a8e3-5022-11dc-9153-0090f5284f85\r\nb816a8e4-5022-11dc-9153-0090f5284f85\r\nb816a8e5-5022-11dc-9153-0090f5284f85\r\nb816a8e6-5022-11dc-9153-0090f5284f85\r\nb816a8e7-5022-11dc-9153-0090f5284f85\r\nb816a8e8-5022-11dc-9153-0090f5284f85\r\nb816a8e9-5022-11dc-9153-0090f5284f85\r\nb816a8ea-5022-11dc-9153-0090f5284f85\r\nb816a8eb-5022-11dc-9153-0090f5284f85\r\nb816a8ec-5022-11dc-9153-0090f5284f85\r\nb816a8ed-5022-11dc-9153-0090f5284f85\r\nb816a8ee-5022-11dc-9153-0090f5284f85\r\nb816a8ef-5022-11dc-9153-0090f5284f85\r\nb816a8f0-5022-11dc-9153-0090f5284f85\r\nb816a8f1-5022-11dc-9153-0090f5284f85\r\nb816a8f2-5022-11dc-9153-0090f5284f85\r\nb816a8f3-5022-11dc-9153-0090f5284f85\r\nb816a8f4-5022-11dc-9153-0090f5284f85\r\nb816a8f5-5022-11dc-9153-0090f5284f85\r\nb816a8f6-5022-11dc-9153-0090f5284f85\r\nb816a8f7-5022-11dc-9153-0090f5284f85\r\nb816a8f8-5022-11dc-9153-0090f5284f85\r\nb816a8f9-5022-11dc-9153-0090f5284f85\r\nb816a8fa-5022-11dc-9153-0090f5284f85\r\nb816a8fb-5022-11dc-9153-0090f5284f85\r\nb816a8fc-5022-11dc-9153-0090f5284f85\r\nb816a8fd-5022-11dc-9153-0090f5284f85\r\nb816a8fe-5022-11dc-9153-0090f5284f85\r\nb816a8ff-5022-11dc-9153-0090f5284f85\r\nb816a900-5022-11dc-9153-0090f5284f85\r\nb816a901-5022-11dc-9153-0090f5284f85\r\nb816a902-5022-11dc-9153-0090f5284f85\r\nb816a903-5022-11dc-9153-0090f5284f85\r\nb816a904-5022-11dc-9153-0090f5284f85\r\nb816a905-5022-11dc-9153-0090f5284f85\r\nb816a906-5022-11dc-9153-0090f5284f85\r\nb816a907-5022-11dc-9153-0090f5284f85\r\nb816a908-5022-11dc-9153-0090f5284f85\r\nb816a909-5022-11dc-9153-0090f5284f85\r\nb816a90a-5022-11dc-9153-0090f5284f85\r\nb816a90b-5022-11dc-9153-0090f5284f85\r\nb816a90c-5022-11dc-9153-0090f5284f85\r\nb816a90d-5022-11dc-9153-0090f5284f85\r\nb816a90e-5022-11dc-9153-0090f5284f85\r\nb816a90f-5022-11dc-9153-0090f5284f85\r\nb816a910-5022-11dc-9153-0090f5284f85\r\nb816a911-5022-11dc-9153-0090f5284f85\r\nb816a912-5022-11dc-9153-0090f5284f85\r\nb816a913-5022-11dc-9153-0090f5284f85\r\nb816a914-5022-11dc-9153-0090f5284f85\r\nb816a915-5022-11dc-9153-0090f5284f85\r\nb816a916-5022-11dc-9153-0090f5284f85\r\nb816a917-5022-11dc-9153-0090f5284f85\r\nb816a918-5022-11dc-9153-0090f5284f85\r\nb816a919-5022-11dc-9153-0090f5284f85\r\nb816a91a-5022-11dc-9153-0090f5284f85\r\nb816a91b-5022-11dc-9153-0090f5284f85\r\nb816a91c-5022-11dc-9153-0090f5284f85\r\nb816a91d-5022-11dc-9153-0090f5284f85\r\nb816a91e-5022-11dc-9153-0090f5284f85\r\nb816a91f-5022-11dc-9153-0090f5284f85\r\nb816a920-5022-11dc-9153-0090f5284f85\r\nb816a921-5022-11dc-9153-0090f5284f85\r\nb816a922-5022-11dc-9153-0090f5284f85\r\nb816a923-5022-11dc-9153-0090f5284f85\r\nb816a924-5022-11dc-9153-0090f5284f85\r\nb816a925-5022-11dc-9153-0090f5284f85\r\nb816a926-5022-11dc-9153-0090f5284f85\r\nb816a927-5022-11dc-9153-0090f5284f85\r\nb816a928-5022-11dc-9153-0090f5284f85\r\nb816a929-5022-11dc-9153-0090f5284f85\r\nb816a92a-5022-11dc-9153-0090f5284f85\r\nb816a92b-5022-11dc-9153-0090f5284f85\r\nb816a92c-5022-11dc-9153-0090f5284f85\r\nb816a92d-5022-11dc-9153-0090f5284f85\r\nb816a92e-5022-11dc-9153-0090f5284f85\r\nb816a92f-5022-11dc-9153-0090f5284f85\r\nb816a930-5022-11dc-9153-0090f5284f85\r\nb816a931-5022-11dc-9153-0090f5284f85\r\nb816a932-5022-11dc-9153-0090f5284f85\r\nb816a933-5022-11dc-9153-0090f5284f85\r\nb816a934-5022-11dc-9153-0090f5284f85\r\nb816a935-5022-11dc-9153-0090f5284f85\r\nb816a936-5022-11dc-9153-0090f5284f85\r\nb816a937-5022-11dc-9153-0090f5284f85\r\nb816a938-5022-11dc-9153-0090f5284f85\r\nb816a939-5022-11dc-9153-0090f5284f85\r\nb816a93a-5022-11dc-9153-0090f5284f85\r\nb816a93b-5022-11dc-9153-0090f5284f85\r\nb816a93c-5022-11dc-9153-0090f5284f85\r\nb816a93d-5022-11dc-9153-0090f5284f85\r\nb816a93e-5022-11dc-9153-0090f5284f85\r\nb816a93f-5022-11dc-9153-0090f5284f85\r\nb816a940-5022-11dc-9153-0090f5284f85\r\nb816a941-5022-11dc-9153-0090f5284f85\r\nb816a942-5022-11dc-9153-0090f5284f85\r\nb816a943-5022-11dc-9153-0090f5284f85\r\nb816a944-5022-11dc-9153-0090f5284f85\r\nb816a945-5022-11dc-9153-0090f5284f85\r\nb816a946-5022-11dc-9153-0090f5284f85\r\nb816a947-5022-11dc-9153-0090f5284f85\r\nb816a948-5022-11dc-9153-0090f5284f85\r\nb816a949-5022-11dc-9153-0090f5284f85\r\nb816a94a-5022-11dc-9153-0090f5284f85\r\nb816a94b-5022-11dc-9153-0090f5284f85\r\nb816a94c-5022-11dc-9153-0090f5284f85\r\nb816a94d-5022-11dc-9153-0090f5284f85\r\nb816a94e-5022-11dc-9153-0090f5284f85\r\nb816a94f-5022-11dc-9153-0090f5284f85\r\nb816a950-5022-11dc-9153-0090f5284f85\r\nb816a951-5022-11dc-9153-0090f5284f85\r\nb816a952-5022-11dc-9153-0090f5284f85\r\nb816a953-5022-11dc-9153-0090f5284f85\r\nb816a954-5022-11dc-9153-0090f5284f85\r\nb816a955-5022-11dc-9153-0090f5284f85\r\nb816a956-5022-11dc-9153-0090f5284f85\r\nb816a957-5022-11dc-9153-0090f5284f85\r\nb816a958-5022-11dc-9153-0090f5284f85\r\nb816a959-5022-11dc-9153-0090f5284f85\r\nb816a95a-5022-11dc-9153-0090f5284f85\r\nb816a95b-5022-11dc-9153-0090f5284f85\r\nb816a95c-5022-11dc-9153-0090f5284f85\r\nb816a95d-5022-11dc-9153-0090f5284f85\r\nb816a95e-5022-11dc-9153-0090f5284f85\r\nb816a95f-5022-11dc-9153-0090f5284f85\r\nb816a960-5022-11dc-9153-0090f5284f85\r\nb816a961-5022-11dc-9153-0090f5284f85\r\nb816a962-5022-11dc-9153-0090f5284f85\r\nb816a963-5022-11dc-9153-0090f5284f85\r\nb816a964-5022-11dc-9153-0090f5284f85\r\nb816a965-5022-11dc-9153-0090f5284f85\r\nb816a966-5022-11dc-9153-0090f5284f85\r\nb816a967-5022-11dc-9153-0090f5284f85\r\nb816a968-5022-11dc-9153-0090f5284f85\r\nb816a969-5022-11dc-9153-0090f5284f85\r\nb816a96a-5022-11dc-9153-0090f5284f85\r\nb816a96b-5022-11dc-9153-0090f5284f85\r\nb816a96c-5022-11dc-9153-0090f5284f85\r\nb816a96d-5022-11dc-9153-0090f5284f85\r\nb816a96e-5022-11dc-9153-0090f5284f85\r\nb816a96f-5022-11dc-9153-0090f5284f85\r\nb816a970-5022-11dc-9153-0090f5284f85\r\nb816a971-5022-11dc-9153-0090f5284f85\r\nb816a972-5022-11dc-9153-0090f5284f85\r\nb816a973-5022-11dc-9153-0090f5284f85\r\nb816a974-5022-11dc-9153-0090f5284f85\r\nb816a975-5022-11dc-9153-0090f5284f85\r\nb816a976-5022-11dc-9153-0090f5284f85\r\nb816a977-5022-11dc-9153-0090f5284f85\r\nb816a978-5022-11dc-9153-0090f5284f85\r\nb816a979-5022-11dc-9153-0090f5284f85\r\nb816a97a-5022-11dc-9153-0090f5284f85\r\nb816a97b-5022-11dc-9153-0090f5284f85\r\nb816a97c-5022-11dc-9153-0090f5284f85\r\nb816a97d-5022-11dc-9153-0090f5284f85\r\nb816a97e-5022-11dc-9153-0090f5284f85\r\nb816a97f-5022-11dc-9153-0090f5284f85\r\nb816a980-5022-11dc-9153-0090f5284f85\r\nb816a981-5022-11dc-9153-0090f5284f85\r\nb816a982-5022-11dc-9153-0090f5284f85\r\nb816a983-5022-11dc-9153-0090f5284f85\r\nb816a984-5022-11dc-9153-0090f5284f85\r\nb816a985-5022-11dc-9153-0090f5284f85\r\nb816a986-5022-11dc-9153-0090f5284f85\r\nb816a987-5022-11dc-9153-0090f5284f85\r\nb816a988-5022-11dc-9153-0090f5284f85\r\nb816a989-5022-11dc-9153-0090f5284f85\r\nb816a98a-5022-11dc-9153-0090f5284f85\r\nb816a98b-5022-11dc-9153-0090f5284f85\r\nb816a98c-5022-11dc-9153-0090f5284f85\r\nb816a98d-5022-11dc-9153-0090f5284f85\r\nb816a98e-5022-11dc-9153-0090f5284f85\r\nb816a98f-5022-11dc-9153-0090f5284f85\r\nb816a990-5022-11dc-9153-0090f5284f85\r\nb816a991-5022-11dc-9153-0090f5284f85\r\nb816a992-5022-11dc-9153-0090f5284f85\r\nb816a993-5022-11dc-9153-0090f5284f85\r\nb816a994-5022-11dc-9153-0090f5284f85\r\nb816a995-5022-11dc-9153-0090f5284f85\r\nb816a996-5022-11dc-9153-0090f5284f85\r\nb816a997-5022-11dc-9153-0090f5284f85\r\nb816a998-5022-11dc-9153-0090f5284f85\r\nb816a999-5022-11dc-9153-0090f5284f85\r\nb816a99a-5022-11dc-9153-0090f5284f85\r\nb816a99b-5022-11dc-9153-0090f5284f85\r\nb816a99c-5022-11dc-9153-0090f5284f85\r\nb816a99d-5022-11dc-9153-0090f5284f85\r\nb816a99e-5022-11dc-9153-0090f5284f85\r\nb816a99f-5022-11dc-9153-0090f5284f85\r\nb816a9a0-5022-11dc-9153-0090f5284f85\r\nb816a9a1-5022-11dc-9153-0090f5284f85\r\nb816a9a2-5022-11dc-9153-0090f5284f85\r\nb816a9a3-5022-11dc-9153-0090f5284f85\r\nb816a9a4-5022-11dc-9153-0090f5284f85\r\nb816a9a5-5022-11dc-9153-0090f5284f85\r\nb816a9a6-5022-11dc-9153-0090f5284f85\r\nb816a9a7-5022-11dc-9153-0090f5284f85\r\nb816a9a8-5022-11dc-9153-0090f5284f85\r\nb816a9a9-5022-11dc-9153-0090f5284f85\r\nb816a9aa-5022-11dc-9153-0090f5284f85\r\nb816a9ab-5022-11dc-9153-0090f5284f85\r\nb816a9ac-5022-11dc-9153-0090f5284f85\r\nb816a9ad-5022-11dc-9153-0090f5284f85\r\nb816a9ae-5022-11dc-9153-0090f5284f85\r\nb816a9af-5022-11dc-9153-0090f5284f85\r\nb816a9b0-5022-11dc-9153-0090f5284f85\r\nb816a9b1-5022-11dc-9153-0090f5284f85\r\nb816a9b2-5022-11dc-9153-0090f5284f85\r\nb816a9b3-5022-11dc-9153-0090f5284f85\r\nb816a9b4-5022-11dc-9153-0090f5284f85\r\nb816a9b5-5022-11dc-9153-0090f5284f85\r\nb816a9b6-5022-11dc-9153-0090f5284f85\r\nb816a9b7-5022-11dc-9153-0090f5284f85\r\nb816a9b8-5022-11dc-9153-0090f5284f85\r\nb816a9b9-5022-11dc-9153-0090f5284f85\r\nb816a9ba-5022-11dc-9153-0090f5284f85\r\nb816a9bb-5022-11dc-9153-0090f5284f85\r\nb816a9bc-5022-11dc-9153-0090f5284f85\r\nb816a9bd-5022-11dc-9153-0090f5284f85\r\nb816a9be-5022-11dc-9153-0090f5284f85\r\nb816a9bf-5022-11dc-9153-0090f5284f85\r\nb816a9c0-5022-11dc-9153-0090f5284f85\r\nb816a9c1-5022-11dc-9153-0090f5284f85\r\nb816a9c2-5022-11dc-9153-0090f5284f85\r\nb816a9c3-5022-11dc-9153-0090f5284f85\r\nb816a9c4-5022-11dc-9153-0090f5284f85\r\nb816a9c5-5022-11dc-9153-0090f5284f85\r\nb816a9c6-5022-11dc-9153-0090f5284f85\r\nb816a9c7-5022-11dc-9153-0090f5284f85\r\nb816a9c8-5022-11dc-9153-0090f5284f85\r\nb816a9c9-5022-11dc-9153-0090f5284f85\r\nb816a9ca-5022-11dc-9153-0090f5284f85\r\nb816a9cb-5022-11dc-9153-0090f5284f85\r\nb816a9cc-5022-11dc-9153-0090f5284f85\r\nb816a9cd-5022-11dc-9153-0090f5284f85\r\nb816a9ce-5022-11dc-9153-0090f5284f85\r\nb816a9cf-5022-11dc-9153-0090f5284f85\r\nb816a9d0-5022-11dc-9153-0090f5284f85\r\nb816a9d1-5022-11dc-9153-0090f5284f85\r\nb816a9d2-5022-11dc-9153-0090f5284f85\r\nb816a9d3-5022-11dc-9153-0090f5284f85\r\nb816a9d4-5022-11dc-9153-0090f5284f85\r\nb816a9d5-5022-11dc-9153-0090f5284f85\r\nb816a9d6-5022-11dc-9153-0090f5284f85\r\nb816a9d7-5022-11dc-9153-0090f5284f85\r\nb816a9d8-5022-11dc-9153-0090f5284f85\r\nb816a9d9-5022-11dc-9153-0090f5284f85\r\nb816a9da-5022-11dc-9153-0090f5284f85\r\nb816a9db-5022-11dc-9153-0090f5284f85\r\nb816a9dc-5022-11dc-9153-0090f5284f85\r\nb816a9dd-5022-11dc-9153-0090f5284f85\r\nb816a9de-5022-11dc-9153-0090f5284f85\r\nb816a9df-5022-11dc-9153-0090f5284f85\r\nb816a9e0-5022-11dc-9153-0090f5284f85\r\nb816a9e1-5022-11dc-9153-0090f5284f85\r\nb816a9e2-5022-11dc-9153-0090f5284f85\r\nb816a9e3-5022-11dc-9153-0090f5284f85\r\nb816a9e4-5022-11dc-9153-0090f5284f85\r\nb816a9e5-5022-11dc-9153-0090f5284f85\r\nb816a9e6-5022-11dc-9153-0090f5284f85\r\nb816a9e7-5022-11dc-9153-0090f5284f85\r\nb816a9e8-5022-11dc-9153-0090f5284f85\r\nb816a9e9-5022-11dc-9153-0090f5284f85\r\nb816a9ea-5022-11dc-9153-0090f5284f85\r\nb816a9eb-5022-11dc-9153-0090f5284f85\r\nb816a9ec-5022-11dc-9153-0090f5284f85\r\nb816a9ed-5022-11dc-9153-0090f5284f85\r\nb816a9ee-5022-11dc-9153-0090f5284f85\r\nb816a9ef-5022-11dc-9153-0090f5284f85\r\nb816a9f0-5022-11dc-9153-0090f5284f85\r\nb816a9f1-5022-11dc-9153-0090f5284f85\r\nb816a9f2-5022-11dc-9153-0090f5284f85\r\nb816a9f3-5022-11dc-9153-0090f5284f85\r\nb816a9f4-5022-11dc-9153-0090f5284f85\r\nb816a9f5-5022-11dc-9153-0090f5284f85\r\nb816a9f6-5022-11dc-9153-0090f5284f85\r\nb816a9f7-5022-11dc-9153-0090f5284f85\r\nb816a9f8-5022-11dc-9153-0090f5284f85\r\nb816a9f9-5022-11dc-9153-0090f5284f85\r\nb816a9fa-5022-11dc-9153-0090f5284f85\r\nb816a9fb-5022-11dc-9153-0090f5284f85\r\nb816a9fc-5022-11dc-9153-0090f5284f85\r\nb816a9fd-5022-11dc-9153-0090f5284f85\r\nb816a9fe-5022-11dc-9153-0090f5284f85\r\nb816a9ff-5022-11dc-9153-0090f5284f85\r\nb816aa00-5022-11dc-9153-0090f5284f85\r\nb816aa01-5022-11dc-9153-0090f5284f85\r\nb816aa02-5022-11dc-9153-0090f5284f85\r\nb816aa03-5022-11dc-9153-0090f5284f85\r\nb816aa04-5022-11dc-9153-0090f5284f85\r\nb816aa05-5022-11dc-9153-0090f5284f85\r\nb816aa06-5022-11dc-9153-0090f5284f85\r\nb816aa07-5022-11dc-9153-0090f5284f85\r\nb816aa08-5022-11dc-9153-0090f5284f85\r\nb816aa09-5022-11dc-9153-0090f5284f85\r\nb816aa0a-5022-11dc-9153-0090f5284f85\r\nb816aa0b-5022-11dc-9153-0090f5284f85\r\nb816aa0c-5022-11dc-9153-0090f5284f85\r\nb816aa0d-5022-11dc-9153-0090f5284f85\r\nb816aa0e-5022-11dc-9153-0090f5284f85\r\nb816aa0f-5022-11dc-9153-0090f5284f85\r\nb816aa10-5022-11dc-9153-0090f5284f85\r\nb816aa11-5022-11dc-9153-0090f5284f85\r\nb816aa12-5022-11dc-9153-0090f5284f85\r\nb816aa13-5022-11dc-9153-0090f5284f85\r\nb816aa14-5022-11dc-9153-0090f5284f85\r\nb816aa15-5022-11dc-9153-0090f5284f85\r\nb816aa16-5022-11dc-9153-0090f5284f85\r\nb816aa17-5022-11dc-9153-0090f5284f85\r\nb816aa18-5022-11dc-9153-0090f5284f85\r\nb816aa19-5022-11dc-9153-0090f5284f85\r\nb816aa1a-5022-11dc-9153-0090f5284f85\r\nb816aa1b-5022-11dc-9153-0090f5284f85\r\nb816aa1c-5022-11dc-9153-0090f5284f85\r\nb816aa1d-5022-11dc-9153-0090f5284f85\r\nb816aa1e-5022-11dc-9153-0090f5284f85\r\nb816aa1f-5022-11dc-9153-0090f5284f85\r\nb816aa20-5022-11dc-9153-0090f5284f85\r\nb816aa21-5022-11dc-9153-0090f5284f85\r\nb816aa22-5022-11dc-9153-0090f5284f85\r\nb816aa23-5022-11dc-9153-0090f5284f85\r\nb816aa24-5022-11dc-9153-0090f5284f85\r\nb816aa25-5022-11dc-9153-0090f5284f85\r\nb816aa26-5022-11dc-9153-0090f5284f85\r\nb816aa27-5022-11dc-9153-0090f5284f85\r\nb816aa28-5022-11dc-9153-0090f5284f85\r\nb816aa29-5022-11dc-9153-0090f5284f85\r\nb816aa2a-5022-11dc-9153-0090f5284f85\r\nb816aa2b-5022-11dc-9153-0090f5284f85\r\n"
  },
  {
    "path": "po/CMakeLists.txt",
    "content": "set(LANGUAGES\n  bg\n  ca\n  cs\n  cy\n  da_DK\n  de\n  el_GR\n  es\n  et\n  fi\n  fr\n  he\n  hi\n  hu\n  it\n  ja\n  ko\n  lv\n  nl\n  pl\n  pt\n  pt_BR\n  ro\n  ru\n  sk\n  sv\n  tr\n  zh_CN\n  zh_TW)\n\nforeach(lang ${LANGUAGES})\n  set(COMPILED_TRANSLATION \"${lang}/swish.mo\")\n  install(FILES \"${COMPILED_TRANSLATION}\" DESTINATION \"${lang}/LC_MESSAGES\")\nendforeach()\n"
  },
  {
    "path": "po/bg/swish.po",
    "content": "# \n# Translators:\n# albertvision <avbincco@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: albertvision <avbincco@gmail.com>\\n\"\n\"Language-Team: Bulgarian (http://www.transifex.com/projects/p/swish/language/bg/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: bg\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Не мога да създам файла на сървъра\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Копиране на '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"на '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Папката вече съдържа файл с име '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Искате ли да го презапишете?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Потвърди презаписа\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Копиране...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Не мога да кача файловете\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Нямате права за писане в тази директория\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Нова SFTP връзка\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Създай\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Име:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Например: \\\"Home Computer\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Посочете подробна информация за компютъра и акаунта, който бихте искали да се свържете:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Хост:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Порт:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Потребител:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Посочете директория на сървъра, в която искате Swish да се логне:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"П&ът:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Например: /home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Отказ\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Името не може да е по дълго от 30 знака.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Името на хоста е грешно.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Портът е невалиден(между 0 и 65535)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Потр. име е невалидно.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Пътят е невалиден.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Конекция със същото име вече съществува. Моля сменте името.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Попълнете всички полета.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Парола\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"ОК\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Покажи &подробности (може да не е на вашия език)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Скрий &подробности\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Папката вече съдържа файл с името '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Искате ли да презапишете файла\\n\\n\\t%1%\\n\\nс този?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Файлът вече съществува\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Несъвпадащ host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ВНИМАНИЕ: SSH host-key е променен!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH host-key изпратен от '%1%' да се идентифицира, несъвпада с вече познатия ключ за този сървър. Това означава, че или трето лице се представя за компютъра с, който се опитвате се свържете или системния администратор може да е сменил ключа.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Важно е да проверите за правилния key fingerprint:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Доверявам се на ключа: &обнови и се свържи\\nНяма да има нужда, да проверявате ключа, освен ако не се промени\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Вярвам на ключа: &просто се свържи\\nЩе бъдете предупредени отново при следващото свързване\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Отказ\\nИзберете тази опция, освен ако не сте сигурни, че ключа е превилен\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Непознат host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Сървър  '%1%' се идентифицира с SSH host-key, с отпечтък:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Ако не очаквате този ключ, трето лице може да се представя за компютъра, с който се опитвате да се свържете.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Потвърди непознат SSH host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Доверявам се на ключа: &запази го и се свържи\\nНяма да има нужда, да проверявате ключа, освен ако не се промени\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Доверявам се на ключа:  &само се свържи\\nНяма да има нужда, да проверявате ключа, освен ако не се промени\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Име\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Хост\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Потр. име\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Порт\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Отдалечен път\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Тип\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Мрежово у-во\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Добави SFTP Връзка\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Създай нова SFTP връзка с Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Добави SFTP Връзка...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Добави Взръзка\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP Задачи\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Тези задачи ти помагат да управляваш SFTP връзките на Swish.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Зареди key agent\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Зареди Putty SSH key agent\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Зареди key agent\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Премахни SFTP Връзка\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Премахни SFTP връзката създадена с Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Премахни SFTP Връзката...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Премахни Връзката\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Размер\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Дата на промяна\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Дата на достъп\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Права\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Притежател\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Група\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID на притежателя\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID на групата\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Отвори &линк\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Отвори\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Не мога да отворя линк\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Може би нямате права.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Не мога да отворя айла\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Задачи - папки и файлове\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Тези задачи ти помагат да управляваш отсрещните файлове.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Не може да се изтрие обекта\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Нова &папка\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Създай нова, празна папка в папката, която сте отворили.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Направи нова папка\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Нова папка\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Не може да се създаде нова папка\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Keyboard-interactive request\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Няма достъп до директорията\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Непознат път\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Проверете дали пътя е въведен вярно.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Не може да се преименува обекта\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Няма достъп до обекта\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Няма достъп до обектите\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Няма достъп до папката\"\n"
  },
  {
    "path": "po/ca/swish.po",
    "content": "# \n# Translators:\n# lluis.dalmau <lluis.dalmau@guifi.net>, 2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: lluis.dalmau <lluis.dalmau@guifi.net>\\n\"\n\"Language-Team: Catalan (http://www.transifex.com/projects/p/swish/language/ca/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: ca\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Impossible crear l'arxiu al servidor\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copiant '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"A '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"La carpeta ja conté un arxiu anomenat '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Voleu reemplaçar-lo?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirmar sobrescriptura\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Copiant...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Impossible transferir arxius\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"No teniu permisos per escriure al directori.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nova connexió SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Crear\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Etiqueta:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Per exemple: \\\"El meu ordinador\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Descriure els detalls de l'ordinador i el compte al qual voleu connectar:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Amfitrió:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Usuari\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Descriure el directori del servidor on voleu que Swish comenci la connexió:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"Rut&a:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exemple: /home/elteunom\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Cancel·la\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"L'etiqueta no pot tenir més de 30 caràcters.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"El nom d'amfitrió no és vàlid.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"El port no és vàlid (entre 0 i 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Nom d'usuari invàlid.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Ruta invàlida.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Existeix una connexió amb la mateixa etiqueta. Prova'n una altra.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Completa tots els camps.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Contrasenya\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"D'acord\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Mostra &detalls (poden no ser en la teva llengua)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Amaga &detalls\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"La carpeta té un arxiu anomenat  '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Vols substituir l'arxiu existent⏎\\n⏎\\n⇥%1%⏎\\n⏎\\nper aquest altre?⏎\\n⏎\\n⇥%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"L'arxiu existeix\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Error en la clau\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"PERILL: la clau SSH de l'amfitrió ha canviat!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"La clau SSH de l'anfitrió enviada per '%1%' per a identificar-se no coincideix amb la clau d'aquest servidor.  Això pot ser perque algú està intentant falsejar l'ordinador al qual estàs volent accedir, o bé, que l'administrador del sistema ha canviat la clau SSH.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"És importat comprovar que l'empremta sigui correcta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confio en aquesta clau: &actualitza-la i connecta\\nNo et caldrà tornar-la a verificar mentre no canviï.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Confio en aquesta clau: &només connecta\\nSeràs avisat sobre aquesta clau a la propera connexió\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Cancel·la\\nEscull aquesta opció només si estàs segur que la clau és correcta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Clau desconeguda\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"El servidor '%1%' s'ha identificat amb la clau d'amfitrió SSH, la qual té l'empremta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Si no esperaves aquesta clau, algú podria intentat fer-se passar per l'ordinador amfitrió al que volies connectar.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifica la clau SSH desconeguda\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confio en la clau: %desa i connecta\\nNo hauràs de tornar a verificar la clau mentre no canviï.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Confio en la clau: &només connecta\\nHauràs de tornar a verificar la clau a la propera connexió\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nom\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Amfitrió\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Nom d'usuari\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Ruta remota\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tipus\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Unitat de xarxa\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Afegir connexió SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Crear una nova connexió SFTP amb Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Afegir una connexió SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Afegir Connexió\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Accions SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Aquestes accions t'ajuden a administrar les connexions SFTP.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"Activa el gestor de claus\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Activa l'agent de claus Putty SSH, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Activa l'agent de claus\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Esborra Connexió SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Esborra una connexió SFTP creada per Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Esborra Connexió SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Esborra Connexió\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Mida\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Data de modificació\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Data d'accés\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permisos\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Propietari\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grup\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID propietari\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID grup\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Obrir enllaç\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Obrir\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Impossible obrir l'enllaç\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Pot ser que no tinguis permisos.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Impossible obrir l'arxiu\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Accions sobre arxius i carpetes\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Aquestes accions t'ajuden a administrar els teus arxius remots.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Impossible d'esborrar\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nova &carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Crear nova carpeta i buida a la carpeta que hagi obert.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Crea nova carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nova carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Impossible crear carpeta\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Petició de paraula clau\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Accés impossible al directori\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Ruta no reconeguda\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Comprova que la ruta sigui correcta.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Canvi de nom impossible\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Accés impossible\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Accés impossible\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Accés a la carpeta impossible\"\n"
  },
  {
    "path": "po/compile_mo.sh",
    "content": "#!/bin/sh\nfor lang in *; do\n   if [ -d $lang ]; then\n      msgfmt -c --statistics --verbose -o $lang/swish.mo $lang/swish.po\n   fi\ndone\n"
  },
  {
    "path": "po/cs/swish.po",
    "content": "# \n# Translators:\n# Karol Kružel <k.kruzel@gmail.com>, 2012\n# mishak <mishak@mishak.net>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: mishak <mishak@mishak.net>\\n\"\n\"Language-Team: Czech (http://www.transifex.com/projects/p/swish/language/cs/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: cs\\n\"\n\"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Soubor nelze na serveru vytvořit:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Kopíruje se '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Do '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Tato složka již obsahuje soubor se jménem  '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Chcete provést nahrazení?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Potvrzení přepsání souboru\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopíruji...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Soubory nelze přenést\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Zřejmě nemáte oprávnění pro zápis do této složky.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nové SFTP spojení\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Vytvořit\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Název:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Například: \\\"Počítač v kanceláři\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Zadejte údaje o počítači a účtu ke kterému se chcete připojit:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Hostitel:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Uživatel:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Zadejte počáteční adresář na serveru, ke kterému se má Switch připojit:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Cesta:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Například: /home/vaseJmeno\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Zrušit\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Název nemůže být delší než 30 znaků.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Název hostitele je chybný.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Číslo portu je chybný (musí být mezi 0 a 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Uživatelské jméno je chybné.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Cesta je chybná.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Spojení s tímto názvem už existuje. Zkuste prosím jiný název.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Vyplňte všechny pole.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Heslo\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Zobrazit &detaily (mohou být v jíném jazyce)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Skrýt &detaily\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Složky již obsahuje soubor pojmenovaný  '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Chcete přepsat existující soubor\\n\\n\\t%1%\\n\\ntímto souborem?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Soubor již existuje\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Špatný host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"VAROVÁNÍ: SSH host-key se změnil!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH host-key zaslaný  '%1%' k ověření své identity se neshoduje s již známým klíčem tohoto serveru. Může to znamenat, že se třetí strana snaží předstírat, že je počítač, ke kterému se snažíte připojit nebo správce systému právě změnil přístupový klíč.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Je důležité ověřit, že toto je správný otisk (fingerprint):\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Důvěřuji tomuto klíči: &updatovat a připojit se\\nNemusíte ověřovat tento klíč znovu, pokud se nezmění.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Důvěřuji tomuto klíči: &pouze se připojit\\nBudete varování na tento klíč, jakmile se znovu připojíte\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Stornol\\nVyberte tuto možnost pokud si nejste jisti, zda je klíč správný.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Neznámý host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Server  '%1%' se identifikovat SSH klíčem, ke kterému náleží otisk(fingerprint):\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Pokud je tento klíč neočekávaný, může se jednat o snahu třetí strany předstírat, že je počítačem, ke kterému se snažíte připojit.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Ověřte neznámý SSH host-key\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Důvěřuji tomuto klíči: &uložit a připojit se\\nNebudete muset ověřovat tento klíč v budoucnu, pokud se nezmění.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Důvěřuji tomuto klíči: &pouze připojit\\nBudete znovu vyzvání k ověření toho klíče při příštím připojení\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Jméno\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Hostitel\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Uživatelské jméno\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Vzdálená cesta\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Síťová jednotka\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Přidat SFTP připojení\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Vytvořit nové SFTP připojení se Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Přidat SFTP připojení...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Přidat připojení\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP úkoly\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Tyto úkoly vám pomůžou s nastavením Swish SFTP připojení.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"Spustit k&líčenku\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Spustit Putty SSH klíčenku, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Spustit klíčenku\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Odebrat SFTP připojení\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Odebrat SFTP připojení vytvořené se Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Odebrat SFTP připojení\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Odebrat připojení\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Velikost\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Datum změny\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Datum přístupu\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Oprávnění\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Vlastník\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Skupina\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID vlastníka\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID skupiny\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Otevřít odk&az\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Otevřít\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Nelze otevřít odkaz\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Zřejmě nemáte potřebná oprávnění.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Nelze otevřít soubor\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Operace se soubory a složkami\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Tyto operace slouží ke správě vzdálených souborů.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Položku nelze smazat\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nová &složka\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Vytvořit v otevřené složce novou prázdnou podsložku.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Vytvoř novou složku.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nová složka\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nelze vytvořit novou složku\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Požadavek na použití klávesnice\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Nelze získat přístup ke složce\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Cesta nebyla rozpoznána\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Ověřte, zda byla cesta zadána správně.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Položku nelze přejmenovat.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Položka je nedostupná\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Položky jsou nedostupné\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Složka je nedostupná.\"\n"
  },
  {
    "path": "po/cy/swish.po",
    "content": "# \n# Translators:\n# Alexander Lamaison <alexander.lamaison@gmail.com>, 2013\n# rdj <rhysjones437@googlemail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Alexander Lamaison <alexander.lamaison@gmail.com>\\n\"\n\"Language-Team: Welsh (http://www.transifex.com/projects/p/swish/language/cy/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: cy\\n\"\n\"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copio '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"I '{1}\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Mae'r ffolder yn cynnwys y ffeil '{1}' yn barod\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Hoffech chi eu cyfnewid?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Cadarnhewch y cyfnewidiad\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Adysgrifio...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Methu danfod ty ffeiliau\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Cysylltiad STFP Newydd\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Creu\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Label:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Er engraifft: \\\"Cyfrifiadur Cartref\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Nodwch y wybodaeth amdano'r cyfrifiadur ac yr cyfrif hoffech chi cysylltu a:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Cynhaliwr\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Porth:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Defnyddiwr:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Nodwch pa cyfarwyddiadur ar y gwasanaethydd hoffwch Swish i ddechrau cysylltiad a\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"P&arth:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Er enghraifft: /Cartref/eichcyfeirenw\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Dirymu\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Ni all y label fod yn fwy na 30 llythyren o hyd.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Mae enw'r cynhaliwr yn annilys.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Dydy'r porth ddim yn ddilys (rhwng 0 a 65535)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Mae'r cyfrifenw yn annilys.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Mae'r llwybr yn annilys.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Mae cysylltiad gyda'r un label yn bodoli. A wnewch chi trio un arall.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Cwblhewch pob maes.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Cyfrinair\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Dangos &manylion (nid oes angen iddo fod yn eich iaith)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Cuddiwch &gwybodaeth\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Mae'r ffolder yn cynnwy y ffeil o'r enw '%1%' yn barod\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Hoffech chi amnewid y ffeil presennol\\n \\n»%1%\\n\\ngyda?\\n\\n»%2% \"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Ffeil yn bodoli\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Enw\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Cynhaliwr\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Cyfeirenw\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Porth\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Llwybr anghysbell\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Math\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Gyriant Rhyngwaith\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Ychwanegu Cysylltiad SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Creu Cysylltiad SFTP newydd gyda Swish\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Ychwanegu Cysylltiad SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Ychwanegwch Cysylltiad\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Tasgau SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Mae'r tasgau yma yn eich helpu rheoli eich cysylltiadau SFTP.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Tynnu'r Cysylltiad SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Tynnu cysylltiad a greuwyd gyda Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Tynnu'r Cysylltiad SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Tynnu Cysylltiad\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Maint\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Dyddiad Newidiwyd\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Dyddiad Agorwyd\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Caniataodau\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Perchenog\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grwp\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Adnabyddiaeth Perchennog\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Adnabyddiaeth Grwp\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Agor &linc\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Agor\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Methu agor y cysylltiad\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Efallai nid oes caniatad  ganddoch chi\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Methu agor y ffeil\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Methu dileu'r eitem\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Cais bysyllfwrdd rhyngweithiol\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Methu cyrchu y cyfarwyddiadur\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Llwybr heb eu cydnabod\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Gwiriwch bod y llwybr wedi ei mewnbynnu yn gywir.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Methu ailenwi'r eitem\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Methu ailenwi'r gwrthrych\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Methu agor y gwrthrychau\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Methu cyrchu'r ffolder\"\n"
  },
  {
    "path": "po/da_DK/swish.po",
    "content": "# \n# Translators:\n# Martin Finnerup <hackerfinn@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Martin Finnerup <hackerfinn@gmail.com>\\n\"\n\"Language-Team: Danish (Denmark) (http://www.transifex.com/projects/p/swish/language/da_DK/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: da_DK\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Denne mappe indeholder allerede en fil kaldet '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Vil du gerne overskrive den?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Bekræft overskrivning af fil\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopierer...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Ny SFTP Forbindelse\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Opret\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Mærkat:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"For eksempel: \\\"Hjemme Computer\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Indtast detaljerne for den computer og bruger du gerne vil forbinde til:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Vært:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Bruger:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Specificér den mapper på serveren som du gerne vil have Swish til at starte forbindelsen i:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"S&ti:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Eksempel: /home/ditbrugernavn\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Annuller\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Mærkaten kan ikke være længere end 30 tegn lang.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Værtsnavnet er ugyldigt.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Porten er ikke gyldig (mellem 0 og 65535)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Brugernavnet er ugyldigt.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Stien er ugyldig.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"En forbindelse med den samme mærkat eksisterer allerede. Prøv en anden.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Udfyld alle felter.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Kodeord:\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Vis &detaljer (hvilket måske ikke er i dit sprog)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Skjul &detaljer\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Navn\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Vært\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Brugernavn\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Fjernsti\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Type\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Netværksdrev\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Størrelse\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Dato ændret\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Dato brugt\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Tilladelser\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Ejer\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Gruppe\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Ejer ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Gruppe ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Du har måske ikke tilladelse.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Tastatur-interaktiv forespørgsel\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Ikke i stand til at opnå adgang til mappen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Sti ikke genkendt\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Check om stien blev stavet korrekt.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Ikke i stand til at omdøbe genstand\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"\"\n"
  },
  {
    "path": "po/de/swish.po",
    "content": "# \n# Translators:\n# Gottfried von Makhir <>, 2012\n# leolabs2 <leo.bernard@me.com>, 2012\n# Philippe Käüver <>, 2012\n# vr0 <vr0@penguin.lu>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: vr0 <vr0@penguin.lu>\\n\"\n\"Language-Team: German (http://www.transifex.com/projects/p/swish/language/de/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: de\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Die Datei kann auf dem Server nicht erstellt werden:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"kopiert '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"zu '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Der Ordner enthält bereits eine Datei namens '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Möchten Sie einen Austausch vornehmen?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Austausch der Datei bestätigen\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopieren...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Die Datei kann nicht übertragen werden\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Du hast keine Rechte um in diesem Verzeichniss zu schreiben.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Neue SFTP-Verbindung\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Anlegen\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Bezeichnung\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Zum Beispiel: \\\"Mein Computer\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Details des Rechners und Accounts, mit dem Sie sich verbinden wollen:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Rechner:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Benutzer:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Das Verzeichnis auf dem Rechner, mit dem die Verbindung hergestellt werden soll:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"Pf&ad\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Beispiel: /home/benutzername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Abbrechen\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Die Bezeichnung darf nicht länger als 30 Zeichen sein.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Der Rechnername ist ungültig.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Der Port ist ungültig (nur zwischen 0 und 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Der Benutzername ist ungültig.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Der Pfad ist ungültig\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Eine Verbindung mit dieser Bezeichnung existiert bereits. Bitte versuchen Sie eine andere.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Füllen Sie alle Felder aus.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Passwort\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"&Details anzeigen (möglicherweise ohne Übersetzung)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"&Details verbergen\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Der Ordner enthält bereits eine Datei namens '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Wollen Sie die bestehende Datei\\n\\n\\t%1%\\n\\ndurch diese ersetzen?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Die Datei existiert bereits\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Falscher Host-Schlüssel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ACHTUNG: Der SSH Host-Schlüssel wurde geändert!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Der SSH Host-Schlüssel, der von '%1%' gesendet wurde, um sich zu identifizieren, passt nicht zu dem für diesen Rechner gespeicherten Schlüssel. Das kann bedeuten, dass Dritte vorgeben, der Rechner zu sein, mit dem Sie sich verbinden wollen, oder dass der Systemadministrator diesen Schlüssel ausgetauscht hat.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Es ist wichtig, zu überprüfen ob dies der richtige Fingerabdruck ist:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ich vertraue diesem Schlüssel: &Aktualisieren und Verbinden\\nSie müssen diesen Schlüssel nicht erneut verifizieren, außer er wird verändert\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Ich vertraue diesem Schlüssel: &Nur verbinden\\nSie werden bei der nächsten Verbindung mit diesem Schlüssel erneut gewarnt\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Abbrechen\\nWählen Sie diese Option, außer Sie sind sicher, dass der Schlüssel korrekt ist.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Unbekannter Host-Schlüssel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Der Server '%1%' hat sich mit einem SSH Host-Schlüssel identifiziert, dessen Fingerabdruck lautet:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Falls Sie diesen Schlüssel nicht erwarten, kann es sein, dass Dritte vorgeben, der Computer zu sein, mit dem Sie sich verbinden wollen.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Prüfen Sie unbekannten SSH-Host-Schlüssel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ich vertraue diesem Schlüssel: &Speichern und Verbinden\\nSie müssen den Schlüssel nicht erneut verifizieren, außer er wird verändert\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Ich vertraue diesem Schlüssel: &einfach verbinden\\nSie werden bei der nächsten Verbindung erneut aufgefordert, den Schlüssel zu bestätigen\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Name\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Rechner\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Benutzername\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Entfernter Pfad\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Verbindungstyp\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Netzlaufwerk\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"SFTP-Verbindung &Hinzufügen\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Neue SFTP-Verbindung mit Swish anlegen\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"SFTP-Verbindung &Hinzufügen...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Verbindung Hinzufügen\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Aufgaben für SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Diese Aufgaben helfen Ihnen bei der Verwaltung der Swish SFTP Verbindungen.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Schlüsselverwaltung starten\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Putty SSH Schlüsselverwaltung starten\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Schlüsselverwaltung starten\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"SFTP-Verbindung &entfernen\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Eine mit Swish angelegte SFTP-Verbindung entfernen.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP-Verbindung &entfernen...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Verbindung entfernen\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Größe\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Datum zuletzt geändert\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Datum letzter Zugriff\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Berechtigungen\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Besitzer\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Gruppe\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Besitzer-ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Gruppen-ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Öffnen &link\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Öffnen\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Der link kann nicht geöffnet werden\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Möglicherweise verfügen Sie nicht über die nötigen Berechtigungen.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Die Datei kann nicht geöffnet werden\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Aufgaben für Dateien und Ordner\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Diese Aufgaben helfen Ihnen bei der Verwaltung der Daten auf den Servern.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Das Element kann nicht gelöscht werden\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Neuer &Ordner\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Erstellt einen neuen leeren Ordner.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Neuen Ordner erstellen\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Neuer Ordner\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Neuer Ordner kann nicht angelegt werden\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Interaktive Tastaturabfrage\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Auf das Verzeichnis kann nicht zugegriffen werden\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Pfad nicht erkannt\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Bitte prüfen Sie den Pfad auf mögliche Fehler.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Das Element kann nicht umbenannt werden\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Auf das Element kann nicht zugegriffen werden\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Auf die Elemente kann nicht zugegriffen werden\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Auf das Verzeichnis kann nicht zugegriffen werden\"\n"
  },
  {
    "path": "po/el_GR/swish.po",
    "content": "# \n# Translators:\n# Klonos TwinZ <klonos@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Klonos TwinZ <klonos@gmail.com>\\n\"\n\"Language-Team: Greek (Greece) (http://www.transifex.com/projects/p/swish/language/el_GR/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: el_GR\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Αδύνατη η δημιουργία του αρχείου στον εξυπηρετητή:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Αντιγραφή του '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Σε '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Αυτός ο φάκελος περιέχει ήδη ένα αρχείο με όνομα '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Θα θέλατε να το αντικαταστήσετε?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Επιβεβαίωση Αντικατάστασης Αρχείου\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Γίνεται αντιγραφή...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Αδύνατη η μεταφορά των αρχείων\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Μπορεί να μην έχετε δικαιώματα εγγραφής σε αυτόν τον φάκελο.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Νέα Σύνδεση SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Δημιουργία\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Ετικέτα:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Για παράδειγμα: \\\"Υπολογιστής στο Σπίτι\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Προσδιορίστε τις λεπτομέρειες του υπολογιστή του λογαριασμού χρήστη με τα οποία θα θέλατε να συνδεθείτε:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Host:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Πόρτα:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Χρήστης:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Προσδιορίστε τον φάκελο στον server στον οποίο θα θέλατε το Swish να ξεκινά την σύνδεση:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Διαδρομή:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Παράδειγμα: /home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Ακύρωση\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Η ετικέτα δε μπορεί να είναι μεγαλύτερη από 30 χαρακτήρες\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Το όνομα του host δεν είναι έγκυρο.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Η πόρτα δεν είναι έγκυρο (μεταξύ 0 και 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Το όνομα χρήστη δεν είναι έγκυρο.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Η διαδρομή δεν είναι έγκυρη.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Υπάρχει ήδη μια σύνδεση με την ίδια ετικέτα. Παρακαλώ δοκιμάστε μια άλλη.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Συμπληρώστε όλα τα πεδία.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Συνθηματικό\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Εμφάνιση &λεπτομερειών (που μπορεί να μην είναι στη γλώσσα σας)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Απόκρυψη &λεπτομερειών\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Ο φάκελος περιέχει ήδη ένα αρχείο με όνομα '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Θα θέλατε να αντικαταστήσετε το ήδη υπάρχον αρχείο\\n\\n»%1%\\n\\nμε αυτό?\\n\\n»%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Το αρχείο υπάρχει ήδη\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Το κλειδί host δεν ταιριάζει\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ΠΡΟΕΙΔΟΠΟΙΗΣΗ: το κλειδί SSH του host άλλαξε!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Το κλειδί SSH του host που απεστάλη από '%1%' με σκοπό να το ταυτοποιήσει δεν ταιριάζει το ήδη γνωστό κλειδί γι' αυτόν τον εξυπηρετητή. Αυτό μπορεί να σημαίνει ότι κάποιος τρίτος προσποιείται ότι είναι ο υπολογιστής με τον οποίο προσπαθείτε να συνδεθείτε ή απλά ότι ο διαχειριστής του συστήματος έχει αλλάξει το κλειδί.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Είναι σημαντικό να ελέγξετε ότι αυτό είναι το σωστό αποτύπωμα κλειδιού:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Εμπιστεύομαι αυτό το κλειδί: &ενημέρωση και σύνδεση\\nΔεν θα χρειαστεί να επαληθεύσετε το κλειδί αυτό ξανά έκτος και αν αλλάξει\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Εμπιστεύομαι αυτό το κλειδί: &απλώς σύνδεση\\nΘα ειδοποιηθείτε γι' αυτό το κλειδί ξανά την επόμενη φορά που θα συνδεθείτε\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Ακύρωση\\nΚάνετε αυτή την επιλογή εκτός και αν είστε σίγουροι ποως το κλειδί είναι σωστό\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Άγνωστο κλειδί host\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Ο εξυπηρετητής '%1%' έχει ταυτοποιήσει τον εαυτό του με ένα SSH κλειδί host του οποίου το αποτύπωμα είναι:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Αν δεν περιμένετε αυτό το κλειδί, κάποιος τρίτος ενδέχεται να προσποιείται να είναι ο υπολογιστής με τον οποίο προσπαθείτε να συνδεθείτε.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Επαληθεύστε το άγνωστο SSH κλειδί host\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Εμπιστεύομαι αυτό το κλειδί: &αποθήκευση και σύνδεση\\nΔεν θα χρειαστεί να επαληθεύσετε αυτό το κλειδί ξανά εκτός και αν αλλάξει\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Εμπιστεύομαι αυτό το κλειδί: &σύνδεση μόνο\\nΘα σας ζητηθεί να επαληθεύσετε το κλειδί ξανά την επόμενη φορά που θα συνδεθείτε\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Όνομα\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Host\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Όνομα χρήστη\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Πόρτα\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Απομακρυσμένη διαδρομή\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Είδος\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Δίσκος δικτύου\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Δημιουργία σύνδεσης SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Δημιουργία νέας σύνδεσης SFTP με χρήση του Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Δημιουργία σύνδεσης SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Δημιουργία Σύνδεσης\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Ενέργειες SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Αυτές οι ενέργειες σας βοηθούν να διαχειριστείτε συνδέσεις του Swish SFTP\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Εκτέλεση του διαχειριστή κλειδιών\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Εκτέλεση του διαχειριστή κλειδιών SSH του Putty, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Εκτέλεση του διαχειριστή κλειδιών\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Διαγραφή Σύνδεσης SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"&Διαγραφή σύνδεσης SFTP που έχει γίνει με χρήση του Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Διαγραφή σύνδεσης SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Διαγραφή Σύνδεσης\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Μέγεθος\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Ημερομηνία Τροποποίησης\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Ημερομηνία Προσπέλασης\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Άδειες\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Ιδιοκτήτης\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Ομάδα\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID ιδιοκτήτη\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID ομάδας\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Άνοιγμα &συνδέσμου\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Άνοιγμα\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Αδύνατη το άνοιγμα του συνδέσμου\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Μπορεί να μην έχετε τα κατάλληλα δικαιώματα.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Αδύνατο το άνοιγμα του αρχείου\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Ενέργειες Αρχείων και Φακέλων\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Αυτές οι ενέργειες σας βοηθούν να διαχειριστείτε τα απομακρυσμένα αρχεία σας.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Αδύνατη η διαγραφή του αντικειμένου\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Νέος &φάκελος\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Δημιουργία ενός νέου, κενού φακέλου μέσα στον φάκελο που έχετε ανοιχτό.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Δημιουργία νέου φακέλου\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Νέος φάκελος\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Δεν ήταν δυνατή η δημιουργία νέου φακέλου\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Αναζήτηση με διάδραση πληκτρολογίου\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Δεν είναι δυνατή η προσπέλαση του φακέλου\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Η διαδρομή δεν αναγνωρίζεται\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Ελέγξτε αν η διαδρομή έχει πληκτρολογηθεί σωστά.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Αδύνατη η μετονομασία του αντικειμένου\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Αδύνατη η πρόσβαση στο αντικείμενο\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Αδύνατη η πρόσβαση στα αντικείμενα\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Αδύνατη η πρόσβαση στον φάκελο\"\n"
  },
  {
    "path": "po/es/swish.po",
    "content": "# \n# Translators:\n# Antonio Rico <aricop@gmail.com>, 2012\n# Tomas Figueroa <teft90@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Antonio Rico <aricop@gmail.com>\\n\"\n\"Language-Team: Spanish (http://www.transifex.com/projects/p/swish/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\"Language: es\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Imposible crear fichero en el servidor:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copiando '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"A '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Esta carpeta ya contiene un archivo llamado '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"¿Desea reemplazarlo?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirmar reemplazo de archivo\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Copiando...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"No se pueden trasnferir los archivos\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Puede que no tenga permiso para escribir en este directorio.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nueva conexión SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Crear\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Nombre:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Por ejemplo: \\\"Computadora de casa\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Especifique los detalles del equipo y de la cuenta a la que desea conectarse:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Host:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Puerto:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Usuario:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Especifique el directorio en el servidor en que desea que Swish inicie la conexión:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Ruta:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Ejemplo: /home/usuario\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"El nombre no puede contener más de 30 caracteres.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"El nombre del host no es válido.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"El puerto no es válido (entre 0 y 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"El nombre de usuario no es válido.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"La ruta no es válida.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Ya  existe una conexión con el mismo nombre. Pruebe con otro.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Llene todos los campos.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Contraseña\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"Aceptar\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Mostrar &detalles (podrían no estar en su idioma)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Ocultar &detalles\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"La carpeta ya contiene un archivo llamado '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"¿Desea reemplazar el archivo existente\\n\\n\\t%1%\\n\\ncon este otro?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"El archivo ya existe\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Conflicto con la clave del host\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ADVERTENCIA: la clave del host SSH ha cambiado.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"La clave del host SSH enviada por '%1%' para identificarse no concuerda con la clave conocida para este servidor. Esto podría significar que algún tercero está pretendiendo ser el equipo al que usted está trantando de conectarse, o que el administrador del sistema podría haber cambiado la clave.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Es importante verificar que ésta es la huella digital correcta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confío en esta clave: &actualizar y conectar\\nNo tendrá que verificar esta clave otra vez a menos que cambie\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Confío en esta clave: &solo conectar\\nSe le avisará respecto a esta clave otra vez la próxima vez que se conecte \"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Cancelar\\nElija esta opción a menos que esté seguro de que la clave es correcta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Clave de host desconocida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"El servidor '%1%' se ha identificado con una clave de host SSH cuya huella digital es:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Si no está esperando esta clave, un tercero podría estar pretendiendo ser el equipo al que usted trata de conectarse.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verificar clave de host SSH desconocida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confío en esta clave: &almacenar y conectar\\nNo tendrá que verificar esta clave otra vez a menos que cambie\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Confío en esta clave: &solo conectar\\nSe le pedirá que verifique la clave de nuevo la próxima vez que se conecte\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nombre\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Host\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Usuario\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Puerto\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Ruta remota\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Dispositivo de red\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Agregar Conexión SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Crear una nueva conexión SFTP con Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Agregar Conexión SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Agregar Conexión\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Tareas SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Estas tareas ayudan a administrar las conexiones Swish SFTP.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Invocar agente de claves\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Invocar el agente de claves Putty SSH, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Invocar agente de claves\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Eliminar Conexión SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Eliminar una conexión SFTP creada con Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Eliminar Conexión SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Eliminar Conexión\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Tamaño\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Fecha de modificación\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Fecha de último acceso\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permisos\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Propietario\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupo\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID del propietario\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID del grupo\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Abrir enlace\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"Abrir\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"No se puede abrir el enlace\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Puede que no tenga permiso.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"No se puede abrir el archivo\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Acciones sobre ficheros y carpetas\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Estas acciones le ayudan a gestionar sus ficheros remotos.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Imposible eliminar el elemento\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nueva &carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Crear una nueva carpeta vacia en la carpeta actual.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Crear una nueva carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nueva carpeta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"No se pudo crear una nueva carpeta\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Solicitud de teclado interactivo\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"No se puede acceder al directorio\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Ruta no reconocida\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Verifique que la ruta fue introducida correctamente.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Imposible renombrar el elemento\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Imposible acceder al elemento\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Imposible acceder a los elementos\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Imposible acceder a la carpeta\"\n"
  },
  {
    "path": "po/et/swish.po",
    "content": "# \n# Translators:\n# pexy <pexyyy@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: pexy <pexyyy@gmail.com>\\n\"\n\"Language-Team: Estonian (http://www.transifex.com/projects/p/swish/language/et/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: et\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Ei saanud faili luua serverisse:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Kopeerin '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"> '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"See kaust juba sisaldab faili nimega '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Kas soovid seda asendada?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Kinnita faili asendamine\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopeerin...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Ei saa faile üle kanda\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Sul ei pruugi olla õigusi, et sellesse kausta kirjutada.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Uus SFTP ühendus\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Loo\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Nimi:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Näiteks: \\\"Koduarvuti\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Vali arvuti andmed ja konto millega soovid ühendust võtta:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Server:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Kasutajanimi:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Vali kaust serveris mille Swish avab ühenduse loomisel:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Failitee\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Näiteks: /home/kasutajanimi\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Katkesta\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Nimi võib olla kuni 30 märki pikk.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Serveri nimi on ebakorrektne.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Pordi number on ebakorrektne (0-65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Kasutajanimi on ebakorrektne.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Failitee on ebakorrektne.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Sellise nimega ühendus on juba olemas. Vali uus nimi.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Täida kõik väljad.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Salasõna\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Näita &andmeid (ei pruugi olla sinu keeles)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Peida &andmed\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"See kaust juba sisaldab faili nimega '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Kas soovid asendada olemasolevat faili\\n\\n\\t%1%\\n\\nselle failiga\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Fail on juba olemas\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Serveritunnus ei klapi\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"HOIATUS: SSH serveritunnus on muutunud!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH serveritunnus, millega server '%1%' ennast identifitseerib, ei klapi varem salvestatud tunnusega selle serveri kohta.  See võib tähendada, et kolmas osapool püüab pahatahtlikult esineda serverina või on serveri hooldaja muutnud servertunnust.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"On oluline kontrollida, kas see  serveritunnuse sõrmejälg on õige:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Usalda seda serveritunnust: &uuenda ja ühenda\\nSa ei pea enam seda serveritunnust kontrollima\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Usalda seda serveritunnust: &ainult ühenda\\nJärgmisel ühendamisel hoiatatakse sind uuesti selle serveritunnuse tõttu\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Katkesta\\nKatkesta kui sa pole serveritunnuse õigsuses kindel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Tundmatu serveritunnus\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Server '%1%' identifitseerib ennast SSH serveritunnusega, mille sõrmejälg on:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Kui see pole korrektne serveritunnus, siis võib kolmas osapool püüd pahatahtlikult esineda serverina, millega sa ühenduda tahad.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Kontrolli tundmatu SSH serveritunnus\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Usalda seda serveritunnust: &salvesta ja ühenda\\nSa ei pea enam seda serveritunnust kontrollima\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Usalda seda serveritunnust: &ainult ühenda\\nJärgmisel ühendamisel palutakse sul uuesti serveritunnust kontrollida\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nimi\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Server\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Kasutajanimi\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Kaust serveris\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tüüp\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Võrguketas\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Lisa SFTP ühendus\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Loo Swish abil uus SFTP ühendus.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Lisa SFTP ühendus...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Lisa ühendus\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP tegevused\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Need tegevused aitavad sul hallata Swish SFTP ühendusi.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Käivita võtmehaldur\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Käivita Putty SSH võtmehaldur, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Käivita võtmehaldur\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Kustuta SFTP ühendus\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Kustuta SFTP ühendus, mis on loodud Swish abil.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Kustuta SFTP ühendus...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Kustuta ühendus\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Suurus\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Muutmisaeg\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Vaatamisaeg\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Õigused\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Omanik\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupp\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Omaniku ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Grupi ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Ava &viit\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Ava\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Ei saa avada viita\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Sul ei pruugi õigusi olla.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Ei saa avada faili\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Faili ja kausta tegevused\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Need tegevused aitavad sul hallata faile serveris.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Faili ei saa kustutada\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"&Uus kaust\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Loo uus tühi kaust jooksvasse kausta.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Loo uus kaust\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Uus kaust\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Ei saanud uut kausta luua\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Kausta ei saa avada\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Failiteed ei tuntud ära\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Kontrolli, et kaust sisestati korrektselt.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Faili ei saa ümber nimetada\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Faili ei saa avada\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Faile ei saa avada\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Kausta ei saa avada\"\n"
  },
  {
    "path": "po/fi/swish.po",
    "content": "# \n# Translators:\n# Heikki Salko <hezecc@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Heikki Salko <hezecc@gmail.com>\\n\"\n\"Language-Team: Finnish (http://www.transifex.com/projects/p/swish/language/fi/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: fi\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Tiedostoa ei voida luoda palvelimella:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Kopioidaan '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Kohteeseen '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Tässä kansiossa on jo tiedosto nimeltä '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Haluatko korvata sen?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Vahvista tiedoston korvaaminen\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopioidaan...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Tiedostoja ei voida siirtää\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Sinulla ei ehkä ole oikeuksia kirjoittaa tähän hakemistoon.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Uusi SFTP-yhteys\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Luo\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Nimi:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Esimerkiksi \\\"Kotikone\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Määritä tietokoneen ja käyttäjätunnuksen tiedot, johon haluat yhdistää:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Isäntä:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Portti:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Käyttäjä:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Määritä palvelimen hakemisto, jossa haluat Swish:n aloittavan yhteyden:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"P&olku:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Esimerkki: /home/kayttaja\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Peruuta\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Nimi ei voi olla pidempi kuin 30 merkkiä.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Isännän nimi ei ole kelvollinen.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Portti ei ole kelvollinen (0 ja 65535 välillä).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Käyttäjänimi ei ole kelvollinen.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Polku ei ole kelvollinen.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Tämä nimi on jo toisen yhteyden käytössä. Kokeile toista.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Täytä kaikki kentät.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Salasana\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Näytä &lisätiedot (jotka eivät välttämättä ole kielelläsi)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Piilota &lisätiedot\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Kansiossa on jo tiedosto nimeltä '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Haluatko korvata olemassaolevan tiedoston\\n\\n\\t%1%\\n\\ntällä?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Tiedosto on jo olemassa.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Isäntä-avain ei täsmää\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"VAROITUS: SSH:n isäntä-avain on muuttunut!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Tietokoneen '%1%' tunnistamistaan varten lähettämä SSH:n isäntä-avain ei täsmää tämän palvelimen tunnetun avaimen kanssa. Tämä saattaa tarkoittaa, että jokin kolmas osapuoli esiintyy tietokoneena, johon yrität yhdistää, tai järjestelmän ylläpitäjä on voinut vaihtaa avaimen.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"On tärkeää tarkistaa, että tämä on oikea avaimen sormenjälki:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Luotan tähän avaimeen: pä&ivitä ja yhdistä\\\\nSinun ei tarvitse vahvistaa tätä avainta uudelleen, ellei se muutu\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Luotan tähän avaimeen: &yhdistä\\nSinua varoitetaan tästä avaimesta uudelleen, kun yhdistät seuraavan kerran\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Peruuta\\nValitse tämä vaihtoehto, ellet ole varma avaimen oikeellisuudesta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Tuntematon isäntä-avain\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Palvelin '%1%' on tunnistanut itsensä SSH:n isäntä-avaimella, jonka sormenjälki on:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Jos et odota tätä avainta, jokin kolmas osapuoli saattaa esiintyä tietokoneena, johon yrität yhdistää.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Vahvista tuntematon SSH:n isäntä-avain\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Luotan tähän avaimeen: &tallenna ja yhdistä\\nSinun ei tarvitse vahvistaa tätä avainta uudelleen, ellei se muutu\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Luotan tähän avaimeen: &yhdistä\\nSinua varoitetaan tästä avaimesta uudelleen, kun yhdistät seuraavan kerran\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nimi\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Isäntä\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Käyttäjätunnus\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Portti\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Etäpolku\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tyyppi\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Verkkoasema\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Lisää SFTP-yhteys\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Luo uusi SFTP-yhteys Swish:llä.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Lisää SFTP-yhteys...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Lisää yhteys\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP-tehtävät\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Nämä tehtävät auttavat sinua hallitsemaan Swish:n SFTP-yhteyksiä.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"A&vaa avainagentti\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Avaa PuTTY:n SSH-avainagentti, Pageant\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Avaa avainagentti\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Poista SFTP-yhteys\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Poista Swish:llä luotu SFTP-yhteys.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Poista SFTP-yhteys...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Poista yhteys\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Koko\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Viimeksi muokattu\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Viimeksi käytetty\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Oikeudet\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Omistaja\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Ryhmä\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Omistajan ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Ryhmän ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Avaa &linkki\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Avaa\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Linkkiä ei voida avata\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Sinulla ei ehkä ole oikeuksia.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Tiedostoa ei voida avata\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Tiedosto- ja kansiotehtävät\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Nämä tehtävät helpottavat etätiedostojen hallintaa.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Tiedostoa ei voida poistaa\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Uusi &kansio\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Luo avoinna olevaan kansioon uusi, tyhjä kansio.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Luo uusi kansio\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Uusi kansio\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Uutta kansiota ei voida luoda\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Näppäimistö-interaktiivinen pyyntö\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Hakemistoon ei saada pääsyä\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Polkua ei tunnistettu\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Tarkista, että polku on syötetty oikein.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Kohdetta ei voida nimetä uudelleen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Kohteeseen ei saada pääsyä\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Kohteisiin ei saada pääsyä\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Kansiota ei voida avata\"\n"
  },
  {
    "path": "po/fr/swish.po",
    "content": "# \n# Translators:\n# fkhannouf <fkhannouf@me.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: fkhannouf <fkhannouf@me.com>\\n\"\n\"Language-Team: French (http://www.transifex.com/projects/p/swish/language/fr/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: fr\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Impossible de créer le fichier sur le serveur\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copie de '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Vers '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Ce dossier contient déjà le fichier '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Voulez-vous le remplacer ?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirmer le remplacement\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Copie en cour...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Transfert du fichier impossible\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Il semble que vous n'ayez pas la permission d'écrire dans ce répertoire\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nouvelle connexion SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Créer\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Libellé\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Par exemple : \\\"Ordinateur personnel\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Indiquer les éléments de l'ordinateur et du compte sur lequel vous voulez vous connecter\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Hôte:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Identifiant:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Indiquer le répertoire du serveur sur lequel Swish doit démarrer la connexion\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Chemin:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exemple : /home/identifiant\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Annuler\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Le libellé ne peut faire plus de 30 caractères.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Nom d'hôte non valide.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Numéro de port non valide (entre 0 et 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Identifiant non valide.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Chemin non valide.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Une connexion avec le même nom existe déjà. Essayez un autre nom.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Complétez tous les champs.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Mot de passe\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"Valider\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Montrer les détails (qui peuvent ne pas être en français)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Cacher les détails\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Le dossier contient déjà un fichier nommé '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Voulez-vous remplacer le fichier existant\\n\\n\\t%1%\\n\\npar celui-ci ?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Fichier déjà existant\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Clé d'hôte non reconnue.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ATTENTION : la clé d'hôte a changé !\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"La clé d'hôte envoyée par '%1%' pour s'identifier ne correspond pas à la clé connue pour ce serveur. Cela peut signifier qu'un tiers prétend être l'ordinateur sur lequel vous essayez de vous connecter, ou bien que l'administrateur système a simplement changé la clé.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Il est important de vérifier que c'est la bonne empreinte de clé :\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Je valide cette clé: &Mettre à jour et se connecter\\nVous n'aurez plus à vérifier cette clé tant qu'elle ne change pas\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Je valide cette clé : &Simple connexion\\nVous serez à nouveau averti la prochaine fois que vous vous connectez\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Annuler\\nChoisissez cette option seulement si vous êtes certains que la clé est correcte.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Clé d'hôte inconnue\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Le serveur '%1%' s'est identifié avec une clé d'hôte dont l'empreinte est :\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Si vous n'attendiez pas cette clé, un tiers prétend peut-être être l'ordinateur sur lequel vous voulez vous connecter.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Vérification de clé d'hôte inconnue\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Je valide cette clé: &Enregistrer et se connecter\\nVous n'aurez plus à vérifier cette clé tant qu'elle ne change pas.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Je valide cette clé : &Simple connexion\\nIl vous sera demandé de vérifier la clé à nouveau la prochaine fois que vous vous connecterez\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nom\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Hôte\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Identifiant\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Chemin distant\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Type\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Disque réseau\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Ajouter une connexion SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Créer une nouvelle connexion SFTP avec Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Ajouter une connexion SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Ajouter une connexion\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Tâches SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Ces tâches vous aident à gérer vos connexions Swish SFTP.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Lancer l'agent d'authentification\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"&Lancer l'agent d'authentification de Putty SSH, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Lancer l'agent d'authentification\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Enlever la connexion SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Enlève une connexion SFTP créée avec Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Enlever la connexion SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Enlever la connexion\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Taille\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Date de modification\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Date d'accès\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permissions\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Propriétaire\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Groupe\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID du propriétaire\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID du groupe\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Ouvrir le lien\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"Ouvrir\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Impossible d'ouvrir le lien\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Il semble que vous n'ayez pas les permissions nécessaires\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Impossible d'ouvrir le répertoire\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Tâches liées aux fichiers et répertoires\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Ces tâches vous aident à gérer les fichiers distants\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Impossible de détruire l'article\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nouveau répertoire\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Crée un nouveau répertoire vide dans le répertoire que vous avez ouvert\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Créer un nouveau répertoire\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nouveau répertoire\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Impossible de créer un nouveau répertoire\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Demande interactive du clavier\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Impossible d'atteindre ce répertoire\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Chemin non reconnu\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Vérifiez que le chemin entré est correct\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Impossible de renommer cet article\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Impossible d'accéder à cet article\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Impossble d'accéder à ces articles\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Impossible d'accéder au répertoire\"\n"
  },
  {
    "path": "po/he/swish.po",
    "content": "# \n# Translators:\n# aharonoosh <aharonoosh1@gmail.com>, 2012\n# SmileyBarry <smiley@smileybarry.com>, 2014\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2014-02-22 19:00+0000\\n\"\n\"Last-Translator: SmileyBarry <smiley@smileybarry.com>\\n\"\n\"Language-Team: Hebrew (http://www.transifex.com/projects/p/swish/language/he/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: he\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"לא ניתן ליצור קובץ בשרת:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"מעתיק '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"ל '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"'{1}' התיקייה הזו כבר מכילה קובץ בשם\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"האם אתה רוצה להחליף את זה?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"אשר החלפת קבצים\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"מעתיק...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"לא ניתן להעביר קבצים\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"אולי אין לך הרשאה לכתוב לספרייה זו.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"חדש SFTP חיבור\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"צור\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&שם:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"\\\"Home Computer\\\" לדוגמא \"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"הגדר את הפרטים של המחשב והחשבון שברצונך להתחבר אליו:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&שרת:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&שער:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&משתמש:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"יתחיל בחיבור אליוSwish הקש את הספריה בשרת שאתה רוצה ש\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"נ&תיב\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"/home/yourusername דוגמא:\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"בטל\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"השם לא יכול להיות באורך יותר מ 30 אותיות\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"השרת לא תקין\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"(השער לא תקין (השער התקין הוא בין 0 ל 65535\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"המשתמש לא תקין\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"הנתיב לא חוקי\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"חיבור עם אותו שם קיים. בבקשה נסה אחר.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"השלם את כל השדות.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"סיסמא\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"אישור\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"ה&צג פרטים\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"הסתר &פרטים\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"'%1%' התקיה כבר מכילה קובץ בשם \"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"האם אתה רוצה להחליף את הקובץ הקיים/n\\t%1%\\n\\nאם זה?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"קובץ קיים\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"מפתח לא מתאים\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"אזהרה: הפצח השתנה!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"על מנת לזהות את עצמו לא תואם למפתח קיים לשרת זה. '%1%' שנשלח על ידי SSHמפתח הדבר זה יכול להוות אינדיקציה שצד שלישי מנסה להעמיד פנים שהוא המחשב אליו אתה מנסה להתחבר או שהמנהל מערכת שינה את המפתח.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"זה חשוב לבדוק שזה מפתח החתימה הנכון:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"אני בוטח במפתח זה:&עדכן והתחבר\\nלא יהיה עליך לוודא את מפתח זה שוב אלא אם הוא משתנה\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"אני בוטח במפתח זה: &רק תתחבר\\nאתה תוזהר בפעם הבאה על מפתח זה בפעם הבאה כשתתחבר\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"ביטול&\\nבחר אופציה זאת אלא אם אתה בטוח שהמפתח נכון\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"מפתח-שרת לא ידוע\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \":SSH זיהה את עצמו עם מפתח שרת  '%1%' השרת \"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"אם אתה לא מצפה למפתח זה, צד שלישי יכול להיות מנסה להתחזות להיות המחשב אשר אליו אתה מנסה להתחבר.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"בעל מפתח-שרת לא ידוע SSH וודא \"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"אני בוטח במפתח זה: &אחסן והתחבר\\nלא תצטרך לוודא מפתח זה שוב אלא אם הוא ישתנה\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \" &just connect\\nאני בוטח במפתח זה: אתה תתבקש לוודא את המפתח שוב בפעם הבאה שתתחבר\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"שם\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"שרת\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"משתמש\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"שער\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"נתיב מרוחק\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"סוג\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"כונן רשת\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"SFTP הוסף חיבור&\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Swish עם SFTP צור חיבור \"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"SFTP הוסף חיבור&\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"הוסף חיבור\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP משימות \"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Swish SFTP משימות אלו עוזרות לך לנהל את חיבורי\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"הפעל סוכן מפתח\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&SFTP הסר חיבור\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \".Swish הסר חיבור אשר נוצר עם \"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&...SFTP הסר חיבור \"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"הסר חיבור\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"גודל\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"תאריך שונה\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"תאריך ניגש\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"הרשאות\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"בעלים\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"קבוצה\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"מספר בעלים\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"מספר קבוצה\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"פתח &קישור\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&פתח\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"לא ניתן לפתוח את הקישור\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"אולי אין לך רשות.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"לא ניתן לפתוח את הקובץ\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"משימות קובץ ותיקיה\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"משימות אלה תעזורנה לך לנהל את הקבצים המרוחקים שלך.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"אין אפשרות למחוק את הפריט\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"&תיקייה חדשה\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"צור תיקייה חדשה וריקה בתיקייה שפתחת.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"צור תיקייה חדשה\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"תיקייה חדשה\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"לא ניתן ליצור תיקייה חדשה\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"מקלדת-בקשה אינטראקטיבית\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"אין אפשרות לגשת לספרייה\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"נתיב לא מוכר\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"ודאו כי הנתיב הוזן כהלכה.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"לא ניתן לשנות את שם הפריט\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"אין אפשרות לגשת לפריט\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"לא ניתן לגשת לפריטים\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"לא ניתן לגשת לתיקייה\"\n"
  },
  {
    "path": "po/hi/swish.po",
    "content": "# \n# Translators:\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: alamaison <awl03@doc.ic.ac.uk>\\n\"\n\"Language-Team: Hindi (http://www.transifex.com/projects/p/swish/language/hi/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: hi\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"इस फ़ोल्डर में पहले से ही '{1}' नामक एक फाइल शामिल हैं.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"क्या आप इसे बदलना चाहते हैं?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"फाइल बदलने के लिए पुष्टि\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"प्रतिलिपि कार्य जारी...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"नया SFTP कनेक्शन\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"बनाएँ\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&लेबल:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"उदाहरण के लिए: \\\"गृह कंप्यूटर\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"कंप्यूटर और खाता जिससे कनेक्ट होना चाहते हैं उसका विवरण बताएं:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&होस्ट:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&पोर्ट:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&यूज़र:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"सर्वर पर डाईरेक्टरी बताएं जिससे आप Swish का कनेक्शन शुरू करना चाहते हैं:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&पथ:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"उदाहरण: / होम / आपकायूज़रनेम\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"रद्द करें\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"लेबल 30 अक्षरों से अधिक लंबा नहीं हो सकता.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"होस्ट नाम अमान्य है.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"पोर्ट मान्य नहीं है(0 और 65535 के बीच).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"यूज़रनेम अमान्य है.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"पथ अमान्य है.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"इस लेबल के साथ एक कनेक्शन पहले से ही मौजूद है. कृपया अन्य की कोशिश करें.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"सभी फील्ड को पूरा करें.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"पासवर्ड\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"ठीक है\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"विवरण दिखाएँ (जो आपकी भाषा में नहीं हो सकता)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"विवरण छिपाएँ\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"फ़ोल्डर में पहले से ही एक '%1%' नामक फ़ाइल शामिल\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"क्या आप मौजूदा फ़ाइल को बदलना चाहते\\n\\n%1%\\n\\nइस के साथ?\\n\\n%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"फ़ाइल पहले से मौजूद\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"बेमेल होस्ट-कुंजी\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"चेतावनी: SSH होस्ट-कुंजी बदल गई है!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"'%1%' द्वारा पहचान के लिए भेजी गई SSH होस्ट-कुंजी इस सर्वर को ज्ञात की से मेल नहीं खाती है.  इसका मतलब हो सकता है कि एक तीसरी पार्टी आपके कंप्यूटर से कनेक्ट करने की कोशिश कर रही है या फिर आपके सिस्टम ऐडमिनिस्ट्रेटर ने कुंजी बदल दी है.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"यह महत्वपूर्ण है कि जांच लें यह सही कुंजी फिंगरप्रिंट है:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"मुझे इस कुंजी पर भरोसा है: नवीनीकरण करें और कनेक्ट करें\\nआपको इस कुंजी को फिर से सत्यापित करने की जरूरत नहीं है जब तक यह न बदली जाए\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"मुझे इस कुंजी पर भरोसा है: सिर्फ कनेक्ट करें\\nआपको इस कुंजी के बारे में फिर से चेतावनी दी जाएगी जब आप अगली बार कनेक्ट होंगे \"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"रद्द करें\\nइस विकल्प को चुनें जब तक आप सुनिश्चित न हो कि कुंजी सही है\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"अज्ञात होस्ट-कुंजी\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"सर्वर '%1%' ने SSH एक होस्ट-कुंजी की पहचान की है जिसका फिंगरप्रिंट है:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"यदि आप इस कुंजी की उम्मीद नहीं कर रहे हैं, आप जिस कंप्यूटर से कनेक्ट करने की कोशिश कर रहें हैं वो तीसरी पार्टी का है.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"अज्ञात SSH होस्ट-कुंजी को सत्यापित करें\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"मुझे इस कुंजी पर भरोसा है: संग्रह करें और कनेक्ट करें\\nआपको इस कुंजी को फिर से सत्यापित करने की जरूरत नहीं है जब तक यह न बदली जाए\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"मुझे इस कुंजी पर भरोसा है: सिर्फ कनेक्ट करें\\nआपको इस कुंजी को फिर से सत्यापित करने को कहा जाएगा जब आप अगली बार कनेक्ट होंगे \"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"नाम\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"होस्ट\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"यूज़रनेम\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"पोर्ट\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"दूरस्थ पथ\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"टाइप करें \"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"नेटवर्क ड्राइव\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"S&FTP कनेक्शन जोड़ें\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Swish से एक नया SFTP कनेक्शन बनाएँ.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"S&FTP कनेक्शन जोड़ें...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"कनेक्शन जोड़ें\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP के कार्य\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"इन कार्यों की मदद से आप Swish SFTP कनेक्शन का प्रबंधन कर सकते हैं.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&SFTP कनेक्शन हटाएँ\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Swish से बनाया गया SFTP कनेक्शन हटाएँ.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&SFTP कनेक्शन हटाएँ...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"कनेक्शन हटाएँ\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"साइज़\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"संशोधित करने की तिथि\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"उपयोग करने की तिथि\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"अनुमतियाँ\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"मालिक\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"समूह\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"मालिक आईडी\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"समूह आईडी\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"आप के पास अनुमति नहीं है. \"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"फ़ाइल और फ़ोल्डर कार्य\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"इन कार्यों की मदद से आप अपनी दूरस्थ फ़ाइलों का प्रबंधन कर सकते हैं.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"आइटम को हटाने में असमर्थ\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"नया &फ़ोल्डर\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"आप खुले फ़ोल्डर में एक नया, खाली फ़ोल्डर बनाएँ.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"एक नया फ़ोल्डर बनाएँ\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"नया फ़ोल्डर\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"नया फ़ोल्डर नहीं बनाया जा सका\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"कीबोर्ड-इंटरैक्टिव अनुरोध\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"डाईरेक्टरी का उपयोग करने में असमर्थ\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"सही पथ नहीं.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"जाँच लें कि पथ सही ढंग से दर्ज किया गया है.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"आइटम का नाम बदलने में असमर्थ\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"आइटम तक पहुँचने में असमर्थ\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"आइटम्स तक पहुँचने में असमर्थ\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"फ़ोल्डर तक पहुँचने में असमर्थ\"\n"
  },
  {
    "path": "po/hu/swish.po",
    "content": "# \n# Translators:\n# Laszlo Pap <papla960@gmail.com>, 2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Laszlo Pap <papla960@gmail.com>\\n\"\n\"Language-Team: Hungarian (http://www.transifex.com/projects/p/swish/language/hu/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: hu\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Nem lehet létrehozni a fájlt a szerveren\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"'{1}' másolása\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"'{1}'hez\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Ez a mappa már tartalmaz egy '{1}' nevu fájlt.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Szeretné lecserélni?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Fájlcsere megerosítése\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Másolás...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"A. fájlátvitel nem lehetséges\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Lehetséges, hogy nincs írási engedélye a könyvtárra\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Új SFTP kapcsolat\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Létrehozás\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Címke:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Például: \\\"Otthoni számítógép\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Adja meg a számítógép adatait melyhez csatlakozni kíván:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Gépnév:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"Fel&használó:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Határozza meg a mappát a szerveren melyet a Swish csatlakozás után automatikusan megnyit:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"&Elérési út:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Példa: /home/felhasznaloneve\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Mégse\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"A címke nem lehet hosszabb 30 karakternél.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"A gépnév érvénytelen.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Érvénytelen port (0 és 65535 között).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"A felhasználónév érvénytelen.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Az elérési út érvénytelen.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Egy kapcsolat már létezik az adott címkével. Kérem próbálkozzon másikkal.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Töltse ki az összes mezőt.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Jelszó\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"&Részletek mutatása (nem feltétlenül saját nyelven jelenik meg)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"&Részletek elrejtése\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"A mappa már tartalmaz egy '%1%' nevu fájlt.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Szeretné lecserélni a létezo fájlt\\n\\n\\t%1%\\n\\nezzel?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"A fájl már létezik\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Nem egyezo kiszolgáló-kulcs\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"FIGYELEM: Az SSH kiszolgáló-kulcs megváltozott!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Az SSH kiszolgáló-kulcs melyet a '%1%' kiszolgáló küldött, nem egyezik a kiszolgáló ismert kulcsával. Ez azt jelentheti, hogy egy harmadik-fél tetteti magát a kiszolgálónak, vagy csak az adminisztrátor megváltoztatta a kulcsot.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Fontos, hogy ellenorizze a következo kulcs helyességét:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Megbízom ebben a kulcsban: &frissítés és csatlakozás\\nNem kell újra ellenoriznie ezt a kulcsot, csak ha megváltozik\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Megízom ebben a kulcsban: &csak csatlakozás\\nFigyelmeztetve lesz mikor újra csatlakozik\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Mégse\\nVálassza ezt a lehetoséget ha ön biztos abban, hogy a kulcs nem megfelelo\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Ismeretlen kiszolgáló-kulcs\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"A kiszolgáló '%1%' a következo SSH-kiszolgáló kulccsal azonosította magát:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Ha ön nem ezt a kulcsot várta, egy harmadik-fél tettetheti hogy a számítógép, melyhez ön épp csatlakozni próbál.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Ellenorizze az ismeretlen SSH kiszolgáló-kulcsot\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Megbízom ebben a kulcsban: &eltárolás és csatlakozás\\nNem kell újra ellenoriznie ezt a kulcsot, csak ha megváltozik\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Megízom ebben a kulcsban: &csak csatlakozás\\nÚjra kell majd ellenoriznie a kulcsot mikor ismét csatlakozik\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Név\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Kiszolgáló\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Felhasználónév\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Távoli elérési út\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Típus\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Hálózati Meghajtó\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&SFTP kapcsolat hozzáadása\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Új SFTP kapcsolat létrehozása Swish-el.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&SFTP kapcsolat hozzáadása...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Kapcsolat hozzáadása\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP Feladatok\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Ezek a feladatok segítenek a Swish SFTP kapcsolatok kezelésében.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"A kulcsügynök &futtatása\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"A Pageant Putty SSH kulcsügynök futtatása\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Kulcsügynök futtatása\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"SFTP kapcsolat &eltávolítása\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Swish által készített SFTP kapcsolat eltávolítása. \"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP kapcsolat &eltávolítása...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Kapcsolat eltávolítása\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Méret\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Módosítás dátuma\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Hozzáférés dátuma\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Jogosultságok\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Tulajdonos\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Csoport\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Tulajdonos ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Csoport ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"&Hivatkozás megnyitása\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Megnyitás\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Nem lehet megnyitni a hivatkozást\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Lehetséges, hogy nincs jogosultsága.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"A fájlt nem lehet megnyitni\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Fájl és Mappa Feladatok\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Ezek a feladatok segítenek a távoli fájlok kezelésében.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Nem lehet törölni az adott elemet\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Új &mappa\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Új, üres mappa létrehozása az éppen megnyitott mappában.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Új mappa létrehozása\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Új mappa\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nem sikerült létrehozni az új mappát\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Billentyuzet-interaktív kérés\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Nem lehet hozzáférni a mappához\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Elérési út felismerése nem sikerült\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Ellenorizze, hogy az elérési út helyesen lett-e megadva.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Nem sikerült átnevezni az adott elemet\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Nem lehet hozzáférni az elemhez\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Nem lehet hozzáférni az elemekhez\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Nem lehet hozzáférni a mappához\"\n"
  },
  {
    "path": "po/it/swish.po",
    "content": "# \n# Translators:\n# Alessandro Calorì <axelgenus@gmail.com>, 2012\n# gbdenaro <gb.denaro@gmail.com>, 2012\n# pizar <pietro.ferraresi@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: pizar <pietro.ferraresi@gmail.com>\\n\"\n\"Language-Team: Italian (http://www.transifex.com/projects/p/swish/language/it/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: it\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Impossibile creare il file sul server:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Sto copiando '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"A '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"La cartella contiene già il file '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Vuoi sostituirlo?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Conferma Sostituzione File\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Sto copiando...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Impossibile trasferire i file\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Potresti non avere i permessi di scrittura sulla cartella.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nuova Connessione SFTP \"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Crea\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Nome:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Per esempio: \\\"Server Documenti\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Specifica i dettagli del server e l'account a cui vuoi connetterti:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Server:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Porta:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"Nome &Utente:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Specifica la cartella del server con la quale vuoi iniziare la connessione:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"P&ercorso:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Esempio: /home/utente\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Cancella\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"L'etichetta non può essere più lunga di 30 caratteri\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Nome Server invalido\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"La porta non è valida ( tra 0 e 65535 )\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Il nome utente è invalido\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Il percorso è invalido\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Una connessione con la stessa etichetta è già presente. Riprovare.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Completa tutti i campi.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Password\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Visualizza &Dettagli (potrebbero essere in un'altra lingua)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Nascondi &Dettagli\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"La cartella contiene già il file '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Vuoi sostituire il file esistente\\n\\n\\t%1%\\n\\ncon?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"File già esistente\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Host-key ssh non corrispondenti\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ATTENZIONE: la chiave-host SSH è cambiata!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"L' host-key SSH  inviata da '%1%' per identificare se stesso non corrisponde alla chiave salvata per questo server. Questo potrebbe significare che qualcuno finge di essere il computer a cui si sta cercando di connettersi o l'amministratore di sistema può avere appena cambiato la chiave.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"E' importante verificare la correttezza dell'impronta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confermo questa chiave: $salva e collegati\\nNon verificare nuovamente\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Confermo questa chiave:  &collegati\\nVerifica chiave ad ogni accesso\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Cancella\\nScegli questo opzione se non si è certi che la chiave sia corretta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Host-key sconosciuta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Il server '%1%' si è identificato con una host-key SSH con impronta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Se la chiave non è quella prevista, potrebbe significare che qualcuno finge di essere il computer a cui si sta cercando di connettersi o l'amministratore di sistema può avere appena cambiato la chiave.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifica host-key SSH sconosciuta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Confermo questa chiave: &salva e collegati\\nNon verificare nuovamente\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Confermo questa chiave: &collegati\\nVerifica chiave ad ogni accesso\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nome\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Server\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Nome Utente\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Porta\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Percorso remoto\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Unità di Rete\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Aggiungi Connessione SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Crea nuova connessione SFTP con Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Aggiungi Connessione SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Aggiungi Connessione\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Attività SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Queste attività consentono di gestire connessioni SFTP Swish.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Lancia il gestore delle chiavi\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Lancia il gestore di Putty SSH delle chiavi, Pageant\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Lancia il gestore delle chiavi\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Rimuovi Connessione SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Rimuovi Connessione SFTP creata con Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Rimuovi Connessione SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Rimuovi Connessione\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Dimensioni\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Modificato\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Ultimo Accesso\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permessi\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Proprietario\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Gruppo\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID Proprietario\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID Gruppo\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Apri co&llegamento\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"Apri\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Impossibile aprire il collegamento\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Potresti non avere i permessi.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Impossibile aprire il file\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Operazioni su files e cartelle.\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Queste operazioni ti permettono di gestire i tuoi files remoti.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Non si riesce a cancellare l'oggetto.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nuova &cartella\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Crea una nuova cartella vuota nella cartella che hai aperto.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Crea una nuova cartella\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nuova Cartella\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Non si riesce a creare una nuova cartella\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Richiesto input da tastiera\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Non è possibile accedere alla cartella\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Percorso non riconosciuto\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Verifica che il percorso sia stato inserito correttamente.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Non si riesce a rinominare l'oggetto.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"L'oggetto non è accessibile.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Gli oggetti non sono accessibili.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"La cartella non è accessibile.\"\n"
  },
  {
    "path": "po/ja/swish.po",
    "content": "# \n# Translators:\n# Satoshi Kikuta <kik0220@gmail.com>, 2012\n# Tadashi \"ELF\" Jokagi <elf@poyo.jp>, 2012\n# takaf <m465c3iiwrih@gmail.com>, 2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: takaf <m465c3iiwrih@gmail.com>\\n\"\n\"Language-Team: Japanese (http://www.transifex.com/projects/p/swish/language/ja/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: ja\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"サーバーにファイルを作成できませんでした。\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"'{1}'をコピー中\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"'{1}'へ\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"既に '{1}' という名前のファイルが存在します\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"上書きしてよろしいですか？\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"ファイルの上書き確認\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"コピー中...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"転送に失敗しました\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"子のディレクトリーに書き込む権限がありません\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"新規接続(SFTP)\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"作成する\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"ラベル(&L):\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"例: \\\"自宅パソコン\\\"\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"接続したいコンピューターとアカウントの詳細を指定してください。\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"ホスト名(&H):\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"ポート番号(&P):\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"ユーザー(&U):\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Swishで接続するサーバーのディレクトリーを指定してください\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"パス(&A):\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"例: /home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"キャンセル\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"ラベルは30文字以内で指定してください。\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"ホスト名が正しくありません。\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"ポート番号の指定に誤りがあります(0から65535)。\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"ユーザー名に誤りがあります。\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"パスの指定に誤りがあります。\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"その接続名は既に存在しています。別の名前で登録してください。\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"全項目入力してください。\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"パスワード\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"詳細表示(日本語ではないかもしれません)(&D)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"詳細を隠す(&D)\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"既に '%1%' という名前のファイルが存在します\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"すでに存在しているファイルを置き換えてよろしいですか？\\n\\n»%1%\\n\\n置き換えるファイル\\n\\n»%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"ファイルは既に存在しています\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"ホスト鍵が一致しません\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"警告:SSHホスト鍵が変更されました\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"'%1%' から送信されてきたサーバー側のSSHホスト鍵が一致しません。管理者が変更しただけの可能性もありますが、何者かが接続先に「なりすまし」ている可能性もあります\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"この鍵の指紋(fingerprint)が正しい確認してください。\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"この鍵を信頼します: 鍵情報を更新して接続する(&U)\\n鍵が変更されるまでこの鍵を確認する必要はありません\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"この鍵を信頼します: このまま接続する(&J)\\n次回接続時もこのメッセージが表示されます\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"キャンセル(&C)\\nこの鍵が正しいものであると確信できない場合はこちらを選んでください\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"不明なサーバー鍵です\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"サーバー '%1%' は自身の以下の指紋(fingerprint)のSSH鍵で特定されています。\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"あなたが接続しようとしているこの鍵が予期できない鍵である場合、何者かがなりすましている可能性があります。\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"不明なSSH鍵を確認する\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"この鍵を信頼します: 鍵情報を保管して接続する(&S)\\n鍵が変更されるまでこの鍵を確認する必要はありません\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"この鍵を信頼します: このまま接続する(&J)\\n次回接続時もこのメッセージが表示されます\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"ファイル名/ラベル\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"ホスト名\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"ユーザー名\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"ポート番号\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"接続先のパス\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"タイプ\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"ネットワークドライブ\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"SFTP接続を追加する(&A)\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"新しくSwithのSFTP接続を作成します。\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"SFTP接続を追加する(A&)...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"接続を追加する\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTPタスク\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"これらのタスクでSwishのSFTP接続を管理できます\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"SSH key agentの起動\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Putty SSH key agentのページェントの起動\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"SSH agent programの起動\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"接続を削除する(&R)\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"作成したSwishのSFTP接続を削除します。\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP接続を削除する(&R)...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"接続を削除する\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"サイズ\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"変更日付\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"最終アクセス日付\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"権限(パーミッション)\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"オーナー\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"グループ\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"オーナーID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"グループID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"リンクを開く(&L)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"開く(&O)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"リンクを開けませんでした\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"権限(パーミッション)がありません。\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"ファイルを開けませんでした\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"ファイルとフォルダーのタスク\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"これらのタスクで接続先のファイルを管理できます。\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"削除できません\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"新規フォルダー（＆F)\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"現在開いているフォルダーに新規フォルダーを作成します。\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"新規フォルダーを作成する\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"新規フォルダー\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"新規フォルダーを作成できません\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"対話式でキーボードから入力する\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"ディレクトリーにアクセスできません\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"パスが認識できません。\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"入力したパスが正しいか確認してください。\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"名前の変更はできません\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"アクセスできません\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"アクセスできません\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"フォルダーにアクセスできません\"\n"
  },
  {
    "path": "po/ko/swish.po",
    "content": "# \n# Translators:\n# swkim85 <swkim85@gmail.com>, 2012-2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: swkim85 <swkim85@gmail.com>\\n\"\n\"Language-Team: Korean (http://www.transifex.com/projects/p/swish/language/ko/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: ko\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"서버에 파일을 만들 수 없습니다.\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"복사중 '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"'{1}' 으로\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"폴더에 파일 '{1}' 가 이미 존재합니다.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"덮어 쓸까요?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"파일 대체 확인\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"복사중...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"파일을 전송할 수 없습니다.\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"폴더에 쓰기 권한이 없습니다.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"새로운 SFTP 연결\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"만들기\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"라벨:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"예: \\\"내 컴퓨터\\\"\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"연결하려는 컴퓨터의 계정과 상세정보를 입력하세요.\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"호스트:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"포트:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"사용자:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"접속을 시작할 서버상의 디렉터리 경로를 입력\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"경로\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"예: /home/username\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"취소\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"라벨명은 30글자까지 제한됩니다.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"호스트명이 유효하지 않습니다.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"포트번호가 유효하지 않습니다.(0 에서 65535 까지 숫자)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"사용자명이 유효하지 않습니다.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"경로가 올바르지 않습니다.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"같은 라벨의 접속이 존재합니다. 다른 라벨을 입력하세요.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"모든 필드를 입력하세요.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"비밀번호\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"확인\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"파일이 이미 존재합니다.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"일치하지 않는 호스트 키\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"경고: SSH 호스트키가 변경됨\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"올바른 key fingerprint 인지 확인하세요.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"알려지지 않은 호스트 키\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"알려지지 않은 SSH 호스트 키를 확인\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"이름\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"호스트\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"사용자\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"포트\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"원격경로\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"종류\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"네트워크 드라이브\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"SFTP 연결 추가\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Swish로 새로운 SFTP 연결을 생성\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"SFTP 연결 추가\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"연결 추가\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP 작업들\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"키 에이전트 실행\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"키 에이전트 실행\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"SFTP 연결을 삭제\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP 접속 삭제\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"연결을 삭제\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"크기\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"수정한 날짜\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"접근한 날짜\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"퍼미션\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"사용자\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"그룹\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"사용자ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"그룹ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"링크 열기\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"열기\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"링크를 열 수 없습니다.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"퍼미션이 없습니다.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"파일을 열 수 없습니다.\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"파일과 폴더 작업\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"항목을 삭제할 수 없습니다.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"새로운 폴더\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"새로운 폴더를 생성\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"새 폴더\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"새 폴더를 생성할 수 없습니다.\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Keyboard-interactive request\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"디렉터리에 접근할 수 없습니다.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"경로가 올바르지 않습니다.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"경로가 정확한지 확인하세요.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"이름을 변경할 수 없습니다\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"접근할 수 없습니다.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"항목에 접근할 수 없습니다\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"폴더에 접근할 수 없습니다.\"\n"
  },
  {
    "path": "po/lv/swish.po",
    "content": "# \n# Translators:\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Artūrs Zalužinskis <artchijs2@inbox.lv>\\n\"\n\"Language-Team: Latvian (http://www.transifex.com/projects/p/swish/language/lv/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: lv\\n\"\n\"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Nav iespējams izveidot datni uz servera:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"'{1}' kopēšana\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Uz '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Šī mape jau satur datni ar nosaukumu '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Vai vēlaties aizvietot to?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Apstiprināt datnes aizvietošanu\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopēšana...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Nav iespējams pārnest datnes\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Iespējams Jums nav rakstīšanas tiesību šajā mapē.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Jauns SFTP pieslēgums\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Izveidot\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Etiķete:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Piemēram: \\\"Mājas dators\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Norādiet papildus informāciju par datoru un kontu kuram vēlaties pieslēgties:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Saimniekdators\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Ports:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Lietotājs:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Norādiet mapi uz servera, kuru vēlaties norādīt kā Swish starta pieslēguma punktu:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"C&eļš:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Piemēram: /home/janis\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"At&celt\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Etiķetes garums nedrīkst pārsniegt 30 rakstzīmes.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Saimniekdatora vārds ir nepareizs.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Ports nav derīgs (no 0 līdz 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Lietotājvārds ir nepareizs.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Ceļš ir nepareizs.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Pieslēgums jau eksistē ar šādu etiķeti. Lūdzu izmantojiet citu.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Aizpildiet visus laukus.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Parole\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"Labi\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Rādīt &detaļas (kuras var nebūt jūsu valodā)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Slēpt &detaļas\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Mape jau satur failu ar nosaukumu '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Vai vēlaties aizvietot eksistējošo datni\\n\\n\\t%1%\\n\\nar šo?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Datne jau eksistē\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Host-atslēga nesakrīt\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"UZMANīBU: SSH host-atslēga ir mainīta!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH host-atslēga kas nosūtītā no '%1%' lai identificētu sevi neatbislt zināmai atslēgai kas paredzēta šim serverim. Tas varētu nozīmēt kā trešās personas uzdodas par datoru kuram mēģinat pieslēgties vai iespējams sistēmas administrators tikko nomainīja atslēgu.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Ir svarīgi pārbaudīt tā ir pareizā identificējošā zīmju virkne:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Es uzticos šai atslēgai: &atjaunināt un pieslēgt\\nJums nav nepieciešams apstiprināt šīs atslēgas pareizību kamēr tā netiks nomainīta.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Es uzticos šai atslēgai: &tikai pieslēgt\\nJūs saņemsiet brīdinājumu par šo atslēgu nākamajā pieslēguma reizē\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"At&celt\\nIzvēlaties šo opciju ja neesat pārliecināts ka atslēga ir pareiza\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Nezināmā host-atslēga\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Serveris '%1%' ir identificējis sevi ar SSH host-atslēgu kuras identificējošā zīmju virkne: ir:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Ja jūs neesat sagaidījis šo atslēgu, trešās puses var kļūt par datoru kuram jūs mēģinat pieslēgties.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Apstiprināt nezināmo SSH-host atslēgu\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Es uzticos šai atslēgai: &uzglabāt un pieslēgties\\nJums nav jāapstirina šo atslēgu kamēr tā netiks nomainīta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Es uzticos šai atslegai: &tikai pieslēgties\\nJums tiks pieprasīts apstiprinājums par atslēgas pareizību nākamajā pieslēgšanas reizē\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Vārds\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Saimniekdators\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Lietotājvārds\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Ports\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Attālinātais ceļš\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tips\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Tīkla diskdzinis\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"Pi&evienot SFTP pieslēgumu\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Izveidot jaunu SFTP pieslēgumu ar Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"P&ievienot SFTP pieslēgumu...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Pievienot pieslēgumu\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP uzdevumi\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Šie uzdevumi palīdz jums pārvaldīt Swish SFTP pieslēgumus.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Palaist atslēgu aģentu\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Palaist Putty SSH atslēgu aģentu, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Palaist atslēgu aģentu\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Dzest SFTP pieslēgumu\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Dzēst SFTP pieslēgumu kas ir izveidots ar Swish\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Dzēst SFTP pieslēgumu...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Dzēst pieslēgumu\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Izmērs\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Modificēšanas datums\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Piekļuves datums\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Atļaujas\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Īpašnieks\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupa\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Īpašnieka ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Grupas ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Atvērt &saiti\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Atvērt\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Nav iespējams atvērt saiti\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Iespējams jums nav tiesīnu\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Nav iespējams atvērt datni\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Datņu un mapju uzdevumi\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Šie uzdevumi palīdz jums pārvaldīt jūsu attalinātas datnes.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Nav iespējams dzēst vienību\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Jauna &Mape\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Izveidot jaunu, tukšu mapi atvērtajā mapē\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Izveidot jaunu mapi\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Jauna mape\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nav iespējams izveidot jaunu mapi\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Tastatūras interaktīvais pieprasījums\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Nav iespējams piekļūt mapei\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Ceļš nav atpazīts\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Pārbaudiet vai ceļš ir ievadīts korekti.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Nav iespējams pārdēvēt vienību\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Nav iespējams piekļūt vienībai\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Nav iespējams piekļūt vienībām\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Nav iespējams piekļūt mapei\"\n"
  },
  {
    "path": "po/nl/swish.po",
    "content": "# \n# Translators:\n# Jos Lagerweij <transifex@lagerwey.net>, 2012\n# Stefan Suceveanu <ssuceveanu@yahoo.com>, 2013\n# Thijs-jan Veldhuizen <t.j.veldhuizen@tjveldhuizen.nl>, 2012\n# bericht <tomsanders@live.nl>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Stefan Suceveanu <ssuceveanu@yahoo.com>\\n\"\n\"Language-Team: Dutch (http://www.transifex.com/projects/p/swish/language/nl/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: nl\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Kon geen bestand aanmaken op de server:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"'{1}' wordt gekopieerd\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Naar '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Deze map bevat al een bestand met de naam '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Wil je het vervangen?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Bevestig vervanging\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopiëren...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Kan bestanden niet kopieren\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Mogelijk heeft u geen schrijfrechten voor deze map.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nieuwe SFTP Verbinding\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Aanmaken\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Titel:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Bijvoorbeeld: \\\"Computer Thuis\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Geef de details van de computer en de account waarmee u verbinding wilt maken:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Host:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Poort:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Gebruiker:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Geef de map op de server waar Swish de verbinding in op moet starten:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"P&ad:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Voorbeeld: /home/uwgebruikersnaam\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Annuleren\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"De titel mag niet langer dan 30 lettertekens zijn.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"De hostnaam is ongeldig.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"De poort is niet geldig (tussen 0 en 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"De gebruikersnaam is ongeldig.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Het pad is ongeldig.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Een verbinding met deze titel bestaat al. Probeer een ander.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Vul alle velden in.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Wachtwoord\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"&Details weergeven (mogelijk niet in uw eigen taal)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"&Details verbergen\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Deze map bevat al een bestand met de naam '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Wilt u het bestaande bestand\\n\\n\\t%1%\\n\\nvervangen door dit bestand?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Bestand bestaat al\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Host-sleutel komt niet overeen\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"WAARSCHUWING: de SSH host-sleutel is veranderd!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"De SSH host-sleutel verzonden door '%1%' om zich te identificeren komt niet overeen met de bekende sleutel voor deze server. Mogelijk luistert iemand uw verbinding af, of de systeembeheerder heeft zojuist de sleutel van de host gewijzigd.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Het is belangrijk om te controleren dat dit de juiste sleutel vingerafdruk is:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ik vertrouw deze sleutel: &werk bij en verbind\\nU hoeft deze sleutel niet opnieuw te verifiëren, tenzij deze verandert \"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Ik vertrouw deze sleutel: &verbind\\nU wordt opnieuw gewaarschuwd voor deze sleutel, de volgende keer dat u verbindt\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Annuleer\\nKies deze optie, tenzij u zeker bent dat de sleutel correct is\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Onbekende host-sleutel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"De server %1% heeft zich geïdentificeerd met een SSH host-sleutel waarvan de vingerafdruk is:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Als u deze sleutel niet verwacht, kan een derde beweren de computer te zijn waarmee u een verbinding probeert te maken.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifieer onbekende SSH host-sleutel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ik vertrouw deze sleutel: &opslaan en verbinden\\nU hoeft deze sleutel niet opnieuw te verifiëren, tenzij deze verandert \"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Ik vertrouw deze sleutel: &verbinden\\nU wordt gevraagd om deze sleutel opnieuw te verifiëren, de volgende keer dat u verbindt\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Naam\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Host\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Gebruikersnaam\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Poort\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Extern pad\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Bestandsformaat\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Netwerkschijf\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"SFTP Verbinding &aanmaken\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Maak een niewe SFTP verbinding aan.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"SFTP Verbinding &aanmaken...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Verbinding &aanmaken\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP taken\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Deze taken helpen je bij het beheren van Swish SFTP-verbindingen\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Start sleutelbeheer\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Start Putty SSH sleutelbeheer, Pageant\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Start sleutelbeheer\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"SFTP Verbinding &verwijderen\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Verwijder een SFTP verbinding gemaakt met Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP Verbinding &verwijderen...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Verbinding &verwijderen\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Grootte\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Gewijzigd op\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Geraadpleegd op\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Toegangsrechten\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Eigenaar\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Groep\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Eigenaar ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Groep ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Open link\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"Open\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Kon de link niet openen\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Mogelijk heeft u geen toegangsrechten.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Kon het bestand niet openen\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Taken voor bestanden en mappen\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Deze taken helpen je je bestanden op afstand te beheren.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Het is niet mogelijk om dit item te verwijderen\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nieuwe &map\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Maak een nieuwe, lege, map in de map die je nu open hebt.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Maak een nieuwe map\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nieuwe map\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Kon geen nieuwe map maken\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Keyboard-interactive aanvraag\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Het is niet mogelijk deze map te benaderen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Pad niet herkend\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Controleer of het pad juist is ingevoerd.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Het is niet mogelijk dit item te hernoemen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Het is niet mogelijk dit item te benaderen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Het is niet mogelijk deze items te benaderen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Het is niet mogelijk deze map te benaderen\"\n"
  },
  {
    "path": "po/pl/swish.po",
    "content": "# \n# Translators:\n# Mr Pyo <mrp1990@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Mr Pyo <mrp1990@gmail.com>\\n\"\n\"Language-Team: Polish (http://www.transifex.com/projects/p/swish/language/pl/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: pl\\n\"\n\"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Nie można utworzyć pliku na serwerze\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Kopiowanie '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Do '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Ten folder zawiera już plik o nazwie '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Czy chcesz zamienić?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Potwierdź zamianę pliku\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopiowanie...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Nie można przesłać plików\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Możliwe, że nie możesz zapisywać w tym katalogu.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nowe połączenie SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Utwórz\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Etykieta:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Na przykład: \\\"Komputer domowy\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Podaj szczegóły komputera i konta z którym chcesz się połączyć:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Host:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Użytkownik:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Podaj folder na serwerze w którym Swish ma rozpocząć połączenie:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"Ścieżka:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Przykład: /home/twoja_nazwa_uzytkownika\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Anuluj\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Etykieta nie może być dłuższa niż 30 znaków.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Nazwa hosta jest nieprawidłowa.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Podany numer portu jest nieprawidłowy (prawidłowy zakres: 0-65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Nazwa użytkownika jest nieprawidłowa.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Ścieżka jest nieprawidłowa.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Połączenie z podaną etykietą już istnieje. Proszę użyć innej etykiety.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Wypełnij wszystkie pola.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Hasło\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Pokaż szczegóły\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Ukryj szczegóły\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Ten folder zawiera już plik o nazwie '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Czy chcesz zamienić istniejący plik\\n\\n\\t%1%\\n\\nnastępującym?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Plik już istnieje\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Niepasujący klucz hosta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"UWAGA: klucz SSH hosta uległ zmianie!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Klucz SSH hosta wysłany przez '%1%' w celu identyfikacji nie pasuje do znanego klucza dla tego serwera. Może to oznaczać, że ktoś obcy stara się udać komputer z którym chcesz się połączyć lub administrator systemu zmienił klucz.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Ważnym jest sprawdzenie poprawności odcisku palca tego klucza:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ufam temu kluczowi: &odśwież i połącz\\nNie będziesz musiał weryfikować tego klucza ponownie pod warunkiem, że nie ulegnie on zmianie\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Ufam temu kluczowi: &po prostu połącz\\nBędziesz ostrzeżony o tym kluczu przy następnym połączeniu\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Anuluj\\nWybierz tę opcję jeżeli nie jesteś pewny poprawności klucza \"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Nieznany klucz hosta.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Serwer '%1%' zidentyfikował się kluczem SSH hosta o odcisku palca:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Jeżeli nie spodziewasz się tego klucza, może to oznaczać, że ktoś obcy stara się udać komputer z którym chcesz się połączyć.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Zweryfikuj nieznany klucz SSH hosta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Ufam temu kluczowi: &zachowaj i połącz\\nNie będziesz musiał weryfikować tego klucza ponownie pod warunkiem, że nie ulegnie on zmianie\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Ufam temu kluczowi: &po prostu połącz\\nBędziesz zapytany o ponowną weryfikację tego klucza przy następnym połączeniu\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nazwa\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Host\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Użytkownik\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Zdalna ścieżka\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Dysk sieciowy\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"Dodaj połączenie \"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Utwórz nowe połączenie SFTP przy użyciu Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"Dodaj połączenie SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Dodaj połączenie\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Zadania SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Te zadania pomagają zarządzać twoimi połączeniami SFTP.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"Uruchom agenta kluczy\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Uruchom Pageant, agenta kluczy Putty.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Uruchom agenta kluczy\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"Usuń połączenie SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Usuń połączenie utworzone przy użyciu Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"Usuń połączenie SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Usuń połączenie\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Rozmiar\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Data modyfikacji\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Data dostępu\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Uprawnienia\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Właściciel\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupa\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID Właściciela\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID Grupy\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Otwórz łącze\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Otwórz\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Nie można otworzyć łącza\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Możliwy brak uprawnień.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Nie można otworzyć pliku\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Zadania pliku i folderu\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Te zadania pomagają zarządzać twoimi zdalnymi plikami.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Niepowodzenie podczas usuwania elementu.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nowy folder\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Utwórz nowy, pusty folder.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Utwórz nowy folder\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nowy folder\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nie można utworzyć nowego folderu\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Interaktywne żądanie klawiatury\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Brak dostępu do katalogu.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Ścieżka nierozpoznana.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Sprawdź czy podana ścieżka jest poprawna.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Niepowodzenie podczas zmiany nazwy elementu.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Niepowodzenie podczas dostępu do elementu.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Niepowodzenie podczas dostępu do elementów.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Niepowodzenie podczas dostępu do folderu.\"\n"
  },
  {
    "path": "po/pt/swish.po",
    "content": "# \n# Translators:\n# Éliton Claus <eliton@eliton.com.br>, 2012\n# jmruas <jmruas@gmail.com>, 2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: jmruas <jmruas@gmail.com>\\n\"\n\"Language-Team: Portuguese (http://www.transifex.com/projects/p/swish/language/pt/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: pt\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Não foi possível criar ficheiro no servidor:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"A copiar '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Para '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Esta pasta já contém um ficheiro com o nome '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Deseja substituir?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirme substituição\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"A copiar...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Não foi possível transferir os ficheiros\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Pode não ter permissão para escrever nesta pasta.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nova ligação SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Criar\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"Nome da Ligação:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"For example: \\\"Computador de casa\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Indique os detalhes do computador e conta a que se pretende ligar:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Máquina:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Porta:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Utilizador:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Indique qual a pasta no servidor onde o Swich deve iniciar a ligação:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"C&aminho:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exemplo: /home/o_seu_utilizador\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"O nome da ligação não poderá ter mais do que 30 caracteres.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"O nome da máquina é inválido.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"A porta não é válida (deverá estar no intervalo 0 a 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"O utilizador é inválido.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"O caminho é inválido.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Uma ligação com o mesmo nome já existe. Tente outro nome.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Complete todos os campos.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Palavra-chave\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Mostrar &detalhes (que poderão não estar na sua língua)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Esconder &detalhes\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"A pasta já contém um ficheiro com o nome '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Deseja substituir o ficheiro existente\\n\\n\\t%1%\\n\\npor este?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"O ficheiro já existe\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Chave de máquina (host-key) não é idêntica\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ATENÇÃO: a chave de máquina (host-key) SSH mudou!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Host-key SSH enviada por '%1%' para se identificar não corresponde à chave conhecida para este servidor. Isto pode significar que terceiros poderão estar a fazer-se passar pelo computador a que se está a ligar, ou o administrador poderá ter alterado a chave.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"É importante verificar se esta chave de máquina está correta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Eu confio nesta chave de máquina: &actualizar e ligar\\nNão terá que verificar novamente esta chave a não ser que seja alterada\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Eu confio nesta chave de máquina: &ligar apenas\\nSerá notificado sobre esta chave na próxima vez que se ligar\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Cancelar\\nEscolha esta opção se não tiver a certeza que a chave de máquina está correta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Chave de máquina (host-key) desconhecida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"O servidor '%1%' idenficou-se com a seguinte chave de máquina (host-key) SSH:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Se não está à espera desta chave de máquina, isto pode significar que terceiros poderão estar a fazer-se passar pelo computador a que se está a ligar.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifique chave de máquina (host-key) SSH desconhecida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Eu confio nesta chave de máquina: &armazenar e ligar\\nNão terá que verificar novamente esta chave a não ser que seja alterada\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Eu confio nesta chave de máquina: &ligar apenas\\nTerá que verificar novamente esta chave na próxima vez que se ligar\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nome\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Máquina\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Utilizador\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Porta\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Caminho remoto\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Unidade de Rede\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Adicionar ligação SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Criar uma nova ligação SFTP com o Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Adicionar Ligação SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Adicionar Ligação\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Tarefas SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Estas tarefas facilitam a gestão das ligações SFTP do Swish.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"La&nçar agente-chave\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Lançar agente-chave SSH Putty, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Lançar agente-chave\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Remover Ligação SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Remover uma ligação SFTP criada com o Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Remover Ligação SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Remover Ligação\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Tamanho\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Data de Modificação\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Data do Último Acesso\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permissões\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Proprietário (owner)\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupo\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID de proprietário (owner)\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID de grupo\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Abrir &ligação\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Abrir\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Não foi possível abrir a ligação\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Possivelmente não tem permissões.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Não foi possível abrir o ficheiro\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Tarefas de ficheiros e pastas\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Estas tarefas falicitam a gestão dos seus ficheiros remotos.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Não foi possível apagar o item\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nova &pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Criar uma pasta nova (vazia), na pasta que está aberta.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Criar uma nova pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nova pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Não foi possível criar uma nova pasta\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Autenticação interativa com teclado (keyboard-interactive request)\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Não foi possível ter acesso à pasta\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Caminho não reconhecido\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Verifique se o caminho foi introduzido corretamente.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Não foi possível alterar o nome do item\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Não foi possível ter acesso ao item\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Não foi possível ter acesso aos itens\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Não foi possível ter acesso à pasta\"\n"
  },
  {
    "path": "po/pt_BR/swish.po",
    "content": "# \n# Translators:\n# Éliton Claus <eliton@eliton.com.br>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Éliton Claus <eliton@eliton.com.br>\\n\"\n\"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/swish/language/pt_BR/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: pt_BR\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Não foi possível criar o arquivo no servidor:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copiando '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Para '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Esta pasta já contém um arquivo com o nome '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Deseja substituir?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirme a substituição\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Copiando...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Não foi possível transferir os arquivos\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Você pode não ter permissão de escrita neste diretório.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nova conexão SFTP\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Criar\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"Nome da conexão:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Por exemplo: \\\"Computador de casa\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Indique os detalhes do computador e da conta que gostaria de se conectar:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Servidor:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Porta:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Usuário:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Indique a pasta inicial no servidor para a conexão:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"C&aminho:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exemplo: /home/o_seu_utilizador\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"O nome da conexão não pode ter mais do que 30 caracteres.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"O nome do servidor é inválido.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"A porta não é válida (deve estar entre 0 e 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Usuário inválido.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Caminho inválido.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Uma conexão com o mesmo nome já existe. Tente outro nome.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Complete todos os campos.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Senha\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Mostrar &detalhes (que poderão não estar na sua língua)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Esconder &detalhes\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"A pasta já contém um arquivo com o nome '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Deseja substituir o arquivo existente\\n\\n\\t%1%\\n\\npor este?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"O arquivo já existe\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Host-key não é idêntica\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ATENÇÃO: Host-key SSH mudou!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"A Host-key SSH enviada por '%1%' para se identificar não é a mesma que a chave conhecida por você. \\nIsto pode representar alguém tentando se passar pelo servidor que você está se conectando ou o administrador do servidor pode ter alterado a Host-key SSH do servidor.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"É importante verificar se esta chave de máquina está correta:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Eu confio nesta chave de máquina: &atualizar e conectar\\nNão terá que verificar novamente esta chave a não ser que seja alterada\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Eu confio nesta chave de máquina: &conectar apenas\\nSerá notificado sobre esta chave na próxima vez que se conectar\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Cancelar\\nEscolha esta opção se não tiver a certeza que a chave de máquina está correta\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Host-key desconhecida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"O servidor '%1%' idenficou-se com a seguinte Host-key SSH:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Se não está esperando esta Host-key, isto pode significar que terceiros poderão estar se passando pelo servidor que está se conectando.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifique a Host-key SSH desconhecida\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Eu confio nesta chave de máquina: &armazenar e conectar\\nNão terá que verificar novamente esta chave a não ser que seja alterada\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Eu confio nesta chave de máquina: &conectar apenas\\nTerá que verificar novamente esta chave na próxima vez que se conectar\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nome\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Servidor\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Usuário\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Porta\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Caminho remoto\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tipo\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Unidade de Rede\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Adicionar conexão SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Criar uma nova conexão SFTP com o Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Adicionar conexão SFTP...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Adicionar conexão\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Tarefas SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Estas tarefas facilitam a gestão das conexões SFTP do Swish.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"E&xecutar o agente de chaves\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Executar o Pageant, agente de chaves SSH do Putty.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Executar o agente de chaves\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Remover conexão SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Remover uma conexão SFTP criada com o Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Remover conexão SFTP...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Remover conexão\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Tamanho\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Data de Modificação\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Data do Último Acesso\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permissões\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Proprietário (owner)\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupo\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID de proprietário (owner)\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID de grupo\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Abrir e criar &link\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Abrir\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Impossível de abrir o link\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Você possivelmente não tem permissões.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Impossível de abrir o arquivo\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Tarefas de arquivos e pastas\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Estas tarefas falicitam a gestão dos seus arquivos remotos.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Não foi possível apagar o item\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nova &pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Criar uma pasta nova (vazia), dentro da pasta que está aberta.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Criar uma nova pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nova pasta\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Não foi possível criar uma nova pasta\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Autenticação interativa com teclado (senha digitada)\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Não foi possível ter acesso à pasta\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Caminho não reconhecido\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Verifique se o caminho foi digitado corretamente.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Não foi possível alterar o nome do item\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Não foi possível ter acesso ao item\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Não foi possível ter acesso aos itens\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Não foi possível ter acesso à pasta\"\n"
  },
  {
    "path": "po/ro/swish.po",
    "content": "# \n# Translators:\n# Stefan Suceveanu <ssuceveanu@yahoo.com>, 2013\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Stefan Suceveanu <ssuceveanu@yahoo.com>\\n\"\n\"Language-Team: Romanian (http://www.transifex.com/projects/p/swish/language/ro/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: ro\\n\"\n\"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Nu a fost posibilă creerea fișierului pe server:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Copiem '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"în '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Acest director deja conține un fișier cu numele '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Vrei să îl înlocuiești?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Confirmă înlocuire fișierului\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Copiere ...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Fișierul nu a putut fi transferat\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"E posibil ca să nu aveți drepturile necesare de a scrie în acest director.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Conexiune SFTP nouă\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Crează\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"Nume&le conexiunii:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"De exemplu: \\\"Calculatorul de acasă\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Specifică detaliile contului și a calculatorului la care vrei să te conectezi:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Server:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Utilizator:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Specifică directorul de pe server în care Swish se va poziționa la conectare:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"C&alea:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exemplu: /home/numeletau\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Abandon\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Numele conexiunii nu poate fi mai mare de 30 de caractere.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Numele serverului este invalid.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Portul nu este corect (valori permise între 0 și 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Numele utilizatorului nu este corect.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Calea nu este corectă.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Mai există o conexiune cu același nume. Încercați un alt nume.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Completați toate câmpurile.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Parola\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Afișează &detaliile (pot fi în altă limbă)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Ascunde &detaliile\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Directorul conține deja un fișier cu numele '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Dorești înlocuire fișierului existent ⏎ ⏎ »%1%⏎ ⏎ cu acest fișier?⏎ ⏎ »%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Fișierul deja există\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Nepotrivire la cheia serverului\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ATENȚIE: cheia SSH a serverului a fost schimbată!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Cheia SSH trimisă de serverul '%1%' pentru identificare nu se potrivește cu cheia cunoscută a acestui server. Această poate însemna că un alt sistem  poate pretinde ca este calculatorul la care vrei să te conectezi sau administratorul sistemului poate a schimbat cheia de curând.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Este important să verifici că aceasta este cheia amprentă corectă: \"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Am încredere în această cheie: act&ualizare și conectare⏎ Nu mai este necesară verificarea acestei chei atâta timp cât ea nu se va schimba\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Am încredere în această cheie: &numai conectare⏎ Vei fi atenționat din nou la următoarea conectare despre nepotrivirea acestei chei.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Renunță⏎ Alege această opțiune numai dacă nu ești sigur că această cheie este corectă\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Cheia serverului este necunoscută\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Serverul '%1%' s-a identificat cu cheia SSH a serverului a cărei amprentă este:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Dacă nu recunoști această cheie e posibil ca un alt sistem să pretindă că este calculatorul la care încerci să te conectezi.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifică cheia SSH a serverului\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Am încredere în această cheie: &memoare și conectare⏎ Nu mai este necesară verificarea acestei chei atâta timp cât ea nu se va schimba\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Am încredere în această cheie: &numai conectare⏎ La următoarea conectare vei fi rugat din nou să verifici cheia.\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Nume\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Server\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Utilizator\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Calea de pe server\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tip\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Disk de rețea\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Adaugă o conexiune SFTP\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Crează o nouă conexiune SFTP folosind Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Adaugă o conexiune SFTP ...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Adaugă o conexiune\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Gestiune SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Aici poți gestiona conexiunile SFTP Swish.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Lansare key-agent\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Lansare Pageant, agentul pentru cheile SHH Putty.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Lansare key-agent\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"Ște&rge conexiunea SFTP\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Șterge o conexiunea SFTP creată folosind Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"Ște&rge conexiunea SFTP ...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Șterge conexiunea\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Dimensiune\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Data modificării\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Data accesării\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Permisiuni\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Proprietar\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grup\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID Proprietar\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID Grup\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Deschide &link-ul\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Deschide\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Link-ul este imposibil de deschis\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"E posibil să nu ai drepturile necesare.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Fișierul este imposibil de deschis\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Gestiunea fișierelor și directoarelor\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Te ajută să gestionezi fișierele de pe server.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Obiectul nu poate fi șters\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"&Director nou\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Crează un director nou, gol, în directorul actual.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Crează un director nou\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Director nou\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nu a fost posibilă crearea unui director nou\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Cerere de introducere manuală de la tastatură\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Nu pot accesa directorul\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Calea nu este recunoscută.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Verifică dacă calea este introdusă corect.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Redenumirea obiectului nu este posibilă\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Obiectul nu poate fi accesat\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Obiectele nu pot fi accesate\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Directorul nu poate fi accesat\"\n"
  },
  {
    "path": "po/ru/swish.po",
    "content": "# \n# Translators:\n# mPolr <mpolr21@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: mPolr <mpolr21@gmail.com>\\n\"\n\"Language-Team: Russian (http://www.transifex.com/projects/p/swish/language/ru/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: ru\\n\"\n\"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Невозможно создать файл на сервере\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Копирование '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"В '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Каталог уже содержит файл с именем '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Вы хотите заменить его?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Подтверждение замены файла\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Копирую...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Невозможно переместить файлы\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Возможно у вас нет прав для записи в эту папку.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Новое SFTP соединение\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Создать\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Имя:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Пример: \\\"Домашний\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Укажите параметры хоста и аккаунта, к которому Вы соединяетесь:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Хост:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Порт:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Логин:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Укажите каталог удаленного сервера, к которому соединиться:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"П&уть:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Пример: /home/вашлогин\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Отмена\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Имя не может быть длиннее 30 символов.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Имя хоста неверно.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Порт неверен (между 0 и 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Логин неверен.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Путь неверен.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Соединение с таким именем уже существует. Введите другое имя.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Заполните все поля.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Пароль\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"ОК\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Показать &детали (могут быть не на вашем языке)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Спрятать &детали\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Каталог уже содержит файл с именем '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Вы хотите заменить существующий файл\\n\\n\\t%1%\\n\\nэтим?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Файл уже существует\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Не совпадает ключ хоста\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"ВНИМАНИЕ: ключ SSH сервера изменился!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"Ключ SSH хоста '%1%' не совпадает с известным ключем от этого сервера.  Это может означать присутствие третьей стороны в канале передачи данных или смену ключа администратором системы.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Важно проверить отпечаток ключа:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Я доверяю этому ключу: &Обновить и соединить\\nВам не надо будет проверять этот ключ, пока он не сменится\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Я доверяю этому ключу: &Просто соединить\\nВ следующий раз вы будете снова предупреждены о ключе\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"О&тмена\\nВыбирайте эту опцию, пока Вы не уверены в ключе сервера\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Неизвестный ключ хоста\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Сервер  '%1%' идентифицировал себя с SSH хост-ключем с отпечатком:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Если вы ожидаете не этого ключа, третья сторона может присутствовать в канале передачи данных.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Проверьте неизвестный SSH хост-ключ\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Я доверяю этому ключу: &Сохранить и соединить\\nВам не надо будет проверять этот ключ, пока он не сменится\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Я доверяю этому ключу: &Просто соединить\\nВ следующий раз вы будете снова предупреждены о ключе\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Имя\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Хост\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Логин\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Порт\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Удаленный путь\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Тип\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Сетевой диск\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Добавить SFTP-соединение\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Создать новое SFTP соединение с помощью Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Добавить SFTP-соединение...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Добавить соединение\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"Задачи SFTP\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Эти задачи управляют соединениями Swish SFTP\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"&Запустить агента ключей\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Запустить Putty SSH агента ключей, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Запустить агента ключей\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Удалить SFTP-соединение\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Удалить SFTP-соединение, созданное Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Удалить SFTP-соединение...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Удалить соединение\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Размер\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Изменен\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Последний доступ\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Права\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Владелец\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Группа\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID владельца\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID группы\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Открыть &ссылку\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Открыть\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Невозможно открыть ссылку\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"У вас не хватает прав.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Невозможно открыть файл\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Задачи файлов и папок\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Эти задачи управляют вашими удаленными файлами.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Ошибка удаления объекта\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"&Новый каталог\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Создает новый чистый каталог в текущем каталоге.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Создание нового каталога\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Новый каталог\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Ошибка создания каталога\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Ввод с клавиатуры\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Ошибка доступа к каталогу\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Путь не опознан\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Проверьте, чтобы путь был введен правильно.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Невозможно переименовать объект\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Ошибка доступа к объекту\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Ошибка доступа к объектам\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Ошибка доступа к каталогу\"\n"
  },
  {
    "path": "po/sk/swish.po",
    "content": "# \n# Translators:\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: alamaison <awl03@doc.ic.ac.uk>\\n\"\n\"Language-Team: Slovak (http://www.transifex.com/projects/p/swish/language/sk/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: sk\\n\"\n\"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Táto zložka už obsahuje súbor s názvom '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Chcete súbor prepísať?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Potvrdiť prepis súboru\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopírovanie...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Nové SFTP pripojenie\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Vytvoriť\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Visačka:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Napríklad: \\\"Domáci počítač\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Vložte údaje o počítač a konte, na ktoré sa chcete pripojiť:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Hostiteľ:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Používateľ:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Vyberte si zložku na serveri, do ktorej sa má Swish pripojiť pri štarte:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"C&esta:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Príklad: /home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Zrušiť\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Visačka nemôže byť dlhšia ako 30 písmen.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Meno hostiteľa nie je správne.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Port nie je správny (medzi 0 a 65535)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Používateľské meno nie je správne.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Cesta nei je platná.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Pripojenie s rovnakou visačkou už existuje. Skúste inú.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Vyplňte všetky polia.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Heslo\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Zobraziť &podrobnosti (ktoré nemusia byť vo vašom jazyku)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Skryť &podrobnosti\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Táto zložka už obsahuje súbor s názvom '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Chcete prepísať existujúci súbor\\n\\n\\t%1%\\n\\ntýmto?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Súbor už existuje\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Nezhodný hostiteľsý kľúč\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"POZOR: SSH hostiteľský kľúč bol zmenený!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH hostiteľský kľúč poslaný  '%1%' na vlastnú identifikáciu nesúhlasí so známym kľúčom pre tento server. To môže znamenať, že sa tretia strana tvári ako počítač, na ktorý sa snažíte pripojiť, alebo len administrátor tento kľúč zmenil.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Je dôležité skontrolovať, že toto je správny odtlačok kľúča:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Dôverujem tomuto kľúču: &obnoviť a pripojiť\\nTento kľúč už nebudete musieť overovať, pokial sa nezmení\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Dôverujem tomuto kľúču: &iba pripojiť\\nPri ďalšom pripojení budete znovu vyzvaní na overenie kľúča\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Zrušiť\\nAk si nie ste istí, že je kľúč správny, vyberte si túto možnosť\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Neznámy hostiteľský kľúč\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Server '%1%' sa identifikoval SSH hostiteľským kľúčom, ktorého odtlačok je:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Ak ste tento kľúč neočakávali, je možné, že sa tretia strana tvári ako počítač, na ktorý sa snažíte pripojiť.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Overiť neznámy SSH hostiteľský kľúč\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Dôverujem tomuto kľúču: &uložiť a pripojiť\\nTento kľúč už nebudete musieť overovať, pokial sa nezmení\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Dôverujem tomuto kľúču: &iba pripojiť\\nPri ďalšom pripojení budete znovu vyzvaní na overenie kľúča\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Meno\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Hostiteľ\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Používateľské meno\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Vzdialená cesta\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Sieťový disk\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"&Pridať SFTP pripojenie\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Pridať SFTP pripojenie cez Swish\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"&Pridáva sa SFTP pripojenie...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Pridať pripojenie\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP úlohy\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Tieto úlohy vám pomôžu spravovať Swish SFTP pripojenia.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"&Odstrániť SFTP pripojenie...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Odstrániť SFTP pripojenie vytvorené cez Swish.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"&Odstraňovanie SFTP pripojenia...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Odstrániť pripojenie\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Veľkosť\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Dátum zmeny\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Dátum prístupu\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Oprávnenia\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Majiteľ\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Skupina\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"ID majiteľa\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"ID skupiny\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Nemusíte mať oprávnenie\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Úlohy zložiek a súborov\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Tieto úlohy vám pomôžu spravovať vaše vzdialené súbory.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Položka sa nedá vymazať\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Nová &zložka\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Vytvorí novú, prázdnu zložku v zložke, ktorú máte otvorenú.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Vytvoriť novú zložku\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Nová zložka\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Nová zložka sa nedá vytvoriť\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Zložka nie je prístupná\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Cesta nebola nájdená\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Skontroľujte, či bola cesta zadaná správne.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Položka sa nedá premenovať\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"K položke sa nedá pristúpiť\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"K položkám sa nedá pristúpiť\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Zložka nie je prístupná\"\n"
  },
  {
    "path": "po/sv/swish.po",
    "content": "# \n# Translators:\n# Sopor, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: Sopor\\n\"\n\"Language-Team: Swedish (http://www.transifex.com/projects/p/swish/language/sv/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: sv\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"Kunde inte skapa någon fil på servern:\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"Kopierar '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"Till '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Denna mappen innehåller redan en fil med namnet '{1}'.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Vill du ersätta den?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Bekräfta filersättning\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopierar...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"Kunde inte överföra filerna\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"Du verkar inte ha skrivrättigheter i denna mappen.\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Ny STFP-anslutning\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Skapa\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Etikett:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Till exampel: \\\"Hemdatorn\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Specificera detaljerna för datorn och kontot du vill ansluta till:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Värd:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Användare:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Ange mappen på servern du vill att Swish skall börja anslutningen i:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"S&ökväg:\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Exempel: /hemma/dittanvändarnamn\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"Avbryt\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Etiketten kan inte vara längre än 30 tecken\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Värdnamnet är ogiltigt.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Porten är inte giltig (mellan 0 och 65535).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Användarnamnet är ogiltigt.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Sökvägen är ogiltig.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Det finns redan en anslutning med samma namn. Var god använd ett annat namn.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Komplettera alla fält\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Lösenord\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"Visa &detaljer (kan vara på ett annat språk)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"Dölj &detaljer\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Mappen innehåller redan en fil med namnet '%1%'\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Vill du ersätta den befintliga filen\\n\\n\\t%1%\\n\\nwith this one?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Filen finns redan\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Ogiltig värdnyckel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"VARNING: SSH-värdnyckel har ändrats!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"SSH-värdnyckel skickad med '%1%' för att identifiera sig stämmer inte överens med nyckel för den här servern. Detta kan innebära en tredje part låtsas vara den dator du försöker ansluta till eller så har systemadministratören nyligen ändrat nyckeln.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Det är viktigt att kontrollera om detta är rätt nyckel:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Jag litar på denna nyckeln: &uppdatera och anslut \\nDu kommer inte att behöva verifiera denna nyckel igen om den inte ändras\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Jag litar på denna nyckeln: &bara anslut \\nDu kommer att varnas för denna nyckel igen nästa gång du ansluter\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&Avbryt \\nVälj detta alternativ om du är säker på att nyckeln är felaktig\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Okänd värdnyckel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Servern '%1%' har identifierat sig med en SSH-värdnyckel, vars fingeravtryck är:\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Om du inte väntar på denna nyckel kan en tredje part som låtsas vara den dator du försöker ansluta till.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Verifiera okända SSH-värdnyckel\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Jag litar på denna nyckel: &spara och anslut\\nDu kommer inte att behöva verifiera denna nyckel igen om den inte ändras\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Jag litar på denna nyckeln: &bara anslut\\nDu kommer inte att behöva verifiera denna nyckel igen om den inte ändras\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"Namn\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Värd\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Användarnamn\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Fjärrsökväg\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Nätverksenhet\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"Lägg till SFTP-anslutning\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Skapa en ny SFTP-anslutning med Swish.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"Skapa en ny SFTP anslutning...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Skapa anslutning\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP-aktiviteter\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Dessa uppgifter hjälper dig att hantera Swish SFTP-anslutningar.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"Starta nyckelagenten\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"Starta Putty SSH-nyckelagent, Pageant.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"Starta nyckelagenten\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"Ta bort SFTP-anslutning\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Ta bort en SFTP-anslutning skapad med Swish\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"Ta bort en SFTP-anslutning\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Ta bort anslutning\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Storlek\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Ändrad datum\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Använd datum\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"Behörigheter\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Ägare\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grupp\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Ägar-ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Grupp-ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"Öppna &länk\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"&Öppna\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"Kunde inte öppna länken\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"Du kanske inte har behörighet.\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"Kunde inte öppna filen\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"Fil- och mappaktiviteter\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"Dessa uppgifter hjälper dig att hantera dina fjärrfiler.\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"Det går inte att ta bort objektet\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"Ny %mapp\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"Skapa en ny, tom mapp i den mapp som du har öppen.\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"Skapa en ny mapp\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"Ny mapp\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"Kunde inte skapa en ny mapp\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Begäran om tangentbords-interativitet\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"Det går inte att komma åt katalogen\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"Sökvägen hittas inte\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"Kontrollera att sökvägen angetts korrekt.\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"Det går inte att byta namn på objektet\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"Det går inte att komma åt objektet\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"Det går inte att komma åt objekten\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"Det går inte att komma åt mappen\"\n"
  },
  {
    "path": "po/template/swish.pot",
    "content": "# Alexander Lamaison <awl03@doc.ic.ac.uk>, 2010.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: \\n\"\n\"Last-Translator: Alexander Lamaison <awl03@doc.ic.ac.uk>\\n\"\n\"Language-Team: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"Top line of a transfer progress window saying which file is being copied. {1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"Second line of a transfer progress window giving the destination directory. {1} is replaced with the directory path and must be included in your translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"Specify the details of the computer and account you would like to connect to:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"Specify the directory on the server that you would like Swish to start the connection in:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:185\n#: ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"The SSH host-key sent by '%1%' to identify itself doesn't match the known key for this server.  This could mean a third-party is pretending to be the computer you're trying to connect to or the system administrator may have just changed the key.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"The server '%1%' has identified itself with an SSH host-key whose fingerprint is:\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"If you are not expecting this key, a third-party may be pretending to be the computer you're trying to connect to.\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "po/tr/swish.po",
    "content": "# \n# Translators:\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: alamaison <awl03@doc.ic.ac.uk>\\n\"\n\"Language-Team: Turkish (http://www.transifex.com/projects/p/swish/language/tr/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: tr\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"Bu klasör zaten {1} isminde bir dosya içermekte.\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"Üstüne yazmak istiyor musunuz?\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"Dosyanın üstüne yazılacak. Emin misiniz?\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"Kopyalanıyor...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"Yeni SFTP Bağlantısı\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"Oluştur\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"&Etiket:\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"Örneğin: \\\"Bilgisayarım\\\".\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"Bağlanmak istediğiniz bilgisayar ve hesabın detaylarını belirtin:\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"&Sunucu:\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"&Port:\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"&Kullanıcı:\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"Swish'in bağlantıyı içinde başlatmasını istediğiniz klasörü belirtin:\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"Klasör &Yolu\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"Örneğin: /home/<kullanıcı ismi buraya>\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"İptal\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"Etiket 30 karakterden daha uzun olamaz.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"Sunucu ismi geçersiz.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"Port değeri geçerli bir değer değil (0 ile 65535 arasında seçilmeli).\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"Kullanıcı ismi geçersiz.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"Klasör Yolu geçersiz.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"Aynı etiketli başka bir bağlantı zaten var. Lütfen başka bir isim verin.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"Tüm alanları tamamlayın.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"Şifre\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"Tamam\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"Bu klasör zaten '%1%' isminde bir dosya içermekte.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"Var olan\\n \\n\\t%1%'i\\n\\t%2%\\n\\nile değiştirmek istiyor musunuz?\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"Dosya zaten mevcut\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"Sunucu anahtarı eşleşmedi\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"UYARI: SSH Sunucu anahtarı değişti!\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"%1% tarafından kendini tanıtmak için gönderilen SSH sunucu anahtarı bu sunucu için önceden bilinen anahtar ile uyuşmuyor. Bu, bağlanmaya çalıştığınız bilgisayarı taklit eden 3. partilerin araya girmiş olabileceği yada sistem yöneticisinin sunucu anahtarını değiştirmiş olabileceği anlamına gelmektedir.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"Bu anahtar parmak izinin (fingerprint), doğru olduğunu kontrol ettiğinizden emin olun: \"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Bu sunucu anahtarına güveniyorum: &güncelle ve bağlan\\nBu sunucu anahtarını değişmediği sürece bir daha doğrulamak zorunda değilsiniz.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"Bu sunucu anahtarına güveniyorum: &sadece bağlan\\nBir dahaki bağlantınızda bu sunucu anahtarı ile ilgili olarak tekrar uyarı alacaksınız\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"&İptal\\nSunucu anahtarının doğru olduğundan emin değilseniz bu seçeneği seçin.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"Bilinmeyen Sunucu Anahtarı\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"Sunucu %1% kendisini şu parmak izine sahip SSH sunucu anahtarı ile tanıtıyor: \"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"Eğer bu anahtar beklenildiği şekilde değilse 3. partiler araya girmiş ve bağlanmaya çalıştığınız makineyi taklit ediyor olabilir.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"Bilinmeyen SSH sunucu anahtarını doğrulayın\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"Bu sunucu anahtarına güveniyorum: &sakla ve bağlan\\nBu sunucu anahtarını değişmediği sürece bir daha doğrulamak zorunda değilsiniz.\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"Bu sunucu anahtarına güveniyorum: &sadece bağlan\\nBir dahaki bağlantınızda bu sunucu anahtarını doğrulamak üzere tekrar uyarı alacaksınız\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"İsim\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"Sunucu\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"Kullanıcı İsmi\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"Port\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"Uzak Klasör Yolu\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"Tip\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"Ağ Diski\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"Yeni &SFTP Bağlantısı Ekle\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"Swish kullanarak yeni bir SFTP bağlantısı oluştur.\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"Yeni &SFTP Bağlantısı Ekle...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"Bağlantı Ekle\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP Görevleri\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"Bu görevler, Swish SFTP bağlantılarını yönetmenizde size yardımcı olur.\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"SFTP Bağlantısını &Kaldır\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"Swish kullanılarak oluşturulan SFTP Bağlantısını Kaldır.\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"SFTP Bağlantısını &Kaldır...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"Bağlantıyı Kaldır\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"Boyut\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"Değiştirme Zamanı\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"Erişim Zamanı\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"İzinler\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"Dosya Sahibi\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"Grup\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"Sahip ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"Grup ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"Klavye-Etkileşimli İstek\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"\"\n"
  },
  {
    "path": "po/zh_CN/swish.po",
    "content": "# \n# Translators:\n# Rob . <rbnwmk@gmail.com>, 2012\n# vibbow <vibbow@hotmail.com>, 2012\n# ZH CEXO <xcjjzh@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: vibbow <vibbow@hotmail.com>\\n\"\n\"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/swish/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\"Language: zh_CN\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"无法在伺服器上建立文件\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"正在复制 '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"到 '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"这个文件夹里已经存在[{1}]。\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"是否要取代它？\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"确认取代文件\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"正在复制...\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"无法传输文件\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"你没有权限写入此文件夹。\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"新SFTP连接\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"创建\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"标识(&L)：\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"例子：\\\"家中电脑\\\"。\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"指定你将连接的电脑和帐号：\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"主机(&H)：\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"端口(&P)：\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"用户(&U)：\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"指定你开启Swish连接主机时的文件夹开始位置：\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"路径(&a):\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"例子: /home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"取消\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"标签不可超过30个字.\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"主机名字无效.\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"输入端口无效(必须在0-65535之间)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"用户名字无效.\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"路径无效.\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"同名连接已存在. 请另试用其他命名.\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"完成全部字域.\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"密码\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"确认\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"显示详细(也许不是以您的语言显示)(&d)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"隐藏详细(&d)\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"文件夹里已经存在[%1%]\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"是否让此已存在文件\\n\\n\\t%1%\\n\\n被以下文件替代?\\n\\n\\t%2%\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"文件已经存在\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"主机密钥不匹配\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"警告： SSH主机密匙已被更换！\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"[%1%]传送过来的SSH主机密匙和这伺服器上已认证的密匙不匹配。这有可能是第三者在伪装着你想要连上的电脑，或者是系统管理员已经更改了密匙\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"重要！检查是否密匙指纹正确：\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"我信任此密匙：&更新并连接\\n如果没有任何改动前提下，你往后不再需要确认此密匙\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"我信任此密匙：&直接连接\\n下次再次连接时你将会再被警告关于此密匙事宜\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"取消(&C)\\n如果不能确认密匙是正确的话选择此项\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"未知主机密匙\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"伺服器 [%1%] 已认证SSH主机密匙的指纹为：\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"如果这不是你所预计的密匙，也许是某人在伪装为你想连接的电脑。\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"核对不明SSH密匙\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"我信任此密匙：储存并连接(&s)\\n如果没有任何改动前提下，你往后不再需要确认此密匙\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"我信任此密匙：直接连接(&j)\\n下次再次连接时你将会再要求对此密匙进行核对\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"名字\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"主机\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"用户名\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"端口\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"远程路径\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"种类\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"网络驱动器\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"添加 SFTP 连接(&A)\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"用 Swish 创建新的 SFTP 连接。\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"添加 SFTP 连接(&A)...\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"添加连接\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP 任务\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"此任务帮你管理 SFTP 连接。\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"启动密钥代理程序(&L)\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"启动Putty密钥代理程序Pageant\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"启动密钥代理程序\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"删除 SFTP 连接(&R)\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"删除一个 Swish 创建的 SFTP 连接。\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"删除 SFTP 连接(&R)...\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"删除连接\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"大小\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"修改日期\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"上回访问日期\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"权限\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"拥有人\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"组\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"拥有人 ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"组 ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"打开連接(&l)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"打开(&O)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"无法打开连接\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"也许您没有权限。\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"无法打开相关文件\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"文件和文件夹任务\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"此任务帮你管理你的远程文件。\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"无法删除项目\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"新 &文件夹\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"在你已打开的文件夹内创建一个新的空文件夹。\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"创建一个新文件夹\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"新文件夹\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"无法创建新文件夹\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"键盘互动请求\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"无法进入目录\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"无法识别路径\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"检查是否路径正确。\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"不能重命名\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"无法进入此项目\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"无法进入此项目\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"无法进入文件夹\"\n"
  },
  {
    "path": "po/zh_TW/swish.po",
    "content": "# \n# Translators:\n# Rob . <rbnwmk@gmail.com>, 2012\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Swish\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2012-07-30 14:18-0000\\n\"\n\"PO-Revision-Date: 2013-11-20 11:36+0000\\n\"\n\"Last-Translator: alamaison <awl03@doc.ic.ac.uk>\\n\"\n\"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/swish/language/zh_TW/)\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Language: zh_TW\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"X-Poedit-KeywordsList: translate:1,1t;translate:1c,2,2t;translate:1,2,3t\\n\"\n\"X-Poedit-SearchPath-0: ../../swish\\n\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:152\nmsgid \"Unable to create file on the server:\"\nmsgstr \"無法在伺服器上創建文檔：\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:249\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:60\nmsgctxt \"\"\n\"Top line of a transfer progress window saying which file is being copied. \"\n\"{1} is replaced with the file path and must be included in your translation.\"\nmsgid \"Copying '{1}'\"\nmsgstr \"正在複製 '{1}'\"\n\n#: ../../swish/drop_target/CopyFileOperation.cpp:260\n#: ../../swish/drop_target/CreateDirectoryOperation.cpp:71\nmsgctxt \"\"\n\"Second line of a transfer progress window giving the destination directory. \"\n\"{1} is replaced with the directory path and must be included in your \"\n\"translation.\"\nmsgid \"To '{1}'\"\nmsgstr \"到 '{1}'\"\n\n#: ../../swish/drop_target/DropUI.cpp:316\nmsgid \"This folder already contains a file named '{1}'.\"\nmsgstr \"已有稱為{1}的資料夾\"\n\n#: ../../swish/drop_target/DropUI.cpp:319\nmsgid \"Would you like to replace it?\"\nmsgstr \"要覆寫這檔案嗎？\"\n\n#: ../../swish/drop_target/DropUI.cpp:327\nmsgid \"Confirm File Replace\"\nmsgstr \"確認覆寫\"\n\n#: ../../swish/drop_target/DropUI.cpp:366\nmsgctxt \"Progress\"\nmsgid \"Copying...\"\nmsgstr \"正在複製…\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:125\nmsgid \"Unable to transfer files\"\nmsgstr \"無法傳輸文檔\"\n\n#: ../../swish/drop_target/SnitchingDropTarget.cpp:127\nmsgid \"You might not have permission to write to this directory.\"\nmsgstr \"您也許沒有權限寫入此目錄\"\n\n#: ../../swish/forms/add_host.cpp:102\nmsgid \"New SFTP Connection\"\nmsgstr \"新SFTP連線\"\n\n#: ../../swish/forms/add_host.cpp:122\nmsgid \"Create\"\nmsgstr \"建立\"\n\n#: ../../swish/forms/add_host.cpp:143\nmsgctxt \"New Host\"\nmsgid \"&Label:\"\nmsgstr \"連線名稱(&L)：\"\n\n#: ../../swish/forms/add_host.cpp:148\nmsgid \"For example: \\\"Home Computer\\\".\"\nmsgstr \"例如「家裡的電腦」\"\n\n#: ../../swish/forms/add_host.cpp:151\nmsgid \"\"\n\"Specify the details of the computer and account you would like to connect \"\n\"to:\"\nmsgstr \"輸入要連線的電腦及登入資料：\"\n\n#: ../../swish/forms/add_host.cpp:155\nmsgctxt \"New Host\"\nmsgid \"&Host:\"\nmsgstr \"伺服器(&S)：\"\n\n#: ../../swish/forms/add_host.cpp:159\nmsgctxt \"New Host\"\nmsgid \"&Port:\"\nmsgstr \"連接埠(&P)：\"\n\n#: ../../swish/forms/add_host.cpp:164\nmsgctxt \"New Host\"\nmsgid \"&User:\"\nmsgstr \"使用者(&U)：\"\n\n#: ../../swish/forms/add_host.cpp:169\nmsgid \"\"\n\"Specify the directory on the server that you would like Swish to start the \"\n\"connection in:\"\nmsgstr \"輸入開啟Swish時要瀏灠在伺服器上的目錄：\"\n\n#: ../../swish/forms/add_host.cpp:173\nmsgctxt \"New Host\"\nmsgid \"P&ath:\"\nmsgstr \"路徑(&P)；\"\n\n#: ../../swish/forms/add_host.cpp:177\nmsgid \"Example: /home/yourusername\"\nmsgstr \"例如：/home/yourusername\"\n\n#: ../../swish/forms/add_host.cpp:185 ../../swish/forms/password.cpp:68\nmsgid \"Cancel\"\nmsgstr \"取消\"\n\n#: ../../swish/forms/add_host.cpp:331\nmsgid \"The label cannot be longer than 30 characters.\"\nmsgstr \"連線名稱不可長過30個字元\"\n\n#: ../../swish/forms/add_host.cpp:335\nmsgid \"The host name is invalid.\"\nmsgstr \"伺服器地址不正確\"\n\n#: ../../swish/forms/add_host.cpp:341\nmsgid \"The port is not valid (between 0 and 65535).\"\nmsgstr \"連接埠不正確 (要在0至65535之間)\"\n\n#: ../../swish/forms/add_host.cpp:345\nmsgid \"The username is invalid.\"\nmsgstr \"登入名稱不正確\"\n\n#: ../../swish/forms/add_host.cpp:349\nmsgid \"The path is invalid.\"\nmsgstr \"路徑不正確\"\n\n#: ../../swish/forms/add_host.cpp:355\nmsgid \"A connection with the same label already exists. Please try another.\"\nmsgstr \"已有同名稱的連線存在，請使用其他名稱\"\n\n#: ../../swish/forms/add_host.cpp:361\nmsgid \"Complete all fields.\"\nmsgstr \"輸入所有空格\"\n\n#: ../../swish/forms/password.cpp:57\nmsgid \"Password\"\nmsgstr \"密碼\"\n\n#: ../../swish/forms/password.cpp:64\nmsgid \"OK\"\nmsgstr \"確定\"\n\n#: ../../swish/frontend/announce_error.cpp:136\nmsgid \"Show &details (which may not be in your language)\"\nmsgstr \"顯示詳細信息(可能不是以中文表示) (&d)\"\n\n#: ../../swish/frontend/announce_error.cpp:137\nmsgid \"Hide &details\"\nmsgstr \"隠藏詳細信息 (&d)\"\n\n#: ../../swish/frontend/UserInteraction.cpp:195\nmsgid \"The folder already contains a file named '%1%'\"\nmsgstr \"資料夾內已有名為「%1%」的檔案\"\n\n#: ../../swish/frontend/UserInteraction.cpp:200\nmsgid \"\"\n\"Would you like to replace the existing file\\n\"\n\"\\n\"\n\"\\t%1%\\n\"\n\"\\n\"\n\"with this one?\\n\"\n\"\\n\"\n\"\\t%2%\"\nmsgstr \"是否要將現有的檔案\\n\\n\\t%1%\\n\\n換成\\n\\n\\t%2%\\n\\n這個檔案？\"\n\n#: ../../swish/frontend/UserInteraction.cpp:204\nmsgid \"File already exists\"\nmsgstr \"已有這檔案\"\n\n#: ../../swish/frontend/UserInteraction.cpp:242\nmsgid \"Mismatched host-key\"\nmsgstr \"伺服器簽署不一致\"\n\n#: ../../swish/frontend/UserInteraction.cpp:243\nmsgid \"WARNING: the SSH host-key has changed!\"\nmsgstr \"警告：SSH伺服器簽署有變！\"\n\n#: ../../swish/frontend/UserInteraction.cpp:249\nmsgid \"\"\n\"The SSH host-key sent by '%1%' to identify itself doesn't match the known \"\n\"key for this server.  This could mean a third-party is pretending to be the \"\n\"computer you're trying to connect to or the system administrator may have \"\n\"just changed the key.\"\nmsgstr \"「%1%」剛發出的SSH伺服器簽署跟之前的有異。這可能是有其他人偽冒你要連線到的電腦，又或是伺服器管理理剛剛改變了簽署\"\n\n#: ../../swish/frontend/UserInteraction.cpp:256\nmsgid \"It is important to check this is the right key fingerprint:\"\nmsgstr \"必須核實簽署正確\"\n\n#: ../../swish/frontend/UserInteraction.cpp:265\nmsgid \"\"\n\"I trust this key: &update and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"我相信這個簽署：更新記錄並連線(&u)\\n除非簽署改變，不然不用再確認\"\n\n#: ../../swish/frontend/UserInteraction.cpp:270\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be warned about this key again next time you connect\"\nmsgstr \"我相信這個簽署：只連線(&j)\\n下次連線時將會再次警告\"\n\n#: ../../swish/frontend/UserInteraction.cpp:276\n#: ../../swish/frontend/UserInteraction.cpp:320\nmsgid \"\"\n\"&Cancel\\n\"\n\"Choose this option unless you are sure the key is correct\"\nmsgstr \"取消(&C)\\n除排確定簽署正確，不然選這項\"\n\n#: ../../swish/frontend/UserInteraction.cpp:289\nmsgid \"Unknown host-key\"\nmsgstr \"沒有記錄的伺服器簽署\"\n\n#: ../../swish/frontend/UserInteraction.cpp:295\nmsgid \"\"\n\"The server '%1%' has identified itself with an SSH host-key whose \"\n\"fingerprint is:\"\nmsgstr \"伺服器「%1%」用以下的SSH簽署去表示自己：\"\n\n#: ../../swish/frontend/UserInteraction.cpp:299\nmsgid \"\"\n\"If you are not expecting this key, a third-party may be pretending to be the\"\n\" computer you're trying to connect to.\"\nmsgstr \"如果這跟你預期的簽署有別，可能是有其他人偽冒你要連線到的電腦\"\n\n#: ../../swish/frontend/UserInteraction.cpp:302\nmsgid \"Verify unknown SSH host-key\"\nmsgstr \"確認沒記錄的SSH伺服器簽署\"\n\n#: ../../swish/frontend/UserInteraction.cpp:309\nmsgid \"\"\n\"I trust this key: &store and connect\\n\"\n\"You won't have to verify this key again unless it changes\"\nmsgstr \"我相信這個簽署：記錄並連線(&u)\\n除非簽署改變，不然不用再確認\"\n\n#: ../../swish/frontend/UserInteraction.cpp:314\nmsgid \"\"\n\"I trust this key: &just connect\\n\"\n\"You will be asked to verify the key again next time you connect\"\nmsgstr \"我相信這個簽署：只連線(&j)\\n下次連線時將會再次警告\"\n\n#: ../../swish/host_folder/columns.cpp:57\nmsgctxt \"Property (filename/label)\"\nmsgid \"Name\"\nmsgstr \"名稱\"\n\n#: ../../swish/host_folder/columns.cpp:60\nmsgctxt \"Property\"\nmsgid \"Host\"\nmsgstr \"伺服器地址\"\n\n#: ../../swish/host_folder/columns.cpp:63\nmsgctxt \"Property\"\nmsgid \"Username\"\nmsgstr \"登入名稱\"\n\n#: ../../swish/host_folder/columns.cpp:66\nmsgctxt \"Property\"\nmsgid \"Port\"\nmsgstr \"連接埠\"\n\n#: ../../swish/host_folder/columns.cpp:69\nmsgctxt \"Property\"\nmsgid \"Remote path\"\nmsgstr \"伺服器路徑\"\n\n#: ../../swish/host_folder/columns.cpp:72\nmsgctxt \"Property\"\nmsgid \"Type\"\nmsgstr \"種類\"\n\n#: ../../swish/host_folder/properties.cpp:79\nmsgctxt \"FileType\"\nmsgid \"Network Drive\"\nmsgstr \"網路磁碟\"\n\n#: ../../swish/host_folder/commands/Add.cpp:83\nmsgid \"&Add SFTP Connection\"\nmsgstr \"建立SFTP連線(&A)\"\n\n#: ../../swish/host_folder/commands/Add.cpp:84\nmsgid \"Create a new SFTP connection with Swish.\"\nmsgstr \"用Swish去建立新的SFTP連線\"\n\n#: ../../swish/host_folder/commands/Add.cpp:85\nmsgid \"&Add SFTP Connection...\"\nmsgstr \"新增SFTP連線…(&A)\"\n\n#: ../../swish/host_folder/commands/Add.cpp:86\nmsgid \"Add Connection\"\nmsgstr \"建立連線\"\n\n#: ../../swish/host_folder/commands/commands.cpp:91\nmsgid \"SFTP Tasks\"\nmsgstr \"SFTP工作\"\n\n#: ../../swish/host_folder/commands/commands.cpp:104\nmsgid \"These tasks help you manage Swish SFTP connections.\"\nmsgstr \"這些工作可助你管理Swish的SFTP連線\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:98\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:103\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"&Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:99\nmsgid \"Launch Putty SSH key agent, Pageant.\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/LaunchAgent.cpp:106\nmsgctxt \"Title of command used to launch the SSH agent program\"\nmsgid \"Launch key agent\"\nmsgstr \"\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:83\nmsgid \"&Remove SFTP Connection\"\nmsgstr \"移除連線(&R)\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:84\nmsgid \"Remove a SFTP connection created with Swish.\"\nmsgstr \"移除用Swish建立的連線\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:85\nmsgid \"&Remove SFTP Connection...\"\nmsgstr \"移除連線…(&R)\"\n\n#: ../../swish/host_folder/commands/Remove.cpp:86\nmsgid \"Remove Connection\"\nmsgstr \"移除連線\"\n\n#: ../../swish/remote_folder/columns.cpp:82\nmsgctxt \"Property\"\nmsgid \"Size\"\nmsgstr \"大小\"\n\n#: ../../swish/remote_folder/columns.cpp:89\nmsgctxt \"Property\"\nmsgid \"Date Modified\"\nmsgstr \"更新時間\"\n\n#: ../../swish/remote_folder/columns.cpp:93\nmsgctxt \"Property\"\nmsgid \"Date Accessed\"\nmsgstr \"存取時間\"\n\n#: ../../swish/remote_folder/columns.cpp:96\nmsgctxt \"Property\"\nmsgid \"Permissions\"\nmsgstr \"使用權\"\n\n#: ../../swish/remote_folder/columns.cpp:99\nmsgctxt \"Property\"\nmsgid \"Owner\"\nmsgstr \"擁有者\"\n\n#: ../../swish/remote_folder/columns.cpp:102\nmsgctxt \"Property\"\nmsgid \"Group\"\nmsgstr \"群組\"\n\n#: ../../swish/remote_folder/columns.cpp:105\nmsgctxt \"Property\"\nmsgid \"Owner ID\"\nmsgstr \"擁有者ID\"\n\n#: ../../swish/remote_folder/columns.cpp:108\nmsgctxt \"Property\"\nmsgid \"Group ID\"\nmsgstr \"群組ID\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:157\nmsgid \"Open &link\"\nmsgstr \"打開連接 (&l)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:179\nmsgid \"&Open\"\nmsgstr \"打開(&O)\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:273\nmsgid \"Unable to open the link\"\nmsgstr \"無法打開連接\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:274\n#: ../../swish/remote_folder/context_menu_callback.cpp:379\n#: ../../swish/remote_folder/commands/delete.cpp:232\n#: ../../swish/remote_folder/commands/NewFolder.cpp:240\nmsgid \"You might not have permission.\"\nmsgstr \"可能沒有所需權限\"\n\n#: ../../swish/remote_folder/context_menu_callback.cpp:378\nmsgid \"Unable to open the file\"\nmsgstr \"無法打開文檔\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:93\nmsgid \"File and Folder Tasks\"\nmsgstr \"檔案及資料夾工作\"\n\n#: ../../swish/remote_folder/commands/commands.cpp:105\nmsgid \"These tasks help you manage your remote files.\"\nmsgstr \"這些工作可助你管理伺服器上的檔案\"\n\n#: ../../swish/remote_folder/commands/delete.cpp:231\nmsgid \"Unable to delete the item\"\nmsgstr \"不能刪除\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:168\nmsgid \"New &folder\"\nmsgstr \"開新目錄(&F)\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:169\nmsgid \"Create a new, empty folder in the folder you have open.\"\nmsgstr \"在你已開啟的資料夾開再新增一個全新的空資料夾\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:171\nmsgid \"Make a new folder\"\nmsgstr \"開新資料夾\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:211\nmsgctxt \"Initial name\"\nmsgid \"New folder\"\nmsgstr \"新資料夾\"\n\n#: ../../swish/remote_folder/commands/NewFolder.cpp:239\nmsgid \"Could not create a new folder\"\nmsgstr \"開新資料夾時失敗\"\n\n#: ../../swish/shell_folder/KbdInteractiveDialog.cpp:67\nmsgid \"Keyboard-interactive request\"\nmsgstr \"要求以鍵盤輸入\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:182\nmsgid \"Unable to access the directory\"\nmsgstr \"不能讀取目錄\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:255\nmsgid \"Path not recognised\"\nmsgstr \"不能辨識連接埠號碼\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:256\nmsgid \"Check that the path was entered correctly.\"\nmsgstr \"檢查路徑是否正確\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:377\nmsgid \"Unable to rename the item\"\nmsgstr \"不能更名\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:704\nmsgid \"Unable to access the item\"\nmsgstr \"不能讀取\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:705\nmsgid \"Unable to access the items\"\nmsgstr \"不能讀取\"\n\n#: ../../swish/shell_folder/RemoteFolder.cpp:732\nmsgid \"Unable to access the folder\"\nmsgstr \"不能讀取資料夾\"\n"
  },
  {
    "path": "setup_conf.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configurations lcid_type=\"UserExe\" show_language_selector=\"False\" language_selector_title=\"\" language_selector_ok=\"OK\" language_selector_cancel=\"Cancel\" configuration_no_match_message=\"\" ui_level=\"basic\" fileversion=\"@swish_VERSION@.0\" productversion=\"@swish_VERSION@.0\" log_enabled=\"False\" log_file=\"#TEMPPATH\\dotNetInstallerLog.txt\">\n  <schema version=\"2.0.81.0\" generator=\"dotNetInstaller InstallerEditor\" />\n  <fileattributes>\n    <fileattribute name=\"ProductName\" value=\"Swish\" />\n    <fileattribute name=\"LegalCopyright\" value=\"@SWISH_COPYRIGHT@\" />\n    <fileattribute name=\"FileDescription\" value=\"Swish x86/x64 installer\" />\n    <fileattribute name=\"CompanyName\" value=\"swish-sftp.org\" />\n  </fileattributes>\n  <configuration dialog_caption=\"Swish Installer\" dialog_message=\"In order to install Swish you must first install these components:\" dialog_message_uninstall=\"\" dialog_bitmap=\"#APPPATH\\banner.bmp\" skip_caption=\"Skip\" install_caption=\"Install\" uninstall_caption=\"Uninstall\" cancel_caption=\"Close\" status_installed=\" (Installed)\" status_notinstalled=\"\" failed_exec_command_continue=\"\" installation_completed=\"\" uninstallation_completed=\"\" installation_none=\"Swish is already installed!\" uninstallation_none=\"Swish is not installed!\" installing_component_wait=\"Installing %s. Wait, this operation could take some time ...\" uninstalling_component_wait=\"Uninstalling %s. Wait, this operation could take some time ...\" reboot_required=\"To continue the installation you must restart your computer. Restart now?\" must_reboot_required=\"False\" dialog_otherinfo_caption=\"\" dialog_otherinfo_link=\"\" complete_command=\"\" complete_command_silent=\"\" complete_command_basic=\"\" wait_for_complete_command=\"True\" auto_close_if_installed=\"True\" auto_close_on_error=\"True\" reload_on_error=\"False\" dialog_show_installed=\"False\" dialog_show_uninstalled=\"False\" dialog_show_required=\"False\" cab_dialog_message=\"%s\" cab_cancelled_message=\"\" cab_dialog_caption=\"\" cab_path=\"#TEMPPATH\\#GUID\" cab_path_autodelete=\"True\" dialog_default_button=\"cancel\" dialog_position=\"\" dialog_components_list_position=\"\" dialog_message_position=\"\" dialog_bitmap_position=\"\" dialog_otherinfo_link_position=\"\" dialog_osinfo_position=\"\" dialog_install_button_position=\"\" dialog_cancel_button_position=\"\" dialog_skip_button_position=\"\" auto_start=\"True\" auto_continue_on_reboot=\"False\" reboot_cmd=\"\" show_progress_dialog=\"False\" show_cab_dialog=\"True\" disable_wow64_fs_redirection=\"False\" administrator_required=\"False\" administrator_required_message=\"APPLICATION_NAME installation requires administration rights.\" type=\"install\" lcid_filter=\"\" language_id=\"\" language=\"\" os_filter=\"\" os_filter_min=\"\" os_filter_max=\"\" processor_architecture_filter=\"\" supports_install=\"True\" supports_uninstall=\"True\">\n    <component package=\"#CABPATH\\swish-x86.msi\" cmdparameters=\"\" cmdparameters_silent=\"\" cmdparameters_basic=\"\" uninstall_package=\"\" uninstall_cmdparameters=\"/qb-\" uninstall_cmdparameters_silent=\"/qn\" uninstall_cmdparameters_basic=\"/qb-\" disable_wow64_fs_redirection=\"False\" id=\"swish-Win32\" display_name=\"swish-x86.msi\" uninstall_display_name=\"\" os_filter=\"\" os_filter_min=\"\" os_filter_max=\"\" os_filter_lcid=\"\" type=\"msi\" installcompletemessage=\"\" uninstallcompletemessage=\"\" mustreboot=\"False\" reboot_required=\"\" must_reboot_required=\"False\" failed_exec_command_continue=\"\" allow_continue_on_error=\"False\" default_continue_on_error=\"False\" required_install=\"True\" required_uninstall=\"True\" selected_install=\"True\" selected_uninstall=\"True\" note=\"\" processor_architecture_filter=\"x86\" status_installed=\"\" status_notinstalled=\"\" supports_install=\"True\" supports_uninstall=\"True\" show_progress_dialog=\"False\" show_cab_dialog=\"False\" />\n    <component package=\"#CABPATH\\swish-x64.msi\" cmdparameters=\"\" cmdparameters_silent=\"\" cmdparameters_basic=\"\" uninstall_package=\"\" uninstall_cmdparameters=\"/qb-\" uninstall_cmdparameters_silent=\"/qn\" uninstall_cmdparameters_basic=\"/qb-\" disable_wow64_fs_redirection=\"False\" id=\"swish-x64\" display_name=\"swish-x64.msi\" uninstall_display_name=\"\" os_filter=\"\" os_filter_min=\"\" os_filter_max=\"\" os_filter_lcid=\"\" type=\"msi\" installcompletemessage=\"\" uninstallcompletemessage=\"\" mustreboot=\"False\" reboot_required=\"\" must_reboot_required=\"False\" failed_exec_command_continue=\"\" allow_continue_on_error=\"False\" default_continue_on_error=\"False\" required_install=\"True\" required_uninstall=\"True\" selected_install=\"True\" selected_uninstall=\"True\" note=\"\" processor_architecture_filter=\"x64\" status_installed=\"\" status_notinstalled=\"\" supports_install=\"True\" supports_uninstall=\"True\" show_progress_dialog=\"False\" show_cab_dialog=\"False\" />\n    <embedfile sourcefilepath=\".\\bin\\Release-Win32\\swish-@swish_VERSION@.msi\" targetfilepath=\"swish-x86.msi\" />\n    <embedfile sourcefilepath=\".\\bin\\Release-x64\\swish-@swish_VERSION@.msi\" targetfilepath=\"swish-x64.msi\" />\n  </configuration>\n</configurations>\n"
  },
  {
    "path": "ssh/.clang-format",
    "content": "﻿---\nAccessModifierOffset: '-4'\nAllowShortFunctionsOnASingleLine: Empty\nAlwaysBreakTemplateDeclarations: 'true'\nBreakBeforeBraces: Allman\nColumnLimit: '80'\nConstructorInitializerAllOnOneLineOrOnePerLine: 'true'\nForEachMacros: [ foreach, BOOST_FOREACH ]\nIndentWidth: '4'\nMacroBlockBegin: \"^[A-Z_]+_BEGIN$\"\nMacroBlockEnd: \"^[A-Z_]+_END$\"\nMaxEmptyLinesToKeep: '1'\nNamespaceIndentation: None\nPointerAlignment: Left\nSpaceAfterControlStatementKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpacesInAngles:  false\nSpacesInParentheses: false\nUseTab: Never\n\n...\n"
  },
  {
    "path": "ssh/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  agent.hpp\n  detail/agent_state.hpp\n  detail/file_handle_state.hpp\n  detail/libssh2/agent.hpp\n  detail/libssh2/knownhost.hpp\n  detail/libssh2/libssh2.hpp\n  detail/libssh2/session.hpp\n  detail/libssh2/sftp.hpp\n  detail/libssh2/userauth.hpp\n  detail/session_state.hpp\n  detail/sftp_channel_state.hpp\n  filesystem.hpp\n  filesystem/path.hpp\n  host_key.hpp\n  knownhost.hpp\n  session.hpp\n  sftp_error.hpp\n  ssh_error.hpp\n  stream.hpp)\n\nadd_custom_target(ssh-src SOURCES ${SOURCES})\nadd_library(ssh INTERFACE)\ntarget_include_directories(ssh INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})\n\nhunter_add_package(Libssh2)\nfind_package(Libssh2 REQUIRED CONFIG)\n\ntarget_link_libraries(ssh INTERFACE Libssh2::libssh2 ${Boost_LIBRARIES})\n"
  },
  {
    "path": "ssh/agent.hpp",
    "content": "/**\n    @file\n\n    Key-agent protocol.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_AGENT_HPP\n#define SSH_AGENT_HPP\n\n#include <ssh/detail/agent_state.hpp>\n#include <ssh/detail/session_state.hpp>\n#include <ssh/detail/libssh2/agent.hpp> // ssh::detail::libssh2::agent\n\n#include <boost/iterator/iterator_facade.hpp>\n#include <boost/make_shared.hpp>\n#include <boost/ref.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <string>\n\n#include <libssh2.h> // LIBSSH2_AGENT, libssh2_agent_*\n\nnamespace ssh\n{\n\nclass identity\n{\npublic:\n    identity(boost::shared_ptr<detail::agent_state> agent,\n             libssh2_agent_publickey* identity)\n        : m_agent(agent), m_identity(identity)\n    {\n    }\n\n    void authenticate(const std::string& user_name)\n    {\n        detail::agent_state::scoped_lock lock = m_agent->aquire_lock();\n\n        detail::libssh2::agent::userauth(m_agent->agent_ptr(),\n                                         m_agent->session_ptr(),\n                                         user_name.c_str(), m_identity);\n    }\n\nprivate:\n    boost::shared_ptr<detail::agent_state> m_agent;\n    libssh2_agent_publickey* m_identity;\n};\n\nnamespace detail\n{\n\ntemplate <typename IdentityType>\nclass identity_iterator_base\n    : public boost::iterator_facade<\n          identity_iterator_base<IdentityType>, IdentityType,\n          boost::forward_traversal_tag, // this tag allows value return\n          IdentityType>\n{\n    friend class boost::iterator_core_access;\n\n    // Enables conversion constructor to work:\n    template <typename>\n    friend class identity_iterator_base;\n\npublic:\n    identity_iterator_base(boost::shared_ptr<agent_state> agent)\n        : m_agent(agent), m_pos(NULL)\n    {\n        increment();\n    }\n\n    /**\n     * End iterator.\n     */\n    identity_iterator_base() : m_pos(NULL)\n    {\n    }\n\n    /**\n     * Copy conversion constructor.\n     *\n     * Purpose: to allow mutable iterators to be converted to const iterators.\n     */\n    template <typename OtherValue>\n    identity_iterator_base(const identity_iterator_base<OtherValue>& other)\n        : m_agent(other.m_agent), m_pos(other.m_pos)\n    {\n    }\n\nprivate:\n    void increment()\n    {\n        if (!m_agent)\n            BOOST_THROW_EXCEPTION(std::logic_error(\n                \"Can't increment past the end of a collection\"));\n\n        detail::agent_state::scoped_lock lock = m_agent->aquire_lock();\n\n        bool no_more_identities =\n            detail::libssh2::agent::get_identity(m_agent->agent_ptr(),\n                                                 m_agent->session_ptr(), &m_pos,\n                                                 m_pos) == 1;\n\n        if (no_more_identities)\n        {\n            // Use m_agent as the end marker as a NULL m_pos means start again\n            m_agent.reset();\n            m_pos = NULL; // To keep equality with the end iterator happy\n        }\n    }\n\n    bool equal(const identity_iterator_base& other) const\n    {\n        return m_agent == other.m_agent && m_pos == other.m_pos;\n    }\n\n    value_type dereference() const\n    {\n        if (!m_agent)\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\"Can't dereference the end of a collection\"));\n\n        return identity(m_agent, m_pos);\n    }\n\n    boost::shared_ptr<agent_state> m_agent;\n    libssh2_agent_publickey* m_pos;\n};\n}\n\n/**\n * A connection to an SSH key agent.\n *\n * When this object is created, all the identities currently stored in it are\n * copied out.  If you need a fresh list, request a new agent instance.\n */\nclass agent_identities\n{\npublic:\n    typedef detail::identity_iterator_base<identity> iterator;\n    typedef detail::identity_iterator_base<const identity> const_iterator;\n\n    explicit agent_identities(detail::session_state& session)\n        : m_agent(boost::make_shared<detail::agent_state>(boost::ref(session)))\n    // http://stackoverflow.com/a/1374266/67013\n    {\n        // We pull the identities out here (AND ONLY HERE) so that all copies\n        // of the agent, iterators and identity objects refer to valid data.\n        // If we called this when creating the iterator it would wipe out all\n        // other iterators.\n\n        detail::agent_state::scoped_lock lock = m_agent->aquire_lock();\n\n        ::ssh::detail::libssh2::agent::list_identities(m_agent->agent_ptr(),\n                                                       m_agent->session_ptr());\n    }\n\n    iterator begin() const\n    {\n        return iterator(m_agent);\n    }\n\n    iterator end() const\n    {\n        return iterator();\n    }\n\nprivate:\n    boost::shared_ptr<detail::agent_state> m_agent;\n};\n\n} // namespace ssh\n\n#endif\n"
  },
  {
    "path": "ssh/detail/agent_state.hpp",
    "content": "/**\n    @file\n\n    RAII lifetime management of libssh2 agent collections.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_AGENT_STATE_HPP\n#define SSH_DETAIL_AGENT_STATE_HPP\n\n#include <ssh/detail/libssh2/agent.hpp>\n#include <ssh/detail/session_state.hpp>\n\n#include <boost/noncopyable.hpp>\n\n#include <libssh2.h> // LIBSSH2_AGENT\n\nnamespace ssh\n{\nnamespace detail\n{\n\ninline LIBSSH2_AGENT* do_agent_init(session_state& session)\n{\n    detail::session_state::scoped_lock lock = session.aquire_lock();\n\n    return detail::libssh2::agent::init(session.session_ptr());\n}\n\n/**\n * RAII object managing agent state that must be maintained together.\n *\n * Manages the graceful startup/shutdown the agent collection and does so in\n * a thread-safe manner.\n */\nclass agent_state : private boost::noncopyable\n{\n    //\n    // Intentionally not movable to prevent the public classes that own\n    // this object moving it when they are themselves moved.  This object\n    // is referenced by other classes that don't own it so the owning classes\n    // need to leave it where it is when they move so as not to invalidate\n    // the other references.  Making this non-copyable, non-movable enforces\n    // that.\n    //\n\npublic:\n    typedef session_state::scoped_lock scoped_lock;\n\n    /**\n     * Creates agent collection that closes itself in a thread-safe manner\n     * when it goes out of scope.\n     */\n    agent_state(session_state& session)\n        : m_session(session), m_agent(do_agent_init(session_ref()))\n    {\n        detail::session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        try\n        {\n            detail::libssh2::agent::connect(m_agent,\n                                            session_ref().session_ptr());\n        }\n        catch (...)\n        {\n            // If connection fails, the destructor won't be called so we have\n            // to free the agent here as well\n            ::libssh2_agent_disconnect(m_agent);\n        }\n    }\n\n    ~agent_state() throw()\n    {\n        session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        ::libssh2_agent_disconnect(m_agent);\n        ::libssh2_agent_free(m_agent);\n    }\n\n    scoped_lock aquire_lock()\n    {\n        return session_ref().aquire_lock();\n    }\n\n    LIBSSH2_SESSION* session_ptr()\n    {\n        return session_ref().session_ptr();\n    }\n\n    LIBSSH2_AGENT* agent_ptr()\n    {\n        return m_agent;\n    }\n\nprivate:\n    session_state& session_ref()\n    {\n        return m_session;\n    }\n\n    session_state& m_session;\n    LIBSSH2_AGENT* m_agent;\n};\n}\n} // namespace ssh::detail\n\n#endif\n"
  },
  {
    "path": "ssh/detail/file_handle_state.hpp",
    "content": "/**\n    @file\n\n    RAII lifetime management of libssh2 file handles.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_FILE_HANDLE_STATE_HPP\n#define SSH_DETAIL_FILE_HANDLE_STATE_HPP\n\n#include <ssh/detail/libssh2/sftp.hpp> // open\n#include <ssh/detail/sftp_channel_state.hpp>\n\n#include <boost/noncopyable.hpp>\n\n#include <string>\n\n#include <libssh2_sftp.h> // LIBSSH2_SFTP_HANDLE\n\nnamespace ssh\n{\nnamespace detail\n{\n\ninline LIBSSH2_SFTP_HANDLE* do_open(sftp_channel_state& sftp,\n                                    const char* filename,\n                                    unsigned int filename_len,\n                                    unsigned long flags, long mode,\n                                    int open_type)\n{\n    session_state::scoped_lock lock = sftp.aquire_lock();\n\n    return libssh2::sftp::open(sftp.session_ptr(), sftp.sftp_ptr(), filename,\n                               filename_len, flags, mode, open_type);\n}\n\n/**\n * RAII object managing SFTP file handle state that must be maintained together.\n *\n * Manages the graceful opening/closing of file handles.\n */\nclass file_handle_state : private boost::noncopyable\n{\n    //\n    // Intentionally not movable to prevent the public classes that own\n    // this object moving it when they are themselves moved.  This object\n    // is referenced by other classes that don't own it so the owning classes\n    // need to leave it where it is when they move so as not to invalidate\n    // the other references.  Making this non-copyable, non-movable enforces\n    // that.\n    //\n\npublic:\n    typedef sftp_channel_state::scoped_lock scoped_lock;\n\n    /**\n     * Creates a new file handle that closes itself in a thread-safe manner\n     * when it goes out of scope.\n     */\n    file_handle_state(sftp_channel_state& sftp, const char* filename,\n                      unsigned int filename_len, unsigned long flags, long mode,\n                      int open_type)\n        : m_sftp(sftp),\n          m_handle(do_open(sftp_ref(), filename, filename_len, flags, mode,\n                           open_type))\n    {\n    }\n\n    ~file_handle_state() throw()\n    {\n        sftp_channel_state::scoped_lock lock = sftp_ref().aquire_lock();\n\n        ::libssh2_sftp_close_handle(m_handle);\n    }\n\n    scoped_lock aquire_lock()\n    {\n        return sftp_ref().aquire_lock();\n    }\n\n    LIBSSH2_SESSION* session_ptr()\n    {\n        return sftp_ref().session_ptr();\n    }\n\n    LIBSSH2_SFTP* sftp_ptr()\n    {\n        return sftp_ref().sftp_ptr();\n    }\n\n    LIBSSH2_SFTP_HANDLE* file_handle()\n    {\n        return m_handle;\n    }\n\nprivate:\n    sftp_channel_state& sftp_ref()\n    {\n        return m_sftp;\n    }\n\n    sftp_channel_state& m_sftp;\n    LIBSSH2_SFTP_HANDLE* m_handle;\n};\n}\n} // namespace ssh::detail\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/agent.hpp",
    "content": "/**\n    @file\n\n    Exception wrapper round raw libssh2 agent functions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_AGENT_HPP\n#define SSH_DETAIL_LIBSSH2_AGENT_HPP\n\n#include <ssh/ssh_error.hpp> // last_error_code, SSH_DETAIL_THROW_API_ERROR_CODE\n\n#include <boost/exception/info.hpp>  // errinfo_api_function\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n\n#include <libssh2.h> // LIBSSH2_SESSION, LIBSSH2_AGENT, libssh2_agent_*\n\n// See ssh/detail/libssh2/libssh2.hpp for rules governing functions in this\n// namespace\n\nnamespace ssh\n{\nnamespace detail\n{\nnamespace libssh2\n{\nnamespace agent\n{\n\n/**\n * Error-fetching wrapper around libssh2_agent_init.\n */\ninline LIBSSH2_AGENT*\ninit(LIBSSH2_SESSION* session, boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    LIBSSH2_AGENT* agent = ::libssh2_agent_init(session);\n\n    if (!agent)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return agent;\n}\n\n/**\n * Exception wrapper around libssh2_agent_init.\n */\ninline LIBSSH2_AGENT* init(LIBSSH2_SESSION* session)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    LIBSSH2_AGENT* agent = init(session, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_agent_init\");\n    }\n\n    return agent;\n}\n\n/**\n * Error-fetching wrapper around libssh2_agent_connect.\n */\ninline void\nconnect(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session,\n        boost::system::error_code& ec,\n        boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_agent_connect(agent);\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_agent_connect.\n */\ninline void connect(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    connect(agent, session, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_agent_connect\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_agent_get_identity.\n *\n * Returns 1 when reached end of keys.  Return value has no meaning when\n * `ec == false`\n */\ninline int get_identity(\n    LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session,\n    libssh2_agent_publickey** out, libssh2_agent_publickey* previous,\n    boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_agent_get_identity(agent, out, previous);\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return rc;\n}\n\n/**\n * Exception wrapper around libssh2_agent_get_identity.\n *\n * Returns 1 when reached end of keys.\n */\ninline int get_identity(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session,\n                        libssh2_agent_publickey** out,\n                        libssh2_agent_publickey* previous)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    int rc = get_identity(agent, session, out, previous, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_agent_get_identity\");\n    }\n\n    return rc;\n}\n\n/**\n * Error-fetching wrapper around libssh2_agent_list_identities.\n */\ninline void list_identities(\n    LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session,\n    boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_agent_list_identities(agent);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_agent_list_identities.\n */\ninline void list_identities(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    list_identities(agent, session, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_agent_list_identities\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_agent_userauth.\n */\ninline void\nuserauth(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session, const char* user_name,\n         libssh2_agent_publickey* identity, boost::system::error_code& ec,\n         boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_agent_userauth(agent, user_name, identity);\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_agent_userauth.\n */\ninline void userauth(LIBSSH2_AGENT* agent, LIBSSH2_SESSION* session,\n                     const char* user_name, libssh2_agent_publickey* identity)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    userauth(agent, session, user_name, identity, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_agent_userauth\");\n    }\n}\n}\n}\n}\n} // namespace ssh::detail::libssh2::agent\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/knownhost.hpp",
    "content": "/**\n    @file\n\n    Exception wrapper round raw libssh2 knownhost functions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_KNOWNHOST_HPP\n#define SSH_DETAIL_LIBSSH2_KNOWNHOST_HPP\n\n#include <ssh/ssh_error.hpp> // last_error_code, SSH_DETAIL_THROW_API_ERROR_CODE\n\n#include <boost/exception/info.hpp>  // errinfo_api_function\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <libssh2.h> // LIBSSH2_SESSION, LIBSSH2_KNOWNHOSTS, libssh2_knownhost_*\n\n// See ssh/detail/libssh2/libssh2.hpp for rules governing functions in this\n// namespace\n\nnamespace ssh\n{\nnamespace detail\n{\nnamespace libssh2\n{\nnamespace knownhost\n{\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_init.\n */\ninline LIBSSH2_KNOWNHOSTS*\ninit(LIBSSH2_SESSION* session, boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    LIBSSH2_KNOWNHOSTS* hosts = ::libssh2_knownhost_init(session);\n\n    if (!hosts)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return hosts;\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_init.\n */\ninline LIBSSH2_KNOWNHOSTS* init(LIBSSH2_SESSION* session)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    LIBSSH2_KNOWNHOSTS* hosts = init(session, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_knownhost_init\");\n    }\n\n    return hosts;\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_readline.\n */\ninline void\nreadline(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts, const char* line,\n         size_t line_length, int type, boost::system::error_code& ec,\n         boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_readline(hosts, line, line_length, type);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_readline.\n */\ninline void readline(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n                     const char* line, size_t line_length, int type)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    readline(session, hosts, line, line_length, type, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_knownhost_readline\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_writeline.\n */\ninline void\nwriteline(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n          libssh2_knownhost* host, char* buffer, size_t buffer_length,\n          size_t* written_length_out, int type, boost::system::error_code& ec,\n          boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_writeline(hosts, host, buffer, buffer_length,\n                                           written_length_out, type);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_writeline.\n */\ninline void writeline(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n                      libssh2_knownhost* host, char* buffer,\n                      size_t buffer_length, size_t* written_length_out,\n                      int type)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    writeline(session, hosts, host, buffer, buffer_length, written_length_out,\n              type, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_knownhost_writeline\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_get.\n *\n * @returns 1 if finished.  The return code has no meaning if `ec == false`.\n */\ninline int\nget(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n    libssh2_knownhost** store, libssh2_knownhost* current_position,\n    boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_get(hosts, store, current_position);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return rc;\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_get.\n *\n * @returns 1 if finished.\n */\ninline int get(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n               libssh2_knownhost** store, libssh2_knownhost* current_position)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    int rc = get(session, hosts, store, current_position, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_knownhost_get\");\n    }\n\n    return rc;\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_add.\n */\ninline void\nadd(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts, const char* host,\n    const char* salt, const char* key, size_t key_length, int typemask,\n    libssh2_knownhost** store, boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_add(hosts, host, salt, key, key_length,\n                                     typemask, store);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_add.\n */\ninline void add(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n                const char* host, const char* salt, const char* key,\n                size_t key_length, int typemask, libssh2_knownhost** store)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    add(session, hosts, host, salt, key, key_length, typemask, store, ec,\n        message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_knownhost_add\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_del.\n */\ninline void\ndel(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n    libssh2_knownhost* entry, boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_del(hosts, entry);\n\n    if (rc < 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_del.\n */\ninline void del(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n                libssh2_knownhost* entry)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    del(session, hosts, entry, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_knownhost_del\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_knownhost_check.\n */\ninline int\ncheck(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts, const char* host,\n      const char* key, size_t key_length, int typemask,\n      libssh2_knownhost** knownhost, boost::system::error_code& ec,\n      boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_knownhost_check(hosts, host, key, key_length, typemask,\n                                       knownhost);\n\n    switch (rc)\n    {\n    case LIBSSH2_KNOWNHOST_CHECK_MATCH:\n    case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:\n    case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:\n        // Positive values, NOT ERROR CODES, won't appear in last_error\n        return rc;\n\n    case LIBSSH2_KNOWNHOST_CHECK_FAILURE:\n    // This value, also positive, also not an error code, means there\n    // was an error so check last_error to find the error code\n\n    default:\n\n        ec = ssh::detail::last_error_code(session, e_msg);\n        return LIBSSH2_KNOWNHOST_CHECK_FAILURE;\n    }\n}\n\n/**\n * Exception wrapper around libssh2_knownhost_check.\n */\ninline int check(LIBSSH2_SESSION* session, LIBSSH2_KNOWNHOSTS* hosts,\n                 const char* host, const char* key, size_t key_length,\n                 int typemask, libssh2_knownhost** knownhost)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    int rc = check(session, hosts, host, key, key_length, typemask, knownhost,\n                   ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_knownhost_check\");\n    }\n\n    return rc;\n}\n}\n}\n}\n} // namespace ssh::detail::libssh2::knownhost\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/libssh2.hpp",
    "content": "/**\n    @file\n\n    ssh::detail::libssh2 namespace documentation.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_LIBSSH2_HPP\n#define SSH_DETAIL_LIBSSH2_LIBSSH2_HPP\n\nnamespace ssh\n{\nnamespace detail\n{\n\n/**\n * Exception-throwing wrappers around libssh2 functions.\n *\n * This wrapper functions in this namespace adhere to the following\n * restrictions:\n *\n * - The behaviour is identical to that of the wrapped function except\n *   that the range of possible return values (via return or\n *   out-parameter) may be reduced by substituting them for\n *   exceptions.  Additionally, an error code and message out-parameter\n *   may be set to have a value.\n *\n * - The signature, including the return type, exactly matches the\n *   signature of the wrapped function, with three exceptions:\n *\n *   + it may include additional parameters (such as a session pointer)\n *     that are not in the original signature in order to fetch or return\n *     error details.\n *   + if the range of return values is reduced (see above) such that\n *     the remaining values simply indicate success, the return type\n *     may be changed to `void`.\n *\n * - As a consequence of the previous restrictions, any resources that\n *   need freeing when returned by the wrapped function, also need\n *   freeing after calling the wrapped version.\n *\n * - No references to the arguments are stored once the wrapper\n *   terminates, whether that termination is by return or by\n *   exception.  In particular, the exceptions thrown contain\n *   no shared data.\n *\n * - It is permitted to call these functions from within code that is\n *   non-recursively locked on the given session.  Therefore no\n *   coordination of concurrent threads of execution is performed by\n *   the wrappers and only one thread may call these wrapper functions\n *   (or an libssh2 function) with the same session at any time.\n *\n * Any function not able to adhere to these restrictions is not\n * eligible for inclusion in this namespace.\n *\n * Rationale\n * ---------\n *\n * The main reason for keeping these wrappers here is to make sure any\n * locking we introduce in the future for thread-safety spans both the\n * function call and the code to retrieve any error.  This is necessary\n * as otherwise the exception thrown may be from an error caused by\n * another thread's call to a function with the same session (only the\n * details of one error are stored per session).\n *\n * This namespace defines a boundary beyond which all functions behave\n * in the way defined here.  This makes it easier to keep track of\n * session lifetimes as well as where to (and not to) lock the\n * session.\n */\nnamespace libssh2\n{\n}\n}\n} // namespace ssh::detail::libssh2\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/session.hpp",
    "content": "/**\n    @file\n\n    Exception wrapper round raw libssh2 session functions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_SESSION_HPP\n#define SSH_DETAIL_LIBSSH2_SESSION_HPP\n\n#include <ssh/ssh_error.hpp> // last_error_code, SSH_DETAIL_THROW_API_ERROR_CODE\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <exception> // bad_alloc\n\n#include <libssh2.h> // LIBSSH2_SESSION, libssh2_session_*\n\n// See ssh/detail/libssh2/libssh2.hpp for rules governing functions in this\n// namespace\n\nnamespace ssh\n{\nnamespace detail\n{\nnamespace libssh2\n{\nnamespace session\n{\n\n/**\n * Thin exception wrapper around libssh2_session_init.\n */\ninline LIBSSH2_SESSION* init()\n{\n    LIBSSH2_SESSION* session =\n        ::libssh2_session_init_ex(NULL, NULL, NULL, NULL);\n    if (!session)\n        BOOST_THROW_EXCEPTION(\n            std::bad_alloc(\"Failed to allocate new ssh session\"));\n\n    return session;\n}\n\n/**\n * Error-fetching wrapper around libssh2_session_startup.\n */\ninline void\nstartup(LIBSSH2_SESSION* session, int socket, boost::system::error_code& ec,\n        boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_session_startup(session, socket);\n\n    if (rc != 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_session_startup.\n */\ninline void startup(LIBSSH2_SESSION* session, int socket)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    startup(session, socket, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_session_startup\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_session_disconnect.\n */\ninline void disconnect(\n    LIBSSH2_SESSION* session, const char* description,\n    boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_session_disconnect(session, description);\n\n    if (rc != 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_session_disconnect.\n */\ninline void disconnect(LIBSSH2_SESSION* session, const char* description)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    disconnect(session, description, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_session_disconnect\");\n    }\n}\n}\n}\n}\n} // namespace ssh::detail::libssh2::session\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/sftp.hpp",
    "content": "/**\n    @file\n\n    Error-reporting wrapper round raw libssh2 SFTP functions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_SFTP_HPP\n#define SSH_DETAIL_LIBSSH2_SFTP_HPP\n\n#include <ssh/sftp_error.hpp> // last_sftp_error_code\n#include <ssh/ssh_error.hpp>  // last_error_code\n\n#include <boost/optional/optional.hpp>\n#include <boost/system/error_code.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n\n#include <libssh2.h>      // LIBSSH2_SESSION, LIBSSH2_SFTP\n#include <libssh2_sftp.h> // libssh2_sftp_*\n\n// See ssh/detail/libssh2/libssh2.hpp for rules governing functions in this\n// namespace\n\nnamespace ssh\n{\nnamespace detail\n{\nnamespace libssh2\n{\nnamespace sftp\n{\n\n/**\n * Error-fetching wrapper around libssh2_sftp_init.\n */\ninline LIBSSH2_SFTP*\ninit(LIBSSH2_SESSION* session, boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    LIBSSH2_SFTP* sftp = ::libssh2_sftp_init(session);\n    if (!sftp)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return sftp;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_init.\n */\ninline LIBSSH2_SFTP* init(LIBSSH2_SESSION* session)\n{\n    boost::system::error_code ec;\n    std::string message;\n    LIBSSH2_SFTP* sftp = init(session, ec, message);\n    if (ec)\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_sftp_init\");\n\n    return sftp;\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_open_ex.\n */\ninline LIBSSH2_SFTP_HANDLE*\nopen(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* filename,\n     unsigned int filename_len, unsigned long flags, long mode, int open_type,\n     boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    LIBSSH2_SFTP_HANDLE* handle = ::libssh2_sftp_open_ex(\n        sftp, filename, filename_len, flags, mode, open_type);\n    if (!handle)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n\n    return handle;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_open_ex.\n */\ninline LIBSSH2_SFTP_HANDLE* open(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                                 const char* filename,\n                                 unsigned int filename_len, unsigned long flags,\n                                 long mode, int open_type)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    LIBSSH2_SFTP_HANDLE* handle = open(session, sftp, filename, filename_len,\n                                       flags, mode, open_type, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_open_ex\", filename, filename_len);\n    }\n\n    return handle;\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_symlink_ex.\n *\n * The return value has no meaning if `resolve_action` is\n * `LIBSSH2_SFTP_SYMLINK` or if `ec == true`.  If `resolve_action` is\n * `LIBSSH2_SFTP_READLINK` and `LIBSSH2_SFTP_REALPATH` the return code,\n * if successful, is the number of bytes written to the buffer.\n */\ninline int symlink_ex(\n    LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n    unsigned int path_len, char* target, unsigned int target_len,\n    int resolve_action, boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    // Slightly odd treatment of the return value because the success value\n    // is 0 for `LIBSSH2_SFTP_SYMLINK` but >= 0 for `LIBSSH2_SFTP_READLINK` or\n    // `LIBSSH2_SFTP_REALPATH`.\n\n    int rc = ::libssh2_sftp_symlink_ex(sftp, path, path_len, target, target_len,\n                                       resolve_action);\n    switch (resolve_action)\n    {\n    case LIBSSH2_SFTP_READLINK:\n    case LIBSSH2_SFTP_REALPATH:\n        if (rc < 0)\n        {\n            ec = ::ssh::filesystem::detail::last_sftp_error_code(session, sftp,\n                                                                 e_msg);\n        }\n        break;\n\n    case LIBSSH2_SFTP_SYMLINK:\n    default:\n        if (rc != 0)\n        {\n            assert(rc < 0);\n            ec = ::ssh::filesystem::detail::last_sftp_error_code(session, sftp,\n                                                                 e_msg);\n        }\n\n        break;\n    }\n\n    return rc;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_symlink_ex.\n *\n * The return value has no meaning if `resolve_action` is\n * `LIBSSH2_SFTP_SYMLINK`.  If `resolve_action` is`LIBSSH2_SFTP_READLINK` and\n * `LIBSSH2_SFTP_REALPATH` the return code is the number of bytes written to\n * the buffer.\n */\ninline int symlink_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                      const char* path, unsigned int path_len, char* target,\n                      unsigned int target_len, int resolve_action)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    int rc = symlink_ex(session, sftp, path, path_len, target, target_len,\n                        resolve_action, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_symlink_ex\", path, path_len);\n    }\n\n    return rc;\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_symlink.\n */\ninline void\nsymlink(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n        unsigned int path_len, const char* target, unsigned int target_len,\n        boost::system::error_code& ec,\n        boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    // Uses the raw libssh2_sftp_symlink_ex function so we aren't forced\n    // to use strlen.\n    symlink_ex(session, sftp, path, path_len, const_cast<char*>(target),\n               target_len, LIBSSH2_SFTP_SYMLINK, ec, e_msg);\n}\n\n/**\n * Exception wrapper around libssh2_sftp_symlink.\n */\ninline void symlink(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                    const char* path, unsigned int path_len, const char* target,\n                    unsigned int target_len)\n{\n    // Uses the raw libssh2_sftp_symlink_ex function so we aren't forced\n    // to use strlen.\n    symlink_ex(session, sftp, path, path_len, const_cast<char*>(target),\n               target_len, LIBSSH2_SFTP_SYMLINK);\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_stat_ex.\n */\ninline void\nstat(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n     unsigned int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES* attributes,\n     boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc =\n        ::libssh2_sftp_stat_ex(sftp, path, path_len, stat_type, attributes);\n    if (rc < 0)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_stat_ex.\n */\ninline void stat(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n                 unsigned int path_len, int stat_type,\n                 LIBSSH2_SFTP_ATTRIBUTES* attributes)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    stat(session, sftp, path, path_len, stat_type, attributes, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_stat_ex\", path, path_len);\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_fstat_ex.\n */\ninline void\nfstat(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, LIBSSH2_SFTP_HANDLE* handle,\n      LIBSSH2_SFTP_ATTRIBUTES* attributes, int fstat_type,\n      boost::system::error_code& ec,\n      boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_fstat_ex(handle, attributes, fstat_type);\n    if (rc != 0)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_fstat_ex.\n */\ninline void fstat(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                  LIBSSH2_SFTP_HANDLE* handle,\n                  LIBSSH2_SFTP_ATTRIBUTES* attributes, int fstat_type)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    fstat(session, sftp, handle, attributes, fstat_type, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_sftp_fstat_ex\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_unlink_ex.\n */\ninline void\nunlink_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n          unsigned int path_len, boost::system::error_code& ec,\n          boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_unlink_ex(sftp, path, path_len);\n    if (rc < 0)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_unlink_ex.\n */\ninline void unlink_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                      const char* path, unsigned int path_len)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    unlink_ex(session, sftp, path, path_len, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_unlink_ex\", path, path_len);\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_mkdir_ex.\n */\ninline void\nmkdir_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n         unsigned int path_len, long mode, boost::system::error_code& ec,\n         boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_mkdir_ex(sftp, path, path_len, mode);\n    if (rc < 0)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_mkdir_ex.\n */\ninline void mkdir_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                     const char* path, unsigned int path_len, long mode)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    mkdir_ex(session, sftp, path, path_len, mode, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_mkdir_ex\", path, path_len);\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_rmdir_ex.\n */\ninline void\nrmdir_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* path,\n         unsigned int path_len, boost::system::error_code& ec,\n         boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_rmdir_ex(sftp, path, path_len);\n    if (rc < 0)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_rmdir_ex.\n */\ninline void rmdir_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                     const char* path, unsigned int path_len)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    rmdir_ex(session, sftp, path, path_len, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_rmdir_ex\", path, path_len);\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_rename_ex\n */\ninline void\nrename(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, const char* source,\n       unsigned int source_len, const char* destination,\n       unsigned int destination_len, long flags, boost::system::error_code& ec,\n       boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_rename_ex(sftp, source, source_len, destination,\n                                      destination_len, flags);\n    if (rc)\n    {\n        ec =\n            ssh::filesystem::detail::last_sftp_error_code(session, sftp, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_sftp_rename_ex\n */\ninline void rename(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                   const char* source, unsigned int source_len,\n                   const char* destination, unsigned int destination_len,\n                   long flags)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    rename(session, sftp, source, source_len, destination, destination_len,\n           flags, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n            ec, message, \"libssh2_sftp_rename_ex\", source, source_len);\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_read.\n */\ninline ssize_t\nread(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n     LIBSSH2_SFTP_HANDLE* file_handle, char* buffer, size_t buffer_len,\n     boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    ssize_t count = ::libssh2_sftp_read(file_handle, buffer, buffer_len);\n    if (count < 0)\n    {\n        ec = ::ssh::filesystem::detail::last_sftp_error_code(session, sftp,\n                                                             e_msg);\n    }\n\n    return count;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_read.\n */\ninline ssize_t read(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                    LIBSSH2_SFTP_HANDLE* file_handle, char* buffer,\n                    size_t buffer_len)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    ssize_t count =\n        read(session, sftp, file_handle, buffer, buffer_len, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_sftp_read\");\n    }\n\n    return count;\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_write.\n */\ninline ssize_t\nwrite(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n      LIBSSH2_SFTP_HANDLE* file_handle, const char* data, size_t data_len,\n      boost::system::error_code& ec,\n      boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    ssize_t count = ::libssh2_sftp_write(file_handle, data, data_len);\n    if (count < 0)\n    {\n        ec = ::ssh::filesystem::detail::last_sftp_error_code(session, sftp,\n                                                             e_msg);\n    }\n\n    return count;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_write.\n */\ninline ssize_t write(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                     LIBSSH2_SFTP_HANDLE* file_handle, const char* data,\n                     size_t data_len)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    ssize_t count =\n        write(session, sftp, file_handle, data, data_len, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_sftp_write\");\n    }\n\n    return count;\n}\n\n/**\n * Error-fetching wrapper around libssh2_sftp_readdir_ex.\n */\ninline int readdir_ex(\n    LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp, LIBSSH2_SFTP_HANDLE* handle,\n    char* buffer, size_t buffer_len, char* longentry, size_t longentry_len,\n    LIBSSH2_SFTP_ATTRIBUTES* attrs, boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_sftp_readdir_ex(handle, buffer, buffer_len, longentry,\n                                       longentry_len, attrs);\n\n    if (rc < 0)\n    {\n        ec = ::ssh::filesystem::detail::last_sftp_error_code(session, sftp,\n                                                             e_msg);\n    }\n\n    return rc;\n}\n\n/**\n * Exception wrapper around libssh2_sftp_readdir_ex.\n */\ninline int readdir_ex(LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n                      LIBSSH2_SFTP_HANDLE* handle, char* buffer,\n                      size_t buffer_len, char* longentry, size_t longentry_len,\n                      LIBSSH2_SFTP_ATTRIBUTES* attrs)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    int rc = readdir_ex(session, sftp, handle, buffer, buffer_len, longentry,\n                        longentry_len, attrs, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_sftp_readdir_ex\");\n    }\n\n    return rc;\n}\n}\n}\n}\n} // namespace ssh::detail::libssh2::filesystem\n\n#endif\n"
  },
  {
    "path": "ssh/detail/libssh2/userauth.hpp",
    "content": "/**\n    @file\n\n    Exception wrapper round raw libssh2 userauth functions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_LIBSSH2_USERAUTH_HPP\n#define SSH_DETAIL_LIBSSH2_USERAUTH_HPP\n\n#include <ssh/ssh_error.hpp> // last_error_code, SSH_DETAIL_THROW_API_ERROR_CODE\n\n#include <boost/exception/info.hpp> // errinfo_api_function\n#include <boost/optional/optional.hpp>\n#include <boost/system/error_code.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <libssh2.h> // LIBSSH2_SESSION, libssh2_userauth_*\n\n// See ssh/detail/libssh2/libssh2.hpp for rules governing functions in this\n// namespace\n\nnamespace ssh\n{\nnamespace detail\n{\nnamespace libssh2\n{\nnamespace userauth\n{\n\n/**\n * Error-fetching wrapper around libssh2_userauth_list.\n *\n * May return NULL if authentication succeeded with 'none' method.  In this\n * case 'ec == false'.\n */\ninline const char*\nlist(LIBSSH2_SESSION* session, const char* username, unsigned int username_len,\n     boost::system::error_code& ec,\n     boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    const char* method_list =\n        ::libssh2_userauth_list(session, username, username_len);\n\n    if (!method_list)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n\n    return method_list;\n}\n\n/**\n * Exception wrapper around libssh2_userauth_list.\n *\n * Returns NULL if authentication succeeded with 'none' method.\n */\ninline const char* list(LIBSSH2_SESSION* session, const char* username,\n                        unsigned int username_len)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    const char* method_list =\n        list(session, username, username_len, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, \"libssh2_userauth_list\");\n    }\n\n    return method_list;\n}\n\n/**\n * Error-fetching wrapper around libssh2_userauth_password_ex.\n */\ninline void\npassword(LIBSSH2_SESSION* session, const char* username, size_t username_len,\n         const char* password, size_t password_len,\n         LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)),\n         boost::system::error_code& ec,\n         boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_userauth_password_ex(session, username, username_len,\n                                            password, password_len,\n                                            passwd_change_cb);\n\n    if (rc != 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_userauth_password_ex.\n */\ninline void password(LIBSSH2_SESSION* session, const char* username,\n                     size_t username_len, const char* password_string,\n                     size_t password_len,\n                     LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    password(session, username, username_len, password_string, password_len,\n             passwd_change_cb, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(ec, message,\n                                        \"libssh2_userauth_password_ex\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_userauth_keyboard_interactive_ex.\n */\ninline void keyboard_interactive_ex(\n    LIBSSH2_SESSION* session, const char* username, unsigned int username_len,\n    LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)),\n    boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = ::libssh2_userauth_keyboard_interactive_ex(\n        session, username, username_len, response_callback);\n\n    if (rc != 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_userauth_keyboard_interactive_ex.\n */\ninline void keyboard_interactive_ex(\n    LIBSSH2_SESSION* session, const char* username, unsigned int username_len,\n    LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    keyboard_interactive_ex(session, username, username_len, response_callback,\n                            ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(\n            ec, message, \"libssh2_userauth_keyboard_interactive_ex\");\n    }\n}\n\n/**\n * Error-fetching wrapper around libssh2_userauth_publickey_fromfile_ex.\n */\ninline void public_key_from_file(\n    LIBSSH2_SESSION* session, const char* username, size_t username_len,\n    const char* public_key_path, const char* private_key_path,\n    const char* passphrase, boost::system::error_code& ec,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int rc = libssh2_userauth_publickey_fromfile_ex(\n        session, username, username_len, public_key_path, private_key_path,\n        passphrase);\n\n    if (rc != 0)\n    {\n        ec = ssh::detail::last_error_code(session, e_msg);\n    }\n}\n\n/**\n * Exception wrapper around libssh2_userauth_publickey_fromfile_ex.\n */\ninline void public_key_from_file(LIBSSH2_SESSION* session, const char* username,\n                                 size_t username_len,\n                                 const char* public_key_path,\n                                 const char* private_key_path,\n                                 const char* passphrase)\n{\n    boost::system::error_code ec;\n    std::string message;\n\n    public_key_from_file(session, username, username_len, public_key_path,\n                         private_key_path, passphrase, ec, message);\n\n    if (ec)\n    {\n        SSH_DETAIL_THROW_API_ERROR_CODE(\n            ec, message, \"libssh2_userauth_publickey_fromfile_ex\");\n    }\n}\n}\n}\n}\n} // namespace ssh::detail::libssh2::userauth\n\n#endif\n"
  },
  {
    "path": "ssh/detail/session_state.hpp",
    "content": "/**\n    @file\n\n    RAII lifetime management of libssh2 sessions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_SESSION_STATE_HPP\n#define SSH_DETAIL_SESSION_STATE_HPP\n\n#include <ssh/detail/libssh2/session.hpp> // init\n\n#include <boost/noncopyable.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/system/error_code.hpp>\n#include <boost/system/system_error.hpp>\n#include <boost/thread/mutex.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <string>\n\n#include <libssh2.h> // LIBSSH2_SESSION\n\nnamespace ssh\n{\nnamespace detail\n{\n\n/**\n * RAII object managing session state that must be maintained together.\n *\n * Manages the graceful shutdown/destruction of the session.\n *\n * Unlike a lot of simple allocate-deallocate RAII, this class has to manage\n * an, optional, post-allocation 'starup' stage and ensure that, if started,\n * it is shutdown before de-allocation.  This means that we have to be\n * careful of the lifetime of the unstarted session in the code below.\n * The session may fail to start but must still be freed.\n */\nclass session_state : private boost::noncopyable\n{\n    //\n    // Intentionally not movable to prevent the public classes that own\n    // this object moving it when they are themselves moved.  This object\n    // is referenced by other classes that don't own it so the owning classes\n    // need to leave it where it is when they move so as not to invalidate\n    // the other references.  Making this non-copyable, non-movable enforces\n    // that.\n    //\n\npublic:\n    typedef boost::mutex::scoped_lock scoped_lock;\n\n    /**\n     * Creates a session that is not (and never will be) connected to a host.\n     */\n    session_state() : m_session(::ssh::detail::libssh2::session::init())\n    {\n    }\n\n    /**\n     * Creates a session connected to a host over the given socket.\n     */\n    session_state(int socket, const std::string& disconnection_message)\n        : m_session(libssh2::session::init())\n    {\n        // Session is 'alive' from this point onwards.  All paths must\n        // eventually free it.\n\n        boost::system::error_code ec;\n        std::string error_message;\n\n        libssh2::session::startup(m_session, socket, ec, error_message);\n\n        if (ec)\n        {\n            // Must free session here as destructor won't be called\n            ::libssh2_session_free(m_session);\n\n            BOOST_THROW_EXCEPTION(\n                boost::system::system_error(ec, error_message));\n        }\n        else\n        {\n            // Setting the disconnection message signals to the destructor\n            // that disconnection is necessary\n            m_disconnection_message = disconnection_message;\n        }\n    }\n\n    ~session_state() throw()\n    {\n        // Ignoring any errors because there's nothing we can do about them\n\n        if (m_disconnection_message)\n        {\n            boost::system::error_code ec;\n            libssh2::session::disconnect(m_session,\n                                         m_disconnection_message->c_str(), ec);\n        }\n\n        ::libssh2_session_free(m_session);\n    }\n\n    scoped_lock aquire_lock()\n    {\n        return scoped_lock(m_mutex);\n    }\n\n    LIBSSH2_SESSION* session_ptr()\n    {\n        return m_session;\n    }\n\nprivate:\n    mutable boost::mutex m_mutex;\n    ///< Coordinates multiple-threads using of non-thread-safe LIBSSH2_SESSION.\n\n    LIBSSH2_SESSION* m_session;\n\n    // Overloading this to hold both the message and flag whether disconnection\n    // is necessary.\n    boost::optional<std::string> m_disconnection_message;\n};\n}\n} // namespace ssh::detail\n\n#endif\n"
  },
  {
    "path": "ssh/detail/sftp_channel_state.hpp",
    "content": "/**\n    @file\n\n    RAII lifetime management of libssh2 SFTP channels.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_DETAIL_SFTP_CHANNEL_STATE_HPP\n#define SSH_DETAIL_SFTP_CHANNEL_STATE_HPP\n\n#include <ssh/detail/libssh2/sftp.hpp> // init\n#include <ssh/detail/session_state.hpp>\n\n#include <boost/noncopyable.hpp>\n\n#include <libssh2_sftp.h> // LIBSSH2_SFTP\n\nnamespace ssh\n{\nnamespace detail\n{\n\ninline LIBSSH2_SFTP* do_sftp_init(session_state& session)\n{\n    session_state::scoped_lock lock = session.aquire_lock();\n\n    return libssh2::sftp::init(session.session_ptr());\n}\n\n/**\n * RAII object managing SFTP channel state that must be maintained together.\n *\n * Manages the graceful startup/shutdown the SFTP channel and does so in\n * a thread-safe manner.\n */\nclass sftp_channel_state : private boost::noncopyable\n{\n    //\n    // Intentionally not movable to prevent the public classes that own\n    // this object moving it when they are themselves moved.  This object\n    // is referenced by other classes that don't own it so the owning classes\n    // need to leave it where it is when they move so as not to invalidate\n    // the other references.  Making this non-copyable, non-movable enforces\n    // that.\n    //\npublic:\n    typedef session_state::scoped_lock scoped_lock;\n\n    /**\n     * Creates SFTP channel that closes itself in a thread-safe manner\n     * when it goes out of scope.\n     */\n    sftp_channel_state(session_state& session)\n        : m_session(session), m_sftp(do_sftp_init(session_ref()))\n    {\n    }\n\n    ~sftp_channel_state() throw()\n    {\n        session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        ::libssh2_sftp_shutdown(m_sftp);\n    }\n\n    scoped_lock aquire_lock()\n    {\n        return session_ref().aquire_lock();\n    }\n\n    LIBSSH2_SESSION* session_ptr()\n    {\n        return session_ref().session_ptr();\n    }\n\n    LIBSSH2_SFTP* sftp_ptr()\n    {\n        return m_sftp;\n    }\n\nprivate:\n    session_state& session_ref()\n    {\n        return m_session;\n    }\n\n    session_state& m_session;\n    LIBSSH2_SFTP* m_sftp;\n};\n}\n} // namespace ssh::detail\n\n#endif\n"
  },
  {
    "path": "ssh/filesystem/path.hpp",
    "content": "// Copyright 2015, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SSH_FILESYSTEM_PATH_HPP\n#define SSH_FILESYSTEM_PATH_HPP\n\n#include <boost/algorithm/string.hpp>\n#include <boost/iterator/iterator_facade.hpp>\n#include <boost/locale/encoding.hpp> // to_utf\n#include <boost/locale/generator.hpp>\n#include <boost/locale/util.hpp> // get_system_locale\n#include <boost/operators.hpp>\n#include <boost/throw_exception.hpp>\n\n#include <locale>\n#include <ostream>\n#include <stdexcept> // logic_error\n#include <string>\n#include <utility> // swap\n\nnamespace ssh\n{\nnamespace filesystem\n{\n\nnamespace detail\n{\n\n/**\n * String tokeniser that separates on /, unless it is leading or trailing.\n *\n * The filesystem concept treats leading and trailing directory separators (/)\n * specially.  A leading separator is the root dirctory and is kept as a token.\n * A trailing separator is a directory path indicator and causes a dot token (.)\n * to be emitted.\n */\n\n// Because the iterators produce paths, std::lexicograpical_compare will not\n// terminate. Therefore we have our own that works on the string form of the\n// path in each segment\ntemplate <typename Iterator>\ninline int lexical_compare(Iterator lhs, const Iterator& lhs_end, Iterator rhs,\n                           const Iterator& rhs_end)\n{\n    while (lhs != lhs_end && rhs != rhs_end)\n    {\n        int comparison = lhs->native().compare(rhs->native());\n        if (comparison == 0)\n        {\n            ++lhs;\n            ++rhs;\n            continue;\n        }\n        else\n        {\n            return comparison;\n        }\n    }\n\n    if (lhs != lhs_end)\n    {\n        return 1;\n    }\n    else if (rhs != rhs_end)\n    {\n        return -1;\n    }\n    else\n    {\n        return 0;\n    }\n}\n\ntemplate <typename StringType>\ninline typename StringType::size_type\nfind_next_slash(const StringType& string,\n                typename StringType::size_type starting_position)\n{\n    return string.find('/', starting_position);\n}\n\ntemplate <typename StringType>\ninline typename StringType::size_type\nfind_previous_slash(const StringType& string,\n                    typename StringType::size_type starting_position)\n{\n    return string.rfind('/', starting_position);\n}\n\ntemplate <typename StringType>\ninline typename StringType::size_type\nfind_next_non_slash(const StringType& string,\n                    typename StringType::size_type starting_position)\n{\n    return string.find_first_not_of('/', starting_position);\n}\n\ntemplate <typename StringType>\ninline typename StringType::size_type\nfind_previous_non_slash(const StringType& string,\n                        typename StringType::size_type starting_position)\n{\n    // NOTE: We are implementing what should be rfind_last_not_of. This\n    // must already exist somewhere, no?\n\n    // UNOBVIOUS: Because the loop control variable is unsigned and we are\n    // looping downwards, the condition has to check for a number larger than\n    // the range, not smaller\n    for (typename StringType::size_type i = starting_position;\n         i < starting_position + 1; --i)\n    {\n        if (string[i] != '/')\n        {\n            return i;\n        }\n    }\n\n    return StringType::npos;\n}\n\ninline std::locale utf8_locale()\n{\n    return boost::locale::generator().generate(\n        boost::locale::util::get_system_locale(true));\n}\n\ninline std::locale system_locale()\n{\n    return boost::locale::generator().generate(\n        boost::locale::util::get_system_locale(false));\n}\n\ninline std::string from_source(const std::string& source)\n{\n    return source;\n}\n\ninline std::string from_source(const std::wstring& source)\n{\n    return boost::locale::conv::utf_to_utf<char>(source,\n                                                 boost::locale::conv::stop);\n}\n\ntemplate <typename InputIterator>\ninline std::string from_source(const InputIterator& begin,\n                               const InputIterator& end)\n{\n    typedef std::basic_string<\n        typename std::iterator_traits<InputIterator>::value_type> string_type;\n    string_type source_string(begin, end);\n    return from_source(source_string);\n}\n}\n\nclass path : boost::totally_ordered<path>\n{\npublic:\n    typedef std::string string_type;\n    typedef string_type::value_type value_type;\n    class iterator;\n    typedef iterator const_iterator;\n\n    path()\n    {\n    }\n\n    template <typename Source>\n    path(const Source& source)\n        : m_path(detail::from_source(source))\n    {\n    }\n\n    template <typename InputIterator>\n    path(const InputIterator& begin, const InputIterator& end)\n        : m_path(detail::from_source(begin, end))\n    {\n    }\n\n    bool is_relative() const\n    {\n        return !is_absolute();\n    }\n\n    bool is_absolute() const\n    {\n        return !empty() && m_path[0] == '/';\n    }\n\n    bool empty() const\n    {\n        return m_path.empty();\n    }\n\n    bool has_parent_path() const\n    {\n        return !parent_path().empty();\n    }\n\n    path parent_path() const;\n\n    bool has_relative_path() const\n    {\n        return !relative_path().empty();\n    }\n\n    path relative_path() const;\n\n    bool has_filename() const\n    {\n        return !filename().empty();\n    }\n\n    path filename() const;\n\n    string_type native() const\n    {\n        return m_path;\n    }\n\n    operator string_type() const\n    {\n        return native();\n    }\n\n    template <typename CharT, typename Traits>\n    std::basic_string<CharT, Traits> string() const;\n\n    template <>\n    std::string string() const\n    {\n        return native();\n    }\n\n    template <>\n    std::wstring string() const\n    {\n        return boost::locale::conv::utf_to_utf<wchar_t>(m_path);\n    }\n\n    std::string u8string() const\n    {\n        return native();\n    }\n\n    std::string string() const\n    {\n        // Native string is UTF-8 but this method returns string in\n        // platform-native encoding\n        return from_utf(detail::system_locale());\n    }\n\n    std::wstring wstring() const\n    {\n        return string<std::wstring::value_type, std::wstring::traits_type>();\n    }\n\n    int compare(const path& rhs) const;\n\n    iterator begin() const;\n\n    iterator end() const;\n\n    path& operator/=(const path& rhs)\n    {\n        if (!empty())\n        {\n            string_type lhs_string = boost::algorithm::trim_right_copy_if(\n                m_path, boost::algorithm::is_any_of(\"/\"));\n            string_type rhs_string = boost::algorithm::trim_left_copy_if(\n                rhs.m_path, boost::algorithm::is_any_of(\"/\"));\n            m_path = lhs_string + '/' + rhs_string;\n        }\n        else\n        {\n            m_path = rhs.m_path;\n        }\n        return *this;\n    }\n\n    template <typename Source>\n    path& operator/=(const Source& rhs)\n    {\n        return (*this) /= path(rhs);\n    }\n\nprivate:\n    template <typename InputIterator>\n    static path path_from_range(const InputIterator& begin,\n                                const InputIterator& end)\n    {\n        path p;\n        for (InputIterator position = begin; position != end; ++position)\n        {\n            p /= *position;\n        }\n        return p;\n    }\n\n    std::string from_utf(const std::locale& locale) const\n    {\n        return boost::locale::conv::from_utf<char>(m_path, locale,\n                                                   boost::locale::conv::stop);\n    }\n\n    // IMPORTANT: The encoding of this path is UTF8, which is not necessarily\n    // the default encoding for strings of string_type\n    string_type m_path;\n};\n\nclass path::iterator\n    : public boost::iterator_facade<path::iterator, const path,\n                                    boost::bidirectional_traversal_tag>\n{\nprivate:\n    friend class path;\n\n    explicit iterator(const path* source_path, string_type::size_type position)\n        : m_source(source_path),\n          m_current_positions(std::make_pair(\n              string_type::size_type(position),\n              find_element_last_position(m_source->m_path, position))),\n          m_current_segment(path(\n              element_from_positions(m_source->m_path, m_current_positions)))\n    {\n    }\n\n    // NOT the end iterator - behaviour undefined\n    iterator()\n    {\n    }\n\n    friend class boost::iterator_core_access;\n\n    bool equal(const iterator& other) const\n    {\n        return m_source == other.m_source &&\n               m_current_positions == other.m_current_positions;\n    }\n\n    const path& dereference() const\n    {\n        if (m_current_positions.first == m_source->m_path.size())\n        {\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\"dereferencing past the end of the path\"));\n        }\n        else\n        {\n            return m_current_segment;\n        }\n    }\n\n    void increment()\n    {\n        m_current_positions =\n            next_element_positions(m_source->m_path, m_current_positions);\n        if (m_current_positions.first != m_source->m_path.size())\n        {\n            m_current_segment = path(\n                element_from_positions(m_source->m_path, m_current_positions));\n        }\n    }\n\n    void decrement()\n    {\n        m_current_positions =\n            previous_element_positions(m_source->m_path, m_current_positions);\n        m_current_segment =\n            path(element_from_positions(m_source->m_path, m_current_positions));\n    }\n\n    static std::pair<string_type::size_type, string_type::size_type>\n    next_element_positions(const string_type& source,\n                           std::pair<string_type::size_type,\n                                     string_type::size_type> current_positions)\n    {\n        string_type::size_type new_first =\n            find_next_element_first_position(source, current_positions.first);\n        string_type::size_type new_last =\n            find_element_last_position(source, new_first);\n        return std::make_pair(new_first, new_last);\n    }\n\n    static std::pair<string_type::size_type, string_type::size_type>\n    previous_element_positions(\n        const string_type& source,\n        std::pair<string_type::size_type, string_type::size_type>\n            current_positions)\n    {\n        string_type::size_type new_first = find_previous_element_first_position(\n            source, current_positions.first);\n        string_type::size_type new_last =\n            find_element_last_position(source, new_first);\n        return std::make_pair(new_first, new_last);\n    }\n\n    static string_type::size_type\n    find_next_element_first_position(const string_type& source,\n                                     string_type::size_type current_position)\n    {\n        if (current_position == source.size())\n        {\n            BOOST_THROW_EXCEPTION(std::logic_error(\"already at end of path\"));\n        }\n        else if (current_position == source.size() - 1)\n        {\n            return source.size();\n        }\n        else if (source[current_position] == '/')\n        {\n            if (current_position == 0)\n            {\n                string_type::size_type next_non_slash_position =\n                    detail::find_next_non_slash(source, current_position);\n                if (next_non_slash_position == string_type::npos)\n                {\n                    // Path only contains slashes so we have consumed\n                    // everything.\n                    // Move off the end to indicate this\n                    return source.size();\n                }\n                else\n                {\n                    // next segment starts after slash(es)\n                    return next_non_slash_position;\n                }\n            }\n            else\n            {\n                assert(!\"position is at slash that isn't leading or trailing\");\n                BOOST_THROW_EXCEPTION(std::logic_error(\"unreachable\"));\n            }\n        }\n        else\n        {\n            string_type::size_type next_slash_position =\n                detail::find_next_slash(source, current_position);\n            if (next_slash_position == string_type::npos)\n            {\n                // Path ends - no new position\n                return source.size();\n            }\n            else\n            {\n                string_type::size_type next_non_slash_position =\n                    detail::find_next_non_slash(source, next_slash_position);\n                if (next_non_slash_position == string_type::npos)\n                {\n                    // Path ends with trailing slash(es) - slash is new position\n                    return next_slash_position;\n                }\n                else\n                {\n                    // Normal case - slash found - next segment starts after\n                    // slash\n                    return next_non_slash_position;\n                }\n            }\n        }\n    }\n\n    static string_type::size_type find_previous_element_first_position(\n        const string_type& source, string_type::size_type current_position)\n    {\n        if (current_position == 0)\n        {\n            BOOST_THROW_EXCEPTION(std::logic_error(\"already at start of path\"));\n        }\n        else if (current_position == source.size())\n        {\n            // Because iterators are half-open, we may be at a position past the\n            // last character so we have to treat this as a special case\n            string_type::size_type previous_slash_position =\n                detail::find_previous_slash(source, current_position - 1);\n            if (previous_slash_position == string_type::npos)\n            {\n                // we ran off the beginning of the path so we must be off the\n                // end of a single segment relative path\n                return 0;\n            }\n            else\n            {\n                if (previous_slash_position == source.size() - 1)\n                {\n                    return previous_slash_position;\n                }\n                else\n                {\n                    return previous_slash_position + 1;\n                }\n            }\n        }\n        else if (source[current_position] == '/')\n        {\n            if (current_position == source.size() - 1)\n            {\n                string_type::size_type previous_slash_position =\n                    detail::find_previous_slash(source, current_position - 1);\n                if (previous_slash_position == string_type::npos)\n                {\n                    // we ran off the beginning of the path so we must be at the\n                    // slash following the first segment of a relative path\n                    return 0;\n                }\n                else\n                {\n                    return previous_slash_position + 1;\n                }\n            }\n            else\n            {\n                assert(!\"position is at slash that isn't leading or trailing\");\n                BOOST_THROW_EXCEPTION(std::logic_error(\"unreachable\"));\n            }\n        }\n        else\n        {\n            assert(source[current_position - 1] == '/');\n\n            string_type::size_type previous_non_slash_position =\n                detail::find_previous_non_slash(source, current_position - 1);\n            if (previous_non_slash_position == string_type::npos)\n            {\n                // We're at the first segment of an absolute path.\n                // The leading slash is the next position\n                return 0;\n            }\n            else\n            {\n                string_type::size_type previous_slash_position =\n                    detail::find_previous_slash(source,\n                                                previous_non_slash_position);\n\n                if (previous_slash_position == string_type::npos)\n                {\n                    // we ran off the beginning of the path so we must be at the\n                    // start of the second segment of a relative path\n                    return 0;\n                }\n                else\n                {\n                    return previous_slash_position + 1;\n                }\n            }\n        }\n    }\n\n    static string_type::size_type\n    find_element_last_position(const string_type& source,\n                               string_type::size_type first_position)\n    {\n        if (first_position == source.size())\n        {\n            return source.size();\n        }\n        else if (source[first_position] == '/')\n        {\n            if (first_position == source.size() - 1)\n            {\n                return first_position;\n            }\n            else if (first_position == 0)\n            {\n                return first_position;\n            }\n            else\n            {\n                assert(!\"position is at slash that isn't leading or trailing\");\n                BOOST_THROW_EXCEPTION(std::logic_error(\"unreachable\"));\n            }\n        }\n        else\n        {\n            string_type::size_type next_slash_position =\n                source.find_first_of('/', first_position);\n            if (next_slash_position == string_type::npos)\n            {\n                // Path ends - segment ends at last char in string\n                return source.size() - 1;\n            }\n            else\n            {\n                // Normal case - slash found - next segment ends just before it\n                return next_slash_position - 1;\n            }\n        }\n    }\n\n    static string_type element_from_positions(\n        const string_type& source,\n        std::pair<string_type::size_type, string_type::size_type> positions)\n    {\n        if (positions.first == source.size())\n        {\n            return \"\";\n        }\n        else if (source[positions.first] == '/')\n        {\n            // leading or trailing /\n            if (positions.first == 0)\n            {\n                return \"/\";\n            }\n            else if (positions.first == source.size() - 1)\n            {\n                return \".\";\n            }\n            else\n            {\n                assert(!\"position is at slash that isn't leading or trailing\");\n                BOOST_THROW_EXCEPTION(std::logic_error(\"unreachable\"));\n            }\n        }\n        else\n        {\n            return string_type(source.begin() + positions.first,\n                               source.begin() + positions.second + 1);\n        }\n    }\n\n    const path* m_source;\n    std::pair<string_type::size_type, string_type::size_type>\n        m_current_positions;\n    path m_current_segment;\n};\n\ninline path path::parent_path() const\n{\n    if (empty())\n    {\n        return path();\n    }\n    else\n    {\n        return path_from_range(begin(), --end());\n    }\n}\n\ninline path path::relative_path() const\n{\n    if (is_relative())\n    {\n        return *this;\n    }\n    else\n    {\n        return path_from_range(++begin(), end());\n    }\n}\n\ninline path path::filename() const\n{\n    if (empty())\n    {\n        return path();\n    }\n    else\n    {\n        return path_from_range(--end(), end());\n    }\n}\n\ninline int path::compare(const path& rhs) const\n{\n    return detail::lexical_compare(begin(), end(), rhs.begin(), rhs.end());\n}\n\ninline path::iterator path::begin() const\n{\n    return iterator(this, 0);\n}\n\ninline path::iterator path::end() const\n{\n    return iterator(this, m_path.size());\n}\n\ntemplate <typename CharT, typename Traits>\ninline std::basic_ostream<CharT, Traits>&\noperator<<(std::basic_ostream<CharT, Traits>& stream, const path& p)\n{\n    // TODO: quote string so it can roundtrip\n    stream << p.string<CharT, Traits>();\n    return stream;\n}\n\ninline bool operator==(const path& lhs, const path& rhs)\n{\n    return lhs.compare(rhs) == 0;\n}\n\ninline bool operator<(const path& lhs, const path& rhs)\n{\n    return lhs.compare(rhs) < 0;\n}\n\ntemplate <typename Source>\ninline path operator/(const path& lhs, const Source& rhs)\n{\n    path concatenation(lhs);\n    concatenation /= rhs;\n    return concatenation;\n}\n}\n} // namespace ssh::filesystem\n\n#endif\n"
  },
  {
    "path": "ssh/filesystem.hpp",
    "content": "// Copyright 2010, 2012, 2013, 2015, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SSH_FILESYSTEM_HPP\n#define SSH_FILESYSTEM_HPP\n\n#include <ssh/detail/file_handle_state.hpp>\n#include <ssh/detail/sftp_channel_state.hpp>\n#include <ssh/detail/libssh2/sftp.hpp>\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/cstdint.hpp>                      // uint64_t, uintmax_t\n#include <boost/detail/bitmask.hpp>               // BOOST_BITMASK\n#include <boost/detail/scoped_enum_emulation.hpp> // BOOST_SCOPED_ENUM*\n#include <boost/exception/info.hpp>               // errinfo_api_function\n#include <boost/iterator/iterator_facade.hpp>     // iterator_facade\n#include <boost/operators.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/make_shared.hpp>\n#include <boost/move/move.hpp> // BOOST_RV_REF, BOOST_MOVABLE_BUT_NOT_COPYABLE\n#include <boost/noncopyable.hpp>\n#include <boost/ref.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/system/error_code.hpp> // errc\n#include <boost/system/system_error.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <algorithm> // min\n#include <cassert>   // assert\n#include <exception> // bad_alloc\n#include <stdexcept> // invalid_argument\n#include <string>\n#include <vector>\n\n#include <libssh2_sftp.h>\n\nnamespace ssh\n{\n\n// Forward declared so sftp_filesystem can declare session a friend.  This\n// file doesn't depend on session in any other way\nclass session;\n\nnamespace filesystem\n{\n\nclass file_attributes\n{\npublic:\n    enum file_type\n    {\n        normal_file,\n        symbolic_link,\n        directory,\n        character_device,\n        block_device,\n        named_pipe,\n        socket,\n        unknown\n    };\n\n    file_type type() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_PERMISSIONS))\n        {\n            switch (m_attributes.permissions & LIBSSH2_SFTP_S_IFMT)\n            {\n            case LIBSSH2_SFTP_S_IFIFO:\n                return named_pipe;\n            case LIBSSH2_SFTP_S_IFCHR:\n                return character_device;\n            case LIBSSH2_SFTP_S_IFDIR:\n                return directory;\n            case LIBSSH2_SFTP_S_IFBLK:\n                return block_device;\n            case LIBSSH2_SFTP_S_IFREG:\n                return normal_file;\n            case LIBSSH2_SFTP_S_IFLNK:\n                return symbolic_link;\n            case LIBSSH2_SFTP_S_IFSOCK:\n                return socket;\n            default:\n                return unknown;\n            }\n        }\n        else\n        {\n            return unknown;\n        }\n    }\n\n    boost::optional<unsigned long> permissions() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_PERMISSIONS))\n        {\n            return m_attributes.permissions;\n        }\n        else\n        {\n            return boost::optional<unsigned long>();\n        }\n    }\n\n    boost::optional<boost::uint64_t> size() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_SIZE))\n        {\n            return m_attributes.filesize;\n        }\n        else\n        {\n            return boost::optional<boost::uint64_t>();\n        }\n    }\n\n    boost::optional<unsigned long> uid() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_UIDGID))\n        {\n            return m_attributes.uid;\n        }\n        else\n        {\n            return boost::optional<unsigned long>();\n        }\n    }\n\n    boost::optional<unsigned long> gid() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_UIDGID))\n        {\n            return m_attributes.gid;\n        }\n        else\n        {\n            return boost::optional<unsigned long>();\n        }\n    }\n\n    /**\n     * @todo  Use Boost.DateTime or other decent datatype.\n     */\n    boost::optional<unsigned long> last_accessed() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_ACMODTIME))\n        {\n            return m_attributes.atime;\n        }\n        else\n        {\n            return boost::optional<unsigned long>();\n        }\n    }\n\n    /**\n     * @todo  Use Boost.DateTime or other decent datatype.\n     */\n    boost::optional<unsigned long> last_modified() const\n    {\n        if (is_valid_attribute(LIBSSH2_SFTP_ATTR_ACMODTIME))\n        {\n            return m_attributes.mtime;\n        }\n        else\n        {\n            return boost::optional<unsigned long>();\n        }\n    }\n\nprivate:\n    friend class sftp_file;\n    friend class sftp_filesystem; // to construct in attributes method\n\n    explicit file_attributes(const LIBSSH2_SFTP_ATTRIBUTES& raw_attributes)\n        : m_attributes(raw_attributes)\n    {\n    }\n\n    bool is_valid_attribute(unsigned long attribute_type) const\n    {\n        return (m_attributes.flags & attribute_type) != 0;\n    }\n\n    LIBSSH2_SFTP_ATTRIBUTES m_attributes;\n};\n\nclass sftp_file : boost::totally_ordered<sftp_file>\n{\npublic:\n    sftp_file(const path& file, const std::string& long_entry,\n              const LIBSSH2_SFTP_ATTRIBUTES& attributes)\n        : m_file(file), m_long_entry(long_entry), m_attributes(attributes)\n    {\n    }\n\n    ssh::filesystem::path path() const\n    {\n        return m_file;\n    }\n\n    const std::string& long_entry() const\n    {\n        return m_long_entry;\n    }\n\n    const file_attributes& attributes() const\n    {\n        return m_attributes;\n    }\n\nprivate:\n    ::ssh::filesystem::path m_file;\n    std::string m_long_entry;\n    file_attributes m_attributes;\n};\n\ninline bool operator<(const sftp_file& lhs, const sftp_file& rhs)\n{\n    return lhs.path() < rhs.path();\n}\n\nenum file_type\n{\n    // prevent clash with perms::unknown, rename once using C++11 enum classes\n    none_,\n    not_found,\n    regular,\n    directory,\n    symlink,\n    block,\n    character,\n    fifo,\n    socket,\n    // prevent clash with perms::unknown, rename once using C++11 enum classes\n    unknown_,\n};\n\nBOOST_BITMASK(file_type);\n\nenum perms\n{\n    none = 0,\n\n    owner_read = LIBSSH2_SFTP_S_IRUSR,\n    owner_write = LIBSSH2_SFTP_S_IWUSR,\n    owner_exec = LIBSSH2_SFTP_S_IXUSR,\n    owner_all = LIBSSH2_SFTP_S_IRWXU,\n\n    group_read = LIBSSH2_SFTP_S_IRGRP,\n    group_write = LIBSSH2_SFTP_S_IWGRP,\n    group_exec = LIBSSH2_SFTP_S_IXGRP,\n    group_all = LIBSSH2_SFTP_S_IRWXG,\n\n    others_read = LIBSSH2_SFTP_S_IROTH,\n    others_write = LIBSSH2_SFTP_S_IWOTH,\n    others_exec = LIBSSH2_SFTP_S_IXOTH,\n    others_all = LIBSSH2_SFTP_S_IRWXO,\n\n    mask = 07777,\n    unknown = 0xffff,\n    add_perms = 0x10000,\n    remove_perms = 0x20000,\n};\n\nBOOST_BITMASK(perms);\n\nnamespace detail\n{\n\ninline file_type permissions_to_file_type(unsigned long permissions)\n{\n    // Mask permissions to consider only file-type bits\n    switch (permissions & LIBSSH2_SFTP_S_IFMT)\n    {\n    case 0:\n        return file_type::none_;\n    case LIBSSH2_SFTP_S_IFIFO:\n        return file_type::fifo;\n    case LIBSSH2_SFTP_S_IFCHR:\n        return file_type::character;\n    case LIBSSH2_SFTP_S_IFDIR:\n        return file_type::directory;\n    case LIBSSH2_SFTP_S_IFBLK:\n        return file_type::block;\n    case LIBSSH2_SFTP_S_IFREG:\n        return file_type::regular;\n    case LIBSSH2_SFTP_S_IFLNK:\n        return file_type::symlink;\n    case LIBSSH2_SFTP_S_IFSOCK:\n        return file_type::socket;\n    default:\n        return file_type::unknown_;\n    }\n}\n\ninline unsigned long file_type_to_permissions(file_type type)\n{\n    // Mask permissions to consider only file-type bits\n    switch (type)\n    {\n    case file_type::none_:\n        return 0;\n    case file_type::fifo:\n        return LIBSSH2_SFTP_S_IFIFO;\n    case file_type::character:\n        return LIBSSH2_SFTP_S_IFCHR;\n    case file_type::directory:\n        return LIBSSH2_SFTP_S_IFDIR;\n    case file_type::block:\n        return LIBSSH2_SFTP_S_IFBLK;\n    case file_type::regular:\n        return LIBSSH2_SFTP_S_IFREG;\n    case file_type::symlink:\n        return LIBSSH2_SFTP_S_IFLNK;\n    case file_type::socket:\n        return LIBSSH2_SFTP_S_IFSOCK;\n    default:\n        BOOST_THROW_EXCEPTION(std::logic_error(\"Unrecognised file_type\"));\n    }\n}\n}\n\nclass file_status\n{\npublic:\n    // The set of file_type values is a superset of the possible types encoded\n    // by LIBSSH2_SFTP_ATTRIBUTES (e.g. file_type::not_found), so we store the\n    // former, not the latter.\n\n    explicit file_status(file_type type = file_type::none_,\n                         perms permissions = perms::unknown)\n        : m_type(type), m_permissions(permissions)\n    {\n    }\n\n    explicit file_status(const LIBSSH2_SFTP_ATTRIBUTES& attributes)\n    {\n        if (is_available_attribute(attributes, LIBSSH2_SFTP_ATTR_PERMISSIONS))\n        {\n            m_type = detail::permissions_to_file_type(attributes.permissions);\n            m_permissions =\n                static_cast<perms>(attributes.permissions) & perms::mask;\n        }\n        else\n        {\n            m_type = file_type::unknown_;\n            m_permissions = perms::unknown;\n        }\n\n        if (is_available_attribute(attributes, LIBSSH2_SFTP_ATTR_SIZE))\n        {\n            m_file_size = attributes.filesize;\n        }\n\n        if (is_available_attribute(attributes, LIBSSH2_SFTP_ATTR_ACMODTIME))\n        {\n            m_last_write_time = attributes.mtime;\n        }\n    }\n\n    file_type type() const\n    {\n        return m_type;\n    }\n\n    perms permissions() const\n    {\n        return m_permissions;\n    }\n\n    // Non-standard - only included as top-level function in standard\n    boost::uintmax_t file_size() const\n    {\n        if (!m_file_size)\n        {\n            BOOST_THROW_EXCEPTION(\n                boost::system::system_error(boost::system::errc::not_supported,\n                                            boost::system::generic_category()));\n        }\n        return *m_file_size;\n    }\n\n    // Non-standard - only included as top-level function in standard\n    std::time_t last_write_time() const\n    {\n        if (!m_last_write_time)\n        {\n            BOOST_THROW_EXCEPTION(\n                boost::system::system_error(boost::system::errc::not_supported,\n                                            boost::system::generic_category()));\n        }\n        return *m_last_write_time;\n    }\n\nprivate:\n    bool is_available_attribute(const LIBSSH2_SFTP_ATTRIBUTES attributes,\n                                unsigned long attribute_type) const\n    {\n        return (attributes.flags & attribute_type) != 0;\n    }\n\n    file_type m_type;\n    perms m_permissions;\n    boost::optional<boost::uintmax_t> m_file_size;\n    boost::optional<std::time_t> m_last_write_time;\n};\n\nclass sftp_filesystem;\n\nnamespace detail\n{\n\ninline boost::shared_ptr<::ssh::detail::file_handle_state>\nopen_directory(::ssh::detail::sftp_channel_state& channel, const path& path)\n{\n    std::string path_string = path.native();\n\n    return boost::make_shared<::ssh::detail::file_handle_state>(\n        boost::ref(channel), // http://stackoverflow.com/a/1374266/67013\n        path_string.data(), path_string.size(), 0, 0, LIBSSH2_SFTP_OPENDIR);\n}\n}\n\n/**\n * List the files and directories in a directory.\n *\n * The iterator is copyable but all copies are linked so that incrementing\n * one will increment all the copies.\n */\nclass directory_iterator\n    : public boost::iterator_facade<directory_iterator, const sftp_file,\n                                    boost::forward_traversal_tag, sftp_file>\n{\npublic:\n    /**\n     * End-of-directory marker.\n     */\n    // ForwardIterators are REQUIRED to be default-constructible, yukky as\n    // that is\n    directory_iterator()\n    {\n    }\n\n    // directory_iterator is not implemented in terms of the sftp_filesystem\n    // public interface.  It uses the the channel's internals, so the channel\n    // should control it.  Therefore the only way to create it is\n    // via the channel class, which has special access to the private\n    // constructor via the client-attorney idiom.\n\n    /// @cond INTERNAL\n    /**\n     * Defines the single permitted factory of `directory_iterator`\n     * instances.\n     * This class calls the private constructor on behalf of the factory.\n     * See http://stackoverflow.com/q/3217390/67013.\n     */\n    class factory_attorney\n    {\n    private:\n        friend class sftp_filesystem;\n\n        directory_iterator\n        operator()(::ssh::detail::sftp_channel_state& channel, const path& path)\n        {\n            return directory_iterator(channel, path);\n        }\n\n        directory_iterator operator()()\n        {\n            return directory_iterator();\n        }\n    };\n    /// @endcond\n\nprivate:\n    directory_iterator(::ssh::detail::sftp_channel_state& sftp_channel,\n                       const path& path)\n        : m_directory(path),\n          m_handle(detail::open_directory(sftp_channel, path)),\n          m_attributes(LIBSSH2_SFTP_ATTRIBUTES())\n    {\n        next_file();\n    }\n\n    friend class boost::iterator_core_access;\n\n    void increment()\n    {\n        if (m_handle == NULL)\n            BOOST_THROW_EXCEPTION(std::range_error(\"No more files\"));\n        next_file();\n    }\n\n    bool equal(directory_iterator const& other) const\n    {\n        return this->m_handle == other.m_handle;\n    }\n\n    void next_file()\n    {\n        // yuk! hardcoded buffer sizes. unfortunately, libssh2 doesn't\n        // give us a choice so we allocate massive buffers here and then\n        // take measures later to reduce the footprint\n\n        std::vector<char> filename_buffer(1024, '\\0');\n        std::vector<char> longentry_buffer(1024, '\\0');\n        LIBSSH2_SFTP_ATTRIBUTES attrs = LIBSSH2_SFTP_ATTRIBUTES();\n\n        while (true)\n        {\n            int rc;\n            {\n                ::ssh::detail::file_handle_state::scoped_lock lock =\n                    m_handle->aquire_lock();\n\n                rc = ::ssh::detail::libssh2::sftp::readdir_ex(\n                    m_handle->session_ptr(), m_handle->sftp_ptr(),\n                    m_handle->file_handle(), &filename_buffer[0],\n                    filename_buffer.size(), &longentry_buffer[0],\n                    longentry_buffer.size(), &attrs);\n\n                // IMPORTANT: must unlock before possible handle reset below\n                // which would lock the session again to close the file handle\n            }\n\n            if (rc == 0) // end of files\n            {\n                m_handle.reset();\n                return;\n            }\n            else\n            {\n                assert(rc > 0);\n\n                // copy attributes to member one we know we're overwriting the\n                // last-retrieved file's properties\n                m_attributes = attrs;\n\n                // we don't assume that the filename is null-terminated but rc\n                // holds the number of bytes written to the buffer so we can\n                // shrink\n                // the filename string to that size\n                std::string file_name = std::string(\n                    &filename_buffer[0], (std::min)(static_cast<size_t>(rc),\n                                                    filename_buffer.size()));\n                if (file_name == \".\" || file_name == \"..\")\n                {\n                    continue;\n                }\n                else\n                {\n                    m_file_name = file_name;\n                }\n\n                // the long entry must be usable in an ls -l listing according\n                // to\n                // the standard so I'm interpreting this to mean it can't\n                // contain\n                // embedded NULLs so we force NULL-termination and then allocate\n                // the string to be the NULL-terminated size which will likely\n                // be\n                // much smaller than the buffer\n                longentry_buffer[longentry_buffer.size() - 1] = '\\0';\n                m_long_entry = std::string(&longentry_buffer[0]);\n                return;\n            }\n        }\n    }\n\n    sftp_file dereference() const\n    {\n        if (m_handle == NULL)\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\"Can't dereference the end of a collection\"));\n\n        return sftp_file(m_directory / m_file_name, m_long_entry, m_attributes);\n    }\n\n    // The file handle is shared between all copies of the iterator because\n    // iterators must be copyable\n    boost::shared_ptr<::ssh::detail::file_handle_state> m_handle;\n    path m_directory;\n\n    /// @name Properties of last successfully listed file.\n    // @{\n    std::string m_file_name;\n    std::string m_long_entry;\n    LIBSSH2_SFTP_ATTRIBUTES m_attributes;\n    // @}\n};\n\nnamespace detail\n{\n\nBOOST_SCOPED_ENUM_START(path_status){non_existent, non_directory, directory};\nBOOST_SCOPED_ENUM_END\n;\n\ninline BOOST_SCOPED_ENUM(path_status)\n    check_status(sftp_filesystem& filesystem, const path& path);\n}\n\nBOOST_SCOPED_ENUM_START(overwrite_behaviour){\n    /**\n     * Do not overwrite an existing file at the destination.\n     *\n     * If the file exists function will throw an exception.\n     */\n    prevent_overwrite,\n\n    /**\n     * Overwrite any existing file at the destination.\n     *\n     * The SFTP server may not support overwriting files, in which case this\n     * acts like `prevent_overwrite`.\n     */\n    allow_overwrite,\n\n    /**\n     * Overwrite any existing file using *only* atomic methods.  If atomic\n     * methods\n     * are not available on the server, the overwrite will not be performed by\n     * other methods and the function will throw an exception.\n     *\n     * The SFTP server may not support overwriting files, in which case this\n     * acts like `prevent_overwrite`.\n     */\n    atomic_overwrite};\nBOOST_SCOPED_ENUM_END\n\nclass sftp_input_device;\nclass sftp_output_device;\nclass sftp_io_device;\n\n/**\n * Connection to the filesystem on a remote server via an SSH/SFTP connection.\n *\n * Filesystem connections are non-copyable.  The connection is closed when the\n * object is destroyed.\n */\nclass sftp_filesystem : private boost::noncopyable\n{\n    BOOST_MOVABLE_BUT_NOT_COPYABLE(sftp_filesystem)\n\npublic:\n    /**\n     * Move constructor.\n     */\n    sftp_filesystem(BOOST_RV_REF(sftp_filesystem) other)\n        : m_sftp(boost::move(other.m_sftp))\n    {\n    }\n\n    /**\n     * Move-assignment.\n     */\n    sftp_filesystem& operator=(BOOST_RV_REF(sftp_filesystem) other)\n    {\n        m_sftp = boost::move(other.m_sftp);\n        return *this;\n    }\n\n    /**\n     * Create an iterator over the contents of the given directory.\n     *\n     * The iterator is copyable but all copies are linked so that incrementing\n     * one will increment all the copies.\n     *\n     * The `sftp_filesystem` (and, transitively, the `session`) must outlive\n     * all non-end copies of the iterator.  It is the caller's responsibility\n     * to ensure this.\n     */\n    directory_iterator directory_iterator(const path& path)\n    {\n        return ssh::filesystem::directory_iterator::factory_attorney()(\n            sftp_ref(), path);\n    }\n\n    /**\n     * Create an iterator marking the end of a directory.\n     */\n    ssh::filesystem::directory_iterator directory_iterator()\n    {\n        return ssh::filesystem::directory_iterator::factory_attorney()();\n    }\n\n    /**\n     * Query a file for its attributes.\n     *\n     * If @a follow_links is @c true, the file that is queried is the target of\n     * any chain of links.  Otherwise, it is the link itself.\n     *\n     * @todo Split into `status` and `symlink_status` to mirror Boost.Filesystem\n     *       API.\n     */\n    file_attributes attributes(const path& file, bool follow_links)\n    {\n        std::string file_path = file.native();\n        LIBSSH2_SFTP_ATTRIBUTES attributes = LIBSSH2_SFTP_ATTRIBUTES();\n\n        {\n            ::ssh::detail::sftp_channel_state::scoped_lock lock =\n                sftp_ref().aquire_lock();\n\n            ::ssh::detail::libssh2::sftp::stat(\n                sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n                file_path.data(), file_path.size(),\n                (follow_links) ? LIBSSH2_SFTP_STAT : LIBSSH2_SFTP_LSTAT,\n                &attributes);\n        }\n\n        return file_attributes(attributes);\n    }\n\n    path resolve_link_target(const path& link)\n    {\n        std::string link_string = link.native();\n\n        return symlink_resolve(link_string.data(), link_string.size(),\n                               LIBSSH2_SFTP_READLINK);\n    }\n\n    path canonical_path(const path& link)\n    {\n        std::string link_string = link.native();\n\n        return symlink_resolve(link_string.data(), link_string.size(),\n                               LIBSSH2_SFTP_REALPATH);\n    }\n\n    /// @cond INTERNAL\n    /**\n     * Defines the single permitted factory of `sftp_filesystem` instances.\n     * This class calls the private constructor on behalf of the factory.\n     * See http://stackoverflow.com/q/3217390/67013.\n     */\n    class factory_attorney\n    {\n    private:\n        friend class ssh::session;\n\n        sftp_filesystem operator()(::ssh::detail::session_state& session_state)\n        {\n            return sftp_filesystem(session_state);\n        }\n    };\n    /// @endcond\n\nprivate:\n    friend class factory_attorney;\n\n    explicit sftp_filesystem(::ssh::detail::session_state& session_state)\n        : m_sftp(new ::ssh::detail::sftp_channel_state(session_state))\n    {\n    }\n\n    friend class sftp_input_device;\n    friend class sftp_output_device;\n    friend class sftp_io_device;\n\n    friend bool create_directory(sftp_filesystem& fs,\n                                 const path& new_directory);\n    friend void create_symlink(sftp_filesystem& fs, const path& link,\n                               const path& target);\n    friend file_status status(sftp_filesystem& fs, const path& target);\n    friend void permissions(sftp_filesystem& fs, const path& file,\n                            perms new_permissions);\n    friend void rename(sftp_filesystem& fs, const path& source,\n                       const path& destination,\n                       BOOST_SCOPED_ENUM(overwrite_behaviour) overwrite_hint);\n    friend bool remove(sftp_filesystem& fs, const path& target);\n    friend boost::uintmax_t remove_all(sftp_filesystem& fs, const path& target);\n\n    bool create_directory(const path& new_directory)\n    {\n        std::string new_directory_string = new_directory.native();\n        try\n        {\n            ::ssh::detail::sftp_channel_state::scoped_lock lock =\n                sftp_ref().aquire_lock();\n            ::ssh::detail::libssh2::sftp::mkdir_ex(\n                sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n                new_directory_string.data(), new_directory_string.size(),\n                LIBSSH2_SFTP_S_IRWXU | LIBSSH2_SFTP_S_IRGRP |\n                    LIBSSH2_SFTP_S_IXGRP | LIBSSH2_SFTP_S_IROTH |\n                    LIBSSH2_SFTP_S_IXOTH);\n\n            return true;\n        }\n        catch (const boost::system::system_error&)\n        {\n            // Might just be because it already exists.  Let's check that and if\n            // ignore if that's the case.\n            // Doing this test after avoids an extra trip to the server in the\n            // common case.\n\n            // We don't test the exception's error code because OpenSSH just\n            // returns FX_FAILURE which could have many causes.  The only way\n            // to be sure the directory is already there is to check explicitly.\n\n            // There is an inevitable race condition here---the directory may\n            // have been added/removed since the failure---so the result is a\n            // best guess.  Because of the race, it also doesn't matter that we\n            // unlock and re-lock in check_status.  Transactional integrity\n            // isn't lost because it was never there.\n\n            switch (detail::check_status(*this, new_directory))\n            {\n            case detail::path_status::non_directory:\n            case detail::path_status::non_existent:\n                throw;\n\n            case detail::path_status::directory:\n                return false;\n\n            default:\n                assert(false);\n                BOOST_THROW_EXCEPTION(std::logic_error(\"Unknown path status\"));\n                return false;\n            }\n        }\n    }\n\n    void create_symlink(const path& link, const path& target)\n    {\n        std::string link_string = link.native();\n        std::string target_string = target.native();\n\n        ::ssh::detail::sftp_channel_state::scoped_lock lock =\n            sftp_ref().aquire_lock();\n\n        ::ssh::detail::libssh2::sftp::symlink(\n            sftp_ref().session_ptr(), sftp_ref().sftp_ptr(), link_string.data(),\n            link_string.size(), target_string.data(), target_string.size());\n    }\n\n    file_status status(const path& target)\n    {\n        std::string file_path = target.native();\n        LIBSSH2_SFTP_ATTRIBUTES attributes = LIBSSH2_SFTP_ATTRIBUTES();\n\n        {\n            ::ssh::detail::sftp_channel_state::scoped_lock lock =\n                sftp_ref().aquire_lock();\n\n            boost::system::error_code ec;\n            std::string message;\n\n            ::ssh::detail::libssh2::sftp::stat(\n                sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n                file_path.data(), file_path.size(), LIBSSH2_SFTP_STAT,\n                &attributes, ec, message);\n            if (ec)\n            {\n                if (ec == boost::system::errc::no_such_file_or_directory)\n                {\n                    return file_status(file_type::not_found, perms::unknown);\n                }\n                else\n                {\n                    SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(\n                        ec, message, \"libssh2_sftp_stat_ex\", file_path.c_str(),\n                        file_path.size());\n                }\n            }\n        }\n\n        return file_status(attributes);\n    }\n\n    void permissions(const path& file, perms new_permissions)\n    {\n        if (new_permissions & perms::add_perms &&\n            new_permissions & perms::remove_perms)\n        {\n            BOOST_THROW_EXCEPTION(std::logic_error(\n                \"add_perms and remove_perms are mutually exclusive\"));\n        }\n        else if (new_permissions & perms::add_perms)\n        {\n            perms current_permissions = status(file).permissions();\n            new_permissions =\n                (new_permissions & perms::mask) | current_permissions;\n        }\n        else if (new_permissions & perms::remove_perms)\n        {\n            perms current_permissions = status(file).permissions();\n            new_permissions =\n                ~(new_permissions & perms::mask) & current_permissions;\n        }\n\n        std::string file_path = file.native();\n        LIBSSH2_SFTP_ATTRIBUTES attributes = LIBSSH2_SFTP_ATTRIBUTES();\n        attributes.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;\n        attributes.permissions =\n            static_cast<unsigned long>(new_permissions & perms::mask);\n\n        ::ssh::detail::sftp_channel_state::scoped_lock lock =\n            sftp_ref().aquire_lock();\n\n        ::ssh::detail::libssh2::sftp::stat(\n            sftp_ref().session_ptr(), sftp_ref().sftp_ptr(), file_path.data(),\n            file_path.size(), LIBSSH2_SFTP_SETSTAT, &attributes);\n    }\n\n    void rename(const path& source, const path& destination,\n                BOOST_SCOPED_ENUM(overwrite_behaviour) overwrite_hint)\n    {\n        std::string source_string = source.native();\n        std::string destination_string = destination.native();\n\n        int flags;\n        switch (overwrite_hint)\n        {\n        case overwrite_behaviour::prevent_overwrite:\n            flags = 0;\n            break;\n\n        case overwrite_behaviour::allow_overwrite:\n            flags = LIBSSH2_SFTP_RENAME_OVERWRITE;\n            break;\n\n        case overwrite_behaviour::atomic_overwrite:\n            // The spec says OVERWRITE is implied by ATOMIC but specifying both\n            // to be on the safe side\n            flags = LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC;\n            break;\n\n        default:\n            BOOST_THROW_EXCEPTION(\n                std::invalid_argument(\"Unrecognised overwrite behaviour\"));\n        }\n\n        ::ssh::detail::sftp_channel_state::scoped_lock lock =\n            sftp_ref().aquire_lock();\n\n        ::ssh::detail::libssh2::sftp::rename(\n            sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n            source_string.data(), source_string.size(),\n            destination_string.data(), destination_string.size(), flags);\n    }\n\n    bool remove(const path& target)\n    {\n        // Unlike the POSIX/Boost.Filesystem API we are following, the SFTP\n        // protocol mirrors the C API where directories can only be removed\n        // using the special RMDIR command.\n        //\n        // We tried to avoid an extra round trip to the server (to stat the\n        // file) by blindly trying the common case of non-directories and\n        // ignoring the first SFTP error.  The theory was that any real\n        // error should also occur on the second (rmdir) attempt.\n        // But that's not true because the second error might be complaining\n        // that we're trying the wrong kind of delete while the first error\n        // is the actual problem (permissions, for example).  Saving the first\n        // error, and overwriting the second error with it, doesn't solve the\n        // problem either as it could be the second error that gives the real\n        // problem with the first error being wrong-kind-of-delete.  Basically\n        // we can't know which error is 'real'.  If we did, we'd know the\n        // filetype already!\n\n        switch (detail::check_status(*this, target))\n        {\n        case detail::path_status::non_existent:\n            return false;\n\n        case detail::path_status::directory:\n            return remove_empty_directory(target);\n\n        case detail::path_status::non_directory:\n            // This includes 'unknown' file type.  What's the alternative?\n            return remove_one_file(target);\n\n        default:\n            assert(false);\n            BOOST_THROW_EXCEPTION(std::logic_error(\"Unknown path status\"));\n            return 0U;\n        }\n    }\n\n    boost::uintmax_t remove_all(const path& target)\n    {\n        switch (detail::check_status(*this, target))\n        {\n        case detail::path_status::non_existent:\n            return 0U;\n\n        case detail::path_status::directory:\n            return remove_directory(target);\n\n        case detail::path_status::non_directory:\n            // This includes 'unknown' file type.  What's the alternative?\n            return remove_one_file(target);\n\n        default:\n            assert(false);\n            BOOST_THROW_EXCEPTION(std::logic_error(\"Unknown path status\"));\n            return 0U;\n        }\n    }\n\n    bool remove_one_file(const path& file)\n    {\n        return do_remove(file, false);\n    }\n\n    bool remove_empty_directory(const path& file)\n    {\n        return do_remove(file, true);\n    }\n\n    boost::uintmax_t remove_directory(const path& root);\n\n    bool do_remove(const path& target, bool is_directory)\n    {\n        std::string target_string = target.native();\n\n        try\n        {\n            ::ssh::detail::sftp_channel_state::scoped_lock lock =\n                sftp_ref().aquire_lock();\n\n            if (is_directory)\n            {\n                ::ssh::detail::libssh2::sftp::rmdir_ex(\n                    sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n                    target_string.data(), target_string.size());\n            }\n            else\n            {\n                ::ssh::detail::libssh2::sftp::unlink_ex(\n                    sftp_ref().session_ptr(), sftp_ref().sftp_ptr(),\n                    target_string.data(), target_string.size());\n            }\n        }\n        catch (const boost::system::system_error& e)\n        {\n            if (e.code() == boost::system::errc::no_such_file_or_directory)\n            {\n                // Mirror the Boost.Filesystem API which doesn't treat this\n                // as an error.\n                return false;\n            }\n            else\n            {\n                throw;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Common parts of readlink and realpath.\n     */\n    path symlink_resolve(const char* path, unsigned int path_len,\n                         int resolve_action)\n    {\n        // yuk! hardcoded buffer sizes. unfortunately, libssh2 doesn't\n        // give us a choice so we allocate massive buffers here and then\n        // take measures later to reduce the footprint\n\n        std::vector<char> target_path_buffer(1024, '\\0');\n\n        ::ssh::detail::sftp_channel_state::scoped_lock lock =\n            sftp_ref().aquire_lock();\n\n        int len = ::ssh::detail::libssh2::sftp::symlink_ex(\n            sftp_ref().session_ptr(), sftp_ref().sftp_ptr(), path, path_len,\n            &target_path_buffer[0], target_path_buffer.size(), resolve_action);\n\n        return ::ssh::filesystem::path(&target_path_buffer[0],\n                                       &target_path_buffer[0] + len);\n    }\n\n    ::ssh::detail::sftp_channel_state& sftp_ref()\n    {\n        return *m_sftp;\n    }\n\n    // Using an auto_ptr (eventually unique_ptr) so that the other objects\n    // that reference this state continue to reference a valid object even if\n    // this sftp_filesystem object is moved.  The moved filesystem will only\n    // move the pointer but the state will remain at the same address.\n    // Using a value member meant that moving the filesystem, relocated the\n    // channel state but the other objects don't get made aware of that.\n    // Result: crash.\n    // The other objects using this state include directory iterators and\n    // file streams.\n    // See http://stackoverflow.com/a/20493410/67013.\n    std::auto_ptr<::ssh::detail::sftp_channel_state> m_sftp;\n};\n\n// Only needed for C++03 support with Boost move-emulation because C++11\n// std::swap does this type of swap already\ninline void swap(sftp_filesystem& lhs, sftp_filesystem& rhs)\n{\n    sftp_filesystem tmp(boost::move(lhs));\n    lhs = boost::move(rhs);\n    rhs = boost::move(tmp);\n}\n\n/**\n * Make a directory accessible from the given path.\n *\n * @returns `true` if a new directory was created at `new_directory`\n *          `false` if a directory already existed on that path.\n *\n * This function mirrors Boost.Filesystem `create_directory` except that\n * directories are created with 0755 permissions instead of 0777.  0755 is\n * more secure and the recommended permissions for directories on web server\n * so seems more appropriate.  It's not clear why Boost.Filesystem chooses\n * 0777 instead.\n */\ninline bool create_directory(sftp_filesystem& fs, const path& new_directory)\n{\n    return fs.create_directory(new_directory);\n}\n\n/**\n * Create a symbolic link.\n *\n * @param link     Path to the new link on the remote filesystem. Must not\n *                 already exist.\n * @param target   Path of file or directory to be linked to.\n *\n * @WARNING  All versions of OpenSSH and probably many other servers are\n *           implemented incorrectly and swap the order of the @p link and\n *           @p target parameters.  To connect to these servers you will\n *           have to pass the parameters to this function in the wrong\n *           order!\n */\ninline void create_symlink(sftp_filesystem& fs, const path& link,\n                           const path& target)\n{\n    return fs.create_symlink(link, target);\n}\n\n/**\n * Change one path to a file with another.\n *\n * After this function completes, `source` is no longer a path to the\n * file that it referenced before calling the function, and `destination`\n * is a new path to that file.\n *\n * @param source\n *     Path to the file on the remote filesystem. File must already exist.\n * @param destination\n *     Path to which the file will be moved.  File may already exist.  If it\n *     does exist and `allow_overwrite` is `false`, the function will throw\n *     an exception.\n * @param overwrite_hint\n *     Optional hint suggesting preferred overwrite behaviour if\n *     `destination`\n *     is already a path to a file before this function is called.  Only\n *     `prevent_overwrite` is guaranteed to be obeyed.  All other flags are\n *     suggestions that the server is free to disregard (most SFTP servers\n *     disregard these flags).  If it does so and `destination` is already a\n *     path to a file, this function will throw an unspecified\n *     `boost::system::system_error`.\n *\n * @throws `boost::system::system_error` if `destination` is already a\n *         path to a file before this function is called and either\n *         `prevent_overwrite` is specified as `overwrite_hint` or the\n *         server did not support the given hint.\n *\n * `atomic_overwrite` is the default value of `overwrite_hint` to give the\n * closest alignment to POSIX/Boost.Filesystem `rename`.  However, as\n * explained above, the server is free to refuse to overwrite in the\n * presence of an existing `destination`.  Therefore the APIs do not align\n * completely.\n *\n * @todo Not currently supporting the NATIVE flag as it's not at all clear\n *       what it does.\n */\ninline void rename(sftp_filesystem& fs, const path& source,\n                   const path& destination,\n                   BOOST_SCOPED_ENUM(overwrite_behaviour)\n                       overwrite_hint = overwrite_behaviour::atomic_overwrite)\n{\n    fs.rename(source, destination, overwrite_hint);\n}\n\n/**\n * Remove a file.\n *\n * Removes `target` on the filesystem available via this object.  If\n * `target` is a symlink, only removes the link, not what the link\n * resolves to.  If `target` is a directory, removes it only if the\n * directory is empty.\n *\n * @returns `true` if the file was removed and `false` if the file did not\n *          exist in the first place.\n * @throws `boost::system::system_error` if `target` is a non-empty\n *         directory.\n *\n * If the calling code already knows whether `target` is a directory, this\n * function adds the overhead of a single extra stat call to the server\n * above what would be possible using plain SFTP unlink/rmdir.  This trip is\n * needed to find out that information and allows us to mirror the\n * POSIX/Boost.Filesystem remove functions that do not differentiate\n * directories.\n */\ninline bool remove(sftp_filesystem& fs, const path& target)\n{\n    return fs.remove(target);\n}\n\n/**\n * Remove a file and anything below it in the hierarchy.\n *\n * Removes `target` on the filesystem available via this object.  If\n * `target` is a symlink, only removes the link, not what the link\n * resolves to.  If `target` is a directory, removes it and all its\n * contents.\n *\n * @returns the number of files removed.\n *\n * If the calling code already knows whether `target` is a directory, this\n * function adds the overhead of a single extra stat call to the server\n * above what would be possible using plain SFTP unlink/rmdir.  This trip is\n * needed to find out that information and allows us to mirror the\n * POSIX/Boost.Filesystem remove functions that do not differentiate\n * directories.\n *\n * All files below the target must be statted (indirectly via directory listing)\n * by any implementation so this function adds no overhead for those.\n */\ninline boost::uintmax_t remove_all(sftp_filesystem& fs, const path& target)\n{\n    return fs.remove_all(target);\n}\n\nnamespace detail\n{\n\ninline BOOST_SCOPED_ENUM(path_status)\n    check_status(sftp_filesystem& filesystem, const path& path)\n{\n    try\n    {\n        file_attributes attrs = filesystem.attributes(path, false);\n\n        if (attrs.type() == file_attributes::directory)\n        {\n            return path_status::directory;\n        }\n        else\n        {\n            return path_status::non_directory;\n        }\n    }\n    catch (const boost::system::system_error& e)\n    {\n        // Process errors by catching the exception, rather than intercepting\n        // the error code directly, so as not to duplicate the exception info\n        // processing.\n\n        if (e.code() == boost::system::errc::no_such_file_or_directory)\n        {\n            // Mirror the Boost.Filesystem API which doesn't treat this as an\n            // error.\n            return path_status::non_existent;\n        }\n        else\n        {\n            throw;\n        }\n    }\n}\n}\n\n/**\n * Does a file exist at the given path.\n */\ninline bool exists(sftp_filesystem& filesystem, const path& file)\n{\n    try\n    {\n        filesystem.attributes(file, false);\n    }\n    catch (const boost::system::system_error& e)\n    {\n        if (e.code() == boost::system::errc::no_such_file_or_directory)\n        {\n            return false;\n        }\n        else\n        {\n            throw;\n        }\n    }\n\n    return true;\n}\n\ninline path resolve_link_target(sftp_filesystem& filesystem,\n                                const sftp_file& link)\n{\n    return filesystem.resolve_link_target(link.path());\n}\n\ninline path canonical_path(sftp_filesystem& filesystem, const sftp_file& link)\n{\n    return filesystem.canonical_path(link.path());\n}\n\n/**\n * Get the attributes of a file.\n *\n * If the given path does not exist, this function will not throw an\n * exception. Instead, the returned `file_status` will have type\n * `file_type::not_found`.\n */\ninline file_status status(sftp_filesystem& filesystem, const path& file)\n{\n    return filesystem.status(file);\n}\n\n/**\n * Is the give status from a regular file?\n */\ninline bool is_regular_file(const file_status& status)\n{\n    return status.type() == file_type::regular;\n}\n\n/**\n * Is the given path a regular file?\n */\ninline bool is_regular_file(sftp_filesystem& filesystem, const path& file)\n{\n    return is_regular_file(status(filesystem, file));\n}\n\n/**\n * Is the give status from a directory?\n */\ninline bool is_directory(const file_status& status)\n{\n    return status.type() == file_type::directory;\n}\n\n/**\n * Is the given path a directory?\n */\ninline bool is_directory(sftp_filesystem& filesystem, const path& file)\n{\n    return is_directory(status(filesystem, file));\n}\n\n/**\n * Change the permissions for a file.\n *\n * If `perms::add_perms` or `perms::remove_perms` are included in\n * `new_permissions`, the permissions are added to or removed from the\n * existing. If neither `perms::add_perms` nor `perms::remove_perms` is included\n * in `new_permissions`, the permissions are set exactly as given.\n */\ninline void permissions(sftp_filesystem& filesystem, const path& file,\n                        perms new_permissions)\n{\n    return filesystem.permissions(file, new_permissions);\n}\n\n/**\n * Returns the size of the given file, in bytes.\n */\ninline boost::uintmax_t file_size(sftp_filesystem& filesystem, const path& file)\n{\n    return status(filesystem, file).file_size();\n}\n\n/**\n * Returns the time of the last write to the given file.\n *\n * Whether the return value is accurate depends on the filesystem on which the\n * file is stored.\n */\ninline std::time_t last_write_time(sftp_filesystem& filesystem,\n                                   const path& file)\n{\n    return status(filesystem, file).last_write_time();\n}\n\n/**\n * Determine whether the given file or directory is empty.\n */\ninline bool is_empty(sftp_filesystem& filesystem, const path& file)\n{\n    file_status s = status(filesystem, file);\n    if (is_directory(s))\n    {\n        return filesystem.directory_iterator(file) ==\n               filesystem.directory_iterator();\n    }\n    else\n    {\n        return s.file_size() == 0;\n    }\n}\n\n// Needs directory_iterator implementation so outside sftp_filesystem class\n// body\ninline boost::uintmax_t sftp_filesystem::remove_directory(const path& root)\n{\n    boost::uintmax_t count = 0U;\n\n    for (ssh::filesystem::directory_iterator directory =\n             directory_iterator(root);\n         directory != directory_iterator(); ++directory)\n    {\n        const sftp_file& file = *directory;\n\n        if (file.path().filename() == \".\" || file.path().filename() == \"..\")\n        {\n            continue;\n        }\n\n        if (file.attributes().type() == file_attributes::directory)\n        {\n            count += remove_directory(file.path());\n        }\n        else\n        {\n            if (remove_one_file(file.path()))\n            {\n                ++count;\n            }\n            else\n            {\n                // Something else deleted the file before we could\n            }\n        }\n    }\n\n    if (remove_empty_directory(root))\n    {\n        ++count;\n    }\n    else\n    {\n        // Something else deleted the directory before we could or it\n        // never existed in the first place\n    }\n\n    return count;\n}\n}\n} // namespace ssh::filesystem\n\n#endif\n"
  },
  {
    "path": "ssh/host_key.hpp",
    "content": "/**\n    @file\n\n    Host-key wrapper.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_HOST_KEY_HPP\n#define SSH_HOST_KEY_HPP\n\n#include <ssh/detail/session_state.hpp>\n\n#include <boost/foreach.hpp>         // BOOST_FOREACH\n#include <boost/shared_ptr.hpp>      // shared_ptr\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <sstream>   // ostringstream\n#include <stdexcept> // invalid_argument\n#include <string>\n#include <utility> // pair\n#include <vector>\n\n#include <libssh2.h>\n\nnamespace ssh\n{\n\nnamespace detail\n{\n\n/**\n * Thin wrapper around libssh2_session_hostkey.\n */\ninline std::pair<std::string, int> hostkey(session_state& session)\n{\n    // Session owns the string.\n    // Lock until we finish copying the key string from the session.  I\n    // don't know if other calls to the session are currently able to\n    // change it, but they might one day.\n    // Locking it for the duration makes it thread-safe either way.\n\n    detail::session_state::scoped_lock lock = session.aquire_lock();\n\n    size_t len = 0;\n    int type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN;\n    const char* key =\n        libssh2_session_hostkey(session.session_ptr(), &len, &type);\n\n    if (key)\n        return std::make_pair(std::string(key, len), type);\n    else\n        return std::make_pair(std::string(), type);\n}\n\n/**\n * Thin wrapper around libssh2_hostkey_hash.\n *\n * @param T          Type of collection to return.  Sensible examples\n *                   include std::string or std::vector<unsigned char>.\n * @param session    libssh2 session pointer\n * @param hash_type  Hash method being requested.\n */\ntemplate <typename T>\ninline T hostkey_hash(session_state& session, int hash_type)\n{\n    // Session owns the data.\n    // Lock until we finish copying the key hash bytes from the session.  I\n    // don't know if other calls to the session are currently able to\n    // change it, but they might one day.\n    // Locking it for the duration makes it thread-safe either way.\n\n    detail::session_state::scoped_lock lock = session.aquire_lock();\n\n    const T::value_type* hash_bytes = reinterpret_cast<const T::value_type*>(\n        ::libssh2_hostkey_hash(session.session_ptr(), hash_type));\n\n    size_t len = 0;\n    if (hash_type == LIBSSH2_HOSTKEY_HASH_MD5)\n        len = 16;\n    else if (hash_type == LIBSSH2_HOSTKEY_HASH_SHA1)\n        len = 20;\n    else\n        BOOST_THROW_EXCEPTION(std::invalid_argument(\"Unknown hash type\"));\n\n    if (hash_bytes)\n        return T(hash_bytes, hash_bytes + len);\n    else\n        return T();\n}\n\n/**\n * Thin wrapper around libssh2_session_methods.\n */\ninline std::string method(session_state& session, int method_type)\n{\n    // Session owns the string.\n    // Lock until we finish copying the string from the session.  I\n    // don't know if other calls to the session are currently able to\n    // change it, but they might one day.\n    // Locking it for the duration makes it thread-safe either way.\n\n    detail::session_state::scoped_lock lock = session.aquire_lock();\n\n    const char* key_type =\n        libssh2_session_methods(session.session_ptr(), method_type);\n\n    if (key_type)\n        return std::string(key_type);\n    else\n        return std::string();\n}\n}\n\n/**\n * Possible types of host-key algorithm.\n */\nstruct hostkey_type\n{\n    enum enum_t\n    {\n        unknown,\n        rsa1,\n        ssh_rsa,\n        ssh_dss\n    };\n};\n\nnamespace detail\n{\n\n/**\n * Convert the returned key-type from libssh2_session_hostkey into a value from\n * the hostkey_type enum.\n */\ninline hostkey_type::enum_t type_to_hostkey_type(int type)\n{\n    switch (type)\n    {\n    case LIBSSH2_HOSTKEY_TYPE_RSA:\n        return hostkey_type::ssh_rsa;\n    case LIBSSH2_HOSTKEY_TYPE_DSS:\n        return hostkey_type::ssh_dss;\n    default:\n        return hostkey_type::unknown;\n    }\n}\n}\n\n/**\n * Class representing the session's current negotiated host-key.\n *\n * As well as the raw key itself, this class provides MD5 and SHA1 hashes and\n * key metadata.\n */\nclass host_key\n{\npublic:\n    explicit host_key(detail::session_state& session)\n        : // We pull everything out of the session here and store it to avoid\n          // instances of this class depending on the lifetime of the session\n          m_key(detail::hostkey(session)),\n          m_algorithm_name(detail::method(session, LIBSSH2_METHOD_HOSTKEY)),\n          m_md5_hash(detail::hostkey_hash<std::vector<unsigned char>>(\n              session, LIBSSH2_HOSTKEY_HASH_MD5)),\n          m_sha1_hash(detail::hostkey_hash<std::vector<unsigned char>>(\n              session, LIBSSH2_HOSTKEY_HASH_SHA1))\n    {\n    }\n\n    /**\n     * Host-key either raw or base-64 encoded.\n     *\n     * @see is_base64()\n     */\n    std::string key() const\n    {\n        return m_key.first;\n    }\n\n    /**\n     * Is the key returned by key() base64-encoded (printable)?\n     */\n    bool is_base64() const\n    {\n        return false;\n    }\n\n    /**\n     * Type of the key algorithm e.g., ssh-dss.\n     */\n    hostkey_type::enum_t algorithm() const\n    {\n        return detail::type_to_hostkey_type(m_key.second);\n    }\n\n    /**\n     * Printable name of the method negotiated for the key algorithm.\n     */\n    std::string algorithm_name() const\n    {\n        return m_algorithm_name;\n    }\n\n    /**\n     * Hostkey sent by the server to identify itself, hashed with the MD5\n     * algorithm.\n     *\n     * @returns  Hash as binary data; it is not directly printable\n     *           (@see hexify()).\n     */\n    std::vector<unsigned char> md5_hash() const\n    {\n        return m_md5_hash;\n    }\n\n    /**\n     * Hostkey sent by the server to identify itself, hashed with the SHA1\n     * algorithm.\n     *\n     * @returns  Hash as binary data; it is not directly printable\n     *           (@see hexify()).\n     */\n    std::vector<unsigned char> sha1_hash() const\n    {\n        return m_sha1_hash;\n    }\n\nprivate:\n    std::pair<std::string, int> m_key;\n    std::string m_algorithm_name;\n    std::vector<unsigned char> m_md5_hash;\n    std::vector<unsigned char> m_sha1_hash;\n};\n\n/**\n * Turn a collection of bytes into a printable hexidecimal string.\n *\n * @param bytes       Collection of bytes.\n * @param nibble_sep  String to place between each pair of hexidecimal\n *                    characters.\n * @param uppercase   Whether to use uppercase or lowercase hexidecimal.\n */\ntemplate <typename T>\nstd::string hexify(const T& bytes, const std::string& nibble_sep = \":\",\n                   bool uppercase = false)\n{\n    std::ostringstream hex_hash;\n\n    if (uppercase)\n        hex_hash << std::uppercase;\n    else\n        hex_hash << std::nouppercase;\n    hex_hash << std::hex << std::setfill('0');\n\n    BOOST_FOREACH (unsigned char b, bytes)\n    {\n        if (!hex_hash.str().empty())\n            hex_hash << nibble_sep;\n\n        unsigned int i = b;\n        hex_hash << std::setw(2) << i;\n    }\n\n    return hex_hash.str();\n}\n\n} // namespace ssh\n\n#endif\n"
  },
  {
    "path": "ssh/knownhost.hpp",
    "content": "/**\n    @file\n\n    Interface to known-host mechanism.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_KNOWNHOST_HPP\n#define SSH_KNOWNHOST_HPP\n\n#include <ssh/detail/libssh2/knownhost.hpp> // ssh::detail::libssh2::knownhost\n#include <ssh/detail/session_state.hpp>\n#include <ssh/host_key.hpp>\n\n#include <boost/exception/errinfo_file_name.hpp> // errinfo_file_name\n#include <boost/exception/info.hpp>              // errinfo\n#include <boost/filesystem.hpp>                  // path\n#include <boost/filesystem/fstream.hpp>          // path-enabled fstream\n#include <boost/iterator/iterator_facade.hpp>    // iterator_facade\n#include <boost/make_shared.hpp>\n#include <boost/shared_ptr.hpp>        // shared_ptr\n#include <boost/system/error_code.hpp> // errc\n#include <boost/throw_exception.hpp>   // BOOST_THROW_EXCEPTION\n\n#undef min\n#include <algorithm> // for_each, transform\n#include <cassert>   // assert\n#include <iterator>  // iterator_traits\n#include <stdexcept> // invalid_argument, logic_error\n#include <string>\n#include <vector>\n\n#include <libssh2.h>\n\nnamespace ssh\n{\n\nclass knownhost;\n\nnamespace detail\n{\n\n/**\n * Entry-reading functor.\n */\ntemplate <int TYPE, typename T>\nclass read_entry : public std::unary_function<T, void>\n{\npublic:\n    read_entry(boost::shared_ptr<session_state> session,\n               boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts)\n        : m_session(session), m_hosts(hosts)\n    {\n    }\n\n    /**\n     * Read entry into libssh2 knownhost collection.\n     */\n    void operator()(const T& entry)\n    {\n        detail::session_state::scoped_lock lock = m_session->aquire_lock();\n\n        detail::libssh2::knownhost::readline(m_session->session_ptr(),\n                                             m_hosts.get(), entry.data(),\n                                             entry.length(), TYPE);\n    }\n\nprivate:\n    boost::shared_ptr<session_state> m_session;\n    boost::shared_ptr<LIBSSH2_KNOWNHOSTS> m_hosts;\n};\n\n/**\n * Entry-writing functor.\n */\ntemplate <int TYPE>\nclass write_entry : public std::unary_function<const knownhost&, std::string>\n{\npublic:\n    write_entry(boost::shared_ptr<session_state> /*session*/,\n                boost::shared_ptr<LIBSSH2_KNOWNHOSTS> /*hosts*/)\n    {\n    }\n\n    /**\n     * Write entry from collection to string.\n     *\n     * The return type must be able to create a writable char buffer of a\n     * certain size by a call to its constructor like this:\n     * T(size, default_val).  This is the case for both string and\n     * vector<char>.\n     */\n    std::string operator()(const knownhost& host)\n    {\n        return host.to_string(TYPE);\n    }\n};\n\n/**\n * Line proxy to return strings line-by-line from istream_iterator.\n */\nclass line\n{\npublic:\n    friend std::istream& operator>>(std::istream& is, line& l)\n    {\n        std::getline(is, l.m_data);\n        return is;\n    }\n\n    friend std::ostream& operator<<(std::ostream& os, const line& l)\n    {\n        os << l.m_data;\n        return os;\n    }\n\n    friend bool operator!=(const std::string& other, const line& l)\n    {\n        return other != l.m_data;\n    }\n\n    friend bool operator==(const line& l, const std::string& other)\n    {\n        return other == l.m_data;\n    }\n\n    std::string::size_type length() const\n    {\n        return m_data.length();\n    }\n\n    std::string::const_pointer data() const\n    {\n        return m_data.data();\n    }\n\nprivate:\n    std::string m_data;\n};\n\n/**\n * Fetch next host.\n *\n * @returns NULL if finished.\n */\ninline libssh2_knownhost* next_host(boost::shared_ptr<session_state> session,\n                                    boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts,\n                                    libssh2_knownhost* current_position)\n{\n    libssh2_knownhost* host = NULL;\n\n    detail::session_state::scoped_lock lock = session->aquire_lock();\n\n    int rc = ::ssh::detail::libssh2::knownhost::get(\n        session->session_ptr(), hosts.get(), &host, current_position);\n\n    assert(rc == 0 || rc == 1);\n\n    if (rc == 1) // finished\n    {\n        assert(host == NULL);\n        host = NULL;\n    }\n\n    return host;\n}\n\n/**\n * Create new host entry in collection of hosts.\n */\ninline libssh2_knownhost* add(boost::shared_ptr<session_state> session,\n                              boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts,\n                              const std::string& host_or_ip,\n                              const std::string& salt, const std::string& key,\n                              int type, bool base64_key)\n{\n    if (base64_key)\n        type |= LIBSSH2_KNOWNHOST_KEYENC_BASE64;\n    else\n        type |= LIBSSH2_KNOWNHOST_KEYENC_RAW;\n\n    libssh2_knownhost* host = NULL;\n\n    detail::session_state::scoped_lock lock = session->aquire_lock();\n\n    detail::libssh2::knownhost::add(session->session_ptr(), hosts.get(),\n                                    host_or_ip.c_str(),\n                                    (salt.empty()) ? NULL : salt.c_str(),\n                                    key.data(), key.length(), type, &host);\n\n    return host;\n}\n\n/**\n * Return the libssh2 key string which may include a comment appended to\n * the end separated by whitespace.\n */\ninline std::string internal_key(const libssh2_knownhost* pos)\n{\n    return (pos && pos->key) ? pos->key : std::string();\n}\n}\n\nclass knownhost\n{\nprivate:\n    friend class knownhost_collection;\n    friend class knownhost_iterator;\n\n    knownhost(boost::shared_ptr<detail::session_state> session,\n              boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts,\n              libssh2_knownhost* pos)\n        : m_session(session), m_hosts(hosts), m_pos(pos)\n    {\n    }\n\npublic:\n    std::string name() const\n    {\n        return (m_pos && m_pos->name) ? m_pos->name : std::string();\n    }\n\n    std::string key() const\n    {\n        std::string k = detail::internal_key(m_pos);\n        std::string::size_type space = k.find(' ');\n        if (space != std::string::npos)\n            return k.substr(0, space);\n        else\n            return k;\n    }\n\n    /**\n     * Return the optional comment attached to the host entry.\n     *\n     * @todo Fetch comment properly once libssh2 API allows it.\n     */\n    std::string comment() const\n    {\n        std::string k = detail::internal_key(m_pos);\n        std::string::size_type space = k.find(' ');\n        if (space != std::string::npos && space + 1 != std::string::npos)\n            return k.substr(space + 1);\n        else\n            return std::string();\n    }\n\n    std::string to_string(int type) const\n    {\n        // get minimum required buffer size (doesn't include null-term)\n        size_t required_len = 0;\n        boost::system::error_code ec;\n\n        {\n            detail::session_state::scoped_lock lock = m_session->aquire_lock();\n\n            detail::libssh2::knownhost::writeline(m_session->session_ptr(),\n                                                  m_hosts.get(), m_pos, NULL, 0,\n                                                  &required_len, type, ec);\n        }\n\n        assert(ec == boost::system::errc::no_buffer_space);\n        required_len++; // returned val doesn't include NULL-terminator\n\n        // now repeat but with a properly allocated buffer and no ec so\n        // errors cause exception\n        std::vector<char> buf(required_len);\n\n        {\n            detail::session_state::scoped_lock lock = m_session->aquire_lock();\n\n            ::ssh::detail::libssh2::knownhost::writeline(\n                m_session->session_ptr(), m_hosts.get(), m_pos, &buf[0],\n                buf.size(), &required_len, type);\n        }\n\n        assert(required_len == buf.size() - 1);\n\n        // Return line excluding '\\n' and NULL-terminator\n        return std::string(&buf[0], std::min(buf.size() - 2, required_len - 1));\n    }\n\n    /**\n     * The key algorithm as an algorithm name.\n     */\n    std::string key_algo() const\n    {\n        switch (m_pos->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)\n        {\n        case LIBSSH2_KNOWNHOST_KEY_RSA1:\n            return \"rsa1\";\n        case LIBSSH2_KNOWNHOST_KEY_SSHRSA:\n            return \"ssh-rsa\";\n        case LIBSSH2_KNOWNHOST_KEY_SSHDSS:\n            return \"ssh-dss\";\n        default:\n            return \"unknown\";\n        }\n    }\n\n    /** @name Predicate members. */\n    // @{\n\n    /**\n     * Hostname is not encoded; it is plain-text.\n     * e.g. hostname.example.com\n     */\n    bool is_name_plain() const\n    {\n        return (m_pos->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==\n               LIBSSH2_KNOWNHOST_TYPE_PLAIN;\n    }\n\n    /**\n     * Hostname and salt is hashed using sha1 and base64-encoded.\n     *\n     * When this predicate is true, name() returns an empty string as the\n     * hash can't be converted back to a hostname.\n     */\n    bool is_name_sha1() const\n    {\n        return (m_pos->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==\n               LIBSSH2_KNOWNHOST_TYPE_SHA1;\n    }\n\n    /**\n     * Hostname encoded with some user-defined encoding.\n     */\n    bool is_name_custom() const\n    {\n        return (m_pos->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==\n               LIBSSH2_KNOWNHOST_TYPE_CUSTOM;\n    }\n\n    // @}\n\nprivate:\n    boost::shared_ptr<detail::session_state> m_session;\n    boost::shared_ptr<LIBSSH2_KNOWNHOSTS> m_hosts;\n    libssh2_knownhost* m_pos;\n};\n\n/**\n * @todo  Should libssh2_knownhost_get take a const.\n */\nclass knownhost_iterator\n    : public boost::iterator_facade<knownhost_iterator, const knownhost,\n                                    boost::forward_traversal_tag, knownhost>\n{\npublic:\n    /**\n     * Create an iterator to the end of the collection.\n     */\n    knownhost_iterator() : m_pos(NULL)\n    {\n    }\n\n    /**\n     * Remove a host at the given iterator position from the collection.\n     *\n     * After this function returns, any iterators that pointed to the removed\n     * item (including the given one) will be invalidated.  Attempting to\n     * call any of their member functions results in undefined behaviour.\n     *\n     * @returns  Iterator to the next item in the collection or the end of the\n     *           collection if there are no more items.\n     */\n    friend knownhost_iterator erase(knownhost_iterator it)\n    {\n        knownhost_iterator next = it;\n        next++;\n\n        detail::session_state::scoped_lock lock = it.m_session->aquire_lock();\n\n        // this call invalidates the given iterator\n        detail::libssh2::knownhost::del(it.m_session->session_ptr(),\n                                        it.m_hosts.get(), it.m_pos);\n\n        return next;\n    }\n\nprivate:\n    // knownhost_collection is the  factories for non-end knownhost_iterators,\n    // so is declared a friend of knownhost_iterator so that it may call the\n    // private constructors\n\n    friend class knownhost_collection;\n\n    /**\n     * Create an iterator to the beginning of the collection.\n     */\n    knownhost_iterator(boost::shared_ptr<detail::session_state> session,\n                       boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts)\n        : m_session(session),\n          m_hosts(hosts),\n          m_pos(detail::next_host(m_session, m_hosts, NULL))\n    {\n    }\n\n    /**\n     * Create an iterator to a point in the collection indicated by pos.\n     */\n    knownhost_iterator(boost::shared_ptr<detail::session_state> session,\n                       boost::shared_ptr<LIBSSH2_KNOWNHOSTS> hosts,\n                       libssh2_knownhost* pos)\n        : m_session(session), m_hosts(hosts), m_pos(pos)\n    {\n    }\n\n    friend class boost::iterator_core_access;\n\n    void increment()\n    {\n        if (m_pos == NULL)\n            BOOST_THROW_EXCEPTION(std::logic_error(\n                \"Can't increment past the end of a collection\"));\n        m_pos = detail::next_host(m_session, m_hosts, m_pos);\n    }\n\n    bool equal(knownhost_iterator const& other) const\n    {\n        return this->m_pos == other.m_pos;\n    }\n\n    knownhost dereference() const\n    {\n        if (m_pos == NULL)\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\"Can't dereference the end of a collection\"));\n        return knownhost(m_session, m_hosts, m_pos);\n    }\n\n    boost::shared_ptr<detail::session_state> m_session;\n    boost::shared_ptr<LIBSSH2_KNOWNHOSTS> m_hosts;\n    libssh2_knownhost* m_pos;\n};\n\n/**\n * Result returned by knownhost_collection::find().\n */\nclass knownhost_search_result\n{\npublic:\n    knownhost_search_result(const knownhost_iterator& it,\n                            const knownhost_iterator& end, bool match)\n        : m_host(it), m_end(end), m_match(match)\n    {\n        assert(!match || (m_host != m_end));\n    }\n\n    knownhost_iterator host() const\n    {\n        return m_host;\n    }\n\n    bool mismatch() const\n    {\n        return !m_match && (m_host != m_end);\n    }\n\n    bool match() const\n    {\n        return m_match && (m_host != m_end);\n    }\n\n    bool not_found() const\n    {\n        return m_host == m_end;\n    }\n\nprivate:\n    knownhost_iterator m_host;\n    knownhost_iterator m_end;\n    bool m_match;\n};\n\nnamespace detail\n{\n\n/**\n * Convert a value from the hostkey_type enum into an integer suitable\n * for use by libssh2_knownhost_add.\n */\ninline int hostkey_type_to_add_type(ssh::hostkey_type::enum_t type)\n{\n    switch (type)\n    {\n    case ssh::hostkey_type::rsa1:\n        return LIBSSH2_KNOWNHOST_KEY_RSA1;\n    case ssh::hostkey_type::ssh_rsa:\n        return LIBSSH2_KNOWNHOST_KEY_SSHRSA;\n    case ssh::hostkey_type::ssh_dss:\n        return LIBSSH2_KNOWNHOST_KEY_SSHDSS;\n    case ssh::hostkey_type::unknown:\n    default:\n        BOOST_THROW_EXCEPTION(\n            std::invalid_argument(\"Unrecognised key algorithm\"));\n    }\n}\n\ninline boost::shared_ptr<LIBSSH2_KNOWNHOSTS>\ninit(boost::shared_ptr<session_state> session)\n{\n    detail::session_state::scoped_lock lock = session->aquire_lock();\n\n    return boost::shared_ptr<LIBSSH2_KNOWNHOSTS>(\n        libssh2::knownhost::init(session->session_ptr()),\n        libssh2_knownhost_free);\n}\n}\n\n/**\n * Collection of known-host entries.\n */\nclass knownhost_collection\n{\npublic:\n    explicit knownhost_collection()\n        : m_session(boost::make_shared<detail::session_state>()),\n          m_hosts(detail::init(m_session))\n    {\n        // We construct a new session here, rather than taking one as an\n        // argument, because it is only used for memory allocation.  It\n        // doesn't need to be connected to anything so it's an unnecessary\n        // burden on the caller to expect them to provide one.\n    }\n\n    knownhost_iterator begin() const\n    {\n        return knownhost_iterator(m_session, m_hosts);\n    }\n\n    knownhost_iterator end() const\n    {\n        return knownhost_iterator();\n    }\n\n    knownhost_search_result find(const std::string& host,\n                                 const std::string& key, bool base64_key) const\n    {\n        int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN;\n        if (base64_key)\n            type |= LIBSSH2_KNOWNHOST_KEYENC_BASE64;\n        else\n            type |= LIBSSH2_KNOWNHOST_KEYENC_RAW;\n\n        libssh2_knownhost* match = NULL;\n\n        int rc;\n\n        {\n            detail::session_state::scoped_lock lock = m_session->aquire_lock();\n\n            rc = detail::libssh2::knownhost::check(\n                m_session->session_ptr(), m_hosts.get(), host.c_str(),\n                key.data(), key.length(), type, &match);\n        }\n\n        switch (rc)\n        {\n        case LIBSSH2_KNOWNHOST_CHECK_MATCH:\n            return knownhost_search_result(\n                knownhost_iterator(m_session, m_hosts, match), end(), true);\n\n        case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:\n            return knownhost_search_result(\n                knownhost_iterator(m_session, m_hosts, match), end(), false);\n\n        case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:\n            return knownhost_search_result(end(), end(), false);\n\n        default:\n            assert(false);\n            BOOST_THROW_EXCEPTION(std::logic_error(\"Unexpected return code\"));\n        }\n    }\n\n    knownhost_search_result find(const std::string& host,\n                                 const ssh::host_key& key)\n    {\n        return find(host, key.key(), key.is_base64());\n    }\n\n    knownhost add(const std::string& host_or_ip, const std::string& key,\n                  ssh::hostkey_type::enum_t algorithm, bool base64_key)\n    {\n        int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN |\n                   detail::hostkey_type_to_add_type(algorithm);\n\n        libssh2_knownhost* host =\n            detail::add(m_session, m_hosts, host_or_ip, std::string(), key,\n                        type, base64_key);\n\n        return knownhost(m_session, m_hosts, host);\n    }\n\n    knownhost add(const std::string& host_or_ip, const ssh::host_key& key)\n    {\n        return add(host_or_ip, key.key(), key.algorithm(), key.is_base64());\n    }\n\n    knownhost add_hashed(const std::string& host_or_ip, const std::string& salt,\n                         const std::string& key,\n                         ssh::hostkey_type::enum_t algorithm, bool base64_key)\n    {\n        int type = LIBSSH2_KNOWNHOST_TYPE_SHA1 |\n                   detail::hostkey_type_to_add_type(algorithm);\n\n        libssh2_knownhost* host = detail::add(m_session, m_hosts, host_or_ip,\n                                              salt, key, type, base64_key);\n\n        return knownhost(m_session, m_hosts, host);\n    }\n\n    knownhost add_hashed(const std::string& host_or_ip, const std::string& salt,\n                         const ssh::host_key& key)\n    {\n        return add_hashed(host_or_ip, salt, key.key(), key.algorithm(),\n                          key.is_base64());\n    }\n\n    knownhost add_custom(const std::string& host_or_ip, const std::string& key,\n                         ssh::hostkey_type::enum_t algorithm, bool base64_key)\n    {\n        int type = LIBSSH2_KNOWNHOST_TYPE_CUSTOM |\n                   detail::hostkey_type_to_add_type(algorithm);\n\n        libssh2_knownhost* host =\n            detail::add(m_session, m_hosts, host_or_ip, std::string(), key,\n                        type, base64_key);\n\n        return knownhost(m_session, m_hosts, host);\n    }\n\n    knownhost add_custom(const std::string& host_or_ip,\n                         const ssh::host_key& key)\n    {\n        return add_custom(host_or_ip, key.key(), key.algorithm(),\n                          key.is_base64());\n    }\n\nprotected:\n    /**\n     * Initialise the known-hosts collection from a range of entries.\n     *\n     * @param begin  Iterator to the start of the range.\n     * @param end    Iterator indicating termination (half-closed).\n     * @param TYPE   Type of entry to read.  Currently the only supported type\n     *               is LIBSSH2_KNOWNHOST_FILE_OPENSSH in which case the\n     *               entry must be in OpenSSH known_hosts format (hashed or\n     *               unhashed).\n     * @param It     InputIterator to range of entries in known_hosts format.\n     */\n    template <int TYPE, typename InputIt>\n    void load_entries(const InputIt& begin, const InputIt& end)\n    {\n        typedef std::iterator_traits<InputIt>::value_type value_t;\n\n        std::for_each(begin, end,\n                      detail::read_entry<TYPE, value_t>(m_session, m_hosts));\n    }\n\n    /**\n     * Initialise the known-hosts collection from a range of entries.\n     *\n     * @param begin  Iterator to the start of the range.\n     * @param end    Iterator indicating termination (half-closed).\n     * @param type   Type of entry to read.  Currently the only supported type\n     *               is LIBSSH2_KNOWNHOST_FILE_OPENSSH in which case the\n     *               entry must be in OpenSSH known_hosts format (hashed or\n     *               unhashed).\n     */\n    template <int TYPE, typename OutputIt>\n    OutputIt save_entries(const knownhost_iterator& begin,\n                          const knownhost_iterator& end, OutputIt output) const\n    {\n        return std::transform(begin, end, output,\n                              detail::write_entry<TYPE>(m_session, m_hosts));\n    }\n\nprivate:\n    boost::shared_ptr<detail::session_state> m_session;\n    boost::shared_ptr<LIBSSH2_KNOWNHOSTS> m_hosts;\n};\n\nknownhost add(knownhost_collection& hosts, const std::string& host_or_ip,\n              const ssh::host_key& key)\n{\n    return hosts.add(host_or_ip, key.key(), key.algorithm(), key.is_base64());\n}\n\nknownhost add_hashed(knownhost_collection& hosts, const std::string& host_or_ip,\n                     const std::string& salt, const ssh::host_key& key)\n{\n    return hosts.add_hashed(host_or_ip, salt, key.key(), key.algorithm(),\n                            key.is_base64());\n}\n\nknownhost add_custom(knownhost_collection& hosts, const std::string& host_or_ip,\n                     const ssh::host_key& key)\n{\n    return hosts.add_custom(host_or_ip, key.key(), key.algorithm(),\n                            key.is_base64());\n}\n\nknownhost update(knownhost_collection& hosts, const std::string& host_or_ip,\n                 const ssh::host_key& key, const knownhost_search_result& entry)\n{\n    erase(entry.host());\n    return add(hosts, host_or_ip, key);\n}\n\n/**\n * Collection of known-host entries stored in OpenSSH known_hosts format.\n *\n * In the absence of changes, entries are written back exactly as they\n * were read, with the following exceptions:\n *  - ip,hostname combinations are split onto two lines, ip first\n *  - tabs in seperators are replaced by a single space\n */\nclass openssh_knownhost_collection : public knownhost_collection\n{\npublic:\n    /** Initialise collection from a range of OpenSSH known_hosts lines. */\n    template <typename InputIt>\n    openssh_knownhost_collection(InputIt begin, InputIt end)\n    {\n        load_entries<LIBSSH2_KNOWNHOST_FILE_OPENSSH>(begin, end);\n    }\n\n    /** Initialise collection from an OpenSSH known_hosts file. */\n    openssh_knownhost_collection(const boost::filesystem::path& filename)\n    {\n        boost::filesystem::ifstream file(filename);\n        if (!file)\n            BOOST_THROW_EXCEPTION(\n                boost::enable_error_info(std::runtime_error(\n                    \"Could not read from known-hosts file '\" +\n                    complete(filename).string() + \"'\"))\n                << boost::errinfo_file_name(filename.string()));\n\n        load_entries<LIBSSH2_KNOWNHOST_FILE_OPENSSH>(\n            std::istream_iterator<detail::line>(file),\n            std::istream_iterator<detail::line>());\n    }\n\n    /**\n     * Save range of entries to an output iterator in OpenSSH known_hosts\n     * format.\n     *\n     * Entries do @b not end in a newline character.\n     */\n    template <typename OutputIt>\n    OutputIt save(const knownhost_iterator& begin,\n                  const knownhost_iterator& end, OutputIt output) const\n    {\n        return save_entries<LIBSSH2_KNOWNHOST_FILE_OPENSSH, OutputIt>(\n            begin, end, output);\n    }\n\n    /** Save all entires to an OpenSSH known_hosts file. */\n    void save(const boost::filesystem::path& filename) const\n    {\n        boost::filesystem::ofstream file(filename);\n        if (!file)\n            BOOST_THROW_EXCEPTION(\n                boost::enable_error_info(\n                    std::runtime_error(\"Could not write to known-hosts file\"))\n                << boost::errinfo_file_name(filename.string()));\n\n        save(begin(), end(), std::ostream_iterator<std::string>(file, \"\\n\"));\n    }\n};\n\n} // namespace ssh\n\n#endif\n"
  },
  {
    "path": "ssh/session.hpp",
    "content": "/**\n    @file\n\n    SSH session object.\n\n    @if license\n\n    Copyright (C) 2010, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_SESSION_HPP\n#define SSH_SESSION_HPP\n\n#include <ssh/agent.hpp>\n#include <ssh/detail/libssh2/session.hpp>  // ssh::detail::libssh2::session\n#include <ssh/detail/libssh2/userauth.hpp> // ssh::detail::libssh2::userauth\n#include <ssh/host_key.hpp>\n#include <ssh/filesystem.hpp> // sftp_filesystem\n\n#include <boost/algorithm/string/classification.hpp> // is_any_of\n#include <boost/algorithm/string/split.hpp>\n#include <boost/exception_ptr.hpp>\n#include <boost/filesystem/path.hpp> // path, used for key paths\n#include <boost/make_shared.hpp>\n#include <boost/move/move.hpp> // BOOST_RV_REF, BOOST_MOVABLE_BUT_NOT_COPYABLE\n#include <boost/noncopyable.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/range/adaptor/transformed.hpp>\n#include <boost/range/algorithm_ext/for_each.hpp>\n#include <boost/range/algorithm_ext/push_back.hpp>\n#include <boost/range/iterator_range.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/system/error_code.hpp> // error_code, errc\n#include <boost/system/system_error.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <memory> // auto_ptr\n#include <string>\n#include <utility> // pair, make_pair\n#include <vector>\n\n#include <libssh2.h>\n\nnamespace ssh\n{\n\nnamespace filesystem\n{\nclass sftp_filesystem;\n}\n\nnamespace detail\n{\n\ninline std::pair<std::string, bool>\nconvert_prompt(const LIBSSH2_USERAUTH_KBDINT_PROMPT& prompt)\n{\n    return std::make_pair(std::string(prompt.text, prompt.length), prompt.echo);\n}\n\ninline void convert_response(LIBSSH2_USERAUTH_KBDINT_RESPONSE& raw_response,\n                             const std::string& response)\n{\n    // XXX: Should use session MALLOC here\n    raw_response.text = static_cast<char*>(\n        malloc(response.length() * sizeof(std::string::value_type)));\n    // XXX: what happens if we encounter an exception after this point?\n    raw_response.length = response.length();\n\n    response.copy(raw_response.text, raw_response.length);\n}\n\n/**\n * Glue between libssh2's ideas of a responder and this c++ wrapper's responder.\n *\n * It's not safe to throw exceptions through libssh2 C code, so we have to catch\n * them in the static callback (dethunker), and somehow communicate them back to\n * the C++ code which can safely rethrow them.\n *\n * The only available channel of communication is the challenge-responder in the\n * abstract but the user provides that so we don't want them to have to provide\n * anything special.  This class adds the 'something special' by wrapping the\n * challenge-responder and stashing anything needed to interpret the result.\n */\ntemplate <typename ChallengeResponder>\nclass challenge_response_translator\n{\npublic:\n    challenge_response_translator(const ChallengeResponder& resp)\n        : m_responder(resp), m_called(false)\n    {\n    }\n\n    /**\n     * Perform the challenge-response authentication translating between the\n     * interfaces as we go.\n     */\n    bool do_challenge_response(LIBSSH2_SESSION* session,\n                               const std::string username)\n    {\n        boost::system::error_code ec;\n        std::string message;\n\n        // IMPORTANT: No need to lock here.  The session is locked by the caller\n        // to encompass setting the abstract so that the abstract isn't\n        // inadvertently overwritten.\n\n        libssh2::userauth::keyboard_interactive_ex(\n            session, username.data(), username.size(),\n            &dethunker<ChallengeResponder>, ec, message);\n\n        return translate_status(ec, message);\n    }\n\n    void operator()(const char* name, int name_len, const char* instruction,\n                    int instruction_len, int num_prompts,\n                    const LIBSSH2_USERAUTH_KBDINT_PROMPT* raw_prompts,\n                    LIBSSH2_USERAUTH_KBDINT_RESPONSE* raw_responses) throw()\n    {\n        m_called = true;\n\n        try\n        {\n            call_inner_responder(name, name_len, instruction, instruction_len,\n                                 num_prompts, raw_prompts, raw_responses);\n        }\n        catch (...)\n        {\n            m_exception = boost::current_exception();\n        }\n    }\n\nprivate:\n    /**\n     * Merge any errors reported by libssh2 with any exception throw in\n     * the responder.\n     *\n     * Merging the two is non-trivial.  There are at least 9 scenarios\n     * that can arise:\n     * (1) Authentication was successful:\n     *    a) and the responder executed completely\n     *    b) despite the responder throwing an exception. Possible because\n     *       the exception just causes outstanding responses to be sent to\n     *       the server blank, and the server may be satisfied with these\n     *       blank responses.  There is no way to abort authentication via\n     *       the callback.\n     *       TODO: maybe modify libssh2 to allow aborting from the callback.\n     *    c) without needing to call the responder.  Scary.\n     * (2) Authentication positively rejected:\n     *    a) even though the responder executed completely. E.g. the user\n     *       gave the wrong response.\n     *    b) because the responder threw an exception and the server\n     *       rejected the (possibly partially-complete) responses.\n     *    c) without needing to call the responder, e.g. kb-interactive not\n     *       set up properly on the server (yes, this does actually happen,\n     *       we tested it - e.g the cygwin server).\n     * (3) Some other failure occurred:\n     *    a) even though the responder executed completely.\n     *    b) the responder threw an exception but the failure is unrelated\n     *       (because it's not possible to abort, it must be unrelated).\n     *    c) before needing to call the responder.\n     */\n    bool translate_status(const boost::system::error_code& ec,\n                          const std::string& message)\n    {\n        if (!ec)\n        {\n            // Situation (1) above.  Merge all three situations and just\n            // report the successful authentication.  Any exception thrown in\n            // the\n            // responder is ignored.\n            //\n            // XXX: There is a tricky use-case here. If a user cancels a\n            //      challenge-response prompt and that causes an exception,\n            //      the caller has no way to tell that the user cancelled if the\n            //      authentication nevertheless succeeded.  Arguably, that is\n            //      the correct behaviour as it is more important to know the\n            //      authentication state of the session than the user's\n            //      response.  An even better solution would be if we could\n            //      abort authentication from the callback but that may not be\n            //      possible.  RFC 4256 Section 3.4 says that sending the wrong\n            //      number of responses back must always result in failure, so\n            //      responding with zero replies might work ... unless the\n            //      server\n            //      sent zero prompts.\n            return true;\n        }\n        else if (ec == boost::system::errc::permission_denied)\n        {\n            // Situation (2) above.\n            // a) is a non-error failure.  In other words, the kind of\n            // failure that wouldn't be reported to the user with an error\n            // dialog. The most likely response to this result is to attempt\n            // authentication again.  It wouldn't be appropriate to report\n            // these failures as exceptions so, instead, we return false.\n            //\n            // b) and c) are both errors so we need to throw exceptions.\n            // We can only tell c) and a) apart by whether the responder\n            // was called so we have to wrap the given responder to record\n            // that information. For b) the most relevant exception is the\n            // one thrown by the wrapped responder which is also by this\n            // class.\n\n            if (!m_called)\n            {\n                // c)\n                assert(!m_exception);\n\n                BOOST_THROW_EXCEPTION(boost::system::system_error(ec, message));\n            }\n            else if (m_exception)\n            {\n                // b)\n                boost::rethrow_exception(*m_exception);\n            }\n            else\n            {\n                // a)\n                assert(m_called);\n                return false;\n            }\n        }\n        else\n        {\n            // Situation (3) above\n            BOOST_THROW_EXCEPTION(boost::system::system_error(ec, message));\n        }\n\n        // If the user cancels the operation, our callback should throw an\n        // E_ABORT exception which we catch here.\n    }\n\n    /**\n     * Unpacks the stashed responder.\n     */\n    template <typename ChallengeResponder>\n    static void dethunker(const char* name, int name_len,\n                          const char* instruction, int instruction_len,\n                          int num_prompts,\n                          const LIBSSH2_USERAUTH_KBDINT_PROMPT* raw_prompts,\n                          LIBSSH2_USERAUTH_KBDINT_RESPONSE* raw_responses,\n                          void** abstract) throw()\n    {\n        challenge_response_translator<ChallengeResponder>& responder =\n            *static_cast<challenge_response_translator<ChallengeResponder>*>(\n                *abstract);\n\n        responder(name, name_len, instruction, instruction_len, num_prompts,\n                  raw_prompts, raw_responses);\n    }\n\n    /**\n     * Do the two-way interface translation.\n     */\n    void call_inner_responder(const char* name, int name_len,\n                              const char* instruction, int instruction_len,\n                              int num_prompts,\n                              const LIBSSH2_USERAUTH_KBDINT_PROMPT* raw_prompts,\n                              LIBSSH2_USERAUTH_KBDINT_RESPONSE* raw_responses)\n    {\n        std::vector<std::pair<std::string, bool>> prompts;\n        boost::push_back(\n            prompts,\n            boost::iterator_range<const LIBSSH2_USERAUTH_KBDINT_PROMPT*>(\n                raw_prompts, raw_prompts + num_prompts) |\n                boost::adaptors::transformed(convert_prompt));\n\n        // Either the name or the instruction may be a NULL pointer as they\n        // are optional fields\n        std::string name_string =\n            name ? std::string(name, name_len) : std::string();\n        std::string instruction_string =\n            instruction ? std::string(instruction, instruction_len)\n                        : std::string();\n\n        boost::range::for_each(\n            boost::iterator_range<LIBSSH2_USERAUTH_KBDINT_RESPONSE*>(\n                raw_responses, raw_responses + num_prompts),\n            m_responder(name_string, instruction_string, prompts),\n            convert_response);\n    }\n\n    ChallengeResponder m_responder;\n    bool m_called;\n    boost::optional<boost::exception_ptr> m_exception;\n};\n}\n\n/**\n * An SSH session connected to a host.\n *\n * Sessions are non-copyable.   If copy semantics are required, use\n * a `shared_ptr<ssh::session>`.\n *\n * The session is disconnected from the server when the object is destroyed.\n *\n * Rationale\n * ---------\n * It is important that clients are able to guarantee that a session has been\n * disconnected at a particular point.  Because the underlying SSH session\n * cannot be meaningfully duplicated, making this class copyable would only\n * be possible by sharing the underlying SSH session between the copies.\n * This would mean that the session would only be disconnected when the last\n * copy is destroyed, which is harder to control.\n */\nclass session : private boost::noncopyable\n{\n    BOOST_MOVABLE_BUT_NOT_COPYABLE(session)\n\npublic:\n    /**\n     * Start a new SSH session with a host.\n     *\n     * The host is listening on the other end of the given socket.\n     *\n     * The constructor will throw an exception if it cannot connect to the host\n     * or negotiate an SSH session.  Therefore any instance of this class\n     * begins life successfully connected to the host.  Of course, the\n     * connection may break subsequently and the server is free to terminate\n     * the session at any time.\n     *\n     * @param socket\n     *     The socket through which to communicate with the listening server.\n     * @param disconnection_message\n     *     An optional message sent to the server when the session is\n     *     destroyed.\n     */\n    session(int socket, const std::string& disconnection_message =\n                            \"libssh2 C++ bindings session destructor\")\n        : m_session(new detail::session_state(socket, disconnection_message))\n    {\n    }\n\n    /**\n     * Move constructor.\n     */\n    session(BOOST_RV_REF(session) other)\n        : m_session(boost::move(other.m_session))\n    {\n    }\n\n    /**\n     * Move-assignment.\n     */\n    session& operator=(BOOST_RV_REF(session) other)\n    {\n        m_session = boost::move(other.m_session);\n        return *this;\n    }\n\n    /**\n     * Hostkey sent by the server to identify itself.\n     */\n    ssh::host_key hostkey()\n    {\n        return ssh::host_key(session_ref());\n    }\n\n    /**\n     * Names of the methods the server claims are available for\n     * authentication.\n     *\n     * The server is allowed to lie.\n     *\n     * An empty list does not necessarily mean no methods are available. It\n     * might mean that authentication has already succeeded or that no\n     * authentication was needed.  Calling this method has the side effect\n     * of authenticating the session in the latter case.\n     */\n    std::vector<std::string> authentication_methods(const std::string& username)\n    {\n        boost::system::error_code ec;\n        std::string message;\n\n        // Locking until we copy out the method string owned by the session.\n        // We don't want another thread inadvertently causing it to be\n        // overwritten While we're reading it.\n        detail::session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        const char* method_list = detail::libssh2::userauth::list(\n            session_ref().session_ptr(), username.data(), username.size(), ec,\n            message);\n\n        if (!method_list)\n        {\n            // Because the userauth list is fetched by trying to authenticate\n            // with method \"none\", a NULL return might mean that no\n            // authentication was needed. The error code disambiguates this\n            // from a true error.\n\n            if (!ec)\n            {\n                assert(authenticated());\n                return std::vector<std::string>();\n            }\n            else\n            {\n                BOOST_THROW_EXCEPTION(boost::system::system_error(ec, message));\n            }\n        }\n        else\n        {\n            std::vector<std::string> methods;\n            boost::split(methods, method_list, boost::is_any_of(\",\"));\n\n            return methods;\n        }\n    }\n\n    bool authenticated()\n    {\n        detail::session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        return ::libssh2_userauth_authenticated(session_ref().session_ptr()) !=\n               0;\n    }\n\n    /**\n     * Simple password authentication.\n     *\n     * @param username\n     *     UTF-8 string identifying the user to authenticate as.\n     * @param password\n     *     Password as a UTF-8 string.\n     *\n     * @returns\n     *     `true` if authentication successful, `false` if not.\n     *\n     * @throws `boost::system::system_error`\n     *     if unexpected failure while trying to authenticate.\n     *\n     * @todo  Handle password change callback.\n     */\n    bool authenticate_by_password(const std::string& username,\n                                  const std::string& password)\n    {\n        boost::system::error_code ec;\n        std::string message;\n\n        {\n            detail::session_state::scoped_lock lock =\n                session_ref().aquire_lock();\n\n            detail::libssh2::userauth::password(\n                session_ref().session_ptr(), username.data(), username.size(),\n                password.data(), password.size(), NULL, ec, message);\n        }\n\n        if (!ec)\n        {\n            return true;\n        }\n        else if (ec == boost::system::errc::permission_denied)\n        {\n            // The incorrect password failure is not reported as an exception\n            // because it is not exceptional.\n            return false;\n        }\n        else\n        {\n            BOOST_THROW_EXCEPTION(boost::system::system_error(ec, message));\n        }\n    }\n\n    /**\n     * Challenge-response authentication.\n     *\n     * This is also known as keyboard-interactive authentication.  The server\n     * challenges the user by requesting one or more pieces of information.\n     * Once the user has responded, the server may request more information\n     * any number of time until it is either satisfied and authenticates the\n     * user or refuses to do so.\n     *\n     * @param username\n     *     UTF-8 string identifying the user to authenticate as.\n     * @param responder\n     *     Callback to receive the challenges from the server and provide the\n     *     corresponding responses.\n     *     The callback must be a model of the `ChallengeResponder` concept.\n     *     That means it must be callable with a three arguments:\n     *      - a string giving the challenge title (may be empty),\n     *      - a string giving the challenge instructions (may be empty), and\n     *      - a range of zero or more prompts, each a pair whose first member\n     *        is the prompt text and whose second member is a boolean indicating\n     *        whether the response should be obscured like a password or made\n     *        visible.\n     *     The call must return a range of responses as strings, one for every\n     *     prompt in the same order as the prompts.\n     *\n     * @returns\n     *     `true` if authentication successful, `false` if the server positively\n     *      rejected the responses produced by the `responder` callback.\n     *\n     * @throws `boost::system::system_error`\n     *     if unexpected failure while trying to authenticate or if the server\n     *     positively rejects authentication without even calling the\n     *     `responder`.\n     * @throws user-defined-exception\n     *     if authentication fails because the `responder` threw an exception,\n     *     the exception is throw out of this method.\n     *\n     * @warning  The responder __must not__ call any code that uses the same\n     *           SSH session currently being authenticated.  Doing so results\n     *           in undefined behaviour (likely deadlock).\n     */\n    //\n    // We tried to use Boost.Concept here to verify the ChallengeResponder but\n    // gave up.  We struggled to do anything useful without BOOST_TYPEOF (which\n    // crashed MSVC) and it's not clear what the benefit of the concept would\n    // have been in any case.  It didn't make the requirements of the\n    // responder any more clear than reading the implementation of this\n    // function and I doubt the error messages were any better.\n    // Nevertheless, this might be worth having another go at in the future.\n    //\n    template <typename ChallengeResponder>\n    bool authenticate_interactively(const std::string& username,\n                                    ChallengeResponder responder)\n    {\n        // The libssh2 C API, of course, takes the callback as a plain-old\n        // static function.  The caller, however, may have passed us a callable\n        // object and we need to be able to call that instead.\n        //\n        // As is typical of good C APIs, libssh2 gives us a way to sneak a\n        // pointer to the callback object (or whatever it might be) through\n        // the static callback function via an 'abstract' parameter.\n        //\n        // We set the abstract via the session.  The static callback function\n        // receives that and converts it back to the callable object, which\n        // can then be called in the C++ way.\n        //\n        // As an extra twist in the story, we don't pass the responder directly\n        // in the abstract.  Instead we pass a version wrapped so that it can\n        // store exceptions encountered, which we rethrow afterwards.\n\n        detail::challenge_response_translator<ChallengeResponder>\n            wrapped_responder(responder);\n\n        // IMPORTANT: Locked from this point onwards until returning to the\n        // caller so that abstract is not overwritten by another thread\n        // before we pull the responder out of it later\n        detail::session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        *::libssh2_session_abstract(session_ref().session_ptr()) =\n            &wrapped_responder;\n\n        return wrapped_responder.do_challenge_response(\n            session_ref().session_ptr(), username);\n    }\n\n    /**\n     * Public-key authentication.\n     *\n     * This method requires a path to both the public and private keys because\n     * libssh2 does.  It should be possible to derive one from the other so\n     * when libssh2 supports this the method will take one fewer argument.\n     */\n    void authenticate_by_key_files(const std::string& username,\n                                   const boost::filesystem::path& public_key,\n                                   const boost::filesystem::path& private_key,\n                                   const std::string& passphrase)\n    {\n        detail::session_state::scoped_lock lock = session_ref().aquire_lock();\n\n        detail::libssh2::userauth::public_key_from_file(\n            session_ref().session_ptr(), username.data(), username.size(),\n            public_key.string().c_str(), private_key.string().c_str(),\n            passphrase.c_str());\n    }\n\n    /**\n     * Connect to any agent running on the system and return object to\n     * authenticate using its identities.\n     */\n    ::ssh::agent_identities agent_identities()\n    {\n        return ::ssh::agent_identities(session_ref());\n    }\n\n    /**\n     * Create a new connection to the remote filesystem over this SSH session.\n     *\n     * @warning It is the caller's responsibility to ensure the filesystem is\n     *          connection is shut down before the session is disconnected.\n     *          In other words, that the last moved-to destination of the\n     *          session outlives the last moved-to destination of the\n     *          filesystem.  If neither is moved, this is naturally the case.\n     */\n    filesystem::sftp_filesystem connect_to_filesystem()\n    {\n        return filesystem::sftp_filesystem::factory_attorney()(session_ref());\n    }\n\nprivate:\n    detail::session_state& session_ref()\n    {\n        return *m_session;\n    }\n\n    // Using an auto_ptr (eventually unique_ptr) so that the other objects\n    // that reference this state continue to reference a valid object even if\n    // this session object is moved.  The moved session will only move the\n    // pointer but the state will remain at the same address.\n    // Using a value member meant that moving the session, relocated the\n    // session state but the other objects don't get made aware of that.\n    // Result: crash.\n    // The other objects using this state include filesystem connections\n    // (and transitively directory iterators and file streams) and\n    // agent identity collections.\n    // See http://stackoverflow.com/a/20493410/67013.\n    std::auto_ptr<detail::session_state> m_session;\n};\n\n// C++11 swap has this implementation but we also support C++03\ninline void swap(session& lhs, session& rhs)\n{\n    session tmp(boost::move(lhs));\n    lhs = boost::move(rhs);\n    rhs = boost::move(tmp);\n}\n\n} // namespace ssh\n\n#endif\n"
  },
  {
    "path": "ssh/sftp_error.hpp",
    "content": "/**\n    @file\n\n    SFTP error reporting.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_SFTP_ERROR_HPP\n#define SSH_SFTP_ERROR_HPP\n\n#include <ssh/ssh_error.hpp> // last_error_code\n\n#include <boost/exception/errinfo_file_name.hpp> // errinfo_file_name\n#include <boost/exception/info.hpp>              // errinfo_api_function\n#include <boost/throw_exception.hpp>             // throw_exception\n\n#include <string>\n\n#include <libssh2_sftp.h> // LIBSSH2_FX_*, LIBSSH2_ERROR_SFTP_PROTOCOL,\n                          // libssh2_sftp_last_error\n\nnamespace ssh\n{\nnamespace filesystem\n{\n\ninline boost::system::error_category& sftp_error_category();\n\nnamespace detail\n{\n\n// Cutting LIBSSH2_ prefix off because the FX codes correspond to codes in\n// the spec, not just in the library\n\n#define SSH_CASE_SFTP_RETURN_STRINGISED(x)                                     \\\n    case LIBSSH2_##x:                                                          \\\n        return #x;\n\ninline std::string sftp_error_code_to_string(unsigned long code)\n{\n    switch (code)\n    {\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_OK);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_EOF);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NO_SUCH_FILE);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_PERMISSION_DENIED);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_FAILURE);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_BAD_MESSAGE);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NO_CONNECTION);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_CONNECTION_LOST);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_OP_UNSUPPORTED);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_INVALID_HANDLE);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NO_SUCH_PATH);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_FILE_ALREADY_EXISTS);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_WRITE_PROTECT);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NO_MEDIA);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NO_SPACE_ON_FILESYSTEM);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_QUOTA_EXCEEDED);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_UNKNOWN_PRINCIPAL);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_LOCK_CONFLICT);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_DIR_NOT_EMPTY);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_NOT_A_DIRECTORY);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_INVALID_FILENAME);\n        SSH_CASE_SFTP_RETURN_STRINGISED(FX_LINK_LOOP);\n    default:\n        assert(!\"Unknown code\");\n        return boost::lexical_cast<std::string>(code);\n    }\n}\n\n#undef SSH_CASE_SFTP_RETURN_STRINGISED\n\nclass _sftp_error_category : public boost::system::error_category\n{\n    typedef boost::system::error_category super;\n\npublic:\n    virtual const char* name() const\n    {\n        return \"sftp\";\n    }\n\n    virtual std::string message(int code) const\n    {\n        return sftp_error_code_to_string(code);\n    }\n\n    virtual boost::system::error_condition\n    default_error_condition(int code) const\n    {\n        switch (code)\n        {\n        case LIBSSH2_FX_NO_SUCH_FILE:\n            return boost::system::errc::no_such_file_or_directory;\n\n        case LIBSSH2_FX_FILE_ALREADY_EXISTS:\n            return boost::system::errc::file_exists;\n\n        case LIBSSH2_FX_OP_UNSUPPORTED:\n            return boost::system::errc::operation_not_supported;\n        default:\n            return this->super::default_error_condition(code);\n        }\n    }\n\n    virtual bool\n    equivalent(int code, const boost::system::error_condition& condition) const\n    {\n        // Any match with the code's default condition is equivalent. The\n        // switch below only needs to match _extra_ conditions that are\n        // also equivalent\n\n        if (condition == default_error_condition(code))\n        {\n            return true;\n        }\n\n        switch (code)\n        {\n        case LIBSSH2_FX_OP_UNSUPPORTED:\n            return condition == boost::system::errc::not_supported;\n        default:\n            return condition == default_error_condition(code);\n        }\n    }\n\nprivate:\n    _sftp_error_category()\n    {\n    }\n\n    friend boost::system::error_category&\n    ssh::filesystem::sftp_error_category();\n};\n}\n\ninline boost::system::error_category& sftp_error_category()\n{\n    // C++ standard says this instance is shared across all translation units\n    // http://stackoverflow.com/a/1389403/67013\n    static detail::_sftp_error_category instance;\n    return instance;\n}\n\nnamespace detail\n{\n\n/**\n * Last error encountered by the SFTP channel as an `error_code` and\n * optional error description message.\n */\ninline boost::system::error_code last_sftp_error_code(\n    LIBSSH2_SESSION* session, LIBSSH2_SFTP* sftp,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    // Failing libssh2_sftp_* functions can set an SSH error defined\n    // by the library or an SFTP error defined in the SFTP standard,\n    // in which case the SSH error will be LIBSSH2_ERROR_SFTP_PROTOCOL.\n    // This function checks which case it is and packages the error\n    // with the corresponding category.\n\n    boost::system::error_code error =\n        ::ssh::detail::last_error_code(session, e_msg);\n\n    if (error.value() == LIBSSH2_ERROR_SFTP_PROTOCOL)\n    {\n        error = boost::system::error_code(::libssh2_sftp_last_error(sftp),\n                                          sftp_error_category());\n    }\n\n    return error;\n}\n}\n}\n} // namespace ssh::filesystem\n\n#endif\n"
  },
  {
    "path": "ssh/ssh_error.hpp",
    "content": "/**\n    @file\n\n    SSH error reporting.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_SSH_ERROR_HPP\n#define SSH_SSH_ERROR_HPP\n\n#include <boost/exception/exception.hpp> // enable_error_info\n#include <boost/exception/error_info.hpp>\n#include <boost/exception/errinfo_file_name.hpp>\n#include <boost/exception/errinfo_api_function.hpp>\n#include <boost/exception/info.hpp> // errinfo_api_function\n#include <boost/throw_exception.hpp>\n#include <boost/lexical_cast.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/system/error_code.hpp>\n#include <boost/system/system_error.hpp>\n\n#include <cassert> // assert\n#include <string>\n\n#include <libssh2.h> // libssh2_session_last_error, libssh2_session_last_errno\n\nnamespace ssh\n{\n\ninline boost::system::error_category& ssh_error_category();\n\nnamespace detail\n{\n\n#define SSH_CASE_RETURN_STRINGISED(x)                                          \\\n    case x:                                                                    \\\n        return #x;\n\ninline std::string ssh_error_code_to_string(int code)\n{\n    switch (code)\n    {\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SOCKET_NONE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_BANNER_RECV);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_BANNER_SEND);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_INVALID_MAC);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_KEX_FAILURE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_ALLOC);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SOCKET_SEND);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_TIMEOUT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_HOSTKEY_INIT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_HOSTKEY_SIGN);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_DECRYPT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SOCKET_DISCONNECT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_PROTO);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_PASSWORD_EXPIRED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_FILE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_METHOD_NONE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_AUTHENTICATION_FAILED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_OUTOFORDER);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_FAILURE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_UNKNOWN);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_CLOSED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_CHANNEL_EOF_SENT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SCP_PROTOCOL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_ZLIB);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SOCKET_TIMEOUT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SFTP_PROTOCOL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_REQUEST_DENIED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_METHOD_NOT_SUPPORTED);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_INVAL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_INVALID_POLL_TYPE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_PUBLICKEY_PROTOCOL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_EAGAIN);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_BUFFER_TOO_SMALL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_BAD_USE);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_COMPRESS);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_OUT_OF_BOUNDARY);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_AGENT_PROTOCOL);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_SOCKET_RECV);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_ENCRYPT);\n        SSH_CASE_RETURN_STRINGISED(LIBSSH2_ERROR_BAD_SOCKET);\n    default:\n        assert(!\"Unknown code\");\n        return boost::lexical_cast<std::string>(code);\n    }\n}\n\n#undef SSH_CASE_RETURN_STRINGISED\n\nclass _ssh_error_category : public boost::system::error_category\n{\n    typedef boost::system::error_category super;\n\npublic:\n    const char* name() const\n    {\n        return \"ssh\";\n    }\n\n    std::string message(int code) const\n    {\n        return ssh_error_code_to_string(code);\n    }\n\n    virtual boost::system::error_condition\n    default_error_condition(int code) const\n    {\n        switch (code)\n        {\n        case LIBSSH2_ERROR_AUTHENTICATION_FAILED:\n            return boost::system::errc::permission_denied;\n\n        case LIBSSH2_ERROR_BUFFER_TOO_SMALL:\n            return boost::system::errc::no_buffer_space;\n\n        default:\n            return this->super::default_error_condition(code);\n        }\n    }\n\nprivate:\n    _ssh_error_category()\n    {\n    }\n\n    friend boost::system::error_category& ssh::ssh_error_category();\n};\n}\n\ninline boost::system::error_category& ssh_error_category()\n{\n    // C++ standard says this instance is shared across all translation units\n    // http://stackoverflow.com/a/1389403/67013\n    static detail::_ssh_error_category instance;\n    return instance;\n}\n\nnamespace detail\n{\n\n/**\n * Last error encountered by the session as an `error_code` and optional error\n * description message.\n */\ninline boost::system::error_code last_error_code(\n    LIBSSH2_SESSION* session,\n    boost::optional<std::string&> e_msg = boost::optional<std::string&>())\n{\n    int val = 0;\n\n    if (e_msg)\n    {\n        char* message_buf = NULL; // read-only reference\n        int message_len = 0;      // len not including NULL-term\n        val = ::libssh2_session_last_error(session, &message_buf, &message_len,\n                                           false);\n\n        *e_msg = std::string(message_buf, message_len);\n    }\n    else\n    {\n        val = ::libssh2_session_last_errno(session);\n    }\n\n    assert(val && \"throwing success!\");\n    return boost::system::error_code(val, ssh_error_category());\n}\n\n// Assumes given exception supports error info already.  We don't know the type\n// of the error-info-enabled exception.  This function means we don't need to as\n// it takes it as a template arg\ntemplate <typename Exception>\nBOOST_ATTRIBUTE_NORETURN inline void\nthrow_api_exception(Exception e, const char* current_function,\n                    const char* source_file, int source_line,\n                    const char* api_function, const char* path, size_t path_len)\n{\n    e << boost::errinfo_api_function(api_function)\n      << boost::throw_function(current_function)\n      << boost::throw_file(source_file) << boost::throw_line(source_line);\n\n    if (path && path_len > 0)\n    {\n        e << boost::errinfo_file_name(std::string(path, path_len));\n    }\n\n    boost::throw_exception(e);\n}\n\nBOOST_ATTRIBUTE_NORETURN inline void\nthrow_api_error_code(boost::system::error_code ec, const std::string& message,\n                     const char* current_function, const char* source_file,\n                     int source_line, const char* api_function,\n                     const char* path, size_t path_len)\n{\n    boost::system::system_error e = boost::system::system_error(ec, message);\n    throw_api_exception(boost::enable_error_info(e), current_function,\n                        source_file, source_line, api_function, path, path_len);\n}\n\n} // namespace detail\n\n} // namespace ssh\n\n/// @cond INTERNAL\n\n#define SSH_DETAIL_THROW_API_ERROR_CODE(ec, message, api_function)             \\\n    ::ssh::detail::throw_api_error_code(ec, message, BOOST_CURRENT_FUNCTION,   \\\n                                        __FILE__, __LINE__, api_function,      \\\n                                        NULL, 0)\n\n#define SSH_DETAIL_THROW_API_ERROR_CODE_WITH_PATH(ec, message, api_function,   \\\n                                                  path, path_len)              \\\n    ::ssh::detail::throw_api_error_code(ec, message, BOOST_CURRENT_FUNCTION,   \\\n                                        __FILE__, __LINE__, api_function,      \\\n                                        path, path_len)\n\n/// @endcond\n\n#endif\n"
  },
  {
    "path": "ssh/stream.hpp",
    "content": "/**\n    @file\n\n    SSH SFTP file streams.\n\n    @if license\n\n    Copyright (C) 2013, 2015  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SSH_STREAM_HPP\n#define SSH_STREAM_HPP\n\n#include <ssh/detail/file_handle_state.hpp>\n#include <ssh/detail/session_state.hpp>\n#include <ssh/detail/libssh2/sftp.hpp>\n#include <ssh/session.hpp>\n#include <ssh/filesystem.hpp>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/iostreams/categories.hpp> // seekable, input_seekable,\n                                          // output_seekable\n#include <boost/iostreams/stream.hpp>\n#include <boost/make_shared.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert>   // assert\n#include <stdexcept> // invalid_argument, logic_error\n#include <string>\n\n#include <libssh2_sftp.h>\n\nnamespace ssh\n{\nnamespace filesystem\n{\n\n/**\n * Flags defining how to open a file.\n *\n * Using this rather than `std::ios_base::openmode` to allow us to support\n * non-standard `nocreate` and `noreplace`, which correspond to SFTP file modes,\n * as well as eliminating `ate` and `binary` flags which we don't support.\n *\n * The meaning of the standard flags is the same as in\n * `std::ios_base::openmode`.\n */\nstruct openmode\n{\n    enum value\n    {\n        /**\n         * Open the file so that it is readable.\n         *\n         * The file must already exist unless `trunc` is also given in which\n         * case a new empty file is created with 0644 permissions.\n         */\n        in = std::ios_base::in,\n\n        /**\n         * Open the file so that it is writable.\n         *\n         * The file will be created if it does not already exist, unless `in` is\n         * also given without `trunc`.  If a new file is created it will empty\n         * and have 0644 permissions.\n         *\n         * If neither `in` not `app` are given, will truncate any existing file\n         * (i.e. will have same behaviour as if `trunc` had been given).\n         */\n        out = std::ios_base::out,\n\n        /**\n         * All writes to the file will append to the existing contents.\n         *\n         * This is more than just opening the file at the end as writes _cannot_\n         * modify earlier data even if the file is seeked to an earlier point.\n         *\n         * @warning This flag is not supported by common SFTP servers including\n         *          the ubiquitous OpenSSH making is pretty useless in practice.\n         */\n        app = std::ios_base::app,\n\n        /**\n         * Empties the file when opening it.\n         *\n         * `out` must also be specified for `trunc` to have any effect. `out`\n         * without `app` or `in` behaves as if `trunc` had been given, whether\n         * or not it is.\n         *\n         * @todo How does STL `fstream behave if `out` is not specified? Error?\n         *       Or just ignore it like we do?\n         */\n        trunc = std::ios_base::trunc,\n\n        /**\n         * Fail if the file does not already exist.\n         *\n         * `in` without `trunc` has this behaviour whether or not `nocreate` is\n         * given.\n         */\n        nocreate = 0x40,\n\n        /**\n         * Fail if the file already exists.\n         */\n        noreplace = 0x80\n    };\n};\n\ninline openmode::value operator|(openmode::value l, openmode::value r)\n{\n    return static_cast<openmode::value>(static_cast<int>(l) |\n                                        static_cast<int>(r));\n}\n\ninline openmode::value operator&(openmode::value l, openmode::value r)\n{\n    return static_cast<openmode::value>(static_cast<int>(l) &\n                                        static_cast<int>(r));\n}\n\ninline openmode::value operator^(openmode::value l, openmode::value r)\n{\n    return static_cast<openmode::value>(static_cast<int>(l) ^\n                                        static_cast<int>(r));\n}\n\ninline openmode::value&\noperator|=(openmode::value& l, openmode::value r)\n{\n    return l = l | r;\n}\n\ninline openmode::value& operator&=(openmode::value& l, openmode::value r)\n{\n    return l = l & r;\n}\n\ninline openmode::value& operator^=(openmode::value& l, openmode::value r)\n{\n    return l = l ^ r;\n}\n\n// No operator~ as that might not be safe in C++03 where cannot specify enum\n// underlying type\n// (see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3110.html)\n\nnamespace detail\n{\n\ninline openmode::value translate_flags(std::ios_base::openmode std_mode)\n{\n    openmode::value our_mode = openmode::value();\n\n    if (std_mode & std::ios_base::in)\n    {\n        our_mode |= openmode::in;\n    }\n\n    if (std_mode & std::ios_base::out)\n    {\n        our_mode |= openmode::out;\n    }\n\n    if (std_mode & std::ios_base::ate)\n    {\n        // TODO: support it. Should be simple, all we have to do it seek!\n        BOOST_THROW_EXCEPTION(\n            std::invalid_argument(\"ate flag not yet supported\"));\n    }\n\n    if (std_mode & std::ios_base::app)\n    {\n        our_mode |= openmode::app;\n    }\n\n    if (std_mode & std::ios_base::trunc)\n    {\n        our_mode |= openmode::trunc;\n    }\n\n    if (std_mode & std::ios_base::binary)\n    {\n        ; // do nothing. our streams are always binary\n    }\n\n    return our_mode;\n}\n\ninline long openmode_to_libssh2_flags(openmode::value opening_mode)\n{\n    long flags = 0;\n\n    if (opening_mode & openmode::in)\n    {\n        flags |= LIBSSH2_FXF_READ;\n    }\n\n    if (opening_mode & openmode::out)\n    {\n        flags |= LIBSSH2_FXF_WRITE;\n\n        if (opening_mode & openmode::in)\n        {\n            // in flag suppresses creation\n\n            if (opening_mode & openmode::trunc)\n            {\n                // but trunk flag unsuppresses it again\n\n                if (!(opening_mode & openmode::nocreate))\n                {\n                    // unless nocreate given in which case just truncate\n                    // existing\n                    flags |= LIBSSH2_FXF_CREAT;\n\n                    if (opening_mode & openmode::noreplace)\n                    {\n                        flags |= LIBSSH2_FXF_EXCL;\n                    }\n                }\n                else if (opening_mode & openmode::noreplace)\n                {\n                    BOOST_THROW_EXCEPTION(std::invalid_argument(\n                        \"Cannot combine nocreate and noreplace\"));\n                }\n\n                // XXX: According to SFTP spec, shouldn't be able to have TRUNC\n                // without CREAT but if it works, it works\n                flags |= LIBSSH2_FXF_TRUNC;\n            }\n        }\n        else\n        {\n            // Unlike the C and C++ file APIs, SFTP files opened only for\n            // writing are not created if they do not already exist and are not\n            // truncated if they do exists.  Therefore we explicitly add the\n            // CREAT and TRUNC flags to mirror the C++ fstream behaviour\n\n            if (!(opening_mode & openmode::nocreate))\n            {\n                flags |= LIBSSH2_FXF_CREAT;\n\n                if (opening_mode & openmode::noreplace)\n                {\n                    flags |= LIBSSH2_FXF_EXCL;\n                }\n            }\n            else if (opening_mode & openmode::noreplace)\n            {\n                BOOST_THROW_EXCEPTION(std::invalid_argument(\n                    \"Cannot combine nocreate and noreplace\"));\n            }\n\n            if (opening_mode & openmode::app)\n            {\n                flags |= LIBSSH2_FXF_APPEND;\n            }\n            else\n            {\n                // XXX: According to SFTP spec, shouldn't be able to have TRUNC\n                // without CREAT but if it works, it works\n                flags |= LIBSSH2_FXF_TRUNC;\n            }\n        }\n    }\n\n    return flags;\n}\n\ninline boost::shared_ptr<::ssh::detail::file_handle_state>\nopen_file(::ssh::detail::sftp_channel_state& sftp, const path& open_path,\n          openmode::value opening_mode)\n{\n    std::string path_string = open_path.native();\n\n    // Open with 644 permissions - good for non-directory files\n    return boost::make_shared<::ssh::detail::file_handle_state>(\n        boost::ref(sftp), // http://stackoverflow.com/a/1374266/67013\n        path_string.data(), path_string.size(),\n        openmode_to_libssh2_flags(opening_mode),\n        LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP |\n            LIBSSH2_SFTP_S_IROTH,\n        LIBSSH2_SFTP_OPENFILE);\n}\n\ninline boost::shared_ptr<::ssh::detail::file_handle_state>\nopen_input_file(::ssh::detail::sftp_channel_state& sftp, const path& open_path,\n                openmode::value opening_mode)\n{\n    // For input streams open files for input even if not given in open\n    // flags.  Matches standard library ifstream.\n\n    return open_file(sftp, open_path, opening_mode | openmode::in);\n}\n\ninline boost::shared_ptr<::ssh::detail::file_handle_state>\nopen_output_file(::ssh::detail::sftp_channel_state& sftp, const path& open_path,\n                 openmode::value opening_mode)\n{\n    // For output streams open files for output even if not given in open\n    // flags.  Matches standard library ofstream.\n\n    return open_file(sftp, open_path, static_cast<openmode::value>(\n                                          opening_mode | openmode::out));\n}\n\ninline boost::iostreams::stream_offset\nseek(::ssh::detail::file_handle_state& handle, const path& open_path,\n     boost::iostreams::stream_offset off, std::ios_base::seekdir way)\n{\n    boost::iostreams::stream_offset new_position = 0;\n\n    switch (way)\n    {\n    case std::ios_base::beg:\n        new_position = off;\n        break;\n\n    case std::ios_base::cur:\n    {\n        // FIXME: possible to get integer overflow on addition?\n        new_position = libssh2_sftp_tell64(handle.file_handle()) + off;\n        break;\n    }\n\n    case std::ios_base::end: // MUST ACCESS SERVER\n    {\n        LIBSSH2_SFTP_ATTRIBUTES attributes = LIBSSH2_SFTP_ATTRIBUTES();\n\n        try\n        {\n            ::ssh::detail::file_handle_state::scoped_lock lock =\n                handle.aquire_lock();\n\n            ::ssh::detail::libssh2::sftp::fstat(\n                handle.session_ptr(), handle.sftp_ptr(), handle.file_handle(),\n                &attributes, LIBSSH2_SFTP_STAT);\n        }\n        catch (boost::exception& e)\n        {\n            e << boost::errinfo_file_name(open_path.string());\n            throw;\n        }\n\n        new_position = attributes.filesize + off;\n        break;\n    }\n\n    default:\n        BOOST_THROW_EXCEPTION(std::invalid_argument(\"Unknown seek direction\"));\n    }\n\n    if (new_position < 0)\n    {\n        BOOST_THROW_EXCEPTION(\n            std::logic_error(\"Cannot seek before start of file\"));\n    }\n\n    libssh2_sftp_seek64(handle.file_handle(), new_position);\n\n    return new_position;\n}\n\ninline std::streamsize read(::ssh::detail::file_handle_state& handle,\n                            const path& open_path, char* buffer,\n                            std::streamsize buffer_size)\n{\n    try\n    {\n        // This method is only allowed to return a read count less than the\n        // requested read amount if the end-of-file has been reached.  In other\n        // words, non-blocking short reads are not allowed (see\n        // http://bit.ly/1ixEagu and http://bit.ly/1ejYm2T).  Therefore we loop\n        // until all the given buffer has been filled or we reach EOF.\n\n        ssize_t count = 0;\n        do\n        {\n            ::ssh::detail::file_handle_state::scoped_lock lock =\n                handle.aquire_lock();\n\n            ssize_t rc = ::ssh::detail::libssh2::sftp::read(\n                handle.session_ptr(), handle.sftp_ptr(), handle.file_handle(),\n                buffer + count, buffer_size - count);\n            if (rc == 0)\n                break; // EOF\n\n            count += rc;\n        } while (count < buffer_size);\n\n        return count;\n    }\n    catch (boost::exception& e)\n    {\n        e << boost::errinfo_file_name(open_path.string());\n        throw;\n    }\n}\n\ninline std::streamsize write(::ssh::detail::file_handle_state& handle,\n                             const path& open_path, const char* data,\n                             std::streamsize data_size)\n{\n    try\n    {\n        // Despite it's signature, this method is not allowed to return a\n        // written count less than the given write amount.  The signature is the\n        // way it is so that Boost.IOStreams devices can support non-blocking\n        // behaviour in the future, but we use our devices to implement STL\n        // streams which don't support non-blocking devices (see\n        // http://bit.ly/1ixEagu and http://bit.ly/1ejYm2T).  Therefore we loop\n        // until all data is written.\n\n        ssize_t count = 0;\n        do\n        {\n            ::ssh::detail::file_handle_state::scoped_lock lock =\n                handle.aquire_lock();\n\n            count += ::ssh::detail::libssh2::sftp::write(\n                handle.session_ptr(), handle.sftp_ptr(), handle.file_handle(),\n                data + count, data_size - count);\n        } while (count < data_size);\n\n        assert(count == data_size);\n\n        return count;\n    }\n    catch (boost::exception& e)\n    {\n        e << boost::errinfo_file_name(open_path.string());\n        throw;\n    }\n}\n\nconst std::streamsize DEFAULT_BUFFER_SIZE = 1024 * 32;\n\nstruct input_device_category : boost::iostreams::input_seekable,\n                               boost::iostreams::optimally_buffered_tag\n{\n};\n\nstruct output_device_category : boost::iostreams::output_seekable,\n                                boost::iostreams::optimally_buffered_tag\n{\n};\n\nstruct io_device_category : boost::iostreams::seekable,\n                            boost::iostreams::optimally_buffered_tag\n{\n};\n\n/**\n * Allows setting buffer size on boost::iostreams::stream based streams.\n *\n * `boost::iostreams::stream` only forwards three constructor arguments so this\n * class is necessary to pass up the buffer size argument to the device.\n */\ntemplate <typename Device>\nclass sftp_stream : public boost::iostreams::stream<Device>\n{\npublic:\n    // Using separate constructors rather than default arguments so they pick up\n    // the defaults from the devices\n\n    sftp_stream(sftp_filesystem& channel, const path& open_path)\n    {\n        open(Device(channel, open_path));\n    }\n\n    sftp_stream(sftp_filesystem& channel, const path& open_path,\n                openmode::value opening_mode)\n    {\n        open(Device(channel, open_path, opening_mode));\n    }\n\n    sftp_stream(sftp_filesystem& channel, const path& open_path,\n                openmode::value opening_mode, std::streamsize buffer_size)\n    {\n        open(Device(channel, open_path, opening_mode), buffer_size);\n    }\n\n    sftp_stream(sftp_filesystem& channel, const path& open_path,\n                std::ios_base::openmode opening_mode)\n    {\n        open(Device(channel, open_path, opening_mode));\n    }\n\n    sftp_stream(sftp_filesystem& channel, const path& open_path,\n                std::ios_base::openmode opening_mode,\n                std::streamsize buffer_size)\n    {\n        open(Device(channel, open_path, opening_mode), buffer_size);\n    }\n\n    // We pass the device to `open` rather than creating and passing it to the\n    // stream it in the initialiser list because of a subtle consequence of\n    // ios_base being a virtual base class (via virtual basic_ios) and\n    // ios_base::init having to be called before ios_base destructor.\n    //\n    // If we initialise boost::iostreams::stream in the list but sftp_io_device\n    // constructor throws an exception, we get an access violation because\n    // ios_base is already constructed (virtual bases constructed first\n    // irrespective of hierarchy) but the stream class constructor, which calls\n    // ios_base::init, is not yet called.  The exception prevents the stream\n    // class constructor being called but causes ios_base to be destroyed.\n};\n}\n\nclass sftp_input_device\n    : public boost::iostreams::device<detail::input_device_category>\n{\npublic:\n    sftp_input_device(sftp_filesystem& channel, const path& open_path,\n                      openmode::value opening_mode = openmode::in)\n        : m_open_path(open_path),\n          m_handle(detail::open_input_file(channel.sftp_ref(), m_open_path,\n                                           opening_mode))\n    {\n    }\n\n    sftp_input_device(sftp_filesystem& channel, const path& open_path,\n                      std::ios_base::openmode opening_mode)\n        : m_open_path(open_path),\n          m_handle(\n              detail::open_input_file(channel.sftp_ref(), m_open_path,\n                                      detail::translate_flags(opening_mode)))\n    {\n    }\n\n    std::streamsize optimal_buffer_size() const\n    {\n        return detail::DEFAULT_BUFFER_SIZE;\n    }\n\n    std::streamsize read(char* buffer, std::streamsize buffer_size)\n    {\n        return detail::read(*m_handle, m_open_path, buffer, buffer_size);\n    }\n\n    boost::iostreams::stream_offset seek(boost::iostreams::stream_offset off,\n                                         std::ios_base::seekdir way)\n    {\n        return detail::seek(*m_handle, m_open_path, off, way);\n    }\n\nprivate:\n    path m_open_path;\n    boost::shared_ptr<ssh::detail::file_handle_state> m_handle;\n};\n\n/**\n * Input file stream.\n *\n * File is opened according to `openmode` flags but always opened as if\n * `openmode::in` has been specified, regardless of whether it is.\n *\n * By default opened as if `openmode::in` is the only flag specified. File\n * always opened in binary mode.  SFTP does not have a text mode.\n */\ntypedef detail::sftp_stream<sftp_input_device> ifstream;\n\nclass sftp_output_device\n    : public boost::iostreams::device<detail::output_device_category>\n{\npublic:\n    sftp_output_device(sftp_filesystem& channel, const path& open_path,\n                       openmode::value opening_mode = openmode::out)\n        : m_open_path(open_path),\n          m_handle(detail::open_output_file(channel.sftp_ref(), m_open_path,\n                                            opening_mode))\n    {\n    }\n\n    sftp_output_device(sftp_filesystem& channel, const path& open_path,\n                       std::ios_base::openmode opening_mode)\n        : m_open_path(open_path),\n          m_handle(\n              detail::open_output_file(channel.sftp_ref(), m_open_path,\n                                       detail::translate_flags(opening_mode)))\n    {\n    }\n\n    std::streamsize optimal_buffer_size() const\n    {\n        return detail::DEFAULT_BUFFER_SIZE;\n    }\n\n    std::streamsize write(const char* data, std::streamsize data_size)\n    {\n        return detail::write(*m_handle, m_open_path, data, data_size);\n    }\n\n    boost::iostreams::stream_offset seek(boost::iostreams::stream_offset off,\n                                         std::ios_base::seekdir way)\n    {\n        return detail::seek(*m_handle, m_open_path, off, way);\n    }\n\nprivate:\n    path m_open_path;\n    boost::shared_ptr<::ssh::detail::file_handle_state> m_handle;\n};\n\n/**\n * Output file stream.\n *\n * File is opened according to `openmode` flags but always opened as if\n * `openmode::out` has been specified, regardless of whether it is.\n *\n * By default opened as if `openmode::out` is the only flag specified. File\n * always opened in binary mode.  SFTP does not have a text mode.\n */\ntypedef detail::sftp_stream<sftp_output_device> ofstream;\n\nclass sftp_io_device\n    : public boost::iostreams::device<detail::io_device_category>\n{\npublic:\n    sftp_io_device(sftp_filesystem& channel, const path& open_path,\n                   openmode::value opening_mode = openmode::in | openmode::out)\n        : m_open_path(open_path),\n          m_handle(\n              detail::open_file(channel.sftp_ref(), m_open_path, opening_mode))\n    {\n    }\n\n    sftp_io_device(sftp_filesystem& channel, const path& open_path,\n                   std::ios_base::openmode opening_mode)\n        : m_open_path(open_path),\n          m_handle(detail::open_file(channel.sftp_ref(), m_open_path,\n                                     detail::translate_flags(opening_mode)))\n    {\n    }\n\n    std::streamsize optimal_buffer_size() const\n    {\n        return detail::DEFAULT_BUFFER_SIZE;\n    }\n\n    std::streamsize read(char* buffer, std::streamsize buffer_size)\n    {\n        return detail::read(*m_handle, m_open_path, buffer, buffer_size);\n    }\n\n    std::streamsize write(const char* data, std::streamsize data_size)\n    {\n        return detail::write(*m_handle, m_open_path, data, data_size);\n    }\n\n    boost::iostreams::stream_offset seek(boost::iostreams::stream_offset off,\n                                         std::ios_base::seekdir way)\n    {\n        return detail::seek(*m_handle, m_open_path, off, way);\n    }\n\nprivate:\n    path m_open_path;\n    boost::shared_ptr<::ssh::detail::file_handle_state> m_handle;\n};\n\n/**\n * Input/output file stream.\n *\n * By default opened as if `openmode::in` and `openmode::out` are both\n * specified.\n *\n * File always opened in binary mode.  SFTP does not have a text mode.\n */\ntypedef detail::sftp_stream<sftp_io_device> fstream;\n}\n} // namespace ssh::filesystem\n\n#endif\n"
  },
  {
    "path": "swish/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software; you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation; either version 2 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin\n# Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nadd_subdirectory(connection)\nadd_subdirectory(drop_target)\nadd_subdirectory(forms)\nadd_subdirectory(frontend)\nadd_subdirectory(host_folder)\nadd_subdirectory(nse)\nadd_subdirectory(provider)\nadd_subdirectory(remote_folder)\nadd_subdirectory(shell)\nadd_subdirectory(shell_folder)\nadd_subdirectory(shell_folder/com_dll)\nadd_subdirectory(versions)\n"
  },
  {
    "path": "swish/CoFactory.hpp",
    "content": "/**\n    @file\n\n    Mixin class which gives CComObjects a creator of AddReffed instances.\n\n    @if license\n\n    Copyright (C) 2008, 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"atl.hpp\"      // Common ATL setup\n\nnamespace swish {\n\ntemplate <typename T>\nclass CCoFactory\n{\npublic:\n    /**\n     * Static factory method.\n     *\n     * This creator method provides a CComObject-based class with a way to \n     * create instances with exception-safe lifetimes.  The created \n     * instance is AddReffed, unlike those create by CreateInstance which \n     * have a reference count of 0.\n     *\n     * @returns  Smart pointer to the CComObject<T>-based COM object.\n     * @throws   com_error if creation fails.\n     */\n    static ATL::CComPtr<T> CreateCoObject() throw(...)\n    {\n        ATL::CComObject<T> *pObject = NULL;\n        HRESULT hr = ATL::CComObject<T>::CreateInstance(&pObject);\n        ATLENSURE_SUCCEEDED(hr);\n\n        return pObject;\n    }\n};\n\n}; // namespace swish\n"
  },
  {
    "path": "swish/atl.hpp",
    "content": "/**\n    @file\n\n    Set up ATL support.\n\n    @if license\n\n    Copyright (C) 2009, 2010, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n/**    \n * @file\n *\n * Any files that need ATL support should include this header to ensure\n * that ATL is set up consistently across different files and projects.\n *\n * This file must be included before any other ATL headers as they depend\n * on <atlbase.h> and <atlcom.h> already being included.  Also, any macros\n * in this file must be allowed to affect the behaviour of other parts of \n * ATL.  This is contrary to the usual top-down include order.\n */\n\n#pragma once\n\n#define _ATL_FREE_THREADED\n\n#ifdef _ATL_SINGLE_THREADED\n#error \"_ATL_SINGLE_THREADED conflicts with _ATL_FREE_THREADED\"\n#endif\n\n#ifdef _ATL_APARTMENT_THREADED\n#error \"_ATL_APARTMENT_THREADED conflicts with _ATL_FREE_THREADED\"\n#endif\n\n\n#define _ATL_NO_AUTOMATIC_NAMESPACE\n\n// Make some CString constructors explicit\n#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS\n\n// (U)LONGLONG CComVariant support\n#define _ATL_SUPPORT_VT_I8\n\n#ifdef _DEBUG\n//#define _ATL_DEBUG_QI\n//#define _ATL_DEBUG_INTERFACES\n#endif\n\n#define _ATL_CUSTOM_THROW\n\n#include <WinError.h> // HRESULT\n\n__declspec(noreturn) inline void AtlThrow(HRESULT hr);\n\n#include <atlbase.h> // base ATL classes\n#include <atlcom.h>\n\n#include <comet/error.h> // com_error\n\n/**\n * Custom ATL thrower which throws std::exception derived exceptions.\n */\n__declspec(noreturn) inline void AtlThrow(HRESULT hr)\n{\n    throw comet::com_error(hr);\n};\n"
  },
  {
    "path": "swish/connection/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  authenticated_session.cpp\n  connection_spec.cpp\n  running_session.cpp\n  session_manager.cpp\n  session_pool.cpp\n  authenticated_session.hpp\n  connection_spec.hpp\n  running_session.hpp\n  session_manager.hpp\n  session_pool.hpp)\n\nadd_library(connection ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(connection\n  PUBLIC ssh provider Washer::washer Comet::comet ${Boost_LIBRARIES})"
  },
  {
    "path": "swish/connection/authenticated_session.cpp",
    "content": "/**\n    @file\n\n    SSH session authentication.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"authenticated_session.hpp\"\n\n#include \"swish/utils.hpp\" // WideStringToUtf8String\n\n#include <ssh/knownhost.hpp> // openssh_knownhost_collection\n#include <ssh/session.hpp>\n#include <ssh/filesystem.hpp> // sftp_filesystem\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/bstr.h> // bstr_t\n#include <comet/error.h> // com_error\n\n#include <boost/filesystem.hpp> // path\n#include <boost/filesystem/fstream.hpp> // ofstream\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/function.hpp>\n#include <boost/move/move.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/system/error_code.hpp> // errc\n#include <boost/system/system_error.hpp>\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert>\n#include <exception>\n#include <stdexcept> // logic_error\n#include <string>\n#include <vector>\n\nusing swish::connection::authenticated_session;\nusing swish::connection::running_session;\nusing swish::utils::WideStringToUtf8String;\nusing swish::utils::home_directory;\n\nusing ssh::hexify;\nusing ssh::host_key;\nusing ssh::knownhost_search_result;\nusing ssh::openssh_knownhost_collection;\nusing ssh::session;\nusing ssh::filesystem::sftp_filesystem;\n\nusing comet::bstr_t;\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing boost::filesystem::path;\nusing boost::filesystem::ofstream;\nusing boost::function;\nusing boost::move;\nusing boost::mutex;\nusing boost::optional;\nnamespace errc = boost::system::errc;\nusing boost::system::system_error;\n\nusing std::exception;\nusing std::logic_error;\nusing std::pair;\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\n\nnamespace swish {\nnamespace connection {\n\nnamespace {\n\nconst path known_hosts_path =\n    home_directory<path>() / L\".ssh\" / L\"known_hosts\";\n\nvoid verify_host_key(\n    const wstring& host, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    assert(consumer);\n\n    ssh::session& sess(session.get_session());\n\n    string utf8_host = WideStringToUtf8String(host);\n\n    host_key key = sess.hostkey();\n    bstr_t hostkey_algorithm = key.algorithm_name();\n    bstr_t hostkey_hash = hexify(key.md5_hash());\n\n    assert(!hostkey_hash.empty());\n    assert(!hostkey_algorithm.empty());\n    \n    // YUK YUK YUK: Accessing and modifying host key files should not be\n    // here.  It should be done by the callback.\n\n    // make sure known_hosts file exists\n    create_directories(known_hosts_path.parent_path());\n    ofstream(known_hosts_path, std::ios::app);\n\n    openssh_knownhost_collection hosts(known_hosts_path);\n\n    knownhost_search_result result = hosts.find(utf8_host, key);\n    if (result.mismatch())\n    {\n        HRESULT hr = consumer->OnHostkeyMismatch(\n            bstr_t(host).in(), hostkey_hash.in(), hostkey_algorithm.in());\n        if (hr == S_OK)\n        {\n            update(hosts, utf8_host, key, result); // update known_hosts\n            hosts.save(known_hosts_path);\n        }\n        else if (hr == S_FALSE)\n            return; // continue but don't add\n        else\n            BOOST_THROW_EXCEPTION(\n                com_error(\"User aborted on host key mismatch\", E_ABORT));\n    }\n    else if (result.not_found())\n    {\n        HRESULT hr = consumer->OnHostkeyUnknown(\n            bstr_t(host).in(), hostkey_hash.in(), hostkey_algorithm.in());\n        if (hr == S_OK)\n        {\n            add(hosts, utf8_host, key); // add to known_hosts\n            hosts.save(known_hosts_path);\n        }\n        else if (hr == S_FALSE)\n            return; // continue but don't add\n        else\n            BOOST_THROW_EXCEPTION(\n                com_error(\"User aborted on unknown host key\", E_ABORT));\n    }\n}\n\nBOOST_SCOPED_ENUM_START(authentication_result)\n{\n    authenticated,\n    aborted,\n    try_remaining_methods\n};\nBOOST_SCOPED_ENUM_END\n\n/**\n * Authenticates with remote host by asking the user to supply a password.\n *\n * This uses the callback to the SftpConsumer to obtain the password from\n * the user.  If the password is wrong or other error occurs, the user is\n * asked for the password again.  This repeats until the user supplies a \n * correct password or cancels the request.\n *\n * @throws `std::exception`\n *     if authentication fails for an unexpected reason, in other words,\n *     a reason other than the user cancelling the authentication.\n *\n * @returns\n *     `authenticated` if authentication was successful,\n *     `aborted` if the user aborted early.\n * @note that unsuccessful is not a return value as the function keeps\n *       re-prompting until successful or cancelled.\n */\nBOOST_SCOPED_ENUM(authentication_result) password_authentication(\n    const string& utf8_username, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    // Loop until successfully authenticated or user cancels\n    for(;;)\n    {\n        optional<wstring> password = consumer->prompt_for_password();\n        if (!password)\n        {\n            return authentication_result::aborted;\n        }\n        \n        string utf8_password = WideStringToUtf8String(*password);\n        if (session.get_session().authenticate_by_password(\n            utf8_username, utf8_password))\n        {\n            return authentication_result::authenticated;\n        }\n\n        // TODO: handle password change callback here\n    }\n}\n\nnamespace {\n\n    class user_aborted_authentication : \n        public virtual boost::exception, public std::runtime_error\n    {\n    public:\n        user_aborted_authentication() :\n          boost::exception(), std::runtime_error(\"User aborted authentication\")\n          {}\n    };\n\n    /**\n     * Delegates challenge-response to a consumer.\n     */\n    class consumer_responder\n    {\n    public:\n\n        consumer_responder(com_ptr<ISftpConsumer> consumer)\n            : m_consumer(consumer) {}\n\n        template<typename PromptRange>\n        vector<string> operator()(\n            const string& title, const string& instructions,\n            const PromptRange& prompts)\n        {\n            optional<vector<string>> responses =\n                m_consumer->challenge_response(title, instructions, prompts);\n            if (responses)\n            {\n                return *responses;\n            }\n            else\n            {\n                BOOST_THROW_EXCEPTION(user_aborted_authentication());\n            }\n        }\n\n    private:\n        com_ptr<ISftpConsumer> m_consumer;\n    };\n}\n\n/**\n * Authenticates with remote host by challenge-response interaction.\n *\n * This uses the ISftpConsumer callback to challenge the user for various\n * pieces of information (usually just their password).\n *\n * @returns\n *     `authenticated` if authentication successful,\n *     `aborted` if the `consumer` reports that the user aborted authentication,\n *     `try_remaining_methods` is authentication failed in a way that makes\n *     sense to not give up completely: i.e. if the server positively rejects\n *     authentication without even calling the responder.\n *\n * @throws `boost::system::system_error`\n *     if unexpected SSH-related failure while trying to authenticate or \n * @throws `std::exception`\n *     if authentication fails for an unexpected reason, in other words,\n *     a reason other than the user cancelling the authentication.\n *     If authentication fails because the `consumer` threw an exception,\n *     that exception will be the one throw out of this method.\n * @note that unsuccessful authentication is not a return value as the function\n *     keeps re-prompting until successful or cancelled.\n */\nBOOST_SCOPED_ENUM(authentication_result) keyboard_interactive_authentication(\n    const string& utf8_username, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    // Loop until successfully authenticated or user cancels.\n    try\n    {\n        while (!session.get_session().authenticate_interactively(\n            utf8_username, consumer_responder(consumer))) {}\n    }\n    catch (const system_error& e)\n    {\n        if (e.code() == errc::permission_denied)\n        {\n            // Authentication was positively rejected by the server but not\n            // because of anything our responder did (which would have simply\n            // caused the while loop to end above).  This is most likely the\n            // server lying about supporting kb-int authentication.\n            // Cygwin OpenSSH does this.\n            //\n            // Although an error, we choose to silently ignore this one and\n            // move on to try other authentication methods.\n\n            return authentication_result::try_remaining_methods;\n        }\n        else\n        {\n            throw;\n        }\n    }\n    catch (const user_aborted_authentication&)\n    {\n        // Unlike simple password authentication, the user cancelling an\n        // interactive authentication isn't signalled by the return code\n        // because interactive authentications can't actually be aborted.\n        // Instead we find out about an abortion when authentication fails and\n        // the responder threw an exception.  Therefore we catch our custom\n        // \"user aborted\" exception here and translate that into the boolean\n        // result.\n        return authentication_result::aborted;\n    }\n\n    assert(session.get_session().authenticated()); // Double-check\n    return authentication_result::authenticated;\n}\n\n\nBOOST_SCOPED_ENUM(authentication_result) public_key_file_based_authentication(\n    const string& utf8_username, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    assert(consumer);\n\n    optional<pair<path, path>> key_files = consumer->key_files();\n    if (key_files)\n    {\n        // TODO: unlock public key using passphrase\n        session.get_session().authenticate_by_key_files(\n            utf8_username, key_files->second, key_files->first, \"\");\n\n        assert(session.get_session().authenticated());\n\n        return authentication_result::authenticated;\n    }\n    else\n    {\n        return authentication_result::try_remaining_methods;\n    }\n}\n\nBOOST_SCOPED_ENUM(authentication_result) public_key_agent_authentication(\n    const string& utf8_username, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    try\n    {\n        BOOST_FOREACH(\n            ssh::identity key, session.get_session().agent_identities())\n        {\n            try\n            {\n                key.authenticate(utf8_username);\n                return authentication_result::authenticated;\n            }\n            catch (const exception&)\n            { /* Ignore and try the next */ }\n        }\n    }\n    catch(const exception&)\n    { /* No agent running probably.  Either way, give up. */ }\n\n    // None of the agent identities worked.  Sob. Back to passwords then.\n    return authentication_result::try_remaining_methods;\n}\n\n/**\n * Tries to authenticate the user with the remote server.\n *\n * The remote server is queried for which authentication methods it supports\n * and these are tried one at time until one succeeds in the order:\n * public-key, keyboard-interactive, plain password.\n *\n * @throws com_error if authentication fails:\n * - E_ABORT if user cancelled the operation (via ISftpConsumer)\n * - E_FAIL otherwise\n */\nvoid authenticate_user(\n    const wstring& user, running_session& session,\n    com_ptr<ISftpConsumer> consumer)\n{\n    assert(!user.empty());\n    assert(user[0] != '\\0');\n    string utf8_username = WideStringToUtf8String(user);\n\n    vector<string> method_names =\n        session.get_session().authentication_methods(utf8_username);\n\n    // This test must come _after_ fetching the methods as that is what may\n    // prompt the premature authentication\n    if (session.get_session().authenticated())\n    {\n        // Golly.  What a silly server.\n        return;\n    }\n    else if (method_names.empty())\n    {\n        BOOST_THROW_EXCEPTION(\n            std::exception(\"No supported authentication methods found\"));\n    }\n\n    typedef function<\n        BOOST_SCOPED_ENUM(authentication_result)(\n            const string&, running_session&, com_ptr<ISftpConsumer>)>\n        method;\n\n    vector<method> authentication_methods;\n\n    // The order of adding the methods is important; some are preferred over\n    // others.  Added in descending order of preference.\n    if (find(method_names.begin(), method_names.end(), \"publickey\") != method_names.end())\n    {\n        // This old way is only kept around to support the tests.  Its almost\n        // useless for anything else as we don't pass the 'consumer' enough\n        // information to identify which key to use.\n        authentication_methods.push_back(public_key_file_based_authentication);\n\n        // And now the nice new way using agents.\n        authentication_methods.push_back(public_key_agent_authentication);\n    }\n\n    if (find(method_names.begin(), method_names.end(), \"keyboard-interactive\")\n        != method_names.end())\n    {\n        authentication_methods.push_back(keyboard_interactive_authentication);\n    }\n\n    if (find(method_names.begin(), method_names.end(), \"password\") != method_names.end())\n    {\n        authentication_methods.push_back(password_authentication);\n    }\n\n    BOOST_FOREACH(method& auth_attempt, authentication_methods)\n    {\n        switch (auth_attempt(utf8_username, session, consumer))\n        {\n        case authentication_result::authenticated:\n            return;\n\n        case authentication_result::aborted:\n            BOOST_THROW_EXCEPTION(\n                com_error(\"User aborted authentication\", E_ABORT));\n\n        case authentication_result::try_remaining_methods:\n            continue;\n\n        default:\n            BOOST_THROW_EXCEPTION(\n                logic_error(\"Unrecognised authentication result\"));\n        }\n    }\n\n    BOOST_THROW_EXCEPTION(\n        com_error(\"No authentication method succeeded\", E_FAIL));\n}\n\nrunning_session create_and_authenticate(\n    const wstring& host, unsigned int port, const wstring& user,\n    com_ptr<ISftpConsumer> consumer)\n{\n    running_session session(host, port);\n\n    verify_host_key(host, session, consumer);\n    // Legal to fail here, e.g. user refused to accept host key\n\n    authenticate_user(user, session, consumer);\n    // Legal to fail here, e.g. wrong password/key\n\n    assert(session.get_session().authenticated());\n\n    return move(session);\n}\n\n}\n\nauthenticated_session::authenticated_session(\n    const wstring& host, unsigned int port, const wstring& user,\n    com_ptr<ISftpConsumer> consumer)\n    :\nm_session(create_and_authenticate(host, port, user, consumer)),\nm_filesystem(m_session.get_session().connect_to_filesystem()) {}\n\nauthenticated_session::authenticated_session(\n    BOOST_RV_REF(authenticated_session) other)\n:\nm_session(move(other.m_session)), m_filesystem(move(other.m_filesystem)) {}\n\nauthenticated_session& authenticated_session::operator=(\n    BOOST_RV_REF(authenticated_session) other)\n{\n    swap(authenticated_session(move(other)), *this);\n    return *this;\n}\n\nsession& authenticated_session::get_session()\n{\n    return m_session.get_session();\n}\n\nsftp_filesystem& authenticated_session::get_sftp_filesystem()\n{\n    return m_filesystem;\n}\n\nbool authenticated_session::is_dead()\n{\n   return m_session.is_dead();\n}\n\nvoid swap(authenticated_session& lhs, authenticated_session& rhs)\n{\n    boost::swap(lhs.m_session, rhs.m_session);\n    boost::swap(lhs.m_filesystem, rhs.m_filesystem);\n}\n\n}} // namespace swish::connection"
  },
  {
    "path": "swish/connection/authenticated_session.hpp",
    "content": "/**\n    @file\n\n    SSH session authentication.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_AUTHENTICATED_SESSION_HPP\n#define SWISH_CONNECTION_AUTHENTICATED_SESSION_HPP\n\n#include \"swish/connection/running_session.hpp\"\n#include \"swish/provider/sftp_provider.hpp\" // ISftpConsumer\n\n#include <ssh/session.hpp>\n#include <ssh/filesystem.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/move/move.hpp> // BOOST_RV_REF, BOOST_MOVABLE_BUT_NOT_COPYABLE\n#include <boost/noncopyable.hpp>\n#include <boost/thread/mutex.hpp>\n\n#include <string>\n\nnamespace swish {\nnamespace connection {\n\n/**\n * SSH session authenticated with the server.\n *\n * The point of this class is remove uncertainty as to whether the session \n * is usable.  Every instance is successfully authenticated with the server\n * and has a running SFTP channel.\n *\n * XXX: Maybe the SFTP channel part should be separated.  It's unclear if Swish\n * ever needs the two concepts separately.\n */\nclass authenticated_session : private boost::noncopyable\n{\n    BOOST_MOVABLE_BUT_NOT_COPYABLE(authenticated_session)\n\npublic:\n\n    /*\n    template<typename Authentication>\n    authenticated_session(\n        const std::wstring& host, const std::wstring& user,\n        unsigned int port, Authentication& authentication) :\n        m_session(host, port)\n    {\n        boost::mutex::scoped_lock(m_session.aquire_lock());\n\n        ssh::session session = m_session.get_session();\n        authentication.approve_host_key(session.host_key());\n\n    }\n    */\n\n    /**\n     * Creates and authenticates an SSH session and start SFTP channel.\n     *\n     * @param host\n     *     Name of the remote host to connect the session to.\n     * @param port\n     *    Port on the remote host to connect to.\n     * @param user\n     *     User to authenticate as.\n     * @param consumer\n     *    Callback used for user-interaction needed to authenticate, such as\n     *    requesting a password.\n     *\n     * @throws com_error if any part of this process fails:\n     * - E_ABORT if user cancelled the operation (via ISftpConsumer)\n     * - E_FAIL otherwise\n     */\n    authenticated_session(\n        const std::wstring& host, unsigned int port, const std::wstring& user,\n        comet::com_ptr<ISftpConsumer> consumer);\n\n    /**\n     * Move constructor.\n     */\n    authenticated_session(BOOST_RV_REF(authenticated_session) other);\n\n    /**\n     * Move assignment.\n     */\n    authenticated_session& operator=(BOOST_RV_REF(authenticated_session) other);\n\n    bool is_dead();\n\n    // This class really represents an SFTP channel rather than an\n    // authenticated session.  Clients only use the session accessors\n    // below to report errors and this will be replaced by the wrapper\n    // sftp code which handles this internally.  Therefore we will be able\n    // to remove these accessors from the public interface.\n    ssh::session& get_session();\n\n    ssh::filesystem::sftp_filesystem& get_sftp_filesystem();\n\n    friend void swap(authenticated_session& lhs, authenticated_session& rhs);\n\nprivate:\n    running_session m_session;\n    ssh::filesystem::sftp_filesystem m_filesystem;\n};\n\n}} // namespace swish::connection\n\n#endif\n"
  },
  {
    "path": "swish/connection/connection.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<VisualStudioProject\n\tProjectType=\"Visual C++\"\n\tVersion=\"8.00\"\n\tName=\"connection\"\n\tProjectGUID=\"{4795C72C-1ED4-4900-87F6-DB18602D7EF4}\"\n\tRootNamespace=\"connection\"\n\t>\n\t<Platforms>\n\t\t<Platform\n\t\t\tName=\"Win32\"\n\t\t/>\n\t\t<Platform\n\t\t\tName=\"x64\"\n\t\t/>\n\t</Platforms>\n\t<ToolFiles>\n\t</ToolFiles>\n\t<Configurations>\n\t\t<Configuration\n\t\t\tName=\"Debug|Win32\"\n\t\t\tConfigurationType=\"4\"\n\t\t\tInheritedPropertySheets=\"..\\..\\build\\boost_client.vsprops;..\\..\\build\\swish-Debug-Win32.vsprops;..\\..\\build\\libssh2_client.vsprops\"\n\t\t\tCharacterSet=\"2\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLibrarianTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t\t<Configuration\n\t\t\tName=\"Debug|x64\"\n\t\t\tConfigurationType=\"4\"\n\t\t\tInheritedPropertySheets=\"..\\..\\build\\boost_client.vsprops;..\\..\\build\\swish-Debug-x64.vsprops;..\\..\\build\\libssh2_client.vsprops\"\n\t\t\tCharacterSet=\"2\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLibrarianTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t\t<Configuration\n\t\t\tName=\"Release|Win32\"\n\t\t\tConfigurationType=\"4\"\n\t\t\tInheritedPropertySheets=\"..\\..\\build\\boost_client.vsprops;..\\..\\build\\swish-Release-Win32.vsprops;..\\..\\build\\libssh2_client.vsprops\"\n\t\t\tCharacterSet=\"2\"\n\t\t\tWholeProgramOptimization=\"1\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLibrarianTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t\t<Configuration\n\t\t\tName=\"Release|x64\"\n\t\t\tConfigurationType=\"4\"\n\t\t\tInheritedPropertySheets=\"..\\..\\build\\boost_client.vsprops;..\\..\\build\\swish-Release-x64.vsprops;..\\..\\build\\libssh2_client.vsprops\"\n\t\t\tCharacterSet=\"2\"\n\t\t\tWholeProgramOptimization=\"1\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLibrarianTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t</Configurations>\n\t<References>\n\t\t<ProjectReference\n\t\t\tReferencedProjectIdentifier=\"{057B464F-AFD9-47AA-B2C2-D6F88A9EF27E}\"\n\t\t\tRelativePathToProject=\".\\thirdparty\\libssh2.vcproj\"\n\t\t/>\n\t</References>\n\t<Files>\n\t\t<Filter\n\t\t\tName=\"Source Files\"\n\t\t\tFilter=\"cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx\"\n\t\t\tUniqueIdentifier=\"{4FC737F1-C7A5-4376-A066-2A32D752A2FF}\"\n\t\t\t>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\authenticated_session.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\connection_spec.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\"..\\pch.cpp\"\n\t\t\t\t>\n\t\t\t\t<FileConfiguration\n\t\t\t\t\tName=\"Debug|Win32\"\n\t\t\t\t\t>\n\t\t\t\t\t<Tool\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\n\t\t\t\t\t/>\n\t\t\t\t</FileConfiguration>\n\t\t\t\t<FileConfiguration\n\t\t\t\t\tName=\"Debug|x64\"\n\t\t\t\t\t>\n\t\t\t\t\t<Tool\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\n\t\t\t\t\t/>\n\t\t\t\t</FileConfiguration>\n\t\t\t\t<FileConfiguration\n\t\t\t\t\tName=\"Release|Win32\"\n\t\t\t\t\t>\n\t\t\t\t\t<Tool\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\n\t\t\t\t\t/>\n\t\t\t\t</FileConfiguration>\n\t\t\t\t<FileConfiguration\n\t\t\t\t\tName=\"Release|x64\"\n\t\t\t\t\t>\n\t\t\t\t\t<Tool\n\t\t\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\t\t\tUsePrecompiledHeader=\"1\"\n\t\t\t\t\t/>\n\t\t\t\t</FileConfiguration>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\running_session.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\session_manager.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\session_pool.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t</Filter>\n\t\t<Filter\n\t\t\tName=\"Header Files\"\n\t\t\tFilter=\"h;hpp;hxx;hm;inl;inc;xsd\"\n\t\t\tUniqueIdentifier=\"{93995380-89BD-4b04-88EB-625FBE52EBFB}\"\n\t\t\t>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\authenticated_session.hpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\connection_spec.hpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\running_session.hpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\session_manager.hpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\session_pool.hpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t</Filter>\n\t</Files>\n\t<Globals>\n\t</Globals>\n</VisualStudioProject>\n"
  },
  {
    "path": "swish/connection/connection_spec.cpp",
    "content": "/**\n    @file\n\n    Specify a connection.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"connection_spec.hpp\"\n\n#include \"swish/connection/authenticated_session.hpp\"\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/tuple/tuple.hpp> // tie\n#include <boost/tuple/tuple_comparison.hpp> // <\n\n#include <stdexcept> // invalid_argument\n\nusing comet::com_ptr;\n\nusing boost::tie;\n\nusing std::invalid_argument;\nusing std::wstring;\n\n\nnamespace swish {\nnamespace connection {\n\nconnection_spec::connection_spec(\n    const wstring& host, const wstring& user, const int port)\n: m_host(host), m_user(user), m_port(port)\n{\n    if (host.empty())\n        BOOST_THROW_EXCEPTION(invalid_argument(\"Host name required\"));\n    if (user.empty())\n        BOOST_THROW_EXCEPTION(invalid_argument(\"User name required\"));\n}\n\nauthenticated_session connection_spec::create_session(\n    com_ptr<ISftpConsumer> consumer) const\n{\n    return authenticated_session(m_host, m_port, m_user, consumer);\n}\n\nbool connection_spec::operator<(const connection_spec& other) const\n{\n    // Reusing comparison from tuples - no point reinventing the wheel\n    // See: http://stackoverflow.com/q/6218812/67013\n    return tie(m_host, m_user, m_port) <\n        tie(other.m_host, other.m_user, other.m_port);\n}\n\n}} // namespace swish::connection\n"
  },
  {
    "path": "swish/connection/connection_spec.hpp",
    "content": "/**\n    @file\n\n    Specify a connection.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_CONNECTION_SPEC_HPP\n#define SWISH_CONNECTION_CONNECTION_SPEC_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // ISftpConsumer\n\n#include <comet/ptr.h> // com_ptr\n\n#include <string>\n\nnamespace swish {\nnamespace connection {\n\n// Forward-declaring this here, rather than including the header, prevents\n// test/remote_folder/remote_commands_test.cpp from crashing the compiler by\n// letting that file not include the ssh_error.hpp file that uses lexical_cast\n// and through there.  I don't know why.  Just go with it.\nclass authenticated_session;\n\n/**\n * Represents specification for a connection to an SFTP server.\n *\n * Instances of this class are just recipes for connecting, they are *not*\n * the running connections themselves.  Running connections are called\n * sessions and can be created and queried via this class.\n */\nclass connection_spec\n{\npublic:\n\n    connection_spec(\n        const std::wstring& host, const std::wstring& user, int port);\n\n    /**\n     * Returns a new SFTP session based on this specification.\n     *\n     * The returned session is authenticated ready for use.  Any\n     * interaction needed to authenticate is performed via the `consumer`\n     * callback.\n     */\n    authenticated_session create_session(\n        comet::com_ptr<ISftpConsumer> consumer) const;\n\n    bool operator<(const connection_spec& other) const;\n\nprivate:\n    std::wstring m_host;\n    std::wstring m_user;\n    int m_port;\n};\n\n/**\n * Interface for connection making logic.\n *\n * Connection strategy is not uniform.  Sometime we want to establish a running\n * connection and pass that into an object so that it can use it at will.\n * Other times we want to the connection to be established---an activity that\n * may disturb the user with dialogues---as late as possible just before it\n * will be used.\n *\n * This interface abstracts such decisions behind a uniform way to request a\n * connection.\n */\n/*class connection_maker\n{\npublic:\n    virtual ~connection_maker() = 0;\n    virtual boost::shared_ptr<swish::provider::sftp_provider> provider() = 0;\n    virtual comet::com_ptr<ISftpConsumer> consumer() = 0;\n};*/\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/connection/interruptable_session.hpp",
    "content": "/**\n    @file\n\n    A session that can die mid-way through an operation.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_INTERRUPTABLE_SESSION_HPP\n#define SWISH_CONNECTION_INTERRUPTABLE_SESSION_HPP\n\n#include \"swish/connection/connection_spec.hpp\"\n\n#include <ssh/session.hpp>\n\n#include <memory> // auto_ptr\n\nnamespace swish {\nnamespace connection {\n\nclass interruptable_session\n{\npublic:\n    ssh::host_key hostkey() const;\n\n    std::vector<std::string> authentication_methods(\n        const std::string& username);\n\n    bool authenticated() const;\n\n    bool authenticate_by_password(\n        const std::string& username, const std::string& password);\n\n    template<typename ChallengeResponder>\n    bool authenticate_interactively(\n        const std::string& username, ChallengeResponder responder)\n    {\n        return m_session->authenticate_interactively(username, responder);\n    }\n\n    void authenticate_by_key_files(\n        const std::string& username, const boost::filesystem::path& public_key,\n        const boost::filesystem::path& private_key,\n        const std::string& passphrase);\n\n    ssh::agent_identities agent_identities();\n\n    ssh::filesystem::sftp_filesystem connect_to_filesystem();\n\n    /**\n     * Forcibly disconnect the session.\n     *\n     * Causes all future uses of the object to throw exceptions.\n     */\n    void terminate();\n\nprivate:\n    std::auto_ptr<ssh::session> m_session;\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/connection/running_session.cpp",
    "content": "// Copyright 2008, 2009, 2010, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"running_session.hpp\"\n\n#include \"swish/remotelimits.h\"\n#include \"swish/debug.hpp\"           // Debug macros\n#include \"swish/port_conversion.hpp\" // port_to_string\n#include \"swish/utils.hpp\"           // WideStringToUtf8String\n\n#include <ssh/session.hpp>\n#include <ssh/filesystem.hpp> // sftp_filesystem\n\n#include <boost/asio/ip/tcp.hpp> // Boost sockets: only used for name resolving\n#include <boost/move/move.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert>\n#include <string>\n\nusing swish::port_to_string;\nusing swish::utils::WideStringToUtf8String;\n\nusing ssh::session;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::asio::error::host_not_found;\nusing boost::asio::io_service;\nusing boost::asio::ip::tcp;\nusing boost::move;\nusing boost::shared_ptr;\nusing boost::system::get_system_category;\nusing boost::system::system_error;\nusing boost::system::error_code;\n\nusing std::string;\nusing std::wstring;\n\nnamespace swish\n{\nnamespace connection\n{\n\nnamespace\n{\n\n/**\n * Connect a socket to the given port on the given host.\n *\n * @throws  A boost::system::system_error if there is a failure.\n */\nvoid connect_socket_to_host(tcp::socket& socket, const wstring& host,\n                            unsigned int port, io_service& io)\n{\n    assert(!host.empty());\n    assert(host[0] != L'\\0');\n\n    // Convert host address to a UTF-8 string\n    string host_name = WideStringToUtf8String(host);\n\n    tcp::resolver resolver(io);\n    typedef tcp::resolver::query Lookup;\n    Lookup query(host_name, port_to_string(port));\n\n    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);\n    tcp::resolver::iterator end;\n\n    error_code error = host_not_found;\n    while (error && endpoint_iterator != end)\n    {\n        socket.close();\n        socket.connect(*endpoint_iterator++, error);\n    }\n    if (error)\n        BOOST_THROW_EXCEPTION(system_error(error));\n}\n\n// We have to have this weird function because Boost.ASIO doesn't\n// support Boost.Move rvalue-emulation.\n//\n// Ideally, connect_socket_to_host would return the connected sockets\n// so could be used in the running_session initialiser list below.  And\n// the the initialiser for m_session would have a valid socket to use.\n//\n// But as we can't return the valid socket, we have to connect it *during*\n// the m_session initialisation, *after* the m_socket initialisation. Yuk!\nssh::session session_on_socket(tcp::socket& socket, const wstring& host,\n                               unsigned int port, io_service& io,\n                               const string& disconnection_message)\n{\n    connect_socket_to_host(socket, host, port, io);\n    return ssh::session(socket.native(), disconnection_message);\n}\n}\n\nrunning_session::running_session(const wstring& host, unsigned int port)\n    : m_io(new io_service(0)),\n      m_socket(new tcp::socket(*m_io)),\n      m_session(session_on_socket(*m_socket, host, port, *m_io,\n                                  \"Swish says goodbye.\"))\n{\n}\n\nrunning_session::running_session(BOOST_RV_REF(running_session) other)\n    : m_io(move(other.m_io)),\n      m_socket(move(other.m_socket)),\n      m_session(move(other.m_session))\n{\n}\n\nrunning_session& running_session::operator=(BOOST_RV_REF(running_session) other)\n{\n    swap(running_session(move(other)), *this);\n    return *this;\n}\n\nsession& running_session::get_session()\n{\n    return m_session;\n}\n\nbool running_session::is_dead()\n{\n    fd_set socket_set;\n    FD_ZERO(&socket_set);\n    FD_SET(m_socket->native(), &socket_set);\n    TIMEVAL tv = TIMEVAL();\n\n    int rc = ::select(1, &socket_set, NULL, NULL, &tv);\n    if (rc < 0)\n        BOOST_THROW_EXCEPTION(\n            system_error(::WSAGetLastError(), get_system_category()));\n    return rc != 0;\n}\n\nvoid swap(running_session& lhs, running_session& rhs)\n{\n    boost::swap(lhs.m_io, rhs.m_io);\n    boost::swap(lhs.m_socket, rhs.m_socket);\n    boost::swap(lhs.m_session, rhs.m_session);\n}\n}\n} // namespace swish::connection\n"
  },
  {
    "path": "swish/connection/running_session.hpp",
    "content": "/**\n    @file\n\n    C++ wrapper round Libssh2 SSH and SFTP session creation.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_RUNNING_SESSION_HPP\n#define SWISH_CONNECTION_RUNNING_SESSION_HPP\n\n#include <ssh/session.hpp>\n\n#include <boost/asio/ip/tcp.hpp> // Boost sockets\n#include <boost/move/move.hpp> // BOOST_RV_REF, BOOST_MOVABLE_BUT_NOT_COPYABLE\n#include <boost/noncopyable.hpp>\n\n#include <memory> // auto_ptr\n#include <string>\n\nnamespace swish {\nnamespace connection {\n\n/**\n * An SSH session connected to a port on a server.\n *\n * The session may or may not be authenticated.\n *\n * The point of this class is to add host resolution and death detection\n * to the existing libssh2 C++ binding session object.\n */\nclass running_session : private boost::noncopyable\n{\n    BOOST_MOVABLE_BUT_NOT_COPYABLE(running_session)\n\npublic:\n\n    /**\n     * Connect to host server and start new SSH connection on given port.\n     */\n    running_session(const std::wstring& host, unsigned int port);\n\n    /**\n     * Move constructor.\n     */\n    running_session(BOOST_RV_REF(running_session) other);\n\n    /**\n     * Move assignment.\n     */\n    running_session& operator=(BOOST_RV_REF(running_session) other);\n\n    /**\n     * Has the connection broken since we connected?\n     *\n     * This only gives the correct answer as long as we're not expecting data\n     * to arrive on the socket. select()ing a silent socket should return 0. \n     * If it doesn't, it indicates that the connection is broken.\n     *\n     * XXX: we could double-check this by reading from the socket.  It would return\n     *      0 if the socket is closed.\n     *\n     * @see http://www.libssh2.org/mail/libssh2-devel-archive-2010-07/0050.shtml\n     */\n    bool is_dead();\n\n    ssh::session& get_session();\n\n    friend void swap(running_session& lhs, running_session& rhs);\n\nprivate:\n\n    // Must use auto_ptr for these members to make our class movable because\n    // Boost.ASIO doesn't support move emulation\n\n    std::auto_ptr<boost::asio::io_service> m_io;\n    ///< Boost IO system\n\n    std::auto_ptr<boost::asio::ip::tcp::socket> m_socket;\n    ///< TCP/IP socket to remote host\n\n    ssh::session m_session;\n    ///< libssh2 session\n};\n\n}} // namespace swish::connection\n\n#endif\n"
  },
  {
    "path": "swish/connection/session_manager.cpp",
    "content": "/**\n    @file\n\n    Reservation system for sessions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"session_manager.hpp\"\n\n#include \"swish/connection/session_pool.hpp\"\n\n#include <boost/bind.hpp>\n#include <boost/date_time/posix_time/posix_time_duration.hpp> // seconds\n#include <boost/function.hpp>\n#include <boost/range/adaptor/transformed.hpp>\n#include <boost/thread/condition_variable.hpp>\n#include <boost/thread/mutex.hpp>\n#include <boost/thread/once.hpp> // call_once\n#include <boost/uuid/random_generator.hpp>\n\n#include <map>\n#include <memory> // auto_ptr\n#include <list>\n#include <vector>\n\nusing comet::com_ptr;\n\nusing boost::adaptors::transformed;\nusing boost::bind;\nusing boost::call_once;\nusing boost::condition_variable;\nusing boost::function;\nusing boost::mutex;\nusing boost::noncopyable;\nusing boost::once_flag;\nusing boost::posix_time::seconds;\nusing boost::uuids::random_generator;\nusing boost::uuids::uuid;\n\nusing std::auto_ptr;\nusing std::list;\nusing std::map;\nusing std::string;\nusing std::vector;\n\nnamespace swish {\nnamespace connection {\n\nnamespace {\n\nclass task_registration\n{\npublic:\n    // We tag the registration with a UUID because it, and its copies, must\n    // be uniquely identifiable.  The task name is not enough as many tasks\n    // make share a name.  We can't just use the object address, though,\n    // because copies must be equal.\n    task_registration(\n        const string& task_name, const connection_spec& specification)\n        : m_tag(random_generator()()), m_task_name(task_name),\n          m_specification(specification) {}\n\n    // Copies take same tag\n\n    bool operator==(const task_registration& other) const\n    {\n        return m_tag == other.m_tag;\n    }\n\n    string name() const\n    {\n        return m_task_name;\n    }\n\n    // So that unregistering doesn't have to search all unrelated connection's\n    // tasks to find the matching task ID\n    connection_spec specification() const\n    {\n        return m_specification;\n    }\n\nprivate:\n    uuid m_tag;\n    string m_task_name;\n    connection_spec m_specification;\n};\n\n\n}\n\n// Hides the implementation details from the session_manager.hpp file.\nclass session_reservation_impl : private boost::noncopyable\n{\npublic:\n\n    session_reservation_impl(\n        authenticated_session& session, const function<void()>& unreserve)\n        :\n    m_session(session), m_unreserve(unreserve) {}\n\n    ~session_reservation_impl()\n    {\n        m_unreserve();\n    }\n\n    authenticated_session& session()\n    {\n        return m_session;\n    }\n\nprivate:\n\n    authenticated_session& m_session;\n    function<void()> m_unreserve;\n};\n\nnamespace {\n\n// Purpose: to maintain the book of reservations in an orderly\n// fashion.  This means cleaning out entries for old connection_specs that\n// don't have any more tasks\nclass reservations_ledger\n{\n    typedef map<connection_spec, list<task_registration>> reservations_mapping;\n\npublic:\n\n    void new_reservation(\n        const connection_spec& specification, const task_registration& task)\n    {\n        m_reservations[specification].push_back(task);\n    }\n\n    vector<task_registration> reservations_for_connection(\n        const connection_spec& specification) const\n    {\n        reservations_mapping::const_iterator pos =\n            m_reservations.find(specification);\n\n        if (pos == m_reservations.end())\n        {\n            return vector<task_registration>();\n        }\n        else\n        {\n            return vector<task_registration>(\n                pos->second.begin(), pos->second.end());\n        }\n    }\n\n    void unreserve(const task_registration& task)\n    {\n        list<task_registration>& connection_registrations =\n            m_reservations[task.specification()];\n\n        connection_registrations.remove(task);\n\n        // To stop us building up a map full of empty lists for connections\n        // no longer in use, we remove the connection entry once it has\n        // no more tasks\n        if (connection_registrations.empty())\n        {\n            m_reservations.erase(m_reservations.find(task.specification()));\n        }\n    }\n\nprivate:\n\n    reservations_mapping m_reservations;\n};\n\nstring extract_task_name(const task_registration& task)\n{\n    return task.name();\n}\n\n// Hides the implementation details from the session_manager.hpp file.\nclass session_manager_impl\n{\npublic:\n\n    bool has_session(const connection_spec& specification) const\n    {\n        return session_pool().has_session(specification);\n    }\n\n    session_reservation reserve_session(\n        connection_spec specification, com_ptr<ISftpConsumer> consumer,\n        const std::string& task_name)\n    {\n        task_registration task_id(task_name, specification);\n\n        // Locking just before getting the session from the pool to make sure\n        // another thread can't disconnect it just as we are about to become\n        // first and only reservation (if there were other reservations \n        // already, it couldn't get disconnected regardless)\n        mutex::scoped_lock lock(m_reservations_guard);\n\n        authenticated_session& session =\n            session_pool().pooled_session(specification, consumer);\n\n        m_reservations.new_reservation(specification, task_id);\n\n        m_reservations_changed.notify_all();\n\n        return session_reservation(\n            new session_reservation_impl(\n                session,\n                bind(&session_manager_impl::unreserve_session, this, task_id)));\n    }\n\n    void disconnect_session(\n        const connection_spec& specification,\n        session_manager::progress_callback notification_sink)\n    {\n        // Lock here so that no new reservations can be made once we've decided\n        // to disconnect this one, until we disconnect it\n\n        // Although we lock reservations of ALL sessions, not just this one,\n        // it's not a big problem because we quickly unlock them if waiting\n        // for tasks to unreserve this one.  If not waiting for tasks,\n        // disconnecting the session is quick so also not a problem in practice.\n        mutex::scoped_lock lock(m_reservations_guard);\n\n        bool proceed_with_disconnection = wait_for_remaining_uses(\n            specification, notification_sink, lock);\n\n        if (proceed_with_disconnection)\n        {\n            session_pool().remove_session(specification);\n        }\n    }\n\nprivate:\n\n    bool wait_for_remaining_uses(\n        const connection_spec& specification,\n        session_manager::progress_callback notification_sink,\n        mutex::scoped_lock& lock)\n    {\n        while (true)\n        {\n            vector<task_registration> reservations = \n                m_reservations.reservations_for_connection(specification);\n\n            if (reservations.empty())\n            {\n                // We notify the callback that tasks have completed so it can\n                // shut down any progress UI.\n                // Ideally, we would use a separate no-argument overload for\n                // this, but that requires some way to overload\n                // boost::functions. Basically, we need full type erasure\n                notification_sink(vector<string>());\n                return true;\n            }\n            // The callback controls whether we continue waiting or whether\n            // we abort so that the user's UI isn't blocked\n            else if (notification_sink(\n                reservations | transformed(extract_task_name)))\n            {\n                // It is important to use a timed wait because we need to\n                // respond to cancellation promptly.\n                // If we used a regular wait we would only consult the user\n                // callback, and notice that the user had cancelled, when the\n                // number of tasks waiting changed.  This may be infrequent.\n                // For a single long-running task, that would be the same as\n                // preventing the user cancelling at all.\n\n                // It is important that we wait using a lock on the same mutex\n                // as the thread changing the reservations.  If there is only\n                // one reservation and it goes away because its task completes\n                // on another thread, that thread must not be able to try\n                // and notify us of the change between where we check for\n                // empty reservations (above) and where we wait for empty\n                // reservations (below).  If that could happen, the wait would\n                // have missed the final notification of end-of-reservations.\n                // (see http://stackoverflow.com/a/6924160/67013).\n                //\n                // It's not a fatal problem, because the wait uses a\n                // timeout, but we should still avoid it.\n\n                m_reservations_changed.timed_wait(\n                    lock, boost::posix_time::seconds(3));\n            }\n            else\n            {\n                return false;\n            }\n        }\n    }\n\n    // Used by session_registration to unregister the session when that\n    // ticket object goes out of scope\n    void unreserve_session(const task_registration& task_id)\n    {\n        mutex::scoped_lock lock(m_reservations_guard);\n\n        m_reservations.unreserve(task_id);\n    }\n\n    session_manager_impl() {};\n\n    mutex m_reservations_guard;\n    reservations_ledger m_reservations;\n    condition_variable m_reservations_changed;\n\npublic:\n\n    static session_manager_impl& get()\n    {\n        call_once(m_initialise_once, do_init);\n        return *m_instance;\n    }\n\n    static void do_init()\n    {\n        m_instance.reset(new session_manager_impl());\n    }\n\n    static once_flag m_initialise_once;\n    static auto_ptr<session_manager_impl> m_instance;\n};\n\n\nonce_flag session_manager_impl::m_initialise_once;\nauto_ptr<session_manager_impl> session_manager_impl::m_instance;\n\n}\n\nsession_reservation::session_reservation(session_reservation_impl* pimpl)\n:\nm_pimpl(pimpl) {}\n\nsession_reservation::session_reservation(\n    BOOST_RV_REF(session_reservation) other) : m_pimpl(other.m_pimpl)\n{\n    other.m_pimpl = NULL;\n}\n\nsession_reservation& session_reservation::operator=(\n    BOOST_RV_REF(session_reservation) other)\n{\n    if (&other != this)\n    {\n        delete m_pimpl;\n        m_pimpl = other.m_pimpl;\n        other.m_pimpl = NULL;\n    }\n\n    return *this;\n}\n\nsession_reservation::~session_reservation()\n{\n    delete m_pimpl;\n}\n\nauthenticated_session& session_reservation::session()\n{\n    return m_pimpl->session();\n}\n\nsession_reservation session_manager::reserve_session(\n    const connection_spec& specification, com_ptr<ISftpConsumer> consumer,\n    const string& task_name)\n{\n    return session_manager_impl::get().reserve_session(\n        specification, consumer, task_name);\n}\n\nbool session_manager::has_session(const connection_spec& specification)\n{\n    return session_manager_impl::get().has_session(specification);\n}\n\nvoid session_manager::disconnect_session(\n    const connection_spec& specification, progress_callback notification_sink)\n{\n    return session_manager_impl::get().disconnect_session(\n        specification, notification_sink);\n}\n\n}}"
  },
  {
    "path": "swish/connection/session_manager.hpp",
    "content": "/**\n    @file\n\n    Reservation system for sessions.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_SESSION_FACTORY_HPP\n#define SWISH_CONNECTION_SESSION_FACTORY_HPP\n\n#include \"swish/connection/authenticated_session.hpp\"\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/provider/sftp_provider.hpp\" // ISftpConsumer\n\n#include <comet/ptr.h>\n\n#include <boost/function.hpp>\n#include <boost/move/move.hpp> // BOOST_RV_REF, BOOST_MOVABLE_BUT_NOT_COPYABLE\n#include <boost/noncopyable.hpp>\n#include <boost/range/any_range.hpp>\n\n#include <string>\n\nnamespace swish {\nnamespace connection {\n\nclass session_reservation_impl;\n\n/**\n * Ticket that prevents a session being disconnected.\n *\n * A caller may use a session if-and-only-if they a ticket for it.\n * Using a session without a ticket may lead to the session being\n * destroyed at an unexpected moment and is undefined behaviour.\n */\nclass session_reservation : private boost::noncopyable\n{\n    BOOST_MOVABLE_BUT_NOT_COPYABLE(session_reservation)\n\npublic:\n\n    /**\n     * Move constructor.\n     */\n    session_reservation(BOOST_RV_REF(session_reservation) other);\n\n    /**\n     * Move-assignment.\n     */\n    session_reservation& operator=(BOOST_RV_REF(session_reservation) other);\n\n    /**\n     * Releases session reservation.\n     */\n    ~session_reservation();\n\n    /**\n     * Returns reference to reserved session.\n     *\n     * Only guaranteed valid for the lifetime of this reservation (or any\n     * moved-to destinations).  The reference must not be stored for use\n     * after this reservation is destroyed.\n     */\n    authenticated_session& session();\n\n    session_reservation(session_reservation_impl* pimpl);\n\nprivate:\n\n    session_reservation_impl* m_pimpl;\n};\n\n// ALL Swish sessions (except in unit tests) must be created through this\n// factory to register their interest so that the disconnection code knows\n// which, if any, tasks are preventing disconnection.\n\nclass session_manager\n{\npublic:\n    typedef boost::any_range<\n        std::string, boost::forward_traversal_tag, std::string,\n        std::ptrdiff_t> task_name_range;\n\n    typedef boost::function<bool(const task_name_range&)> progress_callback;\n\n    /**\n     * Register interest in a session.\n     *\n     * Caller receives a ticket containing a reference to the session.  The\n     * session cannot be disconnected until the ticket is destroyed so callers\n     * should hold tickets for the minimum amount of time.\n     *\n     * The session and any objects it creates are only valid for the lifetime\n     * of the ticket.  The caller must not hold a reference to the session or\n     * its createes after the reservation is destroyed as call to \n     * `disconnect_session` will disconnect and destroy the session.  Any \n     * subsequent uses of those references would cause a crash.\n     */\n    session_reservation reserve_session(\n        const connection_spec& specification,\n        comet::com_ptr<ISftpConsumer> consumer,\n        const std::string& task_name);\n\n    /**\n     * Is a connection with the given specification already connected?\n     *\n     * Indicates whether the session matches one already running or whether\n     * the session would need to to be created anew, should the caller decide to\n     * call `reserve_session`.\n     */\n    bool has_session(const connection_spec& specification);\n\n    /**\n     * Disconnect and destroy the session matching the specification.\n     *\n     * If tasks have reserved the session, the call will block until they\n     * all give up their tickets.  The `notification_sink` will be called:\n     * - initially, with the names of the pending tasks\n     * - again, each time a pending task gives up its reservation\n     * - with an empty range when there are no more (or never were any) pending\n     *   tasks\n     */\n    void disconnect_session(\n        const connection_spec& specification,\n        progress_callback notification_sink);\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/connection/session_pool.cpp",
    "content": "/**\n    @file\n\n    Pool of reusuable SFTP connections.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2013, 2014\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"session_pool.hpp\"\n\n// Using ptr_map because move-aware map isn't usable with C++03\n#include <boost/ptr_container/ptr_map.hpp>\n//#include <boost/container/map.hpp> // move-aware map\n#include <boost/thread/mutex.hpp>\n#include <boost/thread/once.hpp> // call_once\n\n#include <memory> // auto_ptr\n\nusing swish::provider::sftp_provider;\n\nusing comet::com_ptr;\n\nusing boost::call_once;\nusing boost::container::map;\nusing boost::mutex;\nusing boost::once_flag;\n\nusing std::auto_ptr;\n\n\nnamespace swish {\nnamespace connection {\n\nnamespace {\n\n/**\n * Hides the implementation details from the session_pool.hpp file.\n */\nclass session_pool_impl\n{\n    // Using ptr_map because move-aware map isn't usable with C++03\n    // (http://bit.ly/1jP9BDL, https://svn.boost.org/trac/boost/ticket/6618)\n    typedef boost::ptr_map<connection_spec, authenticated_session>\n        pool_mapping;\n    //typedef boost::container::map<connection_spec, authenticated_session>\n    //    pool_mapping;\n\npublic:\n\n    static session_pool_impl& get()\n    {\n        call_once(m_initialise_once, do_init);\n        return *m_instance;\n    }\n\n    static void destroy()\n    {\n        m_instance.reset();\n    }\n\n    authenticated_session& pooled_session(\n        connection_spec specification, com_ptr<ISftpConsumer> consumer)\n    {\n        mutex::scoped_lock lock(m_session_pool_guard);\n\n        pool_mapping::iterator session = m_sessions.find(specification);\n\n        if (session != m_sessions.end())\n        {\n            // Dead sessions are replaced in the pool so that we always serve\n            // something usable\n\n            if (session->second->is_dead())\n            {\n                m_sessions.replace(\n                    session,\n                    new authenticated_session(\n                        specification.create_session(consumer)));\n            }\n        }\n        else\n        {\n            session = m_sessions.insert(\n                specification,\n                new authenticated_session(\n                    specification.create_session(consumer))).first;\n        }\n\n        return *(session->second);\n    }\n\n    bool has_session(const connection_spec& specification) const\n    {\n        mutex::scoped_lock lock(m_session_pool_guard);\n\n        return m_sessions.find(specification) != m_sessions.end();\n    }\n\n    void remove_session(const connection_spec& specification)\n    {\n        mutex::scoped_lock lock(m_session_pool_guard);\n\n        m_sessions.erase(specification);\n    }\n\n\nprivate:\n\n    session_pool_impl() {};\n\n    static void do_init()\n    {\n        m_instance.reset(new session_pool_impl);\n    }\n\n    static once_flag m_initialise_once;\n    static auto_ptr<session_pool_impl> m_instance;\n\n    mutable mutex m_session_pool_guard;\n    pool_mapping m_sessions;\n};\n\n\nonce_flag session_pool_impl::m_initialise_once;\nauto_ptr<session_pool_impl> session_pool_impl::m_instance;\n\n}\n\n\nauthenticated_session& session_pool::pooled_session(\n    const connection_spec& specification, com_ptr<ISftpConsumer> consumer)\n{\n    return session_pool_impl::get().pooled_session(specification, consumer);\n}\n\nvoid session_pool::destroy()\n{\n    return session_pool_impl::destroy();\n}\n\nbool session_pool::has_session(const connection_spec& specification) const\n{\n    return session_pool_impl::get().has_session(specification);\n}\n\nvoid session_pool::remove_session(const connection_spec& specification)\n{\n    return session_pool_impl::get().remove_session(specification);\n}\n\n}} // namespace swish::connection\n"
  },
  {
    "path": "swish/connection/session_pool.hpp",
    "content": "/**\n    @file\n\n    Pool of reusable SFTP connections.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_CONNECTION_SESSION_POOL_HPP\n#define SWISH_CONNECTION_SESSION_POOL_HPP\n#pragma once\n\n#include \"swish/connection/authenticated_session.hpp\"\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/provider/sftp_provider.hpp\" // ISftpConsumer\n\n#include <comet/ptr.h> // com_ptr\n\n#include <string>\n\nnamespace swish {\nnamespace connection {\n\n/**\n * Per-process pool of sessions.\n *\n * All instances of this class share the same pool of sessions.\n */\nclass session_pool\n{\npublic:\n\n    /**\n     * Returns a running SFTP session based on the given specification.\n     * \n     * If an appropriate SFTP session already exists in the pool,\n     * that connection is reused.  Otherwise a new one is created, and added\n     * to the pool.\n     *\n     * The returned session is authenticated ready for use.  Any\n     * interaction needed to authenticate is performed via the `consumer`\n     * callback.\n     */\n    authenticated_session& pooled_session(\n        const connection_spec& specification,\n        comet::com_ptr<ISftpConsumer> consumer);\n    \n    /**\n     * Is a connection with the given specification in the pool?\n     *\n     * Indicates whether the session matches one already running or whether\n     * the session would need to to be created anew, should the caller decide to\n     * call pooled_session().\n     */\n    bool has_session(const connection_spec& specification) const;\n\n    /**\n     * Remove the specified session from the pool.\n     */\n    void remove_session(const connection_spec& specification);\n\n    /**\n     * Destroy the singleton pool.\n     */\n    void destroy();\n};\n\n}} // namespace swish::connection\n\n#endif\n"
  },
  {
    "path": "swish/debug.hpp",
    "content": "/**\n    @file\n\n    Debug macros.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include <ComDef.h> // For _com_error\n\n#define TRACE(msg, ...) ATLTRACE(msg ## \"\\n\", __VA_ARGS__)\n#define FUNCTION_TRACE TRACE(__FUNCTION__\" called\");\n#define METHOD_TRACE TRACE(__FUNCTION__\" called (this=%p)\", this);\n\n#ifdef _DEBUG\n#define REPORT(expr) \\\ndo { \\\n    LPVOID lpMsgBuf; \\\n    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \\\n        NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \\\n        (LPTSTR) &lpMsgBuf, 0, NULL ); \\\n    _ASSERT_EXPR((expr), (LPTSTR)lpMsgBuf); LocalFree(lpMsgBuf); \\\n} while(0)\n#else\n#define REPORT(expr) (expr)\n#endif\n\n#ifdef UNREACHABLE\n#undef UNREACHABLE\n#endif\n#ifdef _DEBUG\n#define UNREACHABLE ATLASSERT(0);\n#else\n#define UNREACHABLE __assume(0);\n#endif\n\n#define ATLENSURE_REPORT_HR(expr, error, hr)                         \\\ndo {                                                                 \\\n    int __atl_condVal=!!(expr);                                      \\\n    _ASSERT_EXPR(__atl_condVal, _com_error((error)).ErrorMessage()); \\\n    if(!(__atl_condVal)) return (hr);                                \\\n} while (0)\n\n#define ATLENSURE_REPORT_THROW(expr, error, hr)                      \\\ndo {                                                                 \\\n    int __atl_condVal=!!(expr);                                      \\\n    _ASSERT_EXPR(__atl_condVal, _com_error((error)).ErrorMessage()); \\\n    if(!(__atl_condVal)) AtlThrow(hr);                               \\\n} while (0)\n\n#ifdef _DEBUG\n#define ATLASSERT_REPORT(expr, error)                                \\\ndo {                                                                 \\\n    int __atl_condVal=!!(expr);                                      \\\n    _ASSERT_EXPR(__atl_condVal, _com_error((error)).ErrorMessage()); \\\n} while (0)\n#else\n#define ATLASSERT_REPORT(expr, error) ((void)0)\n#endif // _DEBUG\n\n#ifdef _DEBUG\n#define ATLVERIFY_REPORT(expr, error)                                \\\ndo {                                                                 \\\n    int __atl_condVal=!!(expr);                                      \\\n    _ASSERT_EXPR(__atl_condVal, _com_error((error)).ErrorMessage()); \\\n} while (0)\n#else\n#define ATLVERIFY_REPORT(expr, error) (expr)\n#endif // DEBUG\n"
  },
  {
    "path": "swish/drop_target/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  CopyFileOperation.cpp\n  CreateDirectoryOperation.cpp\n  DropTarget.cpp\n  DropUI.cpp\n  PidlCopyPlan.cpp\n  SequentialPlan.cpp\n  CopyFileOperation.hpp\n  CreateDirectoryOperation.hpp\n  DropActionCallback.hpp\n  DropTarget.hpp\n  DropUI.hpp\n  Operation.hpp\n  PidlCopyPlan.hpp\n  Plan.hpp\n  Progress.hpp\n  RootedSource.hpp\n  SequentialPlan.hpp\n  SftpDestination.hpp)\n\nadd_library(drop_target ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(drop_target\n  PUBLIC Washer::washer Comet::comet\n  PRIVATE ${Boost_LIBRARIES})"
  },
  {
    "path": "swish/drop_target/CopyFileOperation.cpp",
    "content": "/**\n    @file\n\n    File copy operation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"CopyFileOperation.hpp\"\n\n#include \"swish/remote_folder/remote_pidl.hpp\" // create_remote_itemid\n#include \"swish/shell_folder/SftpDirectory.h\" // CSftpDirectory\n\n#include <washer/shell/shell.hpp> // stream_from_pidl\n#include <washer/trace.hpp> // trace\n\n#include <comet/datetime.h> // datetime_t\n#include <comet/error.h> // com_error\n\n#include <boost/cstdint.hpp> // int64_t\n#include <boost/locale/message.hpp> // translate\n#include <boost/locale/format.hpp> // wformat\n#include <boost/shared_ptr.hpp>  // shared_ptr\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <exception>\n#include <iosfwd> // wstringstream\n\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::create_remote_itemid;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::stream_from_pidl;\nusing washer::trace;\n\nusing boost::int64_t;\nusing boost::function;\nusing boost::locale::translate;\nusing boost::locale::wformat;\nusing boost::shared_ptr;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::datetime_t;\n\nusing std::exception;\nusing std::wstringstream;\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace {\n\n    const size_t COPY_CHUNK_SIZE = 1024 * 32;\n\n    /**\n     * Return size of the streamed object in bytes.\n     */\n    int64_t size_of_stream(const com_ptr<IStream>& stream)\n    {\n        STATSTG statstg;\n        HRESULT hr = stream->Stat(&statstg, STATFLAG_NONAME);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(stream, hr));\n\n        return statstg.cbSize.QuadPart;\n    }\n\n    /**\n     * Write a stream to the provider at the given path.\n     *\n     * If it already exists, we want to ask the user for confirmation.\n     * The poor-mans way of checking if the file is already there is to\n     * try to get the file read-only first.  If this fails, assume the\n     * file noes not already exist.\n     *\n     * @bug  The get may have failed for a different reason or this\n     *       may not work reliably on all SFTP servers.  A safer\n     *       solution would be an explicit stat on the file.\n     *\n     * @bug  Of course, there is a race condition here.  After we check if the\n     *       file exists, someone else may have created it.  Unfortunately,\n     *       there is nothing we can do about this as SFTP doesn't give us\n     *       a way to do this atomically such as locking a file.\n     */\n    void copy_stream_to_remote_destination(\n        com_ptr<IStream> local_stream, shared_ptr<sftp_provider> provider,\n        const resolved_destination& target,\n        OperationCallback& callback)\n    {\n        CSftpDirectory sftp_directory(target.directory(), provider);\n\n        cpidl_t file = create_remote_itemid(\n            target.filename(), false, false, L\"\", L\"\", 0, 0, 0, 0,\n            datetime_t::now(), datetime_t::now());\n\n        if (sftp_directory.exists(file))\n        {\n            bool can_overwrite = callback.request_overwrite_permission(\n                target.as_absolute_path());\n\n            if (!can_overwrite)\n                return;\n        }\n\n        com_ptr<IStream> remote_stream;\n\n        try\n        {\n            remote_stream = sftp_directory.GetFile(file, true);\n        }\n        catch (const com_error& provider_error)\n        {\n            // TODO: once we decomtaminate the provider, move this to the\n            // snitching drop target so it can use the info in the task dialog\n\n            wstringstream new_message;\n            new_message <<\n                translate(L\"Unable to create file on the server:\") << L\"\\n\";\n            new_message << provider_error.description();\n            new_message << L\"\\n\" << target.as_absolute_path();\n\n            BOOST_THROW_EXCEPTION(\n                com_error(\n                    new_message.str(), provider_error.hr(),\n                    provider_error.source(), provider_error.guid(),\n                    provider_error.help_file(), provider_error.help_context()));\n        }\n\n        ::SHChangeNotify(\n            SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n            (target.directory() + file).get(), NULL);\n\n        // Set both streams back to the start\n        LARGE_INTEGER move = {0};\n        HRESULT hr = local_stream->Seek(move, SEEK_SET, NULL);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(local_stream, hr));\n\n        hr = remote_stream->Seek(move, SEEK_SET, NULL);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(remote_stream, hr));\n\n        // Do the copy in chunks allowing us to cancel the operation\n        // and display progress\n        ULARGE_INTEGER cb;\n        cb.QuadPart = COPY_CHUNK_SIZE;\n        int64_t done = 0;\n        int64_t total = size_of_stream(local_stream);\n\n        while (true)\n        {\n            callback.check_if_user_cancelled();\n\n            ULARGE_INTEGER cbRead = {0};\n            ULARGE_INTEGER cbWritten = {0};\n            // TODO: make our own CopyTo that propagates errors\n            hr = local_stream->CopyTo(\n                remote_stream.get(), cb, &cbRead, &cbWritten);\n            assert(FAILED(hr) || cbRead.QuadPart == cbWritten.QuadPart);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(\n                    com_error_from_interface(local_stream, hr));\n\n            try\n            {\n                // We create a different version of the PIDL here whose filesize\n                // is the amount copied so far. Otherwise Explorer shows a\n                // 0-byte file when the copying is done.\n                file = create_remote_itemid(\n                    target.filename(), false, false, L\"\", L\"\", 0, 0, 0,\n                    done, datetime_t::now(), datetime_t::now());\n\n                ::SHChangeNotify(\n                    SHCNE_UPDATEITEM, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n                    (target.directory() + file).get(), NULL);\n            }\n            catch(const exception& e)\n            {\n                // Ignoring error; failing to update the shell doesn't\n                // warrant aborting the transfer\n                trace(\"Failed to notify shell of file update %s\") % e.what();\n            }\n\n            // A failure to update the progress isn't a good enough reason\n            // to abort the copy so we swallow the exception.\n            try\n            {\n                done += cbWritten.QuadPart;\n                callback.update_progress(done, total);\n            }\n            catch (const exception& e)\n            {\n                trace(\"Progress update threw exception: %s\") % e.what();\n                assert(false);\n            }\n\n            if (cbRead.QuadPart == 0)\n                break; // finished\n        }\n    }\n\n}\n\nCopyFileOperation::CopyFileOperation(\n    const RootedSource& source, const SftpDestination& destination) :\nm_source(source), m_destination(destination) {}\n\nstd::wstring CopyFileOperation::title() const\n{\n    return (wformat(\n        translate(\n            L\"Top line of a transfer progress window saying which \"\n            L\"file is being copied. {1} is replaced with the file path \"\n            L\"and must be included in your translation.\",\n            L\"Copying '{1}'\"))\n        % m_source.relative_name()).str();\n}\n\nstd::wstring CopyFileOperation::description() const\n{\n    return (wformat(\n        translate(\n            L\"Second line of a transfer progress window giving the destination \"\n            L\"directory. {1} is replaced with the directory path and must be \"\n            L\"included in your translation.\",\n            L\"To '{1}'\"))\n        % m_destination.root_name()).str();\n}\n\nvoid CopyFileOperation::operator()(\n    OperationCallback& callback, shared_ptr<sftp_provider> provider) const\n{\n    com_ptr<IStream> stream = stream_from_pidl(m_source.pidl());\n\n    resolved_destination resolved_target(m_destination.resolve_destination());\n\n    copy_stream_to_remote_destination(\n        stream, provider, resolved_target, callback);\n}\n\nOperation* CopyFileOperation::do_clone() const\n{\n    return new CopyFileOperation(*this);\n}\n\n}}\n"
  },
  {
    "path": "swish/drop_target/CopyFileOperation.hpp",
    "content": "/**\n    @file\n\n    File copy operation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_COPYFILEOPERATION_HPP\n#define SWISH_DROP_TARGET_COPYFILEOPERATION_HPP\n#pragma once\n\n#include \"swish/drop_target/Operation.hpp\"\n#include \"swish/drop_target/RootedSource.hpp\"\n#include \"swish/drop_target/SftpDestination.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <boost/shared_ptr.hpp>\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\nnamespace swish {\nnamespace drop_target {\n\nclass CopyFileOperation : public Operation\n{\npublic:\n\n    CopyFileOperation(\n        const RootedSource& source, const SftpDestination& destination);\n\npublic: // Operation\n\n    virtual std::wstring title() const;\n\n    virtual std::wstring description() const;\n\n    virtual void operator()(\n        OperationCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider) const;\n\nprivate:\n\n    virtual Operation* do_clone() const;\n\n    RootedSource m_source;\n    SftpDestination m_destination;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/CreateDirectoryOperation.cpp",
    "content": "/**\n    @file\n\n    Directory creation operation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"CreateDirectoryOperation.hpp\"\n\n#include \"swish/shell_folder/SftpDirectory.h\" // CSftpDirectory\n\n#include <boost/locale/message.hpp> // translate\n#include <boost/locale/format.hpp> // wformat\n\nusing swish::provider::sftp_provider;\n\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl::apidl_t;\n\nusing boost::function;\nusing boost::locale::translate;\nusing boost::locale::wformat;\nusing boost::shared_ptr;\n\nusing comet::com_ptr;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace drop_target {\n\nCreateDirectoryOperation::CreateDirectoryOperation(\n    const RootedSource& source, const SftpDestination& destination) :\nm_source(source), m_destination(destination) {}\n\nwstring CreateDirectoryOperation::title() const\n{\n    return (wformat(\n        translate(\n            L\"Top line of a transfer progress window saying which \"\n            L\"file is being copied. {1} is replaced with the file path \"\n            L\"and must be included in your translation.\",\n            L\"Copying '{1}'\"))\n        % m_source.relative_name()).str();\n}\n\nwstring CreateDirectoryOperation::description() const\n{\n    return (wformat(\n        translate(\n            L\"Second line of a transfer progress window giving the destination \"\n            L\"directory. {1} is replaced with the directory path and must be \"\n            L\"included in your translation.\",\n            L\"To '{1}'\"))\n        % m_destination.root_name()).str();\n}\n\nvoid CreateDirectoryOperation::operator()(\n    OperationCallback& callback,\n    shared_ptr<sftp_provider> provider) const\n{\n    callback.update_progress(0, 1);\n\n    resolved_destination resolved_target(m_destination.resolve_destination());\n\n    CSftpDirectory sftp_directory(\n        resolved_target.directory(), provider);\n    sftp_directory.CreateDirectory(resolved_target.filename());\n\n    callback.update_progress(1, 1);\n}\n\nOperation* CreateDirectoryOperation::do_clone() const\n{\n    return new CreateDirectoryOperation(*this);\n}\n\n}}\n"
  },
  {
    "path": "swish/drop_target/CreateDirectoryOperation.hpp",
    "content": "/**\n    @file\n\n    Directory creation operation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_CREATEDIRECTORYOPERATION_HPP\n#define SWISH_DROP_TARGET_CREATEDIRECTORYOPERATION_HPP\n#pragma once\n\n#include \"swish/drop_target/Operation.hpp\"\n#include \"swish/drop_target/RootedSource.hpp\"\n#include \"swish/drop_target/SftpDestination.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <boost/shared_ptr.hpp>\n\nnamespace swish {\nnamespace drop_target {\n\nclass CreateDirectoryOperation : public Operation\n{\npublic:\n\n    CreateDirectoryOperation(\n        const RootedSource& source, const SftpDestination& target);\n\n    virtual std::wstring title() const;\n\n    virtual std::wstring description() const;\n\n    virtual void operator()(\n        OperationCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider) const;\n\nprivate:\n\n    virtual Operation* do_clone() const;\n\n    RootedSource m_source;\n    SftpDestination m_destination;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/DropActionCallback.hpp",
    "content": "/**\n    @file\n\n    User interaction during a drop.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_DROPACTIONCALLBACK_HPP\n#define SWISH_DROP_TARGET_DROPACTIONCALLBACK_HPP\n#pragma once\n\n#include <ssh/filesystem/path.hpp>\n\n#include <memory> // auto_ptr\n\nnamespace swish {\nnamespace drop_target {\n\nclass Progress;\n\n/**\n * Interface for drop target to communicate with the user during a drop.\n */\nclass DropActionCallback\n{\npublic:\n    virtual ~DropActionCallback() {}\n    virtual bool can_overwrite(const ssh::filesystem::path& target) = 0;\n    virtual std::auto_ptr<Progress> progress() = 0;\n    virtual void handle_last_exception() = 0;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/DropTarget.cpp",
    "content": "/**\n    @file\n\n    Expose the remote filesystem as an IDropTarget.\n\n    @if license\n\n    Copyright (C) 2009, 2010, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"DropTarget.hpp\"\n\n#include \"swish/drop_target/PidlCopyPlan.hpp\"\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/shell_folder/data_object/ShellDataObject.hpp\"\n                                                  // PidlFormat, ShellDataObject\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <boost/shared_ptr.hpp>  // shared_ptr\n#include <boost/thread.hpp>\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <comet/error.h> // com_error\n#include <comet/git.h>\n#include <comet/ptr.h>  // com_ptr\n#include <comet/util.h> // auto_coinit\n\nusing swish::shell_folder::data_object::ShellDataObject;\nusing swish::shell_folder::data_object::PidlFormat;\nusing swish::provider::sftp_provider;\n\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl::apidl_t;\n\nusing boost::shared_ptr;\nusing boost::thread;\n\nusing comet::auto_coinit;\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::GIT;\nusing comet::GIT_cookie;\n\ntemplate<> struct comet::comtype<IAsyncOperation>\n{\n    static const IID& uuid() throw() { return IID_IAsyncOperation; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comet::comtype<IDataObject>\n{\n    static const IID& uuid() throw() { return IID_IDataObject; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace { // private\n\n    /**\n     * Given a DataObject and bitfield of allowed DROPEFFECTs, determine\n     * which drop effect, if any, should be chosen.  If none are\n     * appropriate, return DROPEFFECT_NONE.\n     */\n    DWORD determine_drop_effect(\n        const com_ptr<IDataObject>& pdo, DWORD allowed_effects)\n    {\n        if (pdo)\n        {\n            PidlFormat format(pdo);\n            if (format.pidl_count() > 0)\n            {\n                if (allowed_effects & DROPEFFECT_COPY)\n                    return DROPEFFECT_COPY;\n            }\n        }\n\n        return DROPEFFECT_NONE;\n    }\n\n}\n\n/**\n * Copy the items in the DataObject to the remote target.\n *\n * @param source_format     Clipboard PIDL format holding the items to be copied.\n * @param provider          SFTP connection to copy data over.\n * @param destination_root  PIDL to target directory in the remote filesystem\n *                          to copy items into.\n * @param progress          Progress dialogue.\n */\nvoid copy_format_to_provider(\n    PidlFormat source_format, shared_ptr<sftp_provider> provider,\n    const apidl_t& destination_root, shared_ptr<DropActionCallback> callback)\n{\n    PidlCopyPlan copy_list(source_format, destination_root);\n\n    copy_list.execute_plan(*callback, provider);\n}\n\nnamespace {\n\nvoid async_copy_format_to_provider(\n    GIT_cookie<IDataObject> marshalling_cookie,\n    shared_ptr<sftp_provider> provider,\n    apidl_t destination_root, shared_ptr<DropActionCallback> callback)\n{\n    auto_coinit com;\n    GIT git;\n\n    // These interface from the GIT will be properly marshalled across thread\n    // apartments\n    com_ptr<IDataObject> data_object = git.get_interface(marshalling_cookie);\n    com_ptr<IAsyncOperation> async = try_cast(data_object);\n\n    try\n    {\n        try\n        {\n            copy_format_to_provider(\n                PidlFormat(data_object), provider, destination_root,\n                callback);\n        }\n        catch (...)\n        {\n            callback->handle_last_exception();\n            throw;\n        }\n    }\n    catch (const com_error& e)\n    {\n        async->EndOperation(e.hr(), NULL, DROPEFFECT_COPY);\n    }\n    catch (...)\n    {\n        async->EndOperation(E_FAIL, NULL, DROPEFFECT_COPY);\n    }\n\n    git.revoke_interface(marshalling_cookie);\n}\n\n}\n\n/**\n * Copy the items in the DataObject to the remote target.\n *\n * @param data_object       IDataObject holding the items to be copied.\n * @param provider          SFTP connection to copy data over.\n * @param remote_directory  PIDL to target directory in the remote filesystem\n *                          to copy items into.\n */\nvoid copy_data_to_provider(\n    com_ptr<IDataObject> data_object, shared_ptr<sftp_provider> provider, \n    const apidl_t& remote_directory, shared_ptr<DropActionCallback> callback)\n{\n    ShellDataObject data(data_object);\n    if (data.has_pidl_format())\n    {\n        if (data.supports_async())\n        {\n            com_ptr<IAsyncOperation> async = data.async();\n            HRESULT hr = async->StartOperation(NULL);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error_from_interface(async, hr));\n\n            // We place the interfaces in the Global Interface Table because\n            // the other thread needs marshalled versions of the interfaces.\n            // The GIT promises to provide that.\n            GIT git;\n            GIT_cookie<IDataObject> marshalling_cookie =\n                git.register_interface(data_object);\n\n            thread(\n                &async_copy_format_to_provider, marshalling_cookie,\n                provider, remote_directory, callback).detach();\n        }\n        else\n        {\n            copy_format_to_provider(\n                PidlFormat(data_object), provider, remote_directory,\n                callback);\n        }\n    }\n    else\n    {\n        BOOST_THROW_EXCEPTION(\n            com_error(\"DataObject doesn't contain a supported format\"));\n    }\n}\n\n/**\n * Create an instance of the DropTarget initialised with a data provider.\n */\nCDropTarget::CDropTarget(\n    shared_ptr<sftp_provider> provider, const apidl_t& remote_directory,\n    shared_ptr<DropActionCallback> callback)\n    :\n    m_provider(provider),\n    m_remote_directory(remote_directory), m_callback(callback) {}\n\n/**\n * Indicate whether the contents of the DataObject can be dropped on\n * this DropTarget.\n *\n * @todo  Take account of the key state.\n */\nSTDMETHODIMP CDropTarget::DragEnter( \n    IDataObject* pdo, DWORD /*grfKeyState*/, POINTL /*pt*/, DWORD* pdwEffect)\n{\n    try\n    {\n        if (!pdwEffect)\n            BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n        m_data_object = pdo;\n\n        *pdwEffect = determine_drop_effect(pdo, *pdwEffect);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Refresh the choice drop effect for the last DataObject passed to DragEnter.\n * Although the DataObject will not have changed, the key state and allowed\n * effects bitfield may have.\n *\n * @todo  Take account of the key state.\n */\nSTDMETHODIMP CDropTarget::DragOver( \n    DWORD /*grfKeyState*/, POINTL /*pt*/, DWORD* pdwEffect)\n{\n    try\n    {\n        if (!pdwEffect)\n            BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n        *pdwEffect = determine_drop_effect(m_data_object, *pdwEffect);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * End the drag-and-drop loop for the current DataObject.\n */\nSTDMETHODIMP CDropTarget::DragLeave()\n{\n    try\n    {\n        m_data_object = NULL;\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Perform the drop operation by either copying or moving the data\n * in the DataObject to the remote target.\n *\n * @todo  Take account of the key state.\n */\nSTDMETHODIMP CDropTarget::Drop( \n    IDataObject* pdo, DWORD /*grfKeyState*/, POINTL /*pt*/, DWORD* pdwEffect)\n{\n    try\n    {\n        try\n        {\n            if (!pdwEffect)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n            // Drop doesn't need to maintain any state and is handed a fresh\n            // copy of the IDataObject so we can can immediately cancel the\n            // one we were using for the other parts of the drag-drop loop\n            m_data_object = NULL;\n\n            *pdwEffect = determine_drop_effect(pdo, *pdwEffect);\n\n            if (pdo && *pdwEffect == DROPEFFECT_COPY)\n            {\n                copy_data_to_provider(\n                    pdo, m_provider, m_remote_directory, m_callback);\n            }\n        }\n        catch (...)\n        {\n            m_callback->handle_last_exception();\n            throw;\n        }\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n}} // namespace swish::drop_target\n"
  },
  {
    "path": "swish/drop_target/DropTarget.hpp",
    "content": "/**\n    @file\n\n    Expose the remote filesystem as an IDropTarget.\n\n    @if license\n\n    Copyright (C) 2009, 2010, 2011, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_DROPTARGET_HPP\n#define SWISH_DROP_TARGET_DROPTARGET_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider\n#include \"swish/drop_target/DropActionCallback.hpp\" // DropActionCallback\n#include \"swish/drop_target/Progress.hpp\" // Progress\n\n#include <washer/object_with_site.hpp> // object_with_site\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <boost/shared_ptr.hpp>\n\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n\n#include <OleIdl.h> // IDropTarget\n#include <OCIdl.h> // IObjectWithSite\n\n\ntemplate<> struct comet::comtype<IDropTarget>\n{\n    static const IID& uuid() throw() { return IID_IDropTarget; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace drop_target {\n\nclass CDropTarget :\n    public comet::simple_object<IDropTarget, washer::object_with_site>\n{\npublic:\n\n    typedef IDropTarget interface_is;\n\n    /**\n     * Create SFTP drop target.\n     */\n    CDropTarget(\n        boost::shared_ptr<swish::provider::sftp_provider> provider,\n        const washer::shell::pidl::apidl_t& remote_directory,\n        boost::shared_ptr<DropActionCallback> callback);\n\n    /** @name IDropTarget methods */\n    // @{\n\n    IFACEMETHODIMP DragEnter( \n        __in_opt IDataObject* pDataObj,\n        __in DWORD grfKeyState,\n        __in POINTL pt,\n        __inout DWORD* pdwEffect);\n\n    IFACEMETHODIMP DragOver( \n        __in DWORD grfKeyState,\n        __in POINTL pt,\n        __inout DWORD* pdwEffect);\n\n    IFACEMETHODIMP DragLeave();\n\n    IFACEMETHODIMP Drop( \n        __in_opt IDataObject* pDataObj,\n        __in DWORD grfKeyState,\n        __in POINTL pt,\n        __inout DWORD* pdwEffect);\n\n    // @}\n\nprivate:\n\n    boost::shared_ptr<swish::provider::sftp_provider> m_provider;\n\n    washer::shell::pidl::apidl_t m_remote_directory;\n    comet::com_ptr<IDataObject> m_data_object;\n    boost::shared_ptr<DropActionCallback> m_callback;\n};\n\nvoid copy_data_to_provider(\n    comet::com_ptr<IDataObject> data_object,\n    boost::shared_ptr<swish::provider::sftp_provider> provider,\n    const washer::shell::pidl::apidl_t& remote_directory,\n    boost::shared_ptr<DropActionCallback> callback);\n\n}} // namespace swish::drop_target\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/DropUI.cpp",
    "content": "/**\n    @file\n\n    User-interaction for DropTarget.\n\n    @if license\n\n    Copyright (C) 2010, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"DropUI.hpp\"\n\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n#include \"swish/trace.hpp\" // trace\n\n#include <washer/com/ole_window.hpp> // window_from_ole_window\n#include <washer/gui/message_box.hpp> // message_box\n#include <washer/gui/progress.hpp>\n#include <washer/window/window.hpp>\n#include <washer/window/window_handle.hpp>\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/locale.hpp> // translate, wformat\n#include <boost/noncopyable.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <iosfwd> // wstringstream\n#include <string>\n\nusing swish::frontend::announce_last_exception;\nusing swish::tracing::trace;\n\nusing washer::com::window_from_ole_window;\nusing namespace washer::gui::message_box;\nusing washer::gui::progress;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing ssh::filesystem::path;\n\nusing boost::locale::translate;\nusing boost::locale::wformat;\nusing boost::noncopyable;\nusing boost::optional;\n\nusing std::auto_ptr;\nusing std::wstringstream;\nusing std::wstring;\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace {\n\n    /**\n     * Drain any messages in the queue.\n     */\n    void do_events()\n    {\n        MSG msg;\n        BOOL result;\n\n        while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))\n        {\n            result = ::GetMessage(&msg, NULL, 0, 0);\n            if (result == 0) // WM_QUIT\n            {\n                ::PostQuitMessage(msg.wParam);\n                break;\n            }\n            else if (result == -1)\n            {\n                return;\n            }\n            else\n            {\n                ::TranslateMessage(&msg);\n                ::DispatchMessage(&msg);\n            }\n        }\n    }\n\n\n    /**\n     * Exception-safe lifetime manager for an IProgressDialog object.\n     *\n     * Calls StartProgressDialog when created and StopProgressDialog when\n     * destroyed.\n     */\n    class DropProgress : public noncopyable, public Progress\n    {\n    public:\n\n        DropProgress(\n            const optional< window<wchar_t> >& owner, const wstring& title)\n            :\n        m_inner(create_dialog(owner, title)) {}\n\n        /**\n         * Has the user cancelled the operation via the progress dialogue?\n         */\n        bool user_cancelled()\n        {\n            return m_inner.user_cancelled();\n        }\n\n        // Because we are no longer doing the transfer in a different COM\n        // apartment, which would pump messages during the call, the UI blocks\n        // on the drop.  That includes not showing the progress dialog.\n        //\n        // Therefore, we pump outstanding messages every time there is\n        // an update.  I don't think this it the right solution, but we can't\n        // run the progress dialog in a different thread as that breaks\n        // the windows rules.\n        //\n        // The UI is still not wonderfully responsive because it can only\n        // update a little each time the progress is updated.  We may be able\n        // to do better once we use libssh2's non-blocking API as then we\n        // can pump messages more frequently.\n\n        /**\n         * Set the indexth line of the display to the given text.\n         */\n        void line(DWORD index, const wstring& text)\n        {\n            m_inner.line(index, text);\n            do_events();\n        }\n\n        /**\n         * Set the indexth line of the display to the given path.\n         *\n         * Uses the inbuilt path compression.\n         */\n        void line_path(DWORD index, const wstring& text)\n        {\n            m_inner.line_compress_paths_if_needed(index, text);\n            do_events();\n        }\n\n        /**\n         * Update the indicator to show current progress level.\n         */\n        void update(ULONGLONG so_far, ULONGLONG out_of)\n        {\n            m_inner.update(so_far, out_of);\n            do_events();\n        }\n\n        /**\n         * Force the dialogue window to disappear.\n         *\n         * Useful, for instance, to temporarily hide the progress display while\n         * displaying other dialogues in the middle of the process whose\n         * progress is being monitored.\n         */\n        void hide()\n        {\n            optional< window<wchar_t> > window = m_inner.window();\n            if (window)\n                window->enable(false);\n            do_events();\n        }\n\n        /**\n         * Force the dialogue window to appear.\n         *\n         * Useful to force the window to appear quicker than it normally would,\n         * and to redisplay the window after hiding it.\n         *\n         * @see hide\n         */\n        void show()\n        {\n            optional< window<wchar_t> > window = m_inner.window();\n            if (window)\n                window->enable(true);\n            do_events();\n        }\n\n    private:\n\n        static progress create_dialog(\n            const optional< window<wchar_t> >& owner, const wstring& title)\n        {\n            return progress(\n                owner, title,\n                progress::modality::non_modal,\n                progress::time_estimation::automatic_time_estimate,\n                progress::bar_type::finite,\n                progress::minimisable::yes,\n                progress::cancellability::cancellable);\n        }\n\n        progress m_inner;\n    };\n\n    /**\n     * Disables a progress window for duration of its scope and reenables\n     * after.\n     */\n    class ScopedDisabler\n    {\n    public:\n        ScopedDisabler(Progress& progress) : m_progress(progress)\n        {\n            m_progress.hide();\n        }\n\n        ~ScopedDisabler()\n        {\n            m_progress.show();\n        }\n\n    private:\n        Progress& m_progress;\n    };\n}\n\nDropUI::DropUI(const optional< window<wchar_t> >& owner) : m_owner(owner) {}\n\n/**\n * Does user give permission to overwrite remote target file?\n */\nbool DropUI::can_overwrite(const path& target)\n{\n    if (!m_owner)\n        return false;\n\n    wstringstream message;\n    message << wformat(translate(\n        L\"This folder already contains a file named '{1}'.\"))\n        % target.filename();\n    message << \"\\n\\n\";\n    message << translate(L\"Would you like to replace it?\");\n\n    // If the caller has already displayed the progress dialog, we must\n    // force-hide it as it gets in the way of other UI\n    ScopedDisabler disable_progress(*m_progress);\n\n    button_type::type button = message_box(\n        (m_owner) ? m_owner->hwnd() : NULL,\n        message.str(), translate(L\"Confirm File Replace\"),\n        box_type::yes_no_cancel, icon_type::question);\n    switch (button)\n    {\n    case button_type::yes:\n        return true;\n    case button_type::no:\n        return false;\n    case button_type::cancel:\n    default:\n        BOOST_THROW_EXCEPTION(com_error(E_ABORT));\n    }\n}\n\nvoid DropUI::handle_last_exception()\n{\n    // Only report errors with a dialog if we are given a window we\n    // can use as a dialogue owner.  We can assume if the caller\n    // didn't give us one, they don't want UI.\n    if (m_owner)\n    {\n        announce_last_exception(\n            m_owner->hwnd(), translate(L\"Unable to transfer files\"),\n            translate(\n                L\"You might not have permission to write to this \"\n                L\"directory.\"));\n    }\n\n    throw;\n}\n\nnamespace {\n\nclass DummyProgress : public Progress\n{\npublic:\n    virtual bool user_cancelled()\n    {\n        return false;\n    };\n\n    virtual void line(DWORD, const std::wstring&) {}\n    virtual void line_path(DWORD, const std::wstring&) {}\n    virtual void update(ULONGLONG, ULONGLONG) {}\n    virtual void hide() {}\n    virtual void show() {}\n};\n\n}\n\n/**\n * Pass ownership of a progress display scope to caller.\n *\n * We hang on to the progress dialog so that we can hide it if and when we\n * show other dialogs (something the built-in Explorer FTP extension doesn't\n * do and really should).\n *\n * The caller gets a Progress object whose lifetime determines when the dialog\n * is started and ended.  When it goes out of scope the dialog is stopped and\n * disappears.  In other words, the progress dialog is safely stopped even\n * if an exception is thrown.\n */\nauto_ptr<Progress> DropUI::progress()\n{\n    auto_ptr<Progress> p;\n    if (m_owner)\n    {\n        p = auto_ptr<Progress>(\n            new DropProgress(m_owner, translate(L\"Progress\", L\"Copying...\")));\n    }\n    else\n    {\n        p = auto_ptr<Progress>(new DummyProgress());\n    }\n\n    // HACK: we keep a raw copy of the pointer so we can hide the progress\n    // if needed later when displaying the confirm-overwrite box.  There\n    // has got to be a safer way to do this.\n    m_progress = p.get();\n\n    return p;\n}\n\n}} // namespace swish::drop_target\n"
  },
  {
    "path": "swish/drop_target/DropUI.hpp",
    "content": "/**\n    @file\n\n    User-interaction for DropTarget.\n\n    @if license\n\n    Copyright (C) 2010, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_DROPUI_HPP\n#define SWISH_DROP_TARGET_DROPUI_HPP\n#pragma once\n\n#include \"swish/drop_target/DropActionCallback.hpp\"\n#include \"swish/drop_target/Progress.hpp\"\n\n#include <washer/window/window.hpp>\n\n#include <boost/optional.hpp>\n\n#include <memory> // auto_ptr\n\nnamespace swish {\nnamespace drop_target {\n\n    /**\n     * DropTarget callback turning requests into GUI windows so user can\n     * handle them.\n     */\n    class DropUI : public DropActionCallback\n    {\n    public:\n        DropUI(\n            const boost::optional< washer::window::window<wchar_t> >& owner);\n\n        virtual bool can_overwrite(const ssh::filesystem::path& target);\n        virtual std::auto_ptr<Progress> progress();\n        virtual void handle_last_exception();\n\n    private:\n        boost::optional< washer::window::window<wchar_t> > m_owner;\n        Progress* m_progress;\n    };\n\n}} // namespace swish::drop_target\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/Operation.hpp",
    "content": "/**\n    @file\n\n    Interface to drop target operations.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_OPERATION_HPP\n#define SWISH_DROP_TARGET_OPERATION_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n\n#include <boost/cstdint.hpp> // uintmax_t\n#include <boost/function.hpp> // function\n#include <boost/shared_ptr.hpp>\n\n#include <ssh/filesystem/path.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <cassert> // assert\n#include <string>\n\nnamespace swish {\nnamespace drop_target {\n\nclass DropActionCallback;\n\n/**\n * Interface through which individual drop operations interact with user.\n *\n * Purpose: to abstract the interaction so that an operation can pretend it\n * is the only operation happening.  The operation doesn't need to think\n * about the lifetime of the progress display and just updates it as it\n * wishes till so_far == out_of.\n */\nclass OperationCallback\n{\npublic:\n\n    /**\n     * Throw com_error(E_ABORT) if user cancelled.\n     *\n     * It throws rather than returning a boolean in order to force the operation\n     * to abort with an exception.  This behaviour is expected by drag-and-drop.\n     */\n    virtual void check_if_user_cancelled() const = 0;\n\n    virtual bool request_overwrite_permission(\n        const ssh::filesystem::path& target) const = 0;\n\n    virtual void update_progress(\n        boost::uintmax_t so_far, boost::uintmax_t out_of) = 0;\n\n    virtual ~OperationCallback() {}\n};\n\n/**\n * Interface of operation functors making up a drop.\n */\nclass Operation\n{\npublic:\n\n    virtual std::wstring title() const = 0;\n\n    virtual std::wstring description() const = 0;\n\n    virtual void operator()(\n        OperationCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider)\n        const = 0;\n\n    Operation* clone() const\n    {\n        Operation* item = do_clone();\n        assert(typeid(*this) == typeid(*item) &&\n            \"do_clone() sliced object!\");\n        return item;\n    }\n\nprivate:\n    virtual Operation* do_clone() const = 0;\n};\n\ninline Operation* new_clone(const Operation& item)\n{\n    return item.clone();\n}\n\n}} // namespace swish::drop_target\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/PidlCopyPlan.cpp",
    "content": "/**\n    @file\n\n    Plan copying items in PIDL clipboard format to remote server.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"PidlCopyPlan.hpp\"\n\n#include \"swish/drop_target/CopyFileOperation.hpp\"\n#include \"swish/drop_target/CreateDirectoryOperation.hpp\"\n#include \"swish/drop_target/RootedSource.hpp\"\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n\n#include <ssh/filesystem.hpp> // path\n\n#include <boost/bind.hpp> // bind\n#include <boost/function_output_iterator.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <washer/shell/shell.hpp> // stream_from_pidl, bind_to_handler_object\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n\n#include <comet/error.h> // com_error\n\n#include <string>\n\nusing swish::provider::sftp_provider;\nusing swish::shell_folder::data_object::PidlFormat;\n\nusing washer::shell::bind_to_handler_object;\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl_shell_item;\nusing washer::shell::stream_from_pidl;\n\nusing ssh::filesystem::path;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing boost::bind;\nusing boost::make_function_output_iterator;\nusing boost::shared_ptr;\nusing boost::ref;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace {\n\n    /**\n     * Return the name the copy should have at the target location.\n     */\n    path target_name_from_source(const RootedSource& source)\n    {\n        return pidl_shell_item(source.pidl()).friendly_name(\n            pidl_shell_item::friendly_name_type::relative);\n    }\n\n    template<typename OutIt>\n    void output_operations_for_stream_pidl(\n        const RootedSource& source, const SftpDestination& destination,\n        OutIt output_iterator)\n    {\n        path new_name = target_name_from_source(source);\n\n        SftpDestination new_destination = destination / new_name;\n\n        CopyFileOperation operation(source, new_destination);\n\n        *output_iterator++ = operation;\n    }\n\n    template<typename OutIt>\n    void output_operations_for_folder_pidl(\n        com_ptr<IShellFolder> folder, const RootedSource& source,\n        const SftpDestination& destination, OutIt output_iterator)\n    {\n        path new_name = target_name_from_source(source);\n\n        SftpDestination new_destination = destination / new_name;\n\n        *output_iterator++ = CreateDirectoryOperation(source, new_destination);\n\n        com_ptr<IEnumIDList> e;\n        HRESULT hr = folder->EnumObjects(\n            NULL,\n            SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN,\n            e.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(folder, hr));\n\n        cpidl_t item;\n        while (hr == S_OK && e->Next(1, item.out(), NULL) == S_OK)\n        {\n            output_operations_for_pidl(\n                source / item, new_destination, output_iterator);\n        }\n    }\n\n    template<typename OutIt>\n    void output_operations_for_pidl(\n        const RootedSource& source, const SftpDestination& destination,\n        OutIt output_iterator)\n    {\n        try\n        {\n            /*\n            Test if streamable.\n            We don't use this stream to perform the operation as that would\n            mean large transfers keeping open a large number of file handles\n            while building the copy plan - a bad idea, especially if the files\n            are on another remote server\n            */\n            stream_from_pidl(source.pidl());\n\n            output_operations_for_stream_pidl(\n                source, destination, output_iterator);\n        }\n        catch (const com_error&)\n        {\n            // Treating the item as something with an IStream has failed\n            // Now we try to treat it as an IShellFolder and hope we\n            // have more success\n\n            com_ptr<IShellFolder> folder =\n                bind_to_handler_object<IShellFolder>(source.pidl());\n\n            output_operations_for_folder_pidl(\n                folder, source, destination, output_iterator);\n        }\n    }\n\n}\n\n/**\n * Create plan to copy items represented by clipboard PIDL format.\n *\n * Expands the top-level PIDLs into a list of all items in the hierarchy.\n */\nPidlCopyPlan::PidlCopyPlan(\n    const PidlFormat& source_format, const apidl_t& destination_root)\n{\n    for (unsigned int i = 0; i < source_format.pidl_count(); ++i)\n    {\n        apidl_t pidl = source_format.file(i);\n\n        output_operations_for_pidl(\n            RootedSource(\n                source_format.parent_folder(), source_format.relative_file(i)),\n            SftpDestination(destination_root, path()),\n            make_function_output_iterator(\n                bind(&SequentialPlan::add_stage, ref(m_plan), _1)));\n    }\n}\n\nvoid PidlCopyPlan::execute_plan(\n    DropActionCallback& callback, shared_ptr<sftp_provider> provider) const\n{\n    m_plan.execute_plan(callback, provider);\n}\n\nvoid PidlCopyPlan::add_stage(const Operation& entry)\n{\n    m_plan.add_stage(entry);\n}\n\n}}\n"
  },
  {
    "path": "swish/drop_target/PidlCopyPlan.hpp",
    "content": "/**\n    @file\n\n    Plan copying items in PIDL clipboard format to remote server.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_PIDLCOPYPLAN_HPP\n#define SWISH_DROP_TARGET_PIDLCOPYPLAN_HPP\n#pragma once\n\n#include \"swish/drop_target/Operation.hpp\"\n#include \"swish/drop_target/Plan.hpp\"\n#include \"swish/drop_target/SequentialPlan.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n#include \"swish/shell_folder/data_object/ShellDataObject.hpp\"  // PidlFormat\n\n#include <boost/shared_ptr.hpp>\n\nnamespace swish {\nnamespace drop_target {\n\n/**\n * Plan copying items in PIDL clipboard format to remote server.\n */\nclass PidlCopyPlan /* final */ : public Plan\n{\npublic:\n\n    PidlCopyPlan(\n        const swish::shell_folder::data_object::PidlFormat& source,\n        const washer::shell::pidl::apidl_t& destination);\n\npublic: // Plan\n\n    virtual void execute_plan(\n        DropActionCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider) const;\n\npublic:\n\n    void add_stage(const Operation& stage);\n\nprivate:\n\n    SequentialPlan m_plan;\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/drop_target/Plan.hpp",
    "content": "/**\n    @file\n\n    Executable schedule of operations.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_PLAN_HPP\n#define SWISH_DROP_TARGET_PLAN_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/drop_target/Progress.hpp\"\n\n#include <boost/shared_ptr.hpp>\n\nnamespace swish {\nnamespace drop_target {\n\nclass DropActionCallback;\n\n/**\n * Interface for executable schedule of operations.\n */\nclass Plan\n{\npublic:\n    virtual ~Plan() {}\n\n    virtual void execute_plan(\n        DropActionCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider) const = 0;\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/drop_target/Progress.hpp",
    "content": "/**\n    @file\n\n    Progress callback.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_PROGRESS_HPP\n#define SWISH_DROP_TARGET_PROGRESS_HPP\n#pragma once\n\n#include <string>\n\n#include <windows.h> // DWORD, ULONGLONG\n\nnamespace swish {\nnamespace drop_target {\n\nclass Progress\n{\npublic:\n    virtual ~Progress() {}\n    virtual bool user_cancelled() = 0;\n    virtual void line(DWORD index, const std::wstring& text) = 0;\n    virtual void line_path(DWORD index, const std::wstring& text) = 0;\n    virtual void update(ULONGLONG so_far, ULONGLONG out_of) = 0;\n    virtual void hide() = 0;\n    virtual void show() = 0;\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/drop_target/RootedSource.hpp",
    "content": "/**\n    @file\n\n    Source PIDL with common root.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_ROOTEDSOURCE_HPP\n#define SWISH_DROP_TARGET_ROOTEDSOURCE_HPP\n#pragma once\n\n#include <washer/shell/pidl.hpp> // apidl_t, pidl_t\n#include <washer/shell/pidl_iterator.hpp>\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n\n#include <string>\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace detail {\n\n    inline std::wstring display_name_of_item(\n        const washer::shell::pidl::apidl_t& pidl)\n    {\n        using washer::shell::pidl_shell_item;\n\n        return pidl_shell_item(pidl).friendly_name(\n            pidl_shell_item::friendly_name_type::relative);\n    }\n\n    /**\n     * Return the parsing path name for a PIDL relative the the given parent.\n     */\n    inline std::wstring relative_name_for_pidl(\n        const washer::shell::pidl::apidl_t& parent,\n        const washer::shell::pidl::pidl_t& pidl)\n    {\n        std::wstring name;\n        washer::shell::pidl::apidl_t abs = parent;\n\n        washer::shell::pidl::pidl_iterator it(pidl);\n        while (it != washer::shell::pidl::pidl_iterator())\n        {\n            if (!name.empty())\n                name += L\"\\\\\";\n\n            abs += *it++;\n            name += display_name_of_item(abs);\n        }\n\n        return name;\n    }\n}\n\n/**\n * Shell-based source relative to a root.\n *\n * Purpose: to maintain the connection between a particular source item in a\n * multi-item transfer and the common root of all the items.\n *\n * To the user, a given source item in a file transfer does not exist in\n * isolation.  All the items in the transfer are with respect to a particular\n * root.  Paths shown as progress information, for example, are typically given\n * with respect rather than as absolute paths.  This class exists to maintain\n * that relationship.\n */\nclass RootedSource\n{\npublic:\n    RootedSource(\n        const washer::shell::pidl::apidl_t& common_root,\n        const washer::shell::pidl::pidl_t& relative_branch)\n        : m_root(common_root), m_branch(relative_branch) {}\n\n    const washer::shell::pidl::apidl_t& common_root() const\n    {\n        return m_root;\n    }\n\n    washer::shell::pidl::apidl_t pidl() const\n    {\n        return m_root + m_branch;\n    }\n\n    std::wstring relative_name() const\n    {\n        return detail::relative_name_for_pidl(m_root, m_branch);\n    }\n\n    RootedSource operator/(const washer::shell::pidl::pidl_t& pidl) const\n    {\n        return RootedSource(m_root, m_branch + pidl);\n    }\n\nprivate:\n    const washer::shell::pidl::apidl_t m_root;\n    const washer::shell::pidl::pidl_t m_branch;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/drop_target/SequentialPlan.cpp",
    "content": "/**\n    @file\n\n    Standard drop operation plan implementation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"SequentialPlan.hpp\"\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/drop_target/DropActionCallback.hpp\"\n#include \"swish/drop_target/Operation.hpp\"\n\n#include <boost/cstdint.hpp> // uintmax_t\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/shared_array.hpp>\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <comet/error.h> // com_error\n\n#include <ssh/filesystem/path.hpp>\n\n#include <cassert> // assert\n#include <memory> // auto_ptr\n\nusing swish::provider::sftp_provider;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing ssh::filesystem::path;\n\nusing boost::numeric_cast;\nusing boost::shared_ptr;\nusing boost::uintmax_t;\n\nusing std::auto_ptr;\nusing std::size_t;\n\nnamespace swish {\nnamespace drop_target {\n\nnamespace {\n\n    /**\n     * Calculate percentage.\n     *\n     * @bug  Throws if using ludicrously large sizes.\n     */\n    uintmax_t percentage(uintmax_t done, uintmax_t total)\n    {\n        if (total == 0)\n            return 100;\n        else\n            return numeric_cast<uintmax_t>((done * 100) / total);\n    }\n\n    /**\n     * Calculator of 'intra-file' progress.\n     *\n     * Purpose: to translate between the progress increments reported by\n     * a single operation and the overall progress of the sequence of\n     * operations.\n     *\n     * In other words, it handles the small increments that happen during the\n     * upload of one file amongst many.  We need this to give meaningful\n     * progress when only a small number of files are being dropped where the\n     * time spent on a single file makes up a significant portion of the\n     * overall transfer.\n     */\n    class IntraSequenceCallback : public OperationCallback\n    {\n    public:\n\n        IntraSequenceCallback(\n            OperationCallback& sequence_callback, size_t current_file_index,\n            size_t total_files)\n            :\n            m_callback(sequence_callback),\n            m_current_file_index(current_file_index),\n            m_total_files(total_files) {}\n\n        virtual void check_if_user_cancelled() const\n        {\n            return m_callback.check_if_user_cancelled();\n        }\n\n        virtual bool request_overwrite_permission(const path& target) const\n        {\n            return m_callback.request_overwrite_permission(target);\n        }\n\n        /**\n         * Update the overall sequence progress with the intra-operation\n         * progress.\n         *\n         * This uses of resolution of 100 update intervals per file in the\n         * sequence.  In other words, the intra-operation is converted to a\n         * percentage.\n         */\n        virtual void update_progress(uintmax_t so_far, uintmax_t out_of)\n        {\n            uintmax_t percent_done = percentage(so_far, out_of);\n\n            uintmax_t current = (m_current_file_index * 100) + percent_done;\n\n            m_callback.update_progress(current, m_total_files * 100);\n        }\n\n    private:\n        OperationCallback& m_callback;\n        size_t m_current_file_index;\n        const size_t m_total_files;\n    };\n\n    /**\n     * Executes one of a sequence of operations.\n     *\n     * Purpose: to liaise between the Operation and the DropActionCallback\n     * interface used to communicator with the user.\n     *\n     * The DropActionCallback creates and starts the progress dialogue when\n     * it is requested so part of that liaison is making sure this only\n     * happens once for the entire sequence of operations.\n     */\n    class OperationExecutor : private OperationCallback\n    {\n    public:\n        OperationExecutor(DropActionCallback& callback)\n            : m_callback(callback) {}\n\n        void operator()(\n            const Operation& operation, size_t operation_index,\n            size_t total_operations,\n            shared_ptr<sftp_provider> provider)\n        {\n            progress().line_path(1, operation.title());\n            progress().line_path(2, operation.description());\n\n            IntraSequenceCallback micro_updater(\n                *this, operation_index, total_operations);\n\n            check_if_user_cancelled();\n\n            operation(micro_updater, provider);\n\n            // We update here as well, fixing the progress to a file\n            // boundary, as we don't completely trust the intra-file\n            // progress.  A stream could have lied about its size messing\n            // up the count.  This will override any such errors.\n\n            assert(operation_index + 1 <= total_operations);\n            progress().update(operation_index + 1, total_operations);\n        }\n\n        virtual void check_if_user_cancelled() const\n        {\n            if (m_progress.get())\n            {\n                if (m_progress->user_cancelled())\n                    BOOST_THROW_EXCEPTION(com_error(E_ABORT));\n            }\n        }\n\n        virtual bool request_overwrite_permission(const path& target) const\n        {\n            return m_callback.can_overwrite(target);\n        }\n\n        virtual void update_progress(uintmax_t so_far, uintmax_t out_of)\n        {\n            progress().update(so_far, out_of);\n        }\n\n    private:\n\n        Progress& progress()\n        {\n            if (!m_progress.get())\n                m_progress = m_callback.progress();\n\n            return *m_progress;\n        }\n\n        DropActionCallback& m_callback;\n        auto_ptr<Progress> m_progress;\n    };\n\n}\n\nvoid SequentialPlan::execute_plan(\n    DropActionCallback& callback, shared_ptr<sftp_provider> provider) const\n{\n    OperationExecutor executor(callback);\n\n    for (unsigned int i = 0; i < m_copy_list.size(); ++i)\n    {\n        const Operation& operation = m_copy_list.at(i);\n        executor(operation, i, m_copy_list.size(), provider);\n    }\n}\n\nvoid SequentialPlan::add_stage(const Operation& entry)\n{\n    m_copy_list.push_back(entry.clone());\n}\n\n}}\n"
  },
  {
    "path": "swish/drop_target/SequentialPlan.hpp",
    "content": "/**\n    @file\n\n    Standard drop operation plan implementation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_SEQUENTIALPLAN_HPP\n#define SWISH_DROP_TARGET_SEQUENTIALPLAN_HPP\n#pragma once\n\n#include \"swish/drop_target/Plan.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <boost/ptr_container/ptr_vector.hpp>\n#include <boost/shared_ptr.hpp>\n\nnamespace swish {\nnamespace drop_target {\n\nclass Operation;\n\n/**\n * Standard plan implementation made from list of Operation objects.\n *\n * Each object is executed in the order they are added and progress\n * displayed accordingly.\n */\nclass SequentialPlan /* final */ : public Plan\n{\npublic: // Plan\n\n    virtual void execute_plan(\n        DropActionCallback& callback,\n        boost::shared_ptr<swish::provider::sftp_provider> provider) const;\n\npublic:\n\n    void add_stage(const Operation& entry);\n\nprivate:\n\n    boost::ptr_vector<Operation> m_copy_list;\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/drop_target/SftpDestination.hpp",
    "content": "/**\n    @file\n\n    Abstraction of SFTP drop destination.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_DROP_TARGET_SFTPDESTINATION_HPP\n#define SWISH_DROP_TARGET_SFTPDESTINATION_HPP\n#pragma once\n\n#include \"swish/remote_folder/swish_pidl.hpp\" // absolute_path_from_swish_pidl\n#include \"swish/remote_folder/remote_pidl.hpp\" // create_remote_itemid\n\n#include <washer/shell/pidl.hpp> // apidl_t\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <stdexcept> // logic_error\n#include <string>\n\nnamespace swish {\nnamespace drop_target {\n\n/**\n * A destination (directory or file) on the remote server given as a\n * directory PIDL and a filename.\n */\nclass resolved_destination\n{\npublic:\n    resolved_destination(\n        const washer::shell::pidl::apidl_t& remote_directory,\n        const std::wstring& filename)\n        : m_remote_directory(remote_directory), m_filename(filename)\n    {\n        if (ssh::filesystem::path(m_filename).has_parent_path())\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\n                    \"Path not properly resolved; filename expected\"));\n    }\n\n    const washer::shell::pidl::apidl_t& directory() const\n    {\n        return m_remote_directory;\n    }\n\n    const std::wstring filename() const\n    {\n        return m_filename;\n    }\n\n    ssh::filesystem::path as_absolute_path() const\n    {\n        return swish::remote_folder::absolute_path_from_swish_pidl(\n            m_remote_directory) / m_filename;\n    }\n\nprivate:\n    washer::shell::pidl::apidl_t m_remote_directory;\n    std::wstring m_filename;\n};\n\n/**\n * A destination (directory or file) on the remote server given as a\n * path relative to a PIDL.\n *\n * As in an FGD, the path may be multi-level.  The directories named by the\n * intermediate sections may not exist so care must be taken that the,\n * destinations are used in the order listed in the FGD which is designed\n * to make sure they exist.\n */\nclass SftpDestination\n{\npublic:\n    SftpDestination(\n        const washer::shell::pidl::apidl_t& remote_root,\n        const ssh::filesystem::path& relative_path)\n        : m_remote_root(remote_root), m_relative_path(relative_path)\n    {\n        if (relative_path.is_absolute())\n            BOOST_THROW_EXCEPTION(\n                std::logic_error(\"Path must be relative to root\"));\n    }\n\n    resolved_destination resolve_destination() const\n    {\n        washer::shell::pidl::apidl_t directory = m_remote_root;\n\n        BOOST_FOREACH(\n            ssh::filesystem::path intermediate_directory_name,\n            m_relative_path.parent_path())\n        {\n            directory += swish::remote_folder::create_remote_itemid(\n                intermediate_directory_name.wstring(), true, false,\n                L\"\", L\"\", 0, 0, 0, 0,\n                comet::datetime_t::now(), comet::datetime_t::now());\n        }\n\n        return resolved_destination(\n            directory, m_relative_path.filename().wstring());\n    }\n\n    SftpDestination operator/(const ssh::filesystem::path& path) const\n    {\n        return SftpDestination(m_remote_root, m_relative_path / path);\n    }\n\n    std::wstring root_name() const\n    {\n        using washer::shell::pidl_shell_item;\n\n        return pidl_shell_item(m_remote_root).friendly_name(\n            pidl_shell_item::friendly_name_type::absolute);\n    }\n\nprivate:\n    washer::shell::pidl::apidl_t m_remote_root;\n    ssh::filesystem::path m_relative_path;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/forms/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  add_host.cpp\n  password.cpp\n  add_host.hpp\n  password.hpp)\n\nadd_library(forms ${SOURCES})\n\nhunter_add_package(Washer)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(forms\n  PRIVATE host_folder Washer::washer ${Boost_LIBRARIES})"
  },
  {
    "path": "swish/forms/add_host.cpp",
    "content": "/**\n    @file\n\n    New host dialogue.\n\n    @if license\n\n    Copyright (C) 2010, 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"add_host.hpp\"\n\n#include \"swish/remotelimits.h\"\n#include \"swish/host_folder/host_management.hpp\" // ConnectionExists\n\n#include <ezel/controls/button.hpp> // button\n#include <ezel/controls/edit.hpp> // edit\n#include <ezel/controls/icon.hpp> // icon\n#include <ezel/controls/label.hpp> // label\n#include <ezel/controls/line.hpp> // line\n#include <ezel/controls/spinner.hpp> // spinner\n#include <ezel/form.hpp> // form\n\n#include <washer/dynamic_link.hpp> // module_handle\n#include <washer/gui/icon/icon.hpp> // load_icon\n\n#include <boost/bind.hpp> // bind\n#include <boost/lexical_cast.hpp> // lexical_cast\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <exception> // exception\n#include <string>\n\nusing swish::host_folder::host_management::ConnectionExists;\n\nusing washer::module_handle;\nusing ezel::form;\nusing washer::gui::hicon;\nusing washer::gui::load_icon;\nusing namespace ezel::controls;\n\nusing boost::bad_lexical_cast;\nusing boost::bind;\nusing boost::lexical_cast;\nusing boost::locale::translate;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace forms {\n\nnamespace {\n    \n    const int DEFAULT_PORT = 22;\n\n    const wchar_t* FORBIDDEN_USER_CHARS = L\":\\t\\n\\r\\b\";\n    const wchar_t* FORBIDDEN_CHARS = L\"@: \\t\\n\\r\\b\\\"'\\\\\";\n    const wchar_t* FORBIDDEN_PATH_CHARS = L\"\\\"\\t\\n\\r\\b\\\\\";\n\n    const wchar_t* ICON_MODULE = L\"user32.dll\";\n    const int ICON_ERROR = 103;\n    const int ICON_INFO = 104;\n    const int ICON_SIZE = 16;\n\n    /**\n     * Host information entry dialog box.\n     *\n     * The dialog obtains SSH connection information from the user.\n     *\n     * Text fields:\n     * - \"Name:\" Friendly name for connection\n     * - \"User:\" SSH acount user name\n     * - \"Host:\" Remote host address/name\n     * - \"Path:\" Path for initial listing\n     * Numeric field:\n     * - \"Port:\" TCP/IP port to connect over\n     *\n     * @image html \"add_host.png\" \"Dialogue box appearance\"\n     */\n    class AddHostForm\n    {\n    public:\n        AddHostForm(HWND owner)\n            :\n            m_form(translate(L\"New SFTP Connection\"), 0, 0, 275, 176),\n            m_cancelled(true),\n            m_name_box(edit(L\"\", 42, 9, 222, 13)),\n            m_host_box(\n                edit(L\"\", 42, 58, 156, 13, edit::style::force_lowercase)),\n            m_port_box(\n                edit(L\"\", 228, 58, 26, 13, edit::style::only_allow_numbers)),\n            m_port_spinner(\n                spinner(\n                    254, 58, 10, 13, MIN_PORT, MAX_PORT, DEFAULT_PORT,\n                    spinner::style::no_thousand_separator)),\n            m_user_box(edit(L\"\", 42, 76, 156, 13)),\n            m_path_box(edit(L\"\", 42, 115, 222, 13)),\n            m_status(\n                label(\n                    L\"\", 23, 158, 135, 20,\n                    label::style::ampersand_not_special)),\n            m_icon(icon(2,153,21,20)),\n            m_ok(\n                button(\n                    translate(L\"Create\"), 162, 155, 50, 14, true))\n        {\n            // every time a field is changed we revalidate all the fields,\n            // enable or disable the OK button and a display a status message\n            // if needed\n            m_name_box.on_change().connect(\n                bind(&AddHostForm::update_validity, this));\n            m_host_box.on_change().connect(\n                bind(&AddHostForm::update_validity, this));\n            m_user_box.on_change().connect(\n                bind(&AddHostForm::update_validity, this));\n            m_path_box.on_change().connect(\n                bind(&AddHostForm::update_validity, this));\n            m_port_box.on_change().connect(\n                bind(&AddHostForm::update_validity, this));\n\n            // TODO: clarify difference between on_change and on_text_changed\n            m_port_box.on_text_changed().connect(\n                bind(&AddHostForm::update_validity, this));\n\n            m_form.add_control(\n                label(translate(L\"New Host\", L\"&Label:\"), 12, 11, 28, 8));\n            m_form.add_control(m_name_box);\n\n            m_form.add_control(\n                label(translate(\n                    L\"For example: \\\"Home Computer\\\".\"), 42, 25, 228, 18));\n            m_form.add_control(\n                label(translate(\n                    L\"Specify the details of the computer and account you \"\n                    L\"would like to connect to:\"), 12, 45, 258, 18));\n            \n            m_form.add_control(\n                label(translate(L\"New Host\", L\"&Host:\"), 12, 60, 30, 8));\n            m_form.add_control(m_host_box);\n\n            m_form.add_control(\n                label(translate(L\"New Host\", L\"&Port:\"), 204, 60, 18, 8));\n            m_form.add_control(m_port_box);\n            m_form.add_control(m_port_spinner);\n\n            m_form.add_control(\n                label(translate(L\"New Host\", L\"&User:\"), 12, 78, 56, 8));\n            m_form.add_control(m_user_box);\n\n            m_form.add_control(\n                label(translate(\n                    L\"Specify the directory on the server that you would like \"\n                    L\"Swish to start the connection in:\"), 12, 96, 258, 18));\n\n            m_form.add_control(\n                label(translate(L\"New Host\", L\"P&ath:\"), 12, 117, 35, 8));\n            m_form.add_control(m_path_box);\n            m_form.add_control(\n                label(\n                    translate(L\"Example: /home/yourusername\"),\n                    42, 131, 144, 8));\n            \n            m_form.add_control(line(0, 147, 277));\n\n            m_ok.on_click().connect(bind(&AddHostForm::on_ok, this));\n            m_form.add_control(m_ok);\n\n            button cancel(translate(L\"Cancel\"), 216, 155, 50, 14);\n            cancel.on_click().connect(m_form.killer());\n            m_form.add_control(cancel);\n\n            m_form.add_control(m_status);\n            m_form.add_control(m_icon);\n\n            m_error = load_icon(\n                module_handle(ICON_MODULE), ICON_ERROR, ICON_SIZE, ICON_SIZE);\n            m_information = load_icon(\n                module_handle(ICON_MODULE), ICON_INFO, ICON_SIZE, ICON_SIZE);\n            \n            update_validity();\n            m_form.show(owner);\n        }\n\n        /** @name Accessors */\n        // @{\n        bool was_cancelled() const { return m_cancelled; }\n\n        const std::wstring name() const { return m_name_box.text(); }\n        const std::wstring host() const { return m_host_box.text(); }\n        const std::wstring user() const { return m_user_box.text(); }\n        int port() const { return lexical_cast<int>(m_port_box.text()); }\n        const std::wstring path() const { return m_path_box.text(); }\n        // @}\n\n    private:        \n\n        /** @name Field validity */\n        // @{\n\n        /**\n         * Check if the value in the dialog box Name field is valid.\n         *\n         * Criteria:\n         * - The field must not contain more than @ref MAX_LABEL_LEN\n         *   characters.\n         */\n        bool is_valid_name() const\n        {\n            return name().length() <= MAX_LABEL_LEN;\n        }\n\n        /**\n         * Check if the value in the dialog box Host field is valid.\n         *\n         * Criteria:\n         * - The field must not contain more than @ref MAX_HOSTNAME_LEN\n         *   characters and must not contain any characters from\n         *   @ref FORBIDDEN_CHARS\n         *\n         * @todo Use a regexp to do this properly.\n         */\n        bool is_valid_host() const\n        {\n            wstring text = host();\n            return text.length() <= MAX_HOSTNAME_LEN &&\n                text.find_first_of(FORBIDDEN_CHARS) == wstring::npos;\n        }\n\n        /**\n         * Check if the value in the dialog box User field is valid.\n         *\n         * Criteria:\n         * - The field must not contain more than @ref MAX_USERNAME_LEN\n         *   characters and must not contain any characters from\n         *   @ref FORBIDDEN_USER_CHARS.\n         *\n         * @todo The validity criteria are woefully inadequate:\n         * - There are many characters that are not allowed in usernames.\n         */\n        bool is_valid_user() const\n        {\n            wstring text = user();\n            return text.length() <= MAX_USERNAME_LEN &&\n                text.find_first_of(FORBIDDEN_USER_CHARS) == wstring::npos;\n        }\n\n        /**\n         * Checks if the value in the dialog box Port field is valid.\n         *\n         * Criteria:\n         * - The field must contain a number between 0 and 65535\n         *   (@ref MAX_PORT).\n         */\n        bool is_valid_port() const\n        {\n            try\n            {\n                return port() >= MIN_PORT && port() <= MAX_PORT;\n            }\n            catch (const bad_lexical_cast&) { return false; }\n        }\n\n        /**\n         * Checks if the value in the dialog box Path field is valid.\n         *\n         * Criteria:\n         * - The path field must not contain more than @ref MAX_PATH_LEN\n         *   characters and must not contain any characters from\n         *   @ref FORBIDDEN_PATH_CHARS.\n         *\n         * @todo The validity criteria are woefully inadequate:\n         * - Paths can contain almost any character.  Some will have to be\n         *   escaped.\n         */\n        bool is_valid_path() const\n        {\n            wstring text = path();\n            return text.length() <= MAX_PATH_LEN &&\n                text.find_first_of(FORBIDDEN_PATH_CHARS) == wstring::npos;\n        }\n\n        bool all_fields_complete() const\n        {\n            return !(name().empty() || host().empty() || user().empty() ||\n                path().empty());\n        }\n\n        // @}\n\n            \n        /** @name Event handlers */\n        // @{\n\n        void on_ok()\n        {\n            m_form.end();\n            m_cancelled = false;\n        }\n\n        /**\n         * Disable the OK button if a field in the dialog is invalid.\n         *\n         * Also set the status icon and message.\n         */\n        void update_validity()\n        {\n            bool enable_ok = false;\n            bool info_icon_if_error = false;\n\n            if (!is_valid_name())\n            {\n                m_status.text(\n                    translate(\n                        L\"The label cannot be longer than 30 characters.\"));\n            }\n            else if (!is_valid_host())\n            {\n                m_status.text(translate(L\"The host name is invalid.\"));\n            }\n            else if (!is_valid_port())\n            {\n                m_status.text(\n                    translate(\n                        L\"The port is not valid (between 0 and 65535).\"));\n            }\n            else if (!is_valid_user())\n            {\n                m_status.text(translate(L\"The username is invalid.\"));\n            }\n            else if (!is_valid_path())\n            {\n                m_status.text(translate(L\"The path is invalid.\"));\n            }\n            else if (ConnectionExists(name()))\n            {\n                m_status.text(\n                    translate(\n                        L\"A connection with the same label already exists. \"\n                        L\"Please try another.\"));\n            }\n            else if (!all_fields_complete())\n            {\n                info_icon_if_error = true;\n                m_status.text(translate(L\"Complete all fields.\"));\n            }\n            else\n            {\n                enable_ok = true;\n            }\n            \n            if (!enable_ok)\n            {\n                m_icon.change_icon(\n                    (info_icon_if_error) ?\n                        m_information.get() : m_error.get());\n            }\n\n            m_icon.visible(!enable_ok);\n            m_status.visible(!enable_ok);\n            m_ok.enable(enable_ok);\n        }\n\n        // @}\n\n        form m_form;\n        bool m_cancelled;\n        \n        /** @name GUI controls */\n        // @{\n        edit m_name_box;\n        edit m_host_box;\n        edit m_port_box;\n        spinner m_port_spinner;\n        edit m_user_box;\n        edit m_path_box;\n        label m_status; ///< Status message window\n        icon m_icon;    ///< Status icon display area\n        button m_ok;\n        // @}\n\n        /** @name Preloaded icons */\n        // @{\n        hicon m_error;       ///< Small icon displaying a red error cross\n        hicon m_information; ///< Small icon displaying a blue 'i' symbol\n        // @}\n    };\n}\n\n/**\n * Display add host dialogue box and return the details entered by the user.\n *\n * @throws  If the user cancels the dialogue.\n */\nhost_info add_host(HWND owner)\n{\n    AddHostForm host_form(owner);\n\n    if (host_form.was_cancelled())\n        BOOST_THROW_EXCEPTION(std::exception(\"user cancelled form\"));\n\n    host_info info = {\n        host_form.name(), host_form.host(), host_form.user(),\n        host_form.port(), host_form.path() };\n\n    return info;\n}\n    \n}} // namespace swish::forms\n"
  },
  {
    "path": "swish/forms/add_host.hpp",
    "content": "/**\n    @file\n\n    New host dialogue.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FORMS_ADD_HOST_HPP\n#define SWISH_FORMS_ADD_HOST_HPP\n#pragma once\n\n#include <string>\n\n#include <Windows.h> // HWND\n\nnamespace swish {\nnamespace forms {\n\nstruct host_info\n{\n    std::wstring name;\n    std::wstring host;\n    std::wstring user;\n    int port;\n    std::wstring path;\n};\n\nhost_info add_host(HWND owner);\n    \n}} // namespace swish::forms\n\n#endif\n"
  },
  {
    "path": "swish/forms/password.cpp",
    "content": "/**\n    @file\n\n    Form class for login password prompt.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"password.hpp\"\n\n#include <ezel/controls/button.hpp> // button\n#include <ezel/controls/edit.hpp> // edit\n#include <ezel/controls/label.hpp> // label\n#include <ezel/form.hpp> // form\n\n#include <boost/bind.hpp> // bind\n#include <boost/locale.hpp> // translate\n\n#include <string>\n\nusing ezel::form;\nusing namespace ezel::controls;\n\nusing boost::bind;\nusing boost::locale::translate;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace forms {\n\nnamespace {\n\n    class PasswordForm\n    {\n    public:\n        PasswordForm(HWND hwnd_owner, const wstring& prompt)\n            :\n            m_form(L\"Swish\", 0, 0, 219, 49),\n            m_cancelled(true),\n            m_password_box(edit(L\"\", 7, 18, 148, 14, edit::style::password))\n        {\n            m_form.add_control(m_password_box);\n            m_form.add_control(label(prompt, 7, 7, 149, 8));\n\n            button ok(translate(L\"OK\"), 162, 7, 50, 16, true);\n            ok.on_click().connect(bind(&PasswordForm::on_ok, this));\n            m_form.add_control(ok);\n\n            button cancel(translate(L\"Cancel\"), 162, 26, 50, 16);\n            cancel.on_click().connect(m_form.killer());\n            m_form.add_control(cancel);\n            \n            m_form.show(hwnd_owner);\n        }\n\n        void on_ok()\n        {\n            m_form.end();\n            m_cancelled = false;\n        }\n\n        bool was_cancelled() const\n        { return m_cancelled; }\n\n        wstring password() const\n        { return m_password_box.text(); }\n\n    private:\n        form m_form;\n        bool m_cancelled;\n        edit m_password_box;\n    };\n}\n\nbool password_prompt(\n    HWND hwnd_owner, const wstring& prompt, wstring& password_out)\n{\n    PasswordForm pass_form(hwnd_owner, prompt);\n    if (pass_form.was_cancelled())\n    {\n        password_out.clear();\n        return false;\n    }\n\n    password_out = pass_form.password();\n    return true;\n}\n    \n}} // namespace swish::forms\n"
  },
  {
    "path": "swish/forms/password.hpp",
    "content": "/**\n    @file\n\n    Login password prompt.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FORMS_PASSWORD_HPP\n#define SWISH_FORMS_PASSWORD_HPP\n#pragma once\n\n#include <string>\n\n#include <windows.h> // HWND\n\nnamespace swish {\nnamespace forms {\n\n\nbool password_prompt(\n    HWND hwnd_owner, const std::wstring& prompt, std::wstring& password_out);\n    \n}} // namespace swish::forms\n\n#endif\n"
  },
  {
    "path": "swish/frontend/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(COMMAND_SOURCES\n  commands/About.cpp\n  commands/About.hpp)\n\nset(SOURCES\n  announce_error.hpp\n  bind_best_taskdialog.hpp\n  UserInteraction.hpp\n  winsparkle_shower.hpp\n  announce_error.cpp\n  bind_best_taskdialog.cpp\n  UserInteraction.cpp\n  winsparkle_shower.cpp\n  ${COMMAND_SOURCES})\n\nadd_library(frontend ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\nhunter_add_package(WinSparkle)\nhunter_add_package(WTL)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\nfind_package(WinSparkle REQUIRED CONFIG)\n\ntarget_link_libraries(frontend\n  PRIVATE nse versions WinSparkle::WinSparkle\n  PUBLIC WTL::wtl Washer::washer Comet::comet)"
  },
  {
    "path": "swish/frontend/UserInteraction.cpp",
    "content": "/**\n    @file\n\n    Component to handle user-interaction between the user and an SftpProvider.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2011, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"UserInteraction.hpp\"\n\n#include \"swish/debug.hpp\"\n#include \"swish/forms/password.hpp\" // password_prompt\n#include \"swish/frontend/bind_best_taskdialog.hpp\" // best_taskdialog\n#include \"swish/shell_folder/KbdInteractiveDialog.h\" // Keyboard-interactive auth dialog box\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n#include <washer/gui/task_dialog.hpp> // task_dialog_builder\n#include <washer/gui/message_box.hpp> // message_box\n\n#include <comet/error.h> // com_error\n\n#include <boost/bind.hpp> // bind\n#include <boost/format.hpp> // format\n#include <boost/locale.hpp> // translate\n\n#include <cassert> // assert\n#include <sstream> // wstringstream\n#include <string>\n\nusing swish::forms::password_prompt;\nusing swish::frontend::best_taskdialog;\n\nusing namespace washer::gui;\n\nusing comet::com_error;\n\nusing boost::filesystem::path;\nusing boost::locale::translate;\nusing boost::optional;\nusing boost::wformat;\n\nusing std::pair;\nusing std::string;\nusing std::vector;\nusing std::wstring;\nusing std::wstringstream;\n\nnamespace swish {\nnamespace frontend {\n\nCUserInteraction::CUserInteraction(HWND hwnd) : m_hwnd(hwnd) {}\n\n/**\n * Displays UI dialog to get password from user and returns it.\n *\n * If no window given, abort authentication.\n */\noptional<wstring> CUserInteraction::prompt_for_password()\n{\n    if (m_hwnd)\n    {\n        wstring password;\n        if (password_prompt(\n                m_hwnd, translate(L\"Prompt on password dialog\", L\"Password:\"),\n                password))\n        {\n            return password;\n        }\n    }\n\n    return optional<wstring>();\n}\n\n\noptional<pair<path, path>> CUserInteraction::key_files()\n{\n    // Swish doesn't use this way of pub-key auth - it uses Pageant via\n    // the agent interface.  This method is only implemented by unit\n    // test helpers.\n    return optional<pair<path, path>>();\n}\n\noptional<vector<string>> CUserInteraction::challenge_response(\n    const string& title, const string& instructions,\n    const vector<pair<string, bool>>& prompts)\n{\n    if (!m_hwnd)\n        BOOST_THROW_EXCEPTION(com_error(\"User interation forbidden\", E_FAIL));\n\n    // We don't show the dialog if there is nothing to tell the user.\n    // Kb-int authentication usually seems to end with such an empty\n    // interaction for some reason.\n    if (title.empty() && instructions.empty() && prompts.empty())\n    {\n        // Not optional<vector<string>> because that means abort.\n        return vector<string>();\n    }\n\n    // Show dialogue and fetch responses when user clicks OK\n    CKbdInteractiveDialog dlg(title, instructions, prompts);\n    if (dlg.DoModal(m_hwnd) == IDCANCEL)\n        return optional<vector<string>>();\n\n    return dlg.GetResponses();\n}\n\nnamespace {\n\nHRESULT on_confirm_overwrite(\n    const wstring& old_file, const wstring& new_file, HWND hwnd)\n{\n    assert(hwnd);\n    if (!hwnd)\n        BOOST_THROW_EXCEPTION(com_error(\"User interation forbidden\", E_FAIL));\n\n    wstringstream message;\n    \n    message << wformat(\n        translate(L\"The folder already contains a file named '%1%'\"))\n        % old_file;\n    message << L\"\\n\\n\";\n    message << wformat(\n        translate(\n            L\"Would you like to replace the existing file\\n\\n\\t%1%\\n\\n\"\n            L\"with this one?\\n\\n\\t%2%\")) % old_file % new_file;\n\n    message_box::button_type::type button_clicked = message_box::message_box(\n        hwnd, message.str(), translate(L\"File already exists\"),\n        message_box::box_type::yes_no, message_box::icon_type::question, 2);\n\n    switch (button_clicked)\n    {\n    case message_box::button_type::yes:\n        return S_OK;\n    case message_box::button_type::no:\n    default:\n        return E_ABORT;\n    }\n}\n\n}\n\nHRESULT CUserInteraction::OnConfirmOverwrite(\n    BSTR bstrOldFile, BSTR bstrNewFile )\n{\n    try\n    {\n        return on_confirm_overwrite(bstrOldFile, bstrNewFile, m_hwnd);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n}\n\n\nnamespace {\n\n/** Click handler callback. */\nHRESULT return_hr(HRESULT hr) { return hr; }\n\nHRESULT on_hostkey_mismatch(\n    const wstring& host, const wstring& key, const wstring& key_type,\n    HWND hwnd)\n{\n    if (!hwnd)\n        BOOST_THROW_EXCEPTION(com_error(\"User interation forbidden\", E_FAIL));\n\n    wstring title = translate(L\"Mismatched host-key\");\n    wstring instruction = translate(L\"WARNING: the SSH host-key has changed!\");\n\n    wstringstream message;\n    \n    message << wformat(\n        translate(\n            L\"The SSH host-key sent by '%1%' to identify itself doesn't match \"\n            L\"the known key for this server.  This could mean a third-party \"\n            L\"is pretending to be the computer you're trying to connect to \"\n            L\"or the system administrator may have just changed the key.\"))\n        % host;\n    message << L\"\\n\\n\";\n    message << translate(\n        L\"It is important to check this is the right key fingerprint:\");\n    message << wformat(L\"\\n\\n        %1%    %2%\") % key_type % key;\n\n    task_dialog::task_dialog_builder<HRESULT, best_taskdialog> td(\n        hwnd, instruction, message.str(), title,\n        washer::gui::task_dialog::icon_type::warning, true,\n        boost::bind(return_hr, E_ABORT));\n    td.add_button(\n        translate(\n            L\"I trust this key: &update and connect\\n\"\n            L\"You won't have to verify this key again unless it changes\"),\n        boost::bind(return_hr, S_OK));\n    td.add_button(\n        translate(\n            L\"I trust this key: &just connect\\n\"\n            L\"You will be warned about this key again next time you \"\n            L\"connect\"),\n        boost::bind(return_hr, S_FALSE));\n    td.add_button(\n        translate(\n            L\"&Cancel\\n\"\n            L\"Choose this option unless you are sure the key is correct\"),\n        boost::bind(return_hr, E_ABORT), true);\n    return td.show();\n}\n\nHRESULT on_hostkey_unknown(\n    const wstring& host, const wstring& key, const wstring& key_type,\n    HWND hwnd)\n{\n    if (!hwnd)\n        BOOST_THROW_EXCEPTION(com_error(\"User interation forbidden\", E_FAIL));\n\n    wstring title = translate(L\"Unknown host-key\");\n\n    wstringstream message;\n    \n    message << wformat(\n        translate(\n            L\"The server '%1%' has identified itself with an SSH host-key \"\n            L\"whose fingerprint is:\")) % host;\n    message << wformat(L\"\\n\\n        %1%    %2%\\n\\n\") % key_type % key;\n    message << translate(\n        L\"If you are not expecting this key, a third-party may be pretending \"\n        L\"to be the computer you're trying to connect to.\");\n\n    wstring instruction = translate(L\"Verify unknown SSH host-key\");\n    task_dialog::task_dialog_builder<HRESULT, best_taskdialog> td(\n        hwnd, instruction, message.str(), title,\n        washer::gui::task_dialog::icon_type::information, true,\n        boost::bind(return_hr, E_ABORT));\n    td.add_button(\n        translate(\n            L\"I trust this key: &store and connect\\n\"\n            L\"You won't have to verify this key again unless it changes\"),\n        boost::bind(return_hr, S_OK));\n    td.add_button(\n        translate(\n            L\"I trust this key: &just connect\\n\"\n            L\"You will be asked to verify the key again next time you \"\n            L\"connect\"),\n        boost::bind(return_hr, S_FALSE));\n    td.add_button(\n        translate(\n            L\"&Cancel\\n\"\n            L\"Choose this option unless you are sure the key is correct\"),\n        boost::bind(return_hr, E_ABORT), true);\n    return td.show();\n}\n}\n\nHRESULT CUserInteraction::OnHostkeyMismatch(\n    BSTR bstrHostName, BSTR bstrHostKey, BSTR bstrHostKeyType)\n{\n    try\n    {\n        return on_hostkey_mismatch(\n            bstrHostName, bstrHostKey, bstrHostKeyType, m_hwnd);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n}\n\nHRESULT CUserInteraction::OnHostkeyUnknown(\n    BSTR bstrHostName, BSTR bstrHostKey, BSTR bstrHostKeyType)\n{\n    try\n    {\n        return on_hostkey_unknown(\n            bstrHostName, bstrHostKey, bstrHostKeyType, m_hwnd);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n}\n\n}} // namespace swish::frontend\n"
  },
  {
    "path": "swish/frontend/UserInteraction.hpp",
    "content": "/**\n    @file\n\n    Component to handle user-interaction between the user and an SftpProvider.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2011, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FRONTEND_USERINTERACTION_HPP\n#define SWISH_FRONTEND_USERINTERACTION_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // ISftpConsumer\n\n#include <comet/server.h> // simple_object\n\nnamespace swish {\nnamespace frontend {\n\nclass CUserInteraction : public comet::simple_object<ISftpConsumer>\n{\npublic:\n\n    typedef ISftpConsumer interface_is;\n\n    CUserInteraction(HWND hwnd);\n\n    /**\n     * User interaction callbacks.\n     * @{\n     */\n    // ISftpConsumer Methods\n\n    virtual boost::optional<std::wstring> prompt_for_password();\n\n    virtual boost::optional<\n        std::pair<boost::filesystem::path, boost::filesystem::path>>\n        key_files();\n\n    virtual boost::optional<std::vector<std::string>> challenge_response(\n        const std::string& title, const std::string& instructions,\n        const std::vector<std::pair<std::string, bool>>& prompts);\n\n    HRESULT OnConfirmOverwrite(\n        __in BSTR bstrOldFile,\n        __in BSTR bstrNewFile\n    );\n    HRESULT OnHostkeyMismatch(\n        __in BSTR bstrHostName,\n        __in BSTR bstrHostKey,\n        __in BSTR bstrHostKeyType\n    );\n    HRESULT OnHostkeyUnknown(\n        __in BSTR bstrHostName,\n        __in BSTR bstrHostKey,\n        __in BSTR bstrHostKeyType\n    );\n    /* @} */\n\nprivate:\n    HWND m_hwnd;      ///< Window to use as parent for user interaction.\n};\n\n}} // namespace swish::frontend\n\n#endif\n"
  },
  {
    "path": "swish/frontend/announce_error.cpp",
    "content": "/**\n    @file\n\n    Reporting exceptions to the user.\n\n    @if license\n\n    Copyright (C) 2010, 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"announce_error.hpp\"\n\n#include \"swish/frontend/bind_best_taskdialog.hpp\" // best_taskdialog\n\n#include <washer/gui/task_dialog.hpp> // task_dialog_builder\n\n#include <comet/error.h> // com_error\n\n#include <boost/exception/diagnostic_information.hpp>\n#include <boost/locale.hpp> // translate\n\n#include <cassert> // assert\n#include <exception>\n#include <sstream> // wstringstream\n\nusing swish::frontend::best_taskdialog;\n\nusing namespace washer::gui::task_dialog;\n\nusing comet::com_error;\n\nusing boost::locale::translate;\nusing boost::diagnostic_information;\n\nusing std::exception;\nusing std::wstring;\nusing std::wstringstream;\n\nnamespace {\n\n    wstring hexify_hr(HRESULT hr)\n    {\n        wstringstream stream;\n        stream << std::hex << std::showbase << hr;\n        return stream.str();\n    }\n\n#define SWISH_ANNOUNCE_ERROR_HRESULT_CASE(hr) case (hr): return L#hr;\n\n    wstring hresult_code(HRESULT hr)\n    {\n        switch (hr)\n        {\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(S_OK);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(S_FALSE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_UNEXPECTED);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_NOTIMPL);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_OUTOFMEMORY);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_INVALIDARG);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_NOINTERFACE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_POINTER);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_HANDLE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_ABORT);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_FAIL);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_ACCESSDENIED);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(E_PENDING);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_CANTSAVE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_INCOMPLETE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_FILENOTFOUND);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_ACCESSDENIED);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_UNIMPLEMENTEDFUNCTION);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_INVALIDHANDLE);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_FILEALREADYEXISTS);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_DISKISWRITEPROTECTED);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_MEDIUMFULL);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_LOCKVIOLATION);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_INVALIDPARAMETER);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_INVALIDFUNCTION);\n            SWISH_ANNOUNCE_ERROR_HRESULT_CASE(STG_E_INSUFFICIENTMEMORY);\n        default:\n            return hexify_hr(hr);\n        }\n    }\n\n#undef SWISH_ANNOUNCE_ERROR_HRESULT_CASE\n\n    /**\n     * @todo  Convert narrow to wide strings properly.\n     */\n    wstring format_exception(const exception& error)\n    {\n        wstringstream details;\n\n        details << error.what();\n        details << \"\\n\\nBug report information: \"\n            << diagnostic_information(error).c_str();\n\n        return details.str();\n    }\n\n    wstring format_exception(const com_error& error)\n    {\n        wstringstream details;\n\n        details << error.w_str();\n        details << L\"\\n\\nHRESULT: \" << hresult_code(error.hr());\n        details << \"\\n\\nBug report information: \"\n            << diagnostic_information(error).c_str();\n\n        return details.str();\n    }\n}\n\nnamespace swish {\nnamespace frontend {\n\nvoid announce_error(\n    HWND hwnd, const wstring& problem, const wstring& suggested_resolution,\n    const wstring& details)\n{\n    task_dialog_builder<void, best_taskdialog> td(\n        hwnd, problem, suggested_resolution, L\"Swish\", icon_type::error, true);\n    td.extended_text(\n        details, expansion_position::below,\n        initial_expansion_state::default,\n        translate(L\"Show &details (which may not be in your language)\"),\n        translate(L\"Hide &details\"));\n    td.show();\n}\n\nvoid announce_last_exception(\n    HWND hwnd, const wstring& title, const wstring& suggested_resolution,\n    bool force_ui)\n{\n    // Only try and announce if we have an owner window\n    if (!force_ui && hwnd == NULL)\n        return;\n\n    // Each call to announce_error below is guarded with a try/catch.\n    // I've tested these catch handler and they works the way I\n    // expected: they swallows the newly thrown exception allowing the\n    // 'throw' statement at the bottom to rethrow the original exception.\n    // XXX: I can't find whether this is guaranteed by the C++\n    // standard.  Implementing this behaviour must mean maintaining\n    // some form of thrown exception stack.\n    try\n    {\n        throw;\n    }\n    catch (const com_error& error)\n    {\n        try\n        {\n            if (error.hr() != E_ABORT)\n                announce_error(\n                    hwnd, title, suggested_resolution, format_exception(error));\n        }\n        catch (...) { assert(!\"Exception announcer threw new exception\"); }\n    }\n    catch (const exception& error)\n    {\n        try\n        {\n            announce_error(\n                hwnd, title, suggested_resolution, format_exception(error));\n        }\n        catch (...) { assert(!\"Exception announcer threw new exception\"); }\n    }\n#ifdef DEBUG\n    catch (...)\n    {\n        try\n        {\n            announce_error(\n                hwnd, title, suggested_resolution,\n                L\"Woooooo there soldier! Completely unrecognised type of \"\n                L\"exception.\");\n        }\n        catch (...) { assert(!\"Exception announcer threw new exception\"); }\n    }\n#endif\n}\n\n}} // namespace swish::frontend\n"
  },
  {
    "path": "swish/frontend/announce_error.hpp",
    "content": "/**\n    @file\n\n    Reporting exceptions to the user.\n\n    @if license\n\n    Copyright (C) 2010, 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FRONTEND_ANNOUNCE_ERROR_HPP\n#define SWISH_FRONTEND_ANNOUNCE_ERROR_HPP\n#pragma once\n\n#include <string>\n\n#include <Windows.h> // HWND\n\nnamespace swish {\nnamespace frontend {\n\nvoid announce_error(\n    HWND hwnd, const std::wstring& problem,\n    const std::wstring& suggested_resolution, const std::wstring& details);\n\nvoid announce_last_exception(\n    HWND hwnd, const std::wstring& title,\n    const std::wstring& suggested_resolution, bool force_ui=false);\n\n}} // namespace swish::frontend\n\n#endif\n"
  },
  {
    "path": "swish/frontend/bind_best_taskdialog.cpp",
    "content": "/**\n    @file\n\n    TaskDialogIndirect implementation selector.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"bind_best_taskdialog.hpp\"\n\n#include \"swish/atl.hpp\" // required by TaskDialog.h\n\n#include <washer/dynamic_link.hpp> // load_function\n\n#include <TaskDialog.h> // Task98DialogIndirect\n\n#include <exception>\n\nusing washer::gui::task_dialog::tdi_function;\nusing washer::load_function;\n\nusing std::exception;\n\nnamespace swish {\nnamespace frontend {\n\ntdi_function bind_best_taskdialog()\n{\n    try\n    {\n        return load_function<\n            HRESULT WINAPI (const TASKDIALOGCONFIG*, int*, int*, BOOL*)>(\n                \"comctl32.dll\", \"TaskDialogIndirect\");\n    }\n    catch (const exception&)\n    {\n        return ::Task98DialogIndirect;\n    }\n}\n\n}} // namespace swish::frontend\n"
  },
  {
    "path": "swish/frontend/bind_best_taskdialog.hpp",
    "content": "/**\n    @file\n\n    TaskDialogIndirect implementation selector.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FRONTEND_BIND_BEST_TASKDIALOG\n#define SWISH_FRONTEND_BIND_BEST_TASKDIALOG\n#pragma once\n\n#include <washer/gui/task_dialog.hpp> // tdi_function, tdi_implementation\n\nnamespace swish {\nnamespace frontend {\n\nwasher::gui::task_dialog::tdi_function bind_best_taskdialog();\n\nclass best_taskdialog : public washer::gui::task_dialog::tdi_implementation\n{\npublic:\n    best_taskdialog()\n        : washer::gui::task_dialog::tdi_implementation(bind_best_taskdialog())\n    {}\n};\n\n}} // namespace swish::frontend\n\n#endif\n"
  },
  {
    "path": "swish/frontend/commands/About.cpp",
    "content": "/* Copyright (C) 2013, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"About.hpp\"\n\n#include \"swish/versions/version.hpp\" // release_version\n\n#include <washer/gui/message_box.hpp>\n#include <washer/dynamic_link.hpp> // module_path\n\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/locale.hpp> // translate\n#include <boost/filesystem/path.hpp>\n\n#include <cassert> // assert\n#include <sstream> // ostringstream\n#include <string>\n\nusing swish::nse::Command;\nusing swish::nse::command_site;\n\nusing namespace washer::gui::message_box;\nusing washer::module_path;\nusing washer::shell::pidl::apidl_t;\nusing washer::window::window;\n\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::locale::translate;\nusing boost::filesystem::path;\nusing boost::optional;\n\nusing std::ostringstream;\nusing std::string;\n\n// http://stackoverflow.com/a/557859/67013\nEXTERN_C IMAGE_DOS_HEADER __ImageBase;\n\nnamespace swish {\nnamespace frontend {\nnamespace commands {\n\nnamespace {\n\n   const uuid_t ABOUT_COMMAND_ID(L\"b816a885-5022-11dc-9153-0090f5284f85\");\n\n   path installation_path()\n   {\n       return module_path<char>(((HINSTANCE)&__ImageBase));\n   }\n}\n\nAbout::About() :\n    Command(\n      translate(\n        L\"Title of command used to show the Swish 'About' box in the \"\n        L\"Explorer Help menu\",\n        L\"About &Swish\"), ABOUT_COMMAND_ID,\n      translate(\n          L\"Displays version, licence and copyright information for Swish.\"))\n    {}\n\nBOOST_SCOPED_ENUM(Command::state) About::state(\n    com_ptr<IShellItemArray>, bool /*ok_to_be_slow*/)\nconst\n{\n    return state::enabled;\n}\n\nvoid About::operator()(\n    com_ptr<IShellItemArray>, const command_site& site, com_ptr<IBindCtx>)\nconst\n{\n    optional<window<wchar_t>> view_window = site.ui_owner();\n    if (!view_window)\n        return;\n\n    string snapshot = snapshot_version();\n    if (snapshot.empty())\n    {\n        snapshot = translate(\n            \"Placeholder version if actual version is not known\",\n            \"unknown\");\n    }\n\n    ostringstream message;\n    message\n        << \"Swish \" << release_version().as_string() << \"\\n\"\n        << translate(\n            \"A short description of Swish\", \"Easy SFTP for Windows Explorer\")\n        << \"\\n\\n\"\n        << \"Copyright (C) 2006-2013  Alexander Lamaison and contributors.\\n\\n\"\n        << \"This program comes with ABSOLUTELY NO WARRANTY. This is free \"\n           \"software, and you are welcome to redistribute it under the terms \"\n           \"of the GNU General Public License as published by the Free \"\n           \"Software Foundation, either version 3 of the License, or \"\n           \"(at your option) any later version.\\n\\n\"\n        << translate(\"Title of a version description\", \"Snapshot:\")\n        << \" \" <<  snapshot << \"\\n\"\n        << translate(\"Title for a date and time\", \"Build time:\")\n        << \" \" << build_date() << \" \" << build_time() << \"\\n\"\n        << translate(\"Title of a filesystem path\", \"Installation path:\")\n        << \" \" << installation_path().string();\n\n   message_box(\n        view_window->hwnd(), message.str(),\n        translate(\"Title of About dialog box\", \"About Swish\"), box_type::ok,\n        icon_type::information);\n}\n\n}}} // namespace swish::frontend::commands\n"
  },
  {
    "path": "swish/frontend/commands/About.hpp",
    "content": "/* Copyright (C) 2013, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_FRONTEND_COMMANDS_ABOUT_HPP\n#define SWISH_FRONTEND_COMMANDS_ABOUT_HPP\n\n#include \"swish/nse/Command.hpp\" // Command\n\nnamespace swish {\nnamespace frontend {\nnamespace commands {\n\nclass About : public swish::nse::Command\n{\npublic:\n    About();\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n};\n\n}}} // namespace swish::frontend::commands\n\n#endif\n"
  },
  {
    "path": "swish/frontend/winsparkle_shower.cpp",
    "content": "/**\n    @file\n\n    Manage WinSparkle initialisation and cleanup.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"winsparkle_shower.hpp\"\n\n#include <winsparkle.h>\n\nusing std::string;\nusing std::wstring;\n\nnamespace swish {\nnamespace frontend {\n\nwinsparkle_shower::winsparkle_shower(\n    const string& appcast_url, const wstring& app_name, \n    const wstring& app_version, const wstring& company_name,\n    const string& relative_registry_path) : m_needs_cleanup(false)\n{\n    win_sparkle_set_appcast_url(appcast_url.c_str());\n    win_sparkle_set_registry_path(relative_registry_path.c_str());\n    win_sparkle_set_app_details(\n        company_name.c_str(), app_name.c_str(), app_version.c_str());\n}\n\nvoid winsparkle_shower::show()\n{\n    // the dialog may be requested more than once so we need to clean up\n    // before showing it again\n    if (m_needs_cleanup)\n        win_sparkle_cleanup();\n\n    m_needs_cleanup = true;\n    win_sparkle_init();\n}\n\nwinsparkle_shower::~winsparkle_shower()\n{\n    win_sparkle_cleanup();\n}\n\n}} // namespace swish::frontend\n"
  },
  {
    "path": "swish/frontend/winsparkle_shower.hpp",
    "content": "/**\n    @file\n\n    Manage WinSparkle initialisation and cleanup.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_FRONTEND_WINSPARKLE_SHOWER_HPP\n#define SWISH_FRONTEND_WINSPARKLE_SHOWER_HPP\n#pragma once\n\n#include <string>\n\nnamespace swish {\nnamespace frontend {\n\nclass winsparkle_shower\n{\npublic:\n    winsparkle_shower(\n        const std::string& appcast_url, const std::wstring& app_name,\n        const std::wstring& app_version, const std::wstring& company_name,\n        const std::string& relative_registry_path);\n    ~winsparkle_shower();\n\n    void show();\n\nprivate:\n    bool m_needs_cleanup;\n};\n\n}} // namespace swish::frontend\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(COMMAND_SOURCES\n  commands/Add.cpp\n  commands/Add.hpp\n  commands/CloseSession.cpp\n  commands/CloseSession.hpp\n  commands/commands.cpp\n  commands/commands.hpp\n  commands/LaunchAgent.cpp\n  commands/LaunchAgent.hpp\n  commands/Remove.cpp\n  commands/Remove.hpp\n  commands/Rename.cpp\n  commands/Rename.hpp)\n\nset(SOURCES\n  columns.cpp\n  context_menu_callback.hpp\n  context_menu_callback.cpp\n  host_itemid_connection.cpp\n  host_management.cpp\n  menu_command_manager.cpp\n  properties.cpp\n  ViewCallback.cpp\n  columns.hpp\n  extract_icon.hpp\n  host_itemid_connection.hpp\n  host_management.hpp\n  host_pidl.hpp\n  menu_command_manager.hpp\n  overlay_icon.hpp\n  properties.hpp\n  ViewCallback.hpp\n  ${COMMAND_SOURCES})\n\nadd_library(host_folder ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(host_folder\n  PRIVATE connection nse version forms frontend shell_folder shell\n  PUBLIC Washer::washer Comet::comet ${Boost_LIBRARIES})\n"
  },
  {
    "path": "swish/host_folder/ViewCallback.cpp",
    "content": "/*  Handler for host folder's interaction with Explorer Shell Folder View.\n\n    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2015\n    Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"ViewCallback.hpp\"\n\n#include \"swish/host_folder/commands/commands.hpp\" // host commands\n#include \"swish/shell/shell_item_array.hpp\"\n#include \"swish/utils.hpp\" // Utf8StringToWideString\n#include \"swish/versions/version.hpp\" // release_version\n\n#include <washer/error.hpp> // last_error\n#include <washer/shell/services.hpp> // shell_browser, shell_view\n#include <washer/window/window.hpp>\n\n#include <boost/exception/errinfo_api_function.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n#include <utility> // pair\n\nusing swish::host_folder::commands::host_folder_task_pane_tasks;\nusing swish::host_folder::commands::host_folder_task_pane_titles;\nusing swish::nse::IEnumUICommand;\nusing swish::nse::IUIElement;\nusing swish::release_version;\nusing swish::utils::Utf8StringToWideString;\n\nusing comet::com_ptr;\n\nusing washer::last_error;\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::shell_browser;\nusing washer::shell::shell_view;;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing boost::enable_error_info;\nusing boost::errinfo_api_function;\n\nusing std::auto_ptr;\nusing std::pair;\nusing std::wstring;\n\ntemplate<> struct comet::comtype<IDataObject>\n{\n    static const IID& uuid() throw() { return IID_IDataObject; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace host_folder {\n\nnamespace {\n\n    /**\n     * Return a ShellItemArray representing the items currently selected.\n     *\n     * @return NULL if nothing is selected.\n     */\n    com_ptr<IShellItemArray> selection_shell_item_array(\n        com_ptr<IShellBrowser> browser)\n    {\n        com_ptr<IShellView> view = shell_view(browser);\n\n        com_ptr<IShellItemArray> item_array;\n        view->GetItemObject(\n            SVGIO_SELECTION, item_array.iid(),\n            reinterpret_cast<void **>(item_array.out()));\n\n        // We don't care if getting the array succeeded - if it did, great;\n        // return it.  If not we will return a NULL pointer indicating that no\n        // items were selected\n\n        return item_array;\n    }\n\n    bool is_vista_or_greater()\n    {\n        OSVERSIONINFO version = OSVERSIONINFO();\n        version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\n        if (::GetVersionEx(&version) == FALSE)\n            BOOST_THROW_EXCEPTION(\n                enable_error_info(last_error()) <<\n                errinfo_api_function(\"GetVersionEx\"));\n\n        return version.dwMajorVersion > 5;\n    }\n}\n\n/**\n * Create customisation callback object for Explorer default shell view.\n *\n * @param folder  The folder for whom we are creating this callback object.\n */\nCViewCallback::CViewCallback(const apidl_t& folder) :\n    m_folder(folder),\n    m_winsparkle(\n        \"http://www.swish-sftp.org/autoupdate/appcast.xml\", L\"Swish\",\n        Utf8StringToWideString(release_version().as_string()),\n        L\"\", \"Software\\\\Swish\\\\Updates\") {}\n\n/**\n * The folder window is being created.\n *\n * The shell is notifying us of the folder view's window handle.\n */\nbool CViewCallback::on_window_created(HWND hwnd_view)\n{\n    if (hwnd_view)\n    {\n        m_view = window<wchar_t>(window_handle::foster_handle(hwnd_view));\n    }\n\n    if (m_view)\n    {\n        m_winsparkle.show();\n    }\n\n    return true;\n}\n\n/**\n * Tell the shell that we might notify it of update events that apply to this\n * folder (specified using our absolute PIDL).\n *\n * We are notified via SFVM_FSNOTIFY if any events indicated here occurr.\n *\n * @TODO: It's possible that @a events already has events set in its bitmask\n *        so maybe we should 'or' our extra events with it.\n */\nbool CViewCallback::on_get_notify(\n    PCIDLIST_ABSOLUTE& pidl_monitor, LONG& events)\n{\n    events = SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | SHCNE_RENAMEITEM |\n        SHCNE_RENAMEFOLDER | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR;\n    pidl_monitor = m_folder.get(); // Owned by us\n    return true;\n}\n\n/**\n * The shell is telling us that an event (probably a SHChangeNotify of some\n * sort) has affected one of our item.  Just nod. If we don't it doesn't work.\n */\nbool CViewCallback::on_fs_notify(\n    PCIDLIST_ABSOLUTE /*pidl*/, LONG /*event*/)\n{\n    return true;\n}\n\n\nbool CViewCallback::on_merge_menu(QCMINFO& menu_info)\n{\n    m_menu_manager = auto_ptr<menu_command_manager>(\n        new menu_command_manager(menu_info, m_view, m_folder));\n\n    return true;\n\n    // I would have expected to have to remove these menu items\n    // in SFVM_UNMERGEMENU but this seems to happen automatically\n}\n\nbool CViewCallback::on_selection_changed(\n    SFV_SELECTINFO& /*selection_info*/)\n{\n    update_menus();\n    return true;\n}\n\nbool CViewCallback::on_init_menu_popup(\n    UINT /*first_command_id*/, int /*menu_index*/, HMENU /*menu*/)\n{\n    update_menus();\n    return true;\n}\n\nbool CViewCallback::on_invoke_command(UINT command_id)\n{\n    return m_menu_manager->invoke(command_id, selection(), ole_site());\n}\n\n#pragma warning(push)\n#pragma warning(disable:4996) // std::copy ... may be unsafe\n\nbool CViewCallback::on_get_help_text(\n    UINT command_id, UINT buffer_size, LPTSTR buffer)\n{\n    wstring help_text;\n    bool handled = m_menu_manager->help_text(\n        command_id, help_text, selection());\n\n    if (handled)\n    {\n        size_t copied = help_text.copy(buffer, buffer_size - 1);\n        buffer[copied] = _T('\\0');\n\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\n#pragma warning(pop)\n\nbool CViewCallback::on_get_webview_content(\n    SFV_WEBVIEW_CONTENT_DATA& content_out)\n{\n    assert(content_out.pFolderTasksExpando == NULL);\n    assert(content_out.pExtraTasksExpando == NULL);\n    assert(content_out.pEnumRelatedPlaces == NULL);\n\n    // HACK: webview conflicts with ExplorerCommands so we disable it if\n    //       ExplorerCommands are likely to be used.\n    if (is_vista_or_greater())\n        return false;\n\n    pair< com_ptr<IUIElement>, com_ptr<IUIElement> > tasks =\n        host_folder_task_pane_titles(m_folder);\n\n    content_out.pExtraTasksExpando = tasks.first.detach();\n    content_out.pFolderTasksExpando = tasks.second.detach();\n    return true;\n}\n\nbool CViewCallback::on_get_webview_tasks(\n    SFV_WEBVIEW_TASKSECTION_DATA& tasks_out)\n{\n    //for some reason this fails on 64-bit\n    //assert(tasks_out.pEnumExtraTasks == NULL);\n\n    assert(tasks_out.pEnumFolderTasks == NULL);\n\n\n    // HACK: webview conflicts with ExplorerCommands so we disable it if\n    //       ExplorerCommands are likely to be used.\n    if (is_vista_or_greater())\n        return false;\n\n    pair< com_ptr<IEnumUICommand>, com_ptr<IEnumUICommand> > commands =\n        host_folder_task_pane_tasks(m_folder);\n\n    tasks_out.pEnumExtraTasks = commands.first.detach();\n    tasks_out.pEnumFolderTasks = commands.second.detach();\n    return true;\n}\n\n/**\n * Items currently selected in the folder view.\n */\ncom_ptr<IShellItemArray> CViewCallback::selection()\n{\n    com_ptr<IShellBrowser> browser = shell_browser(ole_site());\n\n    return selection_shell_item_array(browser);\n}\n\n/**\n * Update the menus to match the current selection.\n */\nvoid CViewCallback::update_menus()\n{\n    m_menu_manager->update_state(selection());\n}\n\n}} // namespace swish::host_folder\n"
  },
  {
    "path": "swish/host_folder/ViewCallback.hpp",
    "content": "/*  Handler for host folder's interaction with Explorer Shell Folder View.\n\n    Copyright (C) 2008, 2009, 2010, 2011, 2015\n    Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_VIEW_CALLBACK_HPP\n#define SWISH_HOST_FOLDER_VIEW_CALLBACK_HPP\n#pragma once\n\n#include \"swish/frontend/winsparkle_shower.hpp\" // winsparkle_shower\n#include \"swish/host_folder/menu_command_manager.hpp\"\n#include \"swish/nse/view_callback.hpp\" // CViewCallback\n\n#include <washer/object_with_site.hpp> // object_with_site\n#include <washer/gui/menu/item/item.hpp>\n#include <washer/shell/pidl.hpp> // apidl_t\n#include <washer/window/window.hpp>\n\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n\n#include <boost/optional/optional.hpp> // optional\n\n#include <memory>\n\nnamespace swish {\nnamespace host_folder {\n\nclass CViewCallback :\n    public comet::simple_object<nse::CViewCallback, washer::object_with_site>\n{\npublic:\n\n    CViewCallback(const washer::shell::pidl::apidl_t& folder_pidl);\n\nprivate:\n\n    /// @name  SFVM_* message handlers\n    // @{\n\n    virtual bool on_window_created(HWND hwnd_view);\n    virtual bool on_get_notify(PCIDLIST_ABSOLUTE& pidl_monitor, LONG& events);\n    virtual bool on_fs_notify(PCIDLIST_ABSOLUTE pidl, LONG event);\n    virtual bool on_merge_menu(QCMINFO& menu_info);\n    virtual bool on_selection_changed(SFV_SELECTINFO& selection_info);\n    virtual bool on_init_menu_popup(\n        UINT first_command_id, int menu_index, HMENU menu);\n    virtual bool on_invoke_command(UINT command_id);\n    virtual bool on_get_help_text(\n        UINT command_id, UINT buffer_size, LPTSTR buffer);\n    virtual bool on_get_webview_content(SFV_WEBVIEW_CONTENT_DATA& content_out);\n    virtual bool on_get_webview_tasks(SFV_WEBVIEW_TASKSECTION_DATA& tasks_out);\n\n    // @}\n\n    comet::com_ptr<IShellItemArray> selection();\n    void update_menus();\n\n    boost::optional<washer::window::window<wchar_t>> m_view;\n    ///< Folder view window\n\n    washer::shell::pidl::apidl_t m_folder; ///< Owning folder\n\n    swish::frontend::winsparkle_shower m_winsparkle; ///< Autoupdate checker\n\n    std::auto_ptr<menu_command_manager> m_menu_manager;\n};\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/columns.cpp",
    "content": "/**\n    @file\n\n    Host folder detail columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"columns.hpp\"\n\n#pragma warning(push)\n#pragma warning(disable: 4510 4610) // Cannot generate default constructor\n#include <boost/array.hpp> // array\n#pragma warning(pop)\n#include <boost/locale.hpp> // translate\n\n#include <string>\n\n#include <Commctrl.h> // LVCFMT_*\n#include <Propkey.h> // PKEY_ *\n\nusing washer::shell::property_key;\n\nusing boost::array;\nusing boost::locale::translate;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\n\nnamespace {\n\n    /**\n     * Static column information.\n     * Order of entries must correspond to the indices in columnIndices.\n     */\n    const boost::array<column_entry, 6> column_key_index = { {\n\n        { PKEY_ItemNameDisplay, translate(\"Property (filename/label)\", \"Name\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30 },\n\n        { PKEY_ComputerName, translate(\"Property\", \"Host\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30 },\n\n        { PKEY_SwishHostUser, translate(\"Property\", \"Username\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30 },\n\n        { PKEY_SwishHostPort, translate(\"Property\", \"Port\"),\n          SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20 },\n\n        { PKEY_ItemPathDisplay, translate(\"Property\", \"Remote path\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30 },\n\n        { PKEY_ItemType, translate(\"Property\", \"Type\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_SECONDARYUI, LVCFMT_LEFT, 30 }\n    } };\n\n}\n\n/**\n * Return a column entry.\n */\nconst column_entry& HostColumnEntries::entry(size_t index) const\n{\n    return column_key_index.at(index);\n}\n\n/**\n * Convert index to a corresponding PROPERTYKEY.\n */\nconst property_key& property_key_from_column_index(size_t index)\n{\n    return column_key_index.at(index).m_key;\n}\n\n}} // namespace swish::host_folder\n"
  },
  {
    "path": "swish/host_folder/columns.hpp",
    "content": "/**\n    @file\n\n    Host folder detail columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_COLUMNS_HPP\n#define SWISH_HOST_FOLDER_COLUMNS_HPP\n#pragma once\n\n#include \"properties.hpp\" // property_from_pidl, compare_pidls_by_property\n\n#include \"swish/nse/StaticColumn.hpp\" // StaticColumn\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <boost/locale/encoding_utf.hpp> // utf_to_utf\n#include <boost/locale.hpp> // message\n\n#include <ShTypes.h> // SHCOLSTATEF\n\n#include <string>\n\nnamespace swish {\nnamespace host_folder {\n\n#pragma warning(push)\n#pragma warning(disable: 4510 4610) // Cannot generate default constructor\n\nstruct column_entry\n{\n    /**\n     * @name Fields.\n     *\n     * These can be set using an aggregate initialiser: { val1, val2, ... }\n     */\n    // @{\n    washer::shell::property_key m_key;\n    boost::locale::message m_title;\n    SHCOLSTATEF m_flags;\n    int m_format;\n    int m_avg_char_width;\n    // @}\n\n    std::wstring title() const\n    {\n        return boost::locale::conv::utf_to_utf<wchar_t>(m_title.str());\n    }\n\n    SHCOLSTATEF flags() const { return m_flags; }\n    int format() const { return m_format; }\n    int avg_char_width() const { return m_avg_char_width; }\n\n    /**\n     * Convert the column's property variant to a string.\n     *\n     * Transforms the output using m_stringifier, if any, otherwise performs\n     * simple wstring conversion.\n     */\n    std::wstring detail(const washer::shell::pidl::cpidl_t& pidl) const\n    {\n        return property_from_pidl(pidl, m_key);\n    }\n\n    int compare(\n        const washer::shell::pidl::cpidl_t& lhs,\n        const washer::shell::pidl::cpidl_t& rhs) const\n    {\n        return compare_pidls_by_property(lhs, rhs, m_key);\n    }\n};\n\n#pragma warning(pop)\n\n/**\n * StaticColumn-compatible interface to the static column data.\n */\nclass HostColumnEntries\n{\nprotected:\n    const column_entry& entry(size_t index) const;\n};\n\ntypedef swish::nse::StaticColumn<HostColumnEntries> Column;\n\nconst washer::shell::property_key& property_key_from_column_index(\n    size_t index);\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/Add.cpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"Add.hpp\"\n\n#include \"swish/forms/add_host.hpp\" // add_host\n#include \"swish/host_folder/host_management.hpp\" // AddConnectionToRegistry\n\n#include <comet/error.h> // com_error\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n#include <utility> // make_pair\n\n#include <shlobj.h> // SHChangeNotify\n\nusing swish::forms::add_host;\nusing swish::forms::host_info;\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing swish::host_folder::host_management::AddConnectionToRegistry;\nusing swish::host_folder::host_management::ConnectionExists;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::window::window;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::locale::translate;\nusing boost::optional;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nnamespace {\n    const uuid_t ADD_COMMAND_ID(L\"b816a880-5022-11dc-9153-0090f5284f85\");\n\n    /**\n     * Cause Explorer to refresh any windows displaying the owning folder.\n     *\n     * Inform shell that something in our folder changed (we don't know\n     * exactly what the new PIDL is until we reload from the registry, hence\n     * UPDATEDIR).\n     */\n    void notify_shell(const apidl_t folder_pidl)\n    {\n        assert(folder_pidl);\n        ::SHChangeNotify(\n            SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n            folder_pidl.get(), NULL);\n    }\n}\n\nAdd::Add(const apidl_t& folder_pidl) :\n    Command(\n        translate(L\"&Add SFTP Connection\"), ADD_COMMAND_ID,\n        translate(L\"Create a new SFTP connection with Swish.\"),\n        L\"shell32.dll,-258\", translate(L\"&Add SFTP Connection...\"),\n        translate(L\"Add Connection\")),\n    m_folder_pidl(folder_pidl) {}\n\nBOOST_SCOPED_ENUM(Command::state) Add::state(\n    com_ptr<IShellItemArray>, bool /*ok_to_be_slow*/)\nconst\n{\n    return state::enabled;\n}\n\n/** Display dialog to get connection info from user. */\nvoid Add::operator()(\n    com_ptr<IShellItemArray>, const command_site& site, com_ptr<IBindCtx>)\nconst\n{\n    optional<window<wchar_t>> view_window = site.ui_owner();\n    if (view_window)\n    {\n        host_info info = add_host(view_window->hwnd());\n\n        if (ConnectionExists(info.name))\n            BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n        AddConnectionToRegistry(\n            info.name, info.host, info.port, info.user, info.path);\n\n        notify_shell(m_folder_pidl);\n    }\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/Add.hpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_ADD_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_ADD_HPP\n\n#include \"swish/nse/Command.hpp\" // Command\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nclass Add : public swish::nse::Command\n{\npublic:\n    Add(const washer::shell::pidl::apidl_t& folder_pidl);\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n\nprivate:\n    washer::shell::pidl::apidl_t m_folder_pidl;\n};\n\n}}} // namespace swish::host_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/CloseSession.cpp",
    "content": "/* Copyright (C) 2013, 2014, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"CloseSession.hpp\"\n\n#include \"swish/connection/session_manager.hpp\"\n#include \"swish/shell/shell_item_array.hpp\"\n#include \"swish/shell/parent_and_item.hpp\"\n#include \"swish/frontend/bind_best_taskdialog.hpp\" // best_taskdialog\n#include \"swish/remote_folder/pidl_connection.hpp\" // connection_from_pidl\n\n#include <washer/gui/task_dialog.hpp>\n\n#include <comet/ptr.h> // com_ptr\n#include <comet/error.h> // com_error, com_error_from_interface\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/bind/bind.hpp>\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/locale.hpp> // translate\n#include <boost/locale/encoding_utf.hpp> // utf_to_utf\n#include <boost/noncopyable.hpp>\n#include <boost/optional/optional.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/thread/future.hpp> // promise, packaged_task\n#include <boost/thread/thread.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/utility/in_place_factory.hpp> // in_place\n\n#include <cassert> // assert\n#include <memory> // auto_ptr\n#include <sstream> // wostringstream;\n#include <string>\n#include <utility> // pair\n\n#include <ShlObj.h> // SHChangeNotify\n\nusing swish::connection::session_manager;\nusing swish::frontend::best_taskdialog;\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing swish::remote_folder::connection_from_pidl;\n\nusing namespace washer::gui::task_dialog;\nusing washer::shell::pidl::apidl_t;\nusing washer::window::window;\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::bind;\nusing boost::unique_future;\nusing boost::locale::conv::utf_to_utf;\nusing boost::locale::translate;\nusing boost::noncopyable;\nusing boost::optional;\nusing boost::packaged_task;\nusing boost::promise;\nusing boost::shared_ptr;\nusing boost::thread;\n\nusing std::auto_ptr;\nusing std::endl;\nusing std::make_pair;\nusing std::pair;\nusing std::string;\nusing std::wostringstream;\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nnamespace {\n    const uuid_t CLOSE_SESSION_COMMAND_ID(\"b816a886-5022-11dc-9153-0090f5284f85\");\n\n    /**\n     * Cause Explorer to refresh the UI view of the given item.\n     */\n    void notify_shell(const apidl_t item)\n    {\n        ::SHChangeNotify(\n            SHCNE_UPDATEITEM, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n            item.get(), NULL);\n    }\n}\n\nCloseSession::CloseSession() :\n    Command(\n        translate(L\"&Close SFTP connection\"), CLOSE_SESSION_COMMAND_ID,\n        translate(L\"Close the authenticated connection to the server.\"),\n        L\"shell32.dll,-11\", translate(L\"&Close SFTP Connection...\"),\n        translate(L\"Close Connection\")) {}\n\nBOOST_SCOPED_ENUM(Command::state) CloseSession::state(\n    com_ptr<IShellItemArray> selection, bool /*ok_to_be_slow*/)\nconst\n{\n    if (!selection)\n    {\n        // Selection unknown.\n        return state::hidden;\n    }\n\n    switch (selection->size())\n    {\n    case 1:\n        {\n            com_ptr<IShellItem> item = selection->at(0);\n            com_ptr<IParentAndItem> folder_and_pidls = try_cast(item);\n            apidl_t item_pidl = folder_and_pidls->absolute_item_pidl();\n            if (session_manager().has_session(connection_from_pidl(item_pidl)))\n                return state::enabled;\n            else\n                return state::hidden;\n        }\n    case 0:\n        return state::hidden;\n    default:\n        // This means multiple items are selected. We disable rather than\n        // hide the buttons to let the user know the option exists but that\n        // we don't support multi-host session closure.\n        return state::disabled;\n    }\n}\n\nnamespace {\n\n    void start_marquee(progress_bar bar)\n    {\n        bar(marquee_progress());\n    }\n\n    template<typename PendingTaskRange>\n    wstring ui_content_text(const PendingTaskRange& pending_tasks)\n    {\n        wostringstream content;\n        content << translate(\n            L\"Explanation in progress dialog\",\n            L\"The following tasks are using the session:\");\n        content << endl << endl;\n\n        BOOST_FOREACH(const std::string& task_name, pending_tasks)\n        {\n            content << L\"\\x2022 \";\n            content << utf_to_utf<wchar_t>(task_name);\n            content << endl;\n        }\n\n        content << endl;\n\n        content << translate(\n            L\"Explanation of why we are displaying progress dialog. \"\n            L\"'them' refers to the tasks we are waiting for.\",\n            L\"Waiting for them to finish.\");\n\n        return content.str();\n    }\n\n    void do_nothing_command() {}\n\n    template<typename Result, typename Callable>\n    pair<shared_ptr<unique_future<Result>>, shared_ptr<thread>>\n    start_async(Callable operation)\n    {\n        packaged_task<Result> task(operation);\n        shared_ptr<unique_future<Result>> result(\n            new unique_future<Result>(boost::move(task.get_future())));\n\n        shared_ptr<thread> running_task(new thread(boost::move(task)));\n\n        return make_pair(result, running_task);\n    }\n\n    template<typename Result, typename Impl>\n    class async_task_dialog_runner : private noncopyable\n    {\n    public:\n        explicit async_task_dialog_runner(task_dialog_builder<Result, Impl> builder)\n            :\n        m_builder(builder),\n        m_dialog(m_promised_dialog.get_future()),\n        m_result(\n            start_async<Result>(\n                bind(&async_task_dialog_runner::dialog_loop<Result>, this)))\n        {}\n\n        ~async_task_dialog_runner()\n        {\n            // Ideally, we would use boost::async to run the dialog, which returns\n            // a future whose destructor blocks until the dialog finishes.  Making\n            // that future a member of this class then ensures the member variables\n            // remain valid for entire lifetime of the async operation.\n            //\n            // However, Boost 1.49 doesn't have async() so we need to keep\n            // the thread around and join in the destructor\n\n            m_result.second->join();\n        }\n\n        task_dialog dialog()\n        {\n            // Dialog creation might have failed so we don't want to block here\n            // on an event that may never happen.\n\n            // FIXME: Horrible mess with a race condition: creation may fail\n            // with an exception after we check for it.  The solution is to\n            // rewrite the task dialog class to use futures.\n\n            if (m_result.first->has_exception())\n            {\n                m_result.first->get();\n            }\n\n            return m_dialog.get();\n        }\n\n        unique_future<Result>& result()\n        {\n            return *(m_result.first);\n        }\n\n    private:\n\n        void on_create(const task_dialog& dialog)\n        {\n            m_promised_dialog.set_value(dialog);\n        }\n\n        template<typename Result>\n        Result dialog_loop()\n        {\n            return m_builder.show(\n                bind(&async_task_dialog_runner::on_create, this, _1));\n        }\n\n        task_dialog_builder<Result, Impl> m_builder;\n\n        // Class is not movable because thread holds reference to this instance\n        // when bound to `dialog_loop`.  Moving it would leave moved thread\n        // using `dialog_loop` with old `this`\n        promise<task_dialog> m_promised_dialog;\n        unique_future<task_dialog> m_dialog;\n        pair<shared_ptr<unique_future<Result>>, shared_ptr<thread>> m_result;\n    };\n\n    class running_dialog\n    {\n    public:\n\n        running_dialog(\n            auto_ptr<async_task_dialog_runner<void, best_taskdialog>> runner,\n            command_id id)\n            :\n            m_dialog_runner(runner), m_id(id) {}\n\n        task_dialog dialog()\n        {\n            return m_dialog_runner->dialog();\n        }\n\n        command_id dismissal_command_id()\n        {\n            return m_id;\n        }\n\n        bool dialog_has_been_dismissed()\n        {\n            return m_dialog_runner->result().has_value();\n        }\n\n    private:\n        auto_ptr<async_task_dialog_runner<void, best_taskdialog>>\n            m_dialog_runner;\n        command_id m_id;\n    };\n\n    template<typename PendingTaskRange>\n    running_dialog run_task_dialog(const PendingTaskRange& pending_tasks)\n    {\n        task_dialog_builder<void, best_taskdialog> builder(\n            NULL, //m_parent_window,\n            translate(\n                L\"Title of a progress dialog\", L\"Disconnecting session\"),\n            ui_content_text(pending_tasks), L\"Swish\", icon_type::information);\n\n        builder.include_progress_bar(start_marquee);\n\n        command_id id = builder.add_button(\n            button_type::cancel, do_nothing_command);\n\n        auto_ptr<async_task_dialog_runner<void, best_taskdialog>> runner(\n            new async_task_dialog_runner<void, best_taskdialog>(builder));\n\n        return running_dialog(runner, id);\n    }\n\n    class waiting_ui\n    {\n    public:\n        template<typename PendingTaskRange>\n        explicit waiting_ui(const PendingTaskRange& pending_tasks)\n            : m_dialog(run_task_dialog(pending_tasks)) {}\n\n        template<typename PendingTaskRange>\n        bool update(const PendingTaskRange& pending_tasks)\n        {\n            if (boost::empty(pending_tasks))\n            {\n                m_dialog.dialog().invoke_command(\n                    m_dialog.dismissal_command_id());\n                return true;\n            }\n            else\n            {\n                m_dialog.dialog().content(ui_content_text(pending_tasks));\n\n                return !m_dialog.dialog_has_been_dismissed();\n            }\n        }\n\n    private:\n        running_dialog m_dialog;\n    };\n\n    class disconnection_progress : private noncopyable\n    {\n    public:\n\n        template<typename PendingTaskRange>\n        bool operator()(const PendingTaskRange& pending_tasks)\n        {\n            if (!m_dialog)\n            {\n                // No need to start dialog if there are no tasks\n                if (!boost::empty(pending_tasks))\n                {\n                    // Using in-place-factory because waiting_ui's copy\n                    // constructor requires NON-const ref which optional\n                    // assignment doesn't allow\n                    m_dialog = in_place(pending_tasks);\n                }\n\n                return true;\n            }\n            else\n            {\n                return m_dialog->update(pending_tasks);\n            }\n        }\n\n    private:\n        optional<waiting_ui> m_dialog;\n    };\n\n};\n\nvoid CloseSession::operator()(\n    com_ptr<IShellItemArray> selection, const command_site&, com_ptr<IBindCtx>)\nconst\n{\n    // TODO: use the view to decide whether to show a progress dialog\n\n    if (selection->size() != 1)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    com_ptr<IShellItem> item = selection->at(0);\n    com_ptr<IParentAndItem> folder_and_pidls = try_cast(item);\n    apidl_t selected_item = folder_and_pidls->absolute_item_pidl();\n\n    disconnection_progress progress;\n\n    session_manager().disconnect_session(\n        connection_from_pidl(selected_item), boost::ref(progress));\n\n    notify_shell(selected_item);\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/CloseSession.hpp",
    "content": "/* Copyright (C) 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_CLOSE_SESSION_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_CLOSE_SESSION_HPP\n#pragma once\n\n#include \"swish/nse/Command.hpp\" // Command\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nclass CloseSession : public swish::nse::Command\n{\npublic:\n    CloseSession();\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n};\n\n}}} // namespace swish::host_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/LaunchAgent.cpp",
    "content": "/* Copyright (C) 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"LaunchAgent.hpp\"\n\n#include <washer/error.hpp> // last_error\n#include <washer/dynamic_link.hpp> // module_path, module_handle\n\n#include <comet/error.h> // com_error\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/exception/info.hpp> // errinfo\n#include <boost/exception/errinfo_file_name.hpp> // errinfo_file_name\n#include <boost/exception/errinfo_api_function.hpp> // errinfo_api_function\n#include <boost/locale.hpp> // translate\n#include <boost/filesystem/path.hpp> // path\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n\n#include <shlobj.h> // SHChangeNotify\n\nusing swish::nse::Command;\nusing swish::nse::command_site;\n\nusing washer::module_handle;\nusing washer::module_path;\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::locale::translate;\nusing boost::filesystem::path;\n\nusing std::wstring;\n\n// http://stackoverflow.com/a/557859/67013\nEXTERN_C IMAGE_DOS_HEADER __ImageBase;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nnamespace {\n   const uuid_t ADD_COMMAND_ID(L\"b816a884-5022-11dc-9153-0090f5284f85\");\n\n   const path PAGEANT_FILE_NAME = L\"pageant.exe\";\n\n   path pageant_path()\n   {\n       return module_path<wchar_t>(((HINSTANCE)&__ImageBase)).parent_path()\n           / PAGEANT_FILE_NAME;\n   }\n\n   /**\n    * Cause Explorer to refresh any windows displaying the owning folder.\n    *\n    * Inform shell that something in our folder changed (we don't know\n    * exactly what the new PIDL is until we reload from the registry, hence\n    * UPDATEDIR).\n    */\n   void notify_shell(const apidl_t folder_pidl)\n   {\n      assert(folder_pidl);\n      ::SHChangeNotify(\n         SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n         folder_pidl.get(), NULL);\n   }\n}\n\nLaunchAgent::LaunchAgent(const apidl_t& folder_pidl) :\n   Command(\n      translate(\n        L\"Title of command used to launch the SSH agent program\",\n        L\"&Launch key agent\"), ADD_COMMAND_ID,\n      translate(L\"Launch Putty SSH key agent, Pageant.\"),\n      L\"\",\n      translate(\n        L\"Title of command used to launch the SSH agent program\",\n        L\"&Launch key agent\"),\n      translate(\n        L\"Title of command used to launch the SSH agent program\",\n        L\"Launch key agent\")),\n   m_folder_pidl(folder_pidl) {}\n\n\nBOOST_SCOPED_ENUM(Command::state) LaunchAgent::state(\n    com_ptr<IShellItemArray>, bool /*ok_to_be_slow*/)\nconst\n{\n    HWND hwnd = ::FindWindowW(L\"Pageant\", L\"Pageant\");\n\n    return (hwnd) ? state::hidden : state::enabled;\n}\n\nvoid LaunchAgent::operator()(\n    com_ptr<IShellItemArray>, const command_site& site, com_ptr<IBindCtx>)\nconst\n{\n    static wstring pageant = pageant_path().wstring();\n\n    STARTUPINFOW si = STARTUPINFOW();\n    PROCESS_INFORMATION pi = PROCESS_INFORMATION();\n    if (!::CreateProcessW(\n        pageant.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))\n            BOOST_THROW_EXCEPTION(\n                boost::enable_error_info(washer::last_error()) <<\n                boost::errinfo_file_name(\"pageant\") <<\n                boost::errinfo_api_function(\"CreateProcess\"));\n\n    // Notify the shell because it needs to prod the commands to recalculate\n    // their visibility so that we can tell it not to show our button now\n    // that Pageant is running.\n    notify_shell(m_folder_pidl);\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/LaunchAgent.hpp",
    "content": "/* Copyright (C) 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_LAUNCH_AGENT_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_LAUNCH_AGENT_HPP\n#pragma once\n\n#include \"swish/nse/Command.hpp\" // Command\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nclass LaunchAgent : public swish::nse::Command\n{\npublic:\n    LaunchAgent(const washer::shell::pidl::apidl_t& folder_pidl);\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n\nprivate:\n    washer::shell::pidl::apidl_t m_folder_pidl;\n};\n\n}}} // namespace swish::host_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/Remove.cpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"Remove.hpp\"\n\n#include \"swish/host_folder/host_management.hpp\" // RemoveConnectionFromRegistry\n#include \"swish/host_folder/host_pidl.hpp\" // find_host_itemid, host_item_view\n#include \"swish/shell/parent_and_item.hpp\"\n#include \"swish/shell/shell_item_array.hpp\"\n\n#include <comet/error.h> // com_error\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing swish::host_folder::find_host_itemid;\nusing swish::host_folder::host_itemid_view;\nusing swish::host_folder::host_management::RemoveConnectionFromRegistry;\n\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::locale::translate;\nusing boost::optional;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nnamespace {\n    const uuid_t REMOVE_COMMAND_ID(\"b816a881-5022-11dc-9153-0090f5284f85\");\n\n    /**\n     * Cause Explorer to refresh any windows displaying the owning folder.\n     *\n     * Inform shell that something in our folder changed (we don't know\n     * exactly what the new PIDL is until we reload from the registry, hence\n     * UPDATEDIR).\n     */\n    void notify_shell(const apidl_t& folder_pidl)\n    {\n        ::SHChangeNotify(\n            SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n            folder_pidl.get(), NULL);\n    }\n}\n\nRemove::Remove(const apidl_t& folder_pidl) :\n    Command(\n        translate(L\"&Remove SFTP Connection\"), REMOVE_COMMAND_ID,\n        translate(L\"Remove a SFTP connection created with Swish.\"),\n        L\"shell32.dll,-240\", translate(L\"&Remove SFTP Connection...\"),\n        translate(L\"Remove Connection\")),\n    m_folder_pidl(folder_pidl) {}\n\nBOOST_SCOPED_ENUM(Command::state) Remove::state(\n    com_ptr<IShellItemArray> selection, bool /*ok_to_be_slow*/)\nconst\n{\n    if (!selection)\n    {\n        // Selection unknown.\n        return state::hidden;\n    }\n\n    switch (selection->size())\n    {\n    case 1:\n        return state::enabled;\n    case 0:\n        return state::hidden;\n    default:\n        // This means multiple items are selected. We disable rather than\n        // hide the buttons to let the user know the option exists but that\n        // we don't support multi-host removal yet.\n\n        // TODO: support multi-host removal\n        return state::disabled;\n    }\n}\n\nvoid Remove::operator()(\n    com_ptr<IShellItemArray> selection, const command_site& site, com_ptr<IBindCtx>)\nconst\n{\n    // XXX: for the moment we only allow removing one item.\n    //      is this what we want?\n    if (selection->size() != 1)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    com_ptr<IShellItem> item = selection->at(0);\n    com_ptr<IParentAndItem> folder_and_pidls = try_cast(item);\n    apidl_t selected_item = folder_and_pidls->absolute_item_pidl();\n\n    wstring label = host_itemid_view(*find_host_itemid(selected_item)).label();\n    assert(!label.empty());\n    if (label.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_UNEXPECTED));\n\n    RemoveConnectionFromRegistry(label);\n    notify_shell(m_folder_pidl);\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/Remove.hpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_REMOVE_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_REMOVE_HPP\n#pragma once\n\n#include \"swish/nse/Command.hpp\" // Command\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nclass Remove : public swish::nse::Command\n{\npublic:\n    Remove(const washer::shell::pidl::apidl_t& folder_pidl);\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n\nprivate:\n    washer::shell::pidl::apidl_t m_folder_pidl;\n};\n\n}}} // namespace swish::host_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/Rename.cpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"Rename.hpp\"\n\n#include \"swish/shell/shell.hpp\" // put_view_item_into_rename_mode\n#include \"swish/shell/parent_and_item.hpp\"\n#include \"swish/shell/shell_item_array.hpp\"\n\n#include <washer/shell/services.hpp> // shell_browser, shell_view\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing swish::shell::put_view_item_into_rename_mode;\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::shell_browser;\nusing washer::shell::shell_view;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::locale::translate;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nnamespace {\n    const uuid_t RENAME_COMMAND_ID(\"b816a883-5022-11dc-9153-0090f5284f85\");\n}\n\nRename::Rename() :\n    Command(\n        translate(L\"&Rename SFTP Connection\"), RENAME_COMMAND_ID,\n        translate(L\"Rename an SFTP connection created with Swish.\"),\n        L\"shell32.dll,133\", translate(L\"&Rename SFTP Connection...\"),\n        translate(L\"Rename Connection\"))\n    {}\n\nBOOST_SCOPED_ENUM(Command::state) Rename::state(\n    com_ptr<IShellItemArray> selection, bool /*ok_to_be_slow*/)\nconst\n{\n    if (!selection)\n    {\n        // Selection unknown.\n        return state::hidden;\n    }\n\n    switch (selection->size())\n    {\n    case 1:\n        return state::enabled;\n    case 0:\n        return state::hidden;\n    default:\n        // This means multiple items are selected. We disable rather than\n        // hide the buttons to let the user know the option exists but that\n        // we don't support multi-host renaming.\n        return state::disabled;\n    }\n}\n\n// This command just puts the item into rename (edit) mode.  When the user\n// finishes typing the new name, the shell takes care of performing the rest of\n// the renaming process by calling SetNameOf() on the HostFolder\nvoid Rename::operator()(\n    com_ptr<IShellItemArray> selection, const command_site& site,\n    com_ptr<IBindCtx>)\nconst\n{\n    if (selection->size() != 1)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    com_ptr<IShellView> view = shell_view(shell_browser(site.ole_site()));\n\n    com_ptr<IShellItem> item = selection->at(0);\n    com_ptr<IParentAndItem> folder_and_pidls = try_cast(item);\n    cpidl_t selected_item = folder_and_pidls->item_pidl();\n\n    put_view_item_into_rename_mode(view, selected_item);\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/Rename.hpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_RENAME_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_RENAME_HPP\n\n#include \"swish/nse/Command.hpp\" // Command\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\nclass Rename : public swish::nse::Command\n{\npublic:\n    Rename();\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n};\n\n}}} // namespace swish::host_folder::commands\n\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/commands/commands.cpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"commands.hpp\"\n\n#include \"swish/host_folder/commands/Add.hpp\"\n#include \"swish/host_folder/commands/CloseSession.hpp\"\n#include \"swish/host_folder/commands/LaunchAgent.hpp\"\n#include \"swish/host_folder/commands/Remove.hpp\"\n#include \"swish/host_folder/commands/Rename.hpp\"\n#include \"swish/nse/explorer_command.hpp\" // CExplorerCommand*\n#include \"swish/nse/task_pane.hpp\" // CUIElementErrorAdapter\n\n#include <comet/server.h> // simple_object\n#include <comet/smart_enum.h> // make_smart_enumeration\n\n#include <boost/locale.hpp> // translate\n#include <boost/make_shared.hpp> // make_shared\n\n#include <cassert> // assert\n#include <string>\n#include <utility> // make_pair\n#include <vector>\n\nusing swish::nse::CExplorerCommand;\nusing swish::nse::CExplorerCommandProvider;\nusing swish::nse::CUICommand;\nusing swish::nse::CUIElementErrorAdapter;\nusing swish::nse::Command;\nusing swish::nse::IEnumUICommand;\nusing swish::nse::IUICommand;\nusing swish::nse::IUIElement;\nusing swish::nse::WebtaskCommandTitleAdapter;\n\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_ptr;\nusing comet::make_smart_enumeration;\nusing comet::simple_object;\n\nusing boost::locale::translate;\nusing boost::make_shared;\nusing boost::shared_ptr;\n\nusing std::make_pair;\nusing std::vector;\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\ncom_ptr<IExplorerCommandProvider> host_folder_command_provider(\n    const apidl_t& folder_pidl)\n{\n    CExplorerCommandProvider::ordered_commands commands;\n    commands.push_back(new CExplorerCommand<Add>(folder_pidl));\n    commands.push_back(new CExplorerCommand<Remove>(folder_pidl));\n    commands.push_back(new CExplorerCommand<Rename>());\n    commands.push_back(new CExplorerCommand<CloseSession>());\n    commands.push_back(new CExplorerCommand<LaunchAgent>(folder_pidl));\n    return new CExplorerCommandProvider(commands);\n}\n\nclass CSftpTasksTitle : public simple_object<CUIElementErrorAdapter>\n{\npublic:\n\n    virtual std::wstring title(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return translate(L\"SFTP Tasks\");\n    }\n\n    virtual std::wstring icon(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return L\"shell32.dll,-9\";\n    }\n\n    virtual std::wstring tool_tip(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return translate(\n            L\"These tasks help you manage Swish SFTP connections.\");\n    }\n};\n\nstd::pair<com_ptr<IUIElement>, com_ptr<IUIElement> >\nhost_folder_task_pane_titles(const apidl_t& /*folder_pidl*/)\n{\n    return make_pair(new CSftpTasksTitle(), com_ptr<IUIElement>());\n}\n\nstd::pair<com_ptr<IEnumUICommand>, com_ptr<IEnumUICommand> >\nhost_folder_task_pane_tasks(const apidl_t& folder_pidl)\n{\n    typedef shared_ptr< vector< com_ptr<IUICommand> > > shared_command_vector;\n    shared_command_vector commands =\n        make_shared< vector< com_ptr<IUICommand> > >();\n\n    commands->push_back(\n        new CUICommand<WebtaskCommandTitleAdapter<Add>>(folder_pidl));\n    commands->push_back(\n        new CUICommand<WebtaskCommandTitleAdapter<Remove>>(folder_pidl));\n    commands->push_back(\n        new CUICommand<WebtaskCommandTitleAdapter<Rename>>());\n    commands->push_back(\n        new CUICommand<WebtaskCommandTitleAdapter<CloseSession>>());\n    commands->push_back(\n        new CUICommand<WebtaskCommandTitleAdapter<LaunchAgent>>(folder_pidl));\n\n    com_ptr<IEnumUICommand> e =\n        make_smart_enumeration<IEnumUICommand>(commands);\n\n    return make_pair(e, com_ptr<IEnumUICommand>());\n}\n\n}}} // namespace swish::host_folder::commands\n"
  },
  {
    "path": "swish/host_folder/commands/commands.hpp",
    "content": "/* Copyright (C) 2010, 2011, 2012, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_COMMANDS_HPP\n#define SWISH_HOST_FOLDER_COMMANDS_HPP\n#pragma once\n\n#include \"swish/nse/UICommand.hpp\" // IUIElement\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <shobjidl.h> // IExplorerCommandProvider\n\nnamespace swish {\nnamespace host_folder {\nnamespace commands {\n\ncomet::com_ptr<IExplorerCommandProvider> host_folder_command_provider(\n    const washer::shell::pidl::apidl_t& folder_pidl);\n\nstd::pair<comet::com_ptr<nse::IUIElement>, comet::com_ptr<nse::IUIElement> >\nhost_folder_task_pane_titles(const washer::shell::pidl::apidl_t& folder_pidl);\n\nstd::pair<\n    comet::com_ptr<nse::IEnumUICommand>,\n    comet::com_ptr<nse::IEnumUICommand> >\nhost_folder_task_pane_tasks(const washer::shell::pidl::apidl_t& folder_pidl);\n\n}}} // namespace swish::host_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/context_menu_callback.cpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"swish/host_folder/commands/Remove.hpp\"\n#include \"swish/host_folder/context_menu_callback.hpp\" // context_menu_callback\n#include \"swish/nse/command_site.hpp\"\n#include \"swish/shell/shell_item_array.hpp\" // shell_item_array_from_data_object\n\n#include <washer/window/window.hpp>\n\n#include <boost/optional/optional.hpp>\n\n#include <shlobj.h> // DFM_CMD_DELETE\n\nusing swish::nse::command_site;\nusing swish::shell::shell_item_array_from_data_object;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing comet::com_ptr;\n\nusing boost::optional;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\n\ncontext_menu_callback::context_menu_callback(const apidl_t& folder_pidl)\n: m_folder_pidl(folder_pidl) {}\n\nnamespace {\n\n    bool do_invoke_command(\n        const apidl_t& folder_pidl, HWND hwnd_view,\n        com_ptr<IDataObject> selection_data_object, UINT item_offset,\n        const wstring& /*arguments*/, int /*window_mode*/,\n        com_ptr<IUnknown> context_menu_site)\n    {\n        if (item_offset == DFM_CMD_DELETE)\n        {\n            com_ptr<IShellItemArray> selection =\n                shell_item_array_from_data_object(selection_data_object);\n\n            // Use given window as a UI owner fallback because, if we compile\n            // with pre-Vista support, the OLE site will always be NULL\n            optional<window<wchar_t>> fallback_ui_owner;\n            if (hwnd_view)\n            {\n                fallback_ui_owner = window<wchar_t>(\n                    window_handle::foster_handle(hwnd_view));\n            }\n\n            commands::Remove deletion_command(folder_pidl);\n            deletion_command(\n                selection,\n                command_site(context_menu_site, fallback_ui_owner), NULL);\n\n            return true;\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n}\n\nbool context_menu_callback::invoke_command(\n    HWND hwnd_view, com_ptr<IDataObject> selection, UINT item_offset,\n    const wstring& arguments)\n{\n    return do_invoke_command(\n        m_folder_pidl, hwnd_view, selection, item_offset, arguments, SW_NORMAL,\n        NULL);\n}\n\n/**\n * @todo  Take account of the behaviour flags.\n */\nbool context_menu_callback::invoke_command(\n    HWND hwnd_view, com_ptr<IDataObject> selection, UINT item_offset,\n    const wstring& arguments, DWORD /*behaviour_flags*/, UINT /*minimum_id*/,\n    UINT /*maximum_id*/, const CMINVOKECOMMANDINFO& invocation_details,\n    com_ptr<IUnknown> context_menu_site)\n{\n    return do_invoke_command(\n        m_folder_pidl, hwnd_view, selection, item_offset, arguments,\n        invocation_details.nShow, context_menu_site);\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/host_folder/context_menu_callback.hpp",
    "content": "/* HostFolder context menu implementation.\n\n   Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_CONTEXT_MENU_CALLBACK_HPP\n#define SWISH_HOST_FOLDER_CONTEXT_MENU_CALLBACK_HPP\n\n#include \"swish/nse/default_context_menu_callback.hpp\"\n                                               // default_context_menu_callback\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <string>\n\n#include <ObjIdl.h> // IDataObject\n#include <ShObjIdl.h> // CMINVOKECOMMANDINFO\n#include <Windows.h> // HWND\n\nnamespace swish {\nnamespace host_folder {\n\nclass context_menu_callback : public swish::nse::default_context_menu_callback\n{\npublic:\n    context_menu_callback(const washer::shell::pidl::apidl_t& folder_pidl);\n\nprivate:\n\n    bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, UINT item_offset,\n        const std::wstring& arguments);\n\n    bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, UINT item_offset,\n        const std::wstring& arguments, DWORD behaviour_flags, UINT minimum_id,\n        UINT maximum_id, const CMINVOKECOMMANDINFO& invocation_details,\n        comet::com_ptr<IUnknown> context_menu_site);\n\n    washer::shell::pidl::apidl_t m_folder_pidl;\n};\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/extract_icon.hpp",
    "content": "/**\n    @file\n\n    Host folder icons.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_EXTRACT_ICON_HPP\n#define SWISH_HOST_FOLDER_EXTRACT_ICON_HPP\n\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view,\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/window/window.hpp>\n\n#include <boost/optional.hpp>\n\n#include <strsafe.h> // StringCchCopy\n\nnamespace swish {\nnamespace host_folder {\n\nclass extract_icon_co : public comet::simple_object<IExtractIconW>\n{\npublic:\n    extract_icon_co(\n        const boost::optional<washer::window::window<wchar_t>>& owning_view,\n        const washer::shell::pidl::cpidl_t& item)\n        :\n    m_owning_view(owning_view), m_item(item) {}\n\n    /**\n     * Extract an icon bitmap given the information passed.\n     *\n     * We return S_FALSE to tell the shell to extract the icons itself.\n     */\n    STDMETHODIMP extract_icon_co::Extract(\n        LPCTSTR /*location*/, UINT /*index*/, HICON* /*large_icon_out*/,\n        HICON* /*small_icon_out*/, UINT /*desired_sizes*/)\n    {\n        return S_FALSE;\n    }\n\n    /**\n     * Retrieve the location of the appropriate icon.\n     *\n     * We set all SFTP hosts to have the icon from shell32.dll.\n     */\n    STDMETHODIMP extract_icon_co::GetIconLocation(\n        UINT /*flags*/, wchar_t* location_buffer_out, \n        UINT buffer_size, int* index_out, UINT* flags_out)\n    {\n        // type of use (flags) is ignored for host folder\n\n        // Set host to have the ICS host icon\n        StringCchCopy(location_buffer_out, buffer_size, L\"shell32.dll\");\n        *index_out = 17;\n\n        // Force call to Extract\n        *flags_out = GIL_DONTCACHE;\n\n        return S_OK;\n    }\n\nprivate:\n    boost::optional<washer::window::window<wchar_t>> m_owning_view;\n    washer::shell::pidl::cpidl_t m_item;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/host_itemid_connection.cpp",
    "content": "/**\n    @file\n\n    Relates host item IDs to SFTP connections.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"host_itemid_connection.hpp\"\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/host_folder/host_pidl.hpp\" // find_host_itemid, host_itemid_view\n\nusing swish::connection::connection_spec;\nusing swish::host_folder::host_itemid_view;\n\nnamespace swish {\nnamespace host_folder {\n\nconnection_spec connection_from_host_itemid(const host_itemid_view& host_itemid)\n{\n    return connection_spec(\n        host_itemid.host(), host_itemid.user(), host_itemid.port());\n}\n\n}} // namespace swish::host_folder\n"
  },
  {
    "path": "swish/host_folder/host_itemid_connection.hpp",
    "content": "/**\n    @file\n\n    Relates host item IDs to SFTP connections.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_HOST_ITEM_CONNECTION_HPP\n#define SWISH_HOST_FOLDER_HOST_ITEM_CONNECTION_HPP\n#pragma once\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view\n\nnamespace swish {\nnamespace host_folder {\n\n/**\n * Converts a host item ID into a connection specification.\n */\nswish::connection::connection_spec connection_from_host_itemid(\n    const host_itemid_view& host_itemid);\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/host_management.cpp",
    "content": "/**\n    @file\n\n    Management functions for host entries saved in the registry.\n\n    @if license\n\n    Copyright (C) 2009, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"host_management.hpp\"\n\n#include \"swish/debug.hpp\"\n#include \"swish/host_folder/host_pidl.hpp\" // create_host_itemid,\n                                           // host_itemid_view\n\n#include <comet/regkey.h>\n\n#include <algorithm>\n#include <stdexcept>\n\nusing swish::host_folder::create_host_itemid;\nusing swish::host_folder::host_itemid_view;\n\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::regkey;\n\nusing boost::optional;\n\nusing std::runtime_error;\nusing std::transform;\nusing std::wstring;\nusing std::vector;\n\nnamespace { // private\n\n    static const wstring CONNECTIONS_REGISTRY_KEY_NAME =\n        L\"Software\\\\Swish\\\\Connections\";\n    static const wchar_t* HOST_VALUE_NAME = L\"Host\";\n    static const wchar_t* PORT_VALUE_NAME = L\"Port\";\n    static const wchar_t* USER_VALUE_NAME = L\"User\";\n    static const wchar_t* PATH_VALUE_NAME = L\"Path\";\n\n    regkey get_connection_from_registry(const wstring& label)\n    {\n        regkey swish_connections = regkey(HKEY_CURRENT_USER).open(\n            CONNECTIONS_REGISTRY_KEY_NAME);\n\n        return swish_connections.open(label);\n    }\n}\n\nnamespace swish {\nnamespace host_folder {\nnamespace host_management {\n\n/**\n * Get a single connection from the registry as a PIDL.\n *\n * @pre The connection, if present, is a subkey of the\n *      @c Software\\\\Swish\\\\Connections registry key whose name is given\n *      by @p label.\n *\n * @param label  Friendly name of the connection to load.\n *\n * @returns  A host PIDL holding the connection details.\n */\noptional<cpidl_t> FindConnectionInRegistry(const wstring& label)\n{\n    regkey swish_connections = regkey(HKEY_CURRENT_USER).open_nothrow(\n        CONNECTIONS_REGISTRY_KEY_NAME);\n    // Legal to fail here - may be first ever connection\n    if(!swish_connections)\n        return optional<cpidl_t>();\n\n    regkey connection = swish_connections.open(label);\n    if(!connection)\n        return optional<cpidl_t>();\n\n    wstring host = connection[HOST_VALUE_NAME];\n    int port = connection[PORT_VALUE_NAME];\n    wstring user = connection[USER_VALUE_NAME];\n    wstring path = connection[PATH_VALUE_NAME];\n\n    return create_host_itemid(host, user, path, port, label);\n}\n\nnamespace {\n\n    cpidl_t GetConnectionDetailsFromRegistry(const wstring& label) {\n        return *FindConnectionInRegistry(label);\n    }\n}\n\n/**\n * Load all the connections stored in the registry into PIDLs.\n *\n * It's possible that there aren't any connections in\n * the @c Software\\\\Swish\\\\Connections key of the registry, in which case\n * the vector is left empty.\n *\n * @returns  Vector of PIDLs containing the details of all the SFTP\n *           stored in the registry.\n * @throws  com_error if something unexpected happens such as corrupt\n *          registry structure.\n */\nvector<cpidl_t> LoadConnectionsFromRegistry()\n{\n    vector<cpidl_t> connection_pidls;\n\n    regkey connections = regkey(HKEY_CURRENT_USER).open(\n        CONNECTIONS_REGISTRY_KEY_NAME);\n\n    if (connections) // Legal to fail here - may be first ever connection\n    {\n        regkey::subkeys_type connection_collection =\n            connections.enumerate().subkeys();\n\n        transform(\n            connection_collection.begin(), connection_collection.end(),\n            back_inserter(connection_pidls), GetConnectionDetailsFromRegistry);\n    }\n\n    return connection_pidls;\n}\n\n/**\n * Add a host entry to the Swish connection key with the given details.\n *\n * If the connections key does not already exits (because no hosts have\n * been added yet) the key is created and the host added to it.\n */\nvoid AddConnectionToRegistry(\n    wstring label, wstring host, int port, wstring username, wstring path)\n{\n    DWORD key_disposition;\n    regkey connection = regkey(HKEY_CURRENT_USER).create(\n        CONNECTIONS_REGISTRY_KEY_NAME + L\"\\\\\" + label,\n        REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, 0, &key_disposition);\n    if (key_disposition == REG_OPENED_EXISTING_KEY)\n    {\n        BOOST_THROW_EXCEPTION(\n            runtime_error(\"connection already exists in registry\"));\n    }\n    else\n    {\n        connection[HOST_VALUE_NAME] = host;\n        connection[PORT_VALUE_NAME] = port;\n        connection[USER_VALUE_NAME] = username;\n        connection[PATH_VALUE_NAME] = path;\n    }\n}\n\nnamespace {\n\n    // TODO: move into Comet\n\n    void delete_subkey_recursively(\n        const regkey& key, const wstring& subkey_name);\n\n    void delete_all_subkeys_recursively(const regkey& key)\n    {\n        regkey::subkeys_type subkeys = key.enumerate().subkeys();\n        for(regkey::subkeys_type::iterator it=subkeys.begin();\n            it != subkeys.end(); ++it)\n        {\n            delete_subkey_recursively(key, *it);\n        }\n    }\n\n    void delete_subkey_recursively(\n        const regkey& key, const wstring& subkey_name)\n    {\n        delete_all_subkeys_recursively(key.open(subkey_name));\n        key.delete_subkey(subkey_name);\n    }\n}\n\n/**\n * Remove a host entry from the Swish connections registry key by label.\n */\nvoid RemoveConnectionFromRegistry(wstring label)\n{\n    regkey connections = regkey(HKEY_CURRENT_USER).open(\n        CONNECTIONS_REGISTRY_KEY_NAME);\n\n    delete_subkey_recursively(connections, label);\n}\n\nvoid RenameConnectionInRegistry(const wstring& from_label, const wstring& to_label)\n{\n    regkey connection = get_connection_from_registry(from_label);\n\n    wstring host = connection[HOST_VALUE_NAME];\n    int port = connection[PORT_VALUE_NAME];\n    wstring user = connection[USER_VALUE_NAME];\n    wstring path = connection[PATH_VALUE_NAME];\n\n    AddConnectionToRegistry(to_label, host, port, user, path);\n    RemoveConnectionFromRegistry(from_label);\n}\n\nnamespace {\n\n    class label_matches\n    {\n    public:\n\n        label_matches(const wstring& label) : m_label(label) {}\n\n        bool operator()(const cpidl_t& connection)\n        {\n            return host_itemid_view(connection).label() == m_label;\n        }\n\n    private:\n        wstring m_label;\n    };\n}\n\n/**\n * Returns whether a host entry with the given label exists in the registry.\n */\nbool ConnectionExists(wstring label)\n{\n    if (label.size() < 1)\n        return false;\n\n    vector<cpidl_t> connections = LoadConnectionsFromRegistry();\n\n    return find_if(\n        connections.begin(), connections.end(), label_matches(label))\n        != connections.end();\n}\n\n}}} // namespace swish::host_folder::host_management\n"
  },
  {
    "path": "swish/host_folder/host_management.hpp",
    "content": "/**\n    @file\n\n    Management functions for host entries saved in the registry.\n\n    @if license\n\n    Copyright (C) 2009, 2011, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_HOST_MANAGEMENT_HPP\n#define SWISH_HOST_FOLDER_HOST_MANAGEMENT_HPP\n#pragma once\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <boost/optional/optional.hpp>\n\n#include <string>\n#include <vector>\n\nnamespace swish {\nnamespace host_folder {\nnamespace host_management {\n\nstd::vector<washer::shell::pidl::cpidl_t> LoadConnectionsFromRegistry();\n\nvoid AddConnectionToRegistry(\n    std::wstring label, std::wstring host, int port,\n    std::wstring username, std::wstring path);\n\nboost::optional<washer::shell::pidl::cpidl_t> FindConnectionInRegistry(\n    const std::wstring& label);\n\nvoid RemoveConnectionFromRegistry(std::wstring label);\n\nvoid RenameConnectionInRegistry(\n    const std::wstring& from_label, const std::wstring& to_label);\n\nbool ConnectionExists(std::wstring label);\n\n}}} // namespace swish::host_folder::host_management\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/host_pidl.hpp",
    "content": "/**\n    @file\n\n    PIDL access particular to host folder PIDLs.\n\n    @if license\n\n    Copyright (C) 2011, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_HOST_PIDL_HPP\n#define SWISH_HOST_FOLDER_HOST_PIDL_HPP\n#pragma once\n\n#include \"swish/remotelimits.h\"  // Text field limits\n\n#include <washer/shell/pidl.hpp> // pidl_t, apidl_t, cpidl_t\n#include <washer/shell/pidl_iterator.hpp> // raw_pidl_iterator\n\n#include <boost/format.hpp> // wformat\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/static_assert.hpp> // BOOST_STATIC_ASSERT\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <ssh/filesystem/path.hpp>\n\n#ifndef STRICT_TYPED_ITEMIDS\n#error Currently, swish requires strict PIDL types: define STRICT_TYPED_ITEMIDS\n#endif\n#include <ShTypes.h> // Raw PIDL types\n#include <StrAlign.h> // ua_wcslen, ua_wcscpy_s\n\n#include <algorithm> // find_if\n#include <cstring> // memset\n#include <exception>\n#include <stdexcept> // runtime_error\n#include <string>\n#include <vector>\n\nnamespace swish {\nnamespace host_folder {\n\nnamespace detail {\n\n#include <pshpack1.h>\nstruct host_item_id\n{\n    USHORT cb;\n    DWORD dwFingerprint;\n    WCHAR wszLabel[MAX_LABEL_LENZ];\n    WCHAR wszUser[MAX_USERNAME_LENZ];\n    WCHAR wszHost[MAX_HOSTNAME_LENZ];\n    WCHAR wszPath[MAX_PATH_LENZ];\n    USHORT uPort;\n\n    static const DWORD FINGERPRINT = 0x496c1066;\n};\n#include <poppack.h>\n\nBOOST_STATIC_ASSERT((sizeof(host_item_id) % sizeof(DWORD)) == 0);\n\ninline std::wstring copy_unaligned_string(const wchar_t __unaligned* source)\n{\n    std::vector<wchar_t> buffer(::ua_wcslen(source) + 1);\n    ::ua_wcscpy_s(&buffer[0], buffer.size(), source);\n    return std::wstring(&buffer[0]);\n}\n\n}\n\n/**\n * View internal fields of host folder PIDLs.\n *\n * The viewer doesn't take ownership of the PIDL it's passed so it must remain\n * valid for the duration of the viewer's use.\n */\nclass host_itemid_view\n{\npublic:\n    // We have to take the PIDL as a template, rather than that as a pidl_t\n    // as the PIDL passed might be a cpidl_t or an apidl_t.  In this case\n    // the pidl would be converted to a pidl_t using a temporary which is\n    // destroyed immediately after the constructor returns, thereby\n    // invalidating the PIDL we've stored a reference to.\n    template<typename T, typename Alloc>\n    explicit host_itemid_view(\n        const washer::shell::pidl::basic_pidl<T, Alloc>& pidl)\n        : m_itemid(reinterpret_cast<const detail::host_item_id*>(pidl.get())) {}\n\n    explicit host_itemid_view(PCUIDLIST_RELATIVE pidl)\n        : m_itemid(reinterpret_cast<const detail::host_item_id*>(pidl)) {}\n\n    bool valid() const\n    {\n        if (m_itemid == NULL)\n            return false;\n\n        return ((m_itemid->cb == sizeof(detail::host_item_id)) &&\n            (m_itemid->dwFingerprint == detail::host_item_id::FINGERPRINT));\n    }\n\n    std::wstring host() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a host item\"));\n        return detail::copy_unaligned_string(m_itemid->wszHost);\n    }\n\n    std::wstring user() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a host item\"));\n        return detail::copy_unaligned_string(m_itemid->wszUser);\n    }\n\n    std::wstring label() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a host item\"));\n        return detail::copy_unaligned_string(m_itemid->wszLabel);\n    }\n\n    ssh::filesystem::path path() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a host item\"));\n        return ssh::filesystem::path(\n            detail::copy_unaligned_string(m_itemid->wszPath));\n    }\n\n    int port() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a host item\"));\n        return m_itemid->uPort;\n    }\n\nprivate:\n    const detail::host_item_id __unaligned* m_itemid;\n};\n\nnamespace detail {\n    struct is_valid_host_item\n    {\n        bool operator()(const washer::shell::pidl::pidl_t& pidl)\n        {\n            return host_itemid_view(pidl).valid();\n        }\n    };\n}\n\n/**\n * Search a (multi-level) PIDL to find the host folder ITEMID.\n *\n * In any Swish PIDL there should be at most one as it doesn't make sense for\n * a file to be under more than one host.\n *\n * @returns an iterator pointing to the position of the host ITEMID in the\n *          original PIDL.\n * @throws if no host ITEMID is found in the PIDL.\n */\ninline washer::shell::pidl::raw_pidl_iterator find_host_itemid(\n    PCIDLIST_ABSOLUTE pidl)\n{\n    washer::shell::pidl::raw_pidl_iterator begin(pidl);\n    washer::shell::pidl::raw_pidl_iterator end;\n\n    // Search along pidl until we find one that matches our fingerprint or\n    // we run off the end\n    washer::shell::pidl::raw_pidl_iterator pos = std::find_if(\n        begin, end, detail::is_valid_host_item());\n    if (pos != end)\n        return pos;\n    else\n        BOOST_THROW_EXCEPTION(\n            std::runtime_error(\"PIDL doesn't contain host ITEMID\"));\n}\n\ninline washer::shell::pidl::raw_pidl_iterator find_host_itemid(\n    const washer::shell::pidl::apidl_t& pidl)\n{\n    return swish::host_folder::find_host_itemid(pidl.get());\n}\n\nnamespace detail {\n\n#include <pshpack1.h>\n    struct host_item_template\n    {\n        host_item_id id;\n        SHITEMID terminator;\n    };\n#include <poppack.h>\n\n}\n\n/**\n * Construct a new host folder PIDL with the fields initialised.\n */\ninline washer::shell::pidl::cpidl_t create_host_itemid(\n    const std::wstring& host, const std::wstring& user,\n    const ssh::filesystem::path& path, int port,\n    const std::wstring& label=std::wstring())\n{\n    // We create the item on the stack and then clone it into\n    // a CoTaskMemAllocated pidl when we return it as a cpidl_t\n    detail::host_item_template item;\n    std::memset(&item, 0, sizeof(item));\n\n    item.id.cb = sizeof(item.id);\n    item.id.dwFingerprint = detail::host_item_id::FINGERPRINT;\n\n#pragma warning(push)\n#pragma warning(disable:4996)\n    host.copy(item.id.wszHost, MAX_HOSTNAME_LENZ);\n    item.id.wszHost[MAX_HOSTNAME_LENZ - 1] = wchar_t();\n\n    user.copy(item.id.wszUser, MAX_USERNAME_LENZ);\n    item.id.wszUser[MAX_USERNAME_LENZ - 1] = wchar_t();\n\n    path.wstring().copy(item.id.wszPath, MAX_PATH_LENZ);\n    item.id.wszPath[MAX_PATH_LENZ - 1] = wchar_t();\n\n    label.copy(item.id.wszLabel, MAX_LABEL_LENZ);\n    item.id.wszLabel[MAX_LABEL_LENZ - 1] = wchar_t();\n#pragma warning(pop)\n\n    item.id.uPort = boost::numeric_cast<USHORT>(port);\n\n    assert(item.terminator.cb == 0);\n\n    return washer::shell::pidl::cpidl_t(\n        reinterpret_cast<PCITEMID_CHILD>(&item));\n}\n\n/**\n * Retrieve the long name of the host connection from the PIDL.\n *\n * The long name is either the canonical form if @a canonical is set:\n *     sftp://username\\@hostname:port/path\n * or, if not set and if the port is the default port, the reduced form:\n *     sftp://username\\@hostname/path\n */\ninline std::wstring url_from_host_itemid(\n    const washer::shell::pidl::cpidl_t itemid, bool canonical)\n{\n    host_itemid_view host_pidl(itemid);\n\n    if (canonical || host_pidl.port() != SFTP_DEFAULT_PORT)\n    {\n        return str(\n            boost::wformat(L\"sftp://%s@%s:%u/%s\")\n            % host_pidl.user() % host_pidl.host() % host_pidl.port()\n            % host_pidl.path().wstring());\n    }\n    else\n    {\n        return str(\n            boost::wformat(L\"sftp://%s@%s/%s\")\n            % host_pidl.user() % host_pidl.host()\n            % host_pidl.path().wstring());\n    }\n}\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/menu_command_manager.cpp",
    "content": "/* Manage complexities of adding and removing menu items in host window.\n\n   Copyright (C) 2013, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"menu_command_manager.hpp\"\n\n#include \"swish/frontend/commands/About.hpp\"\n#include \"swish/host_folder/commands/Add.hpp\"\n#include \"swish/host_folder/commands/CloseSession.hpp\"\n#include \"swish/host_folder/commands/LaunchAgent.hpp\"\n#include \"swish/host_folder/commands/Remove.hpp\"\n#include \"swish/host_folder/commands/Rename.hpp\"\n#include \"swish/nse/command_site.hpp\"\n\n#include <washer/gui/menu/basic_menu.hpp> // find_first_item_with_id\n#include <washer/gui/menu/button/string_button_description.hpp>\n#include <washer/gui/menu/item/command_item_description.hpp>\n#include <washer/gui/menu/item/command_item.hpp>\n#include <washer/gui/menu/item/item_state.hpp> // selectability\n#include <washer/gui/menu/item/separator_item.hpp>\n#include <washer/gui/menu/item/sub_menu_item.hpp>\n#include <washer/trace.hpp> // trace\n\n#include <boost/exception/diagnostic_information.hpp> // diagnostic_information\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/make_shared.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <stdexcept> // logic_error, runtime_error\n\nusing swish::frontend::commands::About;\nusing swish::host_folder::commands::Add;\nusing swish::host_folder::commands::CloseSession;\nusing swish::host_folder::commands::LaunchAgent;\nusing swish::host_folder::commands::Remove;\nusing swish::host_folder::commands::Rename;\nusing swish::nse::Command;\nusing swish::nse::command_site;\n\nusing namespace washer::gui::menu;\nusing washer::shell::pidl::apidl_t;\nusing washer::trace;\nusing washer::window::window;\n\nusing comet::com_ptr;\n\nusing boost::diagnostic_information;\nusing boost::make_shared;\nusing boost::optional;\nusing boost::shared_ptr;\n\nusing std::logic_error;\nusing std::map;\nusing std::runtime_error;\nusing std::wstring;\n\nnamespace swish {\nnamespace host_folder {\n\nnamespace {\n\ntypedef std::map<UINT, boost::shared_ptr<swish::nse::Command>>\n    menu_id_command_map;\n\ntemplate<typename DescriptionType, typename HandleCreator>\nitem item_from_menu(\n    const basic_menu<DescriptionType, HandleCreator>& parent_menu,\n    UINT menu_id)\n{\n    basic_menu<DescriptionType, HandleCreator>::iterator position =\n        find_first_item_with_id(\n            parent_menu.begin(), parent_menu.end(), menu_id);\n\n    if (position != parent_menu.end())\n    {\n        return *position;\n    }\n    else\n    {\n        BOOST_THROW_EXCEPTION(\n            runtime_error(\"Unable to find menu with given ID\"));\n    }\n}\n\nitem fallback_menu(const menu_bar& parent_menu)\n{\n    return item_from_menu(parent_menu, FCIDM_MENU_FILE);\n}\n\n/**\n * Get handle to explorer 'Tools' menu.\n *\n * The menu we want to insert into is actually the @e submenu of the\n * Tools menu @e item.  Confusing!\n */\nitem tools_menu_with_fallback(const menu_bar& parent_menu)\n{\n    try\n    {\n        return item_from_menu(parent_menu, FCIDM_MENU_TOOLS);\n    }\n    catch (const std::exception& e)\n    {\n        trace(\"Failed getting Tools menu: %s\") % diagnostic_information(e);\n\n        return fallback_menu(parent_menu);\n    }\n}\n\n/**\n * Get handle to explorer 'Help' menu.\n *\n * The menu we want to insert into is actually the @e submenu of the\n * Help menu @e item.  Confusing!\n */\nitem help_menu_with_fallback(const menu_bar& parent_menu)\n{\n    try\n    {\n        return item_from_menu(parent_menu, FCIDM_MENU_HELP);\n    }\n    catch (const std::exception& e)\n    {\n        trace(\"Failed getting help menu: %s\") % diagnostic_information(e);\n\n        return fallback_menu(parent_menu);\n    }\n}\n\nvoid merge_command_items(\n    UINT first_command_id, const UINT max_command_id,\n    menu destination, menu::iterator insert_position, \n    const menu_id_command_map& commands)\n{\n    typedef menu_id_command_map::iterator::value_type mapped_command;\n\n    BOOST_FOREACH(const mapped_command& menu_command, commands)\n    {\n        UINT new_command_id = first_command_id + menu_command.first;\n        if (new_command_id > max_command_id)\n            BOOST_THROW_EXCEPTION(\n                runtime_error(\"Exeeded permitted merge space\"));\n\n        command_item_description item(\n            string_button_description(\n                menu_command.second->menu_title(NULL)),\n            new_command_id);\n        \n        // TODO: work out how to hide hidden() items.  For the moment we\n        // treat them the same as disabled().\n        // I don't know how to insert a hidden menu item, but Windows Forms\n        // seems to allow it.  Maybe they maintain a list of menu items\n        // separate from the menu itself and insert/remove the item to\n        // show/hide it.\n\n        BOOST_SCOPED_ENUM(selectability) item_state =\n            menu_command.second->state(NULL, false) == Command::state::enabled ?\n                selectability::enabled : selectability::disabled;\n\n        item.selectability(item_state);\n\n        // We have to be careful to increment the iterator *after* calling\n        // insert in case we are inserting at the end.  Doing \n        // insert_position++ in the call to insert would step off the end.\n\n        destination.insert(item, insert_position);\n        ++insert_position;\n    }\n}\n\nclass merge_tools_command_items\n{\npublic:\n\n    typedef void result_type;\n\n    merge_tools_command_items(\n        UINT first_command_id, const UINT max_command_id,\n        const menu_id_command_map& commands)\n        :\n    m_first_command_id(first_command_id),\n    m_max_command_id(max_command_id), m_commands(commands) {}\n\n    void operator()(sub_menu_item& sub_menu)\n    {\n        menu::iterator insert_position = sub_menu.menu().begin();\n\n        // We hope the 1st and 2nd items are map and unmap network drive, so we\n        // just skip them.  So that we don't fail completely if the Tools\n        // menu is bizarre, we make sure there's actually room to skip first.\n        if (sub_menu.menu().size() >= 2)\n        {\n            insert_position += 2;\n        }\n\n        merge_command_items(\n            m_first_command_id, m_max_command_id, sub_menu.menu(),\n            insert_position, m_commands);\n    }\n\n    void operator()(command_item&)\n    {\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Cannot insert into command item\"));\n    }\n\n    void operator()(separator_item&)\n    {\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Cannot insert into separator\"));\n    }\n\nprivate:\n    UINT m_first_command_id;\n    const UINT m_max_command_id;\n    menu_id_command_map m_commands;\n};\n\nclass merge_help_command_items\n{\npublic:\n\n    typedef void result_type;\n\n    merge_help_command_items(\n        UINT first_command_id, const UINT max_command_id,\n        const menu_id_command_map& commands)\n        :\n    m_first_command_id(first_command_id),\n    m_max_command_id(max_command_id), m_commands(commands) {}\n\n    void operator()(sub_menu_item& sub_menu)\n    {\n        // Inserting into the bottom of the menu\n        menu::iterator insert_position = sub_menu.menu().end();\n\n        merge_command_items(\n            m_first_command_id, m_max_command_id, sub_menu.menu(),\n            insert_position, m_commands);\n    }\n\n    void operator()(command_item&)\n    {\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Cannot insert into command item\"));\n    }\n\n    void operator()(separator_item&)\n    {\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Cannot insert into separator\"));\n    }\n\nprivate:\n    UINT m_first_command_id;\n    const UINT m_max_command_id;\n    menu_id_command_map m_commands;\n};\n\n}\n\n\nmenu_command_manager::menu_command_manager(\n    QCMINFO& menu_info, const optional<window<wchar_t>>& view,\n    const apidl_t& folder)\n:\nm_view(view), m_folder(folder), m_first_command_id(menu_info.idCmdFirst)\n{\n    assert(menu_info.idCmdFirst >= FCIDM_SHVIEWFIRST);\n    assert(menu_info.idCmdLast <= FCIDM_SHVIEWLAST);\n    //assert(::IsMenu(menu_info.hmenu));\n\n    menu_id_command_map tools_menu_commands;\n\n    UINT offset = 0;\n    tools_menu_commands[offset++] = make_shared<Add>(m_folder);\n    tools_menu_commands[offset++] = make_shared<Remove>(m_folder);\n    tools_menu_commands[offset++] = make_shared<Rename>();\n    tools_menu_commands[offset++] = make_shared<CloseSession>();\n    tools_menu_commands[offset++] = make_shared<LaunchAgent>(m_folder);\n\n    // Try to get a handle to the Explorer Tools menu and insert\n    // add and remove connection menu items into it if we find it\n    m_tools_menu = tools_menu_with_fallback(\n        menu_handle::foster_handle(menu_info.hmenu));\n    m_tools_menu->accept(\n        merge_tools_command_items(\n            m_first_command_id, menu_info.idCmdLast, tools_menu_commands));\n\n    menu_id_command_map help_menu_commands;\n    help_menu_commands[offset++] = make_shared<About>();\n\n    // Try to get a handle to the Explorer Help menu and insert About box\n    m_help_menu = help_menu_with_fallback(\n        menu_handle::foster_handle(menu_info.hmenu));\n    m_help_menu->accept(\n        merge_help_command_items(\n            m_first_command_id, menu_info.idCmdLast, help_menu_commands));\n\n    m_commands.insert(tools_menu_commands.begin(), tools_menu_commands.end());\n    m_commands.insert(help_menu_commands.begin(), help_menu_commands.end());\n\n    // Return value of last menu ID plus 1\n    // The following works because maps are sorted so rbegin points to the\n    // last and highest item\n    if (m_commands.rbegin() != m_commands.rend())\n    {\n        menu_info.idCmdFirst += m_commands.rbegin()->first + 1;\n    }\n    \n    // if no commands were added, leave idCmdFirst alone\n}\n\nbool menu_command_manager::invoke(\n    UINT command_id, com_ptr<IShellItemArray> selection,\n    com_ptr<IUnknown> ole_site)\n{\n    menu_id_command_map::iterator pos = m_commands.find(command_id);\n    if (pos != m_commands.end())\n    {\n        // Use given window as a UI owner fallback in case the SFV callback\n        // object was get an OLE site set\n        (*(pos->second))(selection, command_site(ole_site, m_view), NULL);\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\nbool menu_command_manager::help_text(\n    UINT command_id, wstring& text_out, com_ptr<IShellItemArray> selection)\n{\n    menu_id_command_map::iterator pos = m_commands.find(command_id);\n    if (pos != m_commands.end())\n    {\n        text_out = pos->second->tool_tip(selection);\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\nnamespace {\n\nclass update_command_items\n{\npublic:\n\n    typedef void result_type;\n\n    update_command_items(\n        com_ptr<IShellItemArray> selection, UINT first_command_id,\n        const menu_id_command_map& commands)\n        :\n    m_selection(selection), m_first_command_id(first_command_id),\n    m_commands(commands) {}\n\n    class selectability_setter\n    {\n    public:\n        typedef void result_type;\n\n        selectability_setter(BOOST_SCOPED_ENUM(selectability) selectability)\n            : m_selectability(selectability) {}\n\n        void operator()(command_item& item)\n        {\n            item.selectability(m_selectability);\n        }\n\n        template<typename T>\n        void operator()(T&)\n        {\n            BOOST_THROW_EXCEPTION(\n                logic_error(\"Unexpected menu item type\"));\n        }\n\n    private:\n        BOOST_SCOPED_ENUM(selectability) m_selectability;\n    };\n\n    void operator()(sub_menu_item& sub_menu)\n    {\n        typedef menu_id_command_map::iterator::value_type mapped_command;\n\n        BOOST_FOREACH(const mapped_command& menu_command, m_commands)\n        {\n            BOOST_SCOPED_ENUM(selectability) command_state =\n                menu_command.second->state(m_selection, false)\n                == Command::state::enabled ?\n                    selectability::enabled : selectability::disabled;\n\n            item menu_item = item_from_menu(\n                sub_menu.menu(), m_first_command_id + menu_command.first);\n            menu_item.accept(selectability_setter(command_state));\n        }\n    }\n\n    void operator()(command_item&)\n    {\n        BOOST_THROW_EXCEPTION(logic_error(\"Cannot insert into command item\"));\n    }\n\n    void operator()(separator_item&)\n    {\n        BOOST_THROW_EXCEPTION(logic_error(\"Cannot insert into separator\"));\n    }\n\nprivate:\n    com_ptr<IShellItemArray> m_selection;\n    UINT m_first_command_id;\n    menu_id_command_map m_commands;\n};\n\n}\n\nvoid menu_command_manager::update_state(com_ptr<IShellItemArray> selection)\n{\n    if (!m_tools_menu)\n        BOOST_THROW_EXCEPTION(logic_error(\"Missing menu\"));\n\n    m_tools_menu->accept(\n        update_command_items(selection, m_first_command_id, m_commands));\n}\n\n}}\n"
  },
  {
    "path": "swish/host_folder/menu_command_manager.hpp",
    "content": "/* Manage complexities of adding and removing menu items in host window.\n\n   Copyright (C) 2013, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_HOST_FOLDER_MENU_COMMAND_MANAGER\n#define SWISH_HOST_FOLDER_MENU_COMMAND_MANAGER\n\n#include \"swish/nse/Command.hpp\"\n\n#include <washer/gui/menu/menu.hpp>\n#include <washer/shell/pidl.hpp> // apidl_t\n#include <washer/window/window.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/optional/optional.hpp>\n#include <boost/shared_ptr.hpp>\n\n#include <map>\n#include <string>\n\n#include <ShlObj.h> // QCMINFO\n\nnamespace swish {\nnamespace host_folder {\n\n/**\n * Unlike for webview tasks and command items, the shell doesn't recognise an\n * object to manage collections of menu items.  This class fill that gap in\n * order to keep the logic out of the view callback.\n */\nclass menu_command_manager\n{\npublic:\n\n    /**\n     * Merge items into Explorer menus.\n     */\n    menu_command_manager(\n        QCMINFO& menu_info,\n        const boost::optional<washer::window::window<wchar_t>>& view,\n        const washer::shell::pidl::apidl_t& folder);\n\n    /**\n     * Invoke a command by merge offset.\n     */\n    bool invoke(\n        UINT command_id, comet::com_ptr<IShellItemArray> selection,\n        comet::com_ptr<IUnknown> ole_site);\n\n    /**\n     * Request tool tip for command by merge offset.\n     */\n    bool help_text(\n        UINT command_id, std::wstring& text_out,\n        comet::com_ptr<IShellItemArray> selection);\n\n    /**\n     * Refresh command states to match current selection.\n     */\n    void update_state(comet::com_ptr<IShellItemArray> selection);\n\nprivate:\n\n    boost::optional<washer::window::window<wchar_t>> m_view;\n    ///< Folder view window\n\n    washer::shell::pidl::apidl_t m_folder; ///< Owning folder\n\n    UINT m_first_command_id;  ///< Start of our tools menu ID range\n\n    std::map<UINT, boost::shared_ptr<swish::nse::Command>> m_commands;\n    ///< Commands in menu with their menu item ID\n\n    boost::optional<washer::gui::menu::item> m_tools_menu;\n    ///< Handle to the Explorer 'Tools' menu\n\n    boost::optional<washer::gui::menu::item> m_help_menu;\n    ///< Handle to the Explorer 'Help' menu\n};\n\n}}\n\n#endif"
  },
  {
    "path": "swish/host_folder/overlay_icon.hpp",
    "content": "/**\n    @file\n\n    Host folder overlay icons.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_OVERLAY_ICON_HPP\n#define SWISH_HOST_FOLDER_OVERLAY_ICON_HPP\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/connection/session_manager.hpp\"\n#include \"swish/host_folder/host_itemid_connection.hpp\"\n                                                  // connection_from_host_itemid\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <shlobj.h> // SHGetIconOverlayIndex\n\nnamespace swish {\nnamespace host_folder {\n\nclass overlay_icon\n{\npublic:\n    overlay_icon(const washer::shell::pidl::cpidl_t& item)\n        :\n    m_connection(connection_from_host_itemid(host_itemid_view(item)))\n    {}\n\n    bool has_overlay() const\n    {\n        return swish::connection::session_manager().has_session(m_connection);\n    }\n\n    int index() const\n    {\n        return ::SHGetIconOverlayIndexW(NULL, IDO_SHGIOI_DEFAULT);\n    }\n\n    int icon_index() const\n    {\n        return INDEXTOOVERLAYMASK(index());\n    }\n\nprivate:\n    swish::connection::connection_spec m_connection;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/host_folder/properties.cpp",
    "content": "/**\n    @file\n\n    Host folder property columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"properties.hpp\"\n\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view\n\n#include <boost/assign.hpp> // map_list_of\n#include <boost/function.hpp> // function\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <map>\n\n#include <initguid.h> // Make DEFINE_PROPERTYKEY() actually define a key\n#include <Propkey.h> // PKEY_ *\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::property_key;\n\nusing comet::variant_t;\n\nusing boost::assign::map_list_of;\nusing boost::locale::translate;\n\nusing std::map;\n\nnamespace swish {\nnamespace host_folder {\n\n// PKEYs for custom swish details/properties\n// Swish Host FMTID GUID {b816a850-5022-11dc-9153-0090f5284f85}\nDEFINE_PROPERTYKEY(PKEY_SwishHostUser, 0xb816a850, 0x5022, 0x11dc, \\\n                   0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, 0x4f, 0x85, \\\n                   PID_FIRST_USABLE);\nDEFINE_PROPERTYKEY(PKEY_SwishHostPort, 0xb816a850, 0x5022, 0x11dc, \\\n                   0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, 0x4f, 0x85, \\\n                   PID_FIRST_USABLE + 1);\n\nnamespace {\n\n    class unknown_property_error : public std::runtime_error\n    {\n    public:\n        unknown_property_error() : std::runtime_error(\"Unknown property\") {}\n    };\n\n    typedef map<\n        property_key,\n        boost::function<variant_t (const cpidl_t& pidl)> >\n        host_property_map;\n\n    variant_t net_drive_returner(const cpidl_t& /*pidl*/)\n    {\n        return translate(L\"FileType\", L\"Network Drive\").str();\n    }\n\n    variant_t label_getter(const cpidl_t& pidl)\n    { return host_itemid_view(pidl).label(); }\n    variant_t host_getter(const cpidl_t& pidl)\n    { return host_itemid_view(pidl).host(); }\n    variant_t user_getter(const cpidl_t& pidl)\n    { return host_itemid_view(pidl).user(); }\n    variant_t port_getter(const cpidl_t& pidl)\n    { return host_itemid_view(pidl).port(); }\n    variant_t path_getter(const cpidl_t& pidl)\n    { return host_itemid_view(pidl).path().wstring(); }\n\n    const host_property_map host_property_getters = map_list_of\n        (PKEY_ItemNameDisplay, label_getter) // Display name (Label)\n        (PKEY_ComputerName, host_getter) // Hostname\n        (PKEY_SwishHostUser, user_getter) // Username\n        (PKEY_SwishHostPort, port_getter) // SFTP port\n        (PKEY_ItemPathDisplay, path_getter) // Remote filesystem path\n        (PKEY_ItemType, net_drive_returner); // Type: always 'Network Drive'\n}\n\n/**\n * Get the requested property for a file based on its PIDL.\n *\n * Many of these will be standard system properties but some are custom\n * to Swish if an appropriate one did not already exist.\n */\nvariant_t property_from_pidl(const cpidl_t& pidl, const property_key& key)\n{\n    host_property_map::const_iterator pos = host_property_getters.find(key);\n    if (pos == host_property_getters.end())\n        BOOST_THROW_EXCEPTION(unknown_property_error());\n\n    return (pos->second)(pidl.get());\n}\n\n/**\n * Compare two PIDLs by one of their properties.\n *\n * @param left   First PIDL in the comparison.\n * @param right  Second PIDL in the comparison.\n * @param key    Property on which to compare the two PIDLs.\n *\n * @retval -1 if left < right for chosen property.\n * @retval  0 if left == right for chosen property.\n * @retval  1 if left > right for chosen property.\n */\nint compare_pidls_by_property(\n    const cpidl_t& left, const cpidl_t& right, const property_key& key)\n{\n    if (property_from_pidl(left, key) == property_from_pidl(right, key))\n        return 0;\n    else if (property_from_pidl(left, key) < property_from_pidl(right, key))\n        return -1;\n\n    assert(property_from_pidl(left, key) > property_from_pidl(right, key));\n    return 1;\n}\n\n}} // namespace swish::host_folder\n"
  },
  {
    "path": "swish/host_folder/properties.hpp",
    "content": "/**\n    @file\n\n    Properties available for items in a host folder.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_HOST_FOLDER_PROPERTIES_HPP\n#define SWISH_HOST_FOLDER_PROPERTIES_HPP\n#pragma once\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <comet/variant.h> // variant_t\n\n#include <WTypes.h> // PROPERTYKEY\n\nnamespace swish {\nnamespace host_folder {\n\ncomet::variant_t property_from_pidl(\n    const washer::shell::pidl::cpidl_t& pidl,\n    const washer::shell::property_key& key);\n\nint compare_pidls_by_property(\n    const washer::shell::pidl::cpidl_t& pidl_left,\n    const washer::shell::pidl::cpidl_t& pidl_right,\n    const washer::shell::property_key& key);\n\n/**\n * @name Custom properties (PKEYs) for Swish remote folder.\n *\n * Ideally, we want as few of these as possible.  If an appropriate\n * one already exists in propkey.h, that should be used instead.\n *\n * The Swish remote folder FMTID GUID which collects all the custom\n * properties together is @c {b816a851-5022-11dc-9153-0090f5284f85}.\n */\n// @{\nextern \"C\" const PROPERTYKEY PKEY_SwishHostUser;\nextern \"C\" const PROPERTYKEY PKEY_SwishHostPort;\n// @}\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/nse/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  detail/command_state_conversion.hpp\n  Command.cpp\n  command_site.hpp\n  command_site.cpp\n  data_object_util.cpp\n  default_context_menu_callback.cpp\n  explorer_command.cpp\n  UICommand.cpp\n  view_callback.cpp\n  Command.hpp\n  data_object_util.hpp\n  default_context_menu_callback.hpp\n  explorer_command.hpp\n  StaticColumn.hpp\n  task_pane.hpp\n  UICommand.hpp\n  view_callback.hpp)\n\nadd_library(nse ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(nse\n  PRIVATE shell shlwapi ${Boost_LIBRARIES}\n  PUBLIC Washer::washer Comet::comet)\n"
  },
  {
    "path": "swish/nse/Command.cpp",
    "content": "/**\n    @file\n\n    Swish host folder commands.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"Command.hpp\"\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace nse {\n\nCommand::Command(\n    const wstring& title, const uuid_t& guid,\n    const wstring& tool_tip, const wstring& icon_descriptor,\n    const wstring& menu_title, const wstring& webtask_title)\n: m_title(title), m_guid(guid), m_tool_tip(tool_tip),\n  m_icon_descriptor(icon_descriptor), m_menu_title(menu_title),\n  m_webtask_title(webtask_title) {}\n\nwstring Command::title(comet::com_ptr<IShellItemArray>) const\n{ return m_title; }\n\nconst uuid_t& Command::guid() const\n{ return m_guid; }\n\nwstring Command::tool_tip(comet::com_ptr<IShellItemArray>) const\n{ return m_tool_tip; }\n\nwstring Command::icon_descriptor(comet::com_ptr<IShellItemArray>) const\n{ return m_icon_descriptor; }\n\nwstring Command::menu_title(\n    comet::com_ptr<IShellItemArray> selection) const\n{ return (m_menu_title.empty()) ? title(selection) : m_menu_title; }\n\nwstring Command::webtask_title(\n    comet::com_ptr<IShellItemArray> selection) const\n{ return (m_webtask_title.empty()) ? title(selection) : m_webtask_title; }\n\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/Command.hpp",
    "content": "/* Copyright (C) 2010, 2011, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_NSE_COMMAND_HPP\n#define SWISH_NSE_COMMAND_HPP\n#pragma once\n\n#include \"swish/nse/command_site.hpp\"\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h> // com_ptr\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/detail/scoped_enum_emulation.hpp> // BOOST_SCOPED_ENUM\n#include <boost/function.hpp> // function\n#include <boost/preprocessor.hpp> // creating variadic pass-through contructors\n\n#include <ObjIdl.h> // IDataObject, IBindCtx\n#include <shobjidl.h> // IExplorerCommandProvider, IShellItemArray\n\n#include <string>\n\nnamespace swish {\nnamespace nse {\n\nclass Command\n{\npublic:\n    Command(\n        const std::wstring& title, const comet::uuid_t& guid,\n        const std::wstring& tool_tip=std::wstring(),\n        const std::wstring& icon_descriptor=std::wstring(),\n        const std::wstring& menu_title=std::wstring(),\n        const std::wstring& webtask_title=std::wstring());\n\n    virtual ~Command() {}\n\n    /**\n     * Invoke to perform the command.\n     *\n     * Concrete commands will provide their implementation by overriding\n     * this method.\n     *\n     * NOTE: If commands need access to the view's window, to use as a UI owner,\n     * they need to get this from the command site parameter.  If the owner\n     * window is not available from the site, the command must not show UI.\n     *\n     * @param selection    IShellItemArray holding items on which to perform the\n     *                     command.  This may be NULL in which case the\n     *                     command should only execute if it makes sense to\n     *                     do so regardless of selected items.\n     */\n    virtual void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const = 0;\n\n\n    // For any of them methods that take a data_object, the implementation\n    // does what is appropriate for a situation where selection information\n    // is not available.\n    // This differs from the situation where it is known that no objects\n    // are selected.  In that case, a data_object is provided, but it renders\n    // no items.\n\n    /** @name Attributes. */\n    // @{\n    const comet::uuid_t& guid() const;\n    virtual std::wstring title(\n        comet::com_ptr<IShellItemArray> selection) const;\n    virtual std::wstring tool_tip(\n        comet::com_ptr<IShellItemArray> selection) const;\n    virtual std::wstring icon_descriptor(\n        comet::com_ptr<IShellItemArray> selection) const;\n\n    /** @name Optional title variants. */\n    // @{\n    virtual std::wstring menu_title(\n        comet::com_ptr<IShellItemArray> selection) const;\n    virtual std::wstring webtask_title(\n        comet::com_ptr<IShellItemArray> selection) const;\n    // @}\n\n    // @}\n\n    /** @name State. */\n    // @{\n    BOOST_SCOPED_ENUM_START(state)\n    {\n        enabled,\n        disabled,\n        hidden\n    };\n    BOOST_SCOPED_ENUM_END\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const = 0;\n    // @}\n\nprivate:\n    std::wstring m_title;\n    comet::uuid_t m_guid;\n    std::wstring m_tool_tip;\n    std::wstring m_icon_descriptor;\n    std::wstring m_menu_title;\n    std::wstring m_webtask_title;\n};\n\n\n#ifndef COMMAND_ADAPTER_CONSTRUCTOR_MAX_ARGUMENTS\n#define COMMAND_ADAPTER_CONSTRUCTOR_MAX_ARGUMENTS 10\n#endif\n\n#define COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR(N, classname, initialiser) \\\n    BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename A)>) \\\n    explicit classname(BOOST_PP_ENUM_BINARY_PARAMS(N, A, a)) \\\n        : initialiser(BOOST_PP_ENUM_PARAMS(N, a)) {}\n\n// TODO: This class should be changed to use aggregation rather than\n// inheritance because the latter can lead to a stack overflow if the\n// superclass calls title() in its implementation of webtask_title()\n// At the moment, other parts of the code rely on being able to pass\n// commands with extra methods and have them available\ntemplate<typename CommandImpl>\nclass WebtaskCommandTitleAdapter : public CommandImpl\n{\npublic:\n\n// Define pass-through contructors with variable numbers of arguments\n#define BOOST_PP_LOCAL_MACRO(N) \\\n    COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR( \\\n        N, WebtaskCommandTitleAdapter, CommandImpl)\n\n#define BOOST_PP_LOCAL_LIMITS (0, COMMAND_ADAPTER_CONSTRUCTOR_MAX_ARGUMENTS)\n#include BOOST_PP_LOCAL_ITERATE()\n\n    virtual std::wstring title(\n        comet::com_ptr<IShellItemArray> selection) const\n    { return webtask_title(selection); }\n};\n\n#undef COMMAND_ADAPTER_CONSTRUCTOR_MAX_ARGUMENTS\n#undef COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/StaticColumn.hpp",
    "content": "/**\n    @file\n\n    NSE folder columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_STATIC_COLUMN_HPP\n#define SWISH_NSE_STATIC_COLUMN_HPP\n#pragma once\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <string>\n\n#include <ShTypes.h> // SHCOLSTATEF\n\nnamespace swish {\nnamespace nse {\n\ntemplate<typename Base>\nclass StaticColumn : Base\n{\npublic:\n    /**\n     * Create a column manager for the indexth column.\n     *\n     * @throws  std::range_error if the column index is out of range.\n     */\n    StaticColumn(size_t index) : m_index(index) {}\n    virtual ~StaticColumn() {}\n\n    /**\n     * Localised heading of the column.\n     */\n    std::wstring header() const\n    {\n        return entry(m_index).title();\n    }\n\n    /**\n     * Get the contents corresponding to this column for the given PIDL.\n     *\n     * Regardless of the type of the underlying data, this function must always\n     * returns the data as a string.  If any formatting is required, it must\n     * be done in this function.\n     */\n    std::wstring detail(const washer::shell::pidl::cpidl_t& pidl) const\n    {\n        return entry(m_index).detail(pidl);\n    }\n\n    /**\n     * The number of 'x' characters an average item in the column will occupy.\n     */\n    int average_width_in_chars() const\n    {\n        return entry(m_index).avg_char_width();\n    }\n\n    /**\n     * Returns the state (data type and whether to display by default) for the\n     * column.\n     */\n    SHCOLSTATEF state() const\n    {\n        return entry(m_index).flags();\n    }\n\n    /**\n     * How to display the data in the column (e.g. alignment).\n     */\n    int format() const\n    {\n        return entry(m_index).format();\n    }\n\n    /**\n     * Compare two PIDLs by this column's detail.\n     *\n     * @retval -1 if lhs < rhs\n     * @retval  0 if lhs == rhs\n     * @retval  1 if lhs > rhs\n     */\n    int compare(\n        const washer::shell::pidl::cpidl_t& lhs,\n        const washer::shell::pidl::cpidl_t& rhs) const\n    {\n        return entry(m_index).compare(lhs, rhs);\n    }\n\nprivate:\n    const size_t m_index;\n};\n\n}} // swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/UICommand.cpp",
    "content": "/**\n    @file\n\n    Undocumented Windows XP task pane interfaces.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <Windows.h> // IID\n#include <cguid.h> // GUID_NULL\n#include <initguid.h>\n#include \"UICommand.hpp\"\n"
  },
  {
    "path": "swish/nse/UICommand.hpp",
    "content": "/**\n    @file\n\n    Undocumented Windows XP task pane interfaces.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_UICOMMAND_HPP\n#define SWISH_NSE_UICOMMAND_HPP\n#pragma once\n\n#include <comet/enum.h> // enumerated_type_of\n#include <comet/interface.h> // comtype\n\n#include <Guiddef.h> // DEFINE_GUID\n#include <ShObjIdl.h> // IShellItemArray\n\nnamespace swish {\nnamespace nse {\n\nDEFINE_GUID(IID_IUIElement,\n    0xEC6FE84F,0xDC14,0x4FBB,0x88,0x9F,0xEA,0x50,0xFE,0x27,0xFE,0x0F);\n\nDEFINE_GUID(IID_IUICommand,\n    0x4026DFB9,0x7691,0x4142,0xB7,0x1C,0xDC,0xF0,0x8E,0xA4,0xDD,0x9C);\n\nDEFINE_GUID(IID_IEnumUICommand,\n    0x869447DA,0x9F84,0x4E2A,0xB9,0x2D,0x00,0x64,0x2D,0xC8,0xA9,0x11);\n\n/**\n * XP folder web view item.\n *\n * Undocumented by Microsoft.  Based on public domain code at\n * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n *\n * Copyright (C) 1998-2003 Whirling Dervishes Software.\n */\nstruct IUIElement : public IUnknown\n{\n    virtual HRESULT STDMETHODCALLTYPE get_Name(\n        IShellItemArray* pItemArray, wchar_t** ppszName) = 0;\n    virtual HRESULT STDMETHODCALLTYPE get_Icon(\n        IShellItemArray* pItemArray, wchar_t** ppszIcon) = 0;\n    virtual HRESULT STDMETHODCALLTYPE get_Tooltip(\n        IShellItemArray* pItemArray, wchar_t** ppszInfotip) = 0;\n};\n\n/**\n * XP folder web view command.\n *\n * Undocumented by Microsoft.  Based on public domain code at\n * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n *\n * Copyright (C) 1998-2003 Whirling Dervishes Software.\n */\nstruct IUICommand : public IUIElement\n{\n    virtual HRESULT STDMETHODCALLTYPE get_CanonicalName(GUID* pGuid) = 0;\n    virtual HRESULT STDMETHODCALLTYPE get_State(\n        IShellItemArray* pItemArray, int nRequested, EXPCMDSTATE* pState) = 0;\n    virtual HRESULT STDMETHODCALLTYPE Invoke(\n        IShellItemArray* pItemArray, IBindCtx* pCtx) = 0;\n};\n\n/**\n * XP folder web view command enumerator.\n *\n * Undocumented by Microsoft.  Based on public domain code at\n * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n *\n * Copyright (C) 1998-2003 Whirling Dervishes Software.\n */\nstruct IEnumUICommand : public IUnknown\n{\n    virtual HRESULT STDMETHODCALLTYPE Next(\n        ULONG celt, IUICommand** rgelt, ULONG* pceltFetched) = 0;\n    virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt) = 0;\n    virtual HRESULT STDMETHODCALLTYPE Reset() = 0;\n    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumUICommand** ppenum) = 0;\n};\n\n}} // namespace swish::nse\n\ntemplate<> struct comet::comtype<swish::nse::IUIElement>\n{\n    static const IID& uuid() throw()\n    { return swish::nse::IID_IUIElement; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comet::comtype<swish::nse::IUICommand>\n{\n    static const IID& uuid() throw()\n    { return swish::nse::IID_IUICommand; }\n    typedef swish::nse::IUIElement base;\n};\n\ntemplate<> struct comet::comtype<swish::nse::IEnumUICommand>\n{\n    static const IID& uuid() throw()\n    { return swish::nse::IID_IEnumUICommand; }\n    typedef IUnknown base;\n};\n\ntemplate<>\nstruct comet::enumerated_type_of<swish::nse::IEnumUICommand>\n{ typedef swish::nse::IUICommand* is; };\n\ntemplate<>\nstruct comet::impl::type_policy<swish::nse::IUICommand*>\n{\n    template<typename S>\n    static void init(swish::nse::IUICommand*& p, const S& s) \n    {  p = s.get(); p->AddRef(); }\n\n    static void clear(swish::nse::IUICommand*& p) { p->Release(); }    \n};\n\n#endif\n"
  },
  {
    "path": "swish/nse/command_site.cpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"command_site.hpp\"\n\n#include \"swish/shell/shell.hpp\" // window_for_ole_site\n\n#include <cassert>\n#include <exception>\n\nusing swish::shell::window_for_ole_site;\n\nusing washer::window::window;\n\nusing comet::com_ptr;\n\nusing boost::optional;\n\nnamespace swish {\nnamespace nse {\n\ncommand_site::command_site() {}\n\ncommand_site::command_site(com_ptr<IUnknown> ole_site) : m_ole_site(ole_site) {}\n\ncommand_site::command_site(\n    com_ptr<IUnknown> ole_site,\n    const optional<window<wchar_t>>& ui_owner_fallback)\n    : m_ole_site(ole_site), m_ui_owner_fallback(ui_owner_fallback)\n{\n    assert(\"NULL HWND in initialised optional<window>\" &&\n        (!m_ui_owner_fallback || m_ui_owner_fallback->hwnd()));\n}\n\noptional<window<wchar_t>> command_site::ui_owner() const\n{\n    if (m_ole_site)\n    {\n        try\n        {\n            optional<window<wchar_t>> view_window =\n                window_for_ole_site(m_ole_site);\n            if (view_window)\n            {\n                return view_window;\n            }\n            else\n            {\n                return m_ui_owner_fallback;\n            }\n        }\n        catch (const std::exception&)\n        {\n            return m_ui_owner_fallback;\n        }\n    }\n    else\n    {\n        return m_ui_owner_fallback;\n    }\n}\n\ncom_ptr<IUnknown> command_site::ole_site() const\n{\n    return m_ole_site;\n}\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/command_site.hpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_NSE_COMMAND_SITE_HPP\n#define SWISH_NSE_COMMAND_SITE_HPP\n\n#include <washer/window/window.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/optional/optional.hpp>\n\n#include <ObjIdl.h> // IDataObject, IBindCtx\n\nnamespace swish {\nnamespace nse {\n\n/**\n * OLE site with window fallback.\n *\n * The Windows shell shituation for when you can show UI is unclear: should you\n * use the HWND passed in by the shell when it calls your NSE methods, or should\n * you use the OLE site.  Neither method is available everywhere.\n * IExplorerCommands, created via a call to CreateViewObject, never get an HWND,\n * but are treated as an OLE site.  Commands invoked via the context menu\n * integration have an HWND, but no OLE Site.  Since Vista they can receive an\n * OLE site via INVOKECOMMANDEX, but there is no guarantee that code will be\n * invoked that way and, if compiled with support for Windows XP, that argument\n * will not be available on any platform.\n *\n * One thing is clear: our UI must always have an owner window, otherwise bad\n * things may happen (see The Old New Thing book).\n *\n * The strategy we adopt here is to use this class to abstract over precisely\n * where the owner window information may arrive from.  The commands can just\n * ask this class for the window and, if any window is obtainable from any\n * source, the window is returned.  Creation sites must initialise this class\n * with whichever window sources they have: OLE site, window handle, or both.\n *\n * If ui_owner returns an uninitialised value, the calling code must not try to\n * show any UI.\n *\n * This class also makes the OLE site available, if present, for commands that\n * need more specific UI control, such as the ability to set a file icon into\n * rename mode.  This may not be avaible, and the calling code must handle that\n * possibility.\n */\nclass command_site\n{\npublic:\n\n    /**\n     * A site where no UI interaction is permitted.\n     */\n    command_site();\n\n    /**\n     * A site where UI interaction is permitted via the OLE site.\n     */\n    explicit command_site(comet::com_ptr<IUnknown> ole_site);\n\n    /**\n     * A site where UI interaction is permitted via an OLE site or via a window.\n     *\n     * The window, if initialised, is a fallback for the UI owner if the OLE\n     * site was NULL or was not able to provide a window.\n     */\n    command_site(\n        comet::com_ptr<IUnknown> ole_site,\n        const boost::optional<\n            washer::window::window<wchar_t>>& ui_owner_fallback);\n\n    boost::optional<washer::window::window<wchar_t>> ui_owner() const throw();\n\n    comet::com_ptr<IUnknown> ole_site() const throw();\n\nprivate:\n    comet::com_ptr<IUnknown> m_ole_site;\n    boost::optional<washer::window::window<wchar_t>> m_ui_owner_fallback;\n};\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/data_object_util.cpp",
    "content": "/**\n    @file\n\n    Utility functions to work with DataObjects.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"data_object_util.hpp\"\n\n#include <shlguid.h> // BHID_DataObject\n\nusing comet::com_ptr;\n\nnamespace swish {\nnamespace nse {\n\ncom_ptr<IDataObject> data_object_from_item_array(\n    com_ptr<IShellItemArray> items, com_ptr<IBindCtx> bind_ctx)\n{\n    com_ptr<IDataObject> data_object;\n    if (items)\n    {\n        items->BindToHandler(\n            bind_ctx.get(), BHID_DataObject, data_object.iid(),\n            reinterpret_cast<void**>(data_object.out()));\n    }\n\n    // We don't care if binding succeeded - if it did, great; we pass\n    // the DataObject.  If not, the data_object pointer will be NULL\n    // and we can assume that no items were selected\n\n    return data_object;\n}\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/data_object_util.hpp",
    "content": "/**\n    @file\n\n    Utility functions to work with DataObjects.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_DATA_OJECT_UTIL_HPP\n#define SWISH_NSE_DATA_OJECT_UTIL_HPP\n\n#include <comet/interface.h> // uuidof, comtype\n#include <comet/ptr.h> // com_ptr\n\n#include <ObjIdl.h> // IDataObject, IBindCtx\n#include <shobjidl.h> // IShellItemArray\n\ntemplate<> struct comet::comtype<IDataObject>\n{\n    static const IID& uuid() throw() { return IID_IDataObject; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace nse {\n\n/**\n * Convert a ShellItemArray to a DataObject.\n *\n * This DataObject hold the items in the array in the usual form\n * expected of a shell DataObject.\n *\n * @return  NULL if there is a failure.  This indicates that the array\n *          was empty.\n */\ncomet::com_ptr<IDataObject> data_object_from_item_array(\n    comet::com_ptr<IShellItemArray> items,\n    comet::com_ptr<IBindCtx> bind_ctx=NULL);\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/default_context_menu_callback.cpp",
    "content": "/**\n    @file\n\n    Handler for Explorer Default Context Menu messages.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/nse/default_context_menu_callback.hpp\"\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <string>\n\n#include <shlobj.h> // DFM_*\n#include <Windows.h> // HRESULT, HWND, lparam, wparam, HMENU, IDataObject\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing std::string;\nusing std::wstring;\n\nnamespace swish {\nnamespace nse {\n\ndefault_context_menu_callback::~default_context_menu_callback() {}\n\nHRESULT default_context_menu_callback::operator()(\n    HWND hwnd, com_ptr<IDataObject> selection, \n    UINT menu_message_id, WPARAM wparam, LPARAM lparam)\n{\n    try\n    {\n        switch (menu_message_id)\n        {\n        case DFM_MERGECONTEXTMENU:\n            {\n                QCMINFO* info = reinterpret_cast<QCMINFO*>(lparam);\n                if (info == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n                bool also_add_default_verbs = merge_context_menu(\n                    hwnd, selection, info->hmenu, info->indexMenu,\n                    info->idCmdFirst, info->idCmdLast,\n                    static_cast<UINT>(wparam));\n\n                return (also_add_default_verbs) ? S_OK : S_FALSE;\n            }\n        case DFM_INVOKECOMMAND:\n            {\n                const wchar_t* arguments = reinterpret_cast<PCWSTR>(lparam);\n                bool handled = invoke_command(\n                    hwnd, selection, static_cast<UINT>(wparam),\n                    (arguments == NULL) ? wstring() : arguments);\n\n                return (handled) ? S_OK : S_FALSE;\n            }\n        case DFM_INVOKECOMMANDEX:\n            {\n                DFMICS* dfmics = reinterpret_cast<DFMICS*>(lparam);\n                if (dfmics == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n                const wchar_t* arguments =\n                    reinterpret_cast<PCWSTR>(dfmics->lParam);\n\n                if (dfmics->pici == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n                bool handled = invoke_command(\n                    hwnd, selection, static_cast<UINT>(wparam),\n                    (arguments == NULL) ? wstring() : arguments, dfmics->fMask,\n                    dfmics->idCmdFirst, dfmics->idDefMax, *(dfmics->pici),\n#if (NTDDI_VERSION >= NTDDI_VISTA)\n                    dfmics->punkSite);\n#else\n                    NULL);\n#endif\n\n                return (handled) ? S_OK : S_FALSE;\n            }\n        case DFM_GETVERBA:\n            {\n                string result;\n                verb(\n                    hwnd, selection, static_cast<UINT>(LOWORD(wparam)),\n                    result);\n\n                UINT buffer_len = static_cast<UINT>(HIWORD(wparam));\n                if ((result.size() + 1) > buffer_len)\n                    BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n                char* buffer = reinterpret_cast<char*>(lparam);\n                if (buffer == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n#pragma warning(push)\n#pragma warning(disable: 4996)\n                result.copy(buffer, buffer_len);\n#pragma warning(pop)\n                buffer[buffer_len - 1] = char();\n                return S_OK;\n            }\n        case DFM_GETVERBW:\n            {\n                wstring result;\n                verb(\n                    hwnd, selection, static_cast<UINT>(LOWORD(wparam)),\n                    result);\n\n                UINT buffer_len = static_cast<UINT>(HIWORD(wparam));\n                if ((result.size() + 1) > buffer_len)\n                    BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n                wchar_t* buffer = reinterpret_cast<wchar_t*>(lparam);\n                if (buffer == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n#pragma warning(push)\n#pragma warning(disable: 4996)\n                result.copy(buffer, buffer_len);\n#pragma warning(pop)\n                buffer[buffer_len - 1] = wchar_t();\n                return S_OK;\n            }\n        case DFM_GETDEFSTATICID:\n            {\n                UINT* command_id_out = reinterpret_cast<UINT*>(lparam);\n                if (command_id_out == NULL)\n                    BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n                bool use_default = !default_menu_item(\n                    hwnd, selection, *command_id_out);\n\n                return (use_default) ? S_FALSE : S_OK;\n            }\n        default:\n            return on_unknown_dfm(\n                hwnd, selection, menu_message_id, wparam, lparam);\n        }\n    }\n    WASHER_COM_CATCH();\n}\n\nHRESULT default_context_menu_callback::on_unknown_dfm(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/, \n    UINT /*menu_message_id*/, WPARAM /*wparam*/, LPARAM /*lparam*/)\n{\n    return E_NOTIMPL; // Required for Windows 7 to show any menu at all\n}\n\nbool default_context_menu_callback::merge_context_menu(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/, HMENU /*hmenu*/,\n    UINT /*first_item_index*/, UINT& /*minimum_id*/, UINT /*maximum_id*/,\n    UINT /*allowed_changes_flags*/)\n{\n    return true;\n}\n\nbool default_context_menu_callback::invoke_command(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/,\n    UINT /*item_offset*/, const wstring& /*arguments*/)\n{\n    return false;\n}\n\nbool default_context_menu_callback::invoke_command(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/,\n    UINT /*item_offset*/, const wstring& /*arguments*/,\n    DWORD /*behaviour_flags*/, UINT /*minimum_id*/, UINT /*maximum_id*/,\n    const CMINVOKECOMMANDINFO& /*invocation_details*/,\n    comet::com_ptr<IUnknown> /*context_menu_site*/)\n{\n    return false;\n}\n    \nvoid default_context_menu_callback::verb(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/, \n    UINT /*command_id_offset*/, string& /*verb_out*/)\n{\n}\n\nvoid default_context_menu_callback::verb(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/, \n    UINT /*command_id_offset*/, wstring& /*verb_out*/)\n{\n}\n\nbool default_context_menu_callback::default_menu_item(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> /*selection*/,\n    UINT& /*default_command_id*/)\n{\n    return false;\n}\n\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/default_context_menu_callback.hpp",
    "content": "/**\n    @file\n\n    Handler for Explorer Default Context Menu messages.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_DEFAULT_CONTEXT_MENU_CALLBACK_HPP\n#define SWISH_NSE_DEFAULT_CONTEXT_MENU_CALLBACK_HPP\n#pragma once\n\n#include <comet/ptr.h> // com_ptr\n\n#include <string>\n#include <shobjidl.h> // CMINVOKECOMMANDINFO\n#include <Windows.h> // HRESULT, HWND, LPARAM, WPARAM, HMENU, IDataObject\n\nnamespace swish {\nnamespace nse {\n\nclass default_context_menu_callback\n{\npublic:\n\n    virtual ~default_context_menu_callback();\n\n    /**\n     * Cracks the @c DFM_* callback messages and dispatches them to handlers.\n     */\n    HRESULT operator()(\n        HWND hwnd, comet::com_ptr<IDataObject> selection, \n        UINT menu_message_id, WPARAM wparam, LPARAM lparam);\n\nprivate:\n\n    /// @name  DFM_* message handlers\n    // @{\n\n    /**\n     * A message was sent to the callback that we don't know how to crack.\n     *\n     * This method gives subclasses an opportunity to handle messages that we\n     * don't understand or new messages that Microsoft add in the future.\n     *\n     * The default implementation returns E_NOTIMPL.  Override this method in\n     * a subclass to capture unhandled messages.\n     *\n     * @warning  Any implementation must return E_NOTIMPL for messages it\n     *           doesn't recognise.  Failure to do so can cause the default\n     *           context menu to fail entirely.\n     */\n    virtual HRESULT on_unknown_dfm(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, \n        UINT menu_message_id, WPARAM wparam, LPARAM lparam);\n\n    /**\n     * The default context menu is giving us a chance to add custom items.\n     *\n     * Before returning you must set @a minimum_id to be higher than the \n     * highest command ID you added to the menu.  The best way to do this is\n     * to increment @minimum_id for each menu item you add.\n     *\n     * Any changes we make should respect the rules specified via the flags.\n     *\n     * Return true to tell the shell to add default verbs such as Open,\n     * Explorer and Print to the menu. Return false to prevent this.\n     *\n     * The default implementation adds no items to the menu and returns true.\n     * Override this method in a subclass to change the behaviour.\n     */\n    virtual bool merge_context_menu(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        HMENU hmenu, UINT first_item_index, UINT& minimum_id, UINT maximum_id,\n        UINT allowed_changes_flags);\n\n    /**\n     * One of the context menu commands was invoked.\n     *\n     * This could be any of the commands we added via merge_context_menu or\n     * even one of the DFM_CMD_* values which the shell adds for us.\n     *\n     * Return false to tell the shell to handle the command for us.  It may\n     * have an inbuilt action or it may just do nothing.  Return true means\n     * that we completely handled the action.\n     *\n     * The default implementation just returns true to get default shell\n     * behaviour. Override this method in a subclass to change the behaviour.\n     */\n    virtual bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT item_offset, const std::wstring& arguments);\n\n    /**\n     * One of the context menu commands was invoked.\n     *\n     * This could be any of the commands we added via merge_context_menu or\n     * even one of the DFM_CMD_* values which the shell adds for us.\n     *\n     * Return false to tell the shell to handle the command for us.  It may\n     * have an inbuilt action or it may just do nothing.  Return true means\n     * that we completely handled the action.\n     *\n     * The default implementation just returns false to get default shell\n     * behaviour. Override this method in a subclass to change the behaviour.\n     *\n     * @note The context menu site will not be set if compiled with\n     *       NTDDI_VERSION < NTDDI_VISTA.\n     */\n    virtual bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT item_offset, const std::wstring& arguments,\n        DWORD behaviour_flags, UINT minimum_id, UINT maximum_id,\n        const CMINVOKECOMMANDINFO& invocation_details,\n        comet::com_ptr<IUnknown> context_menu_site);\n\n    /**\n     * Convert menu command ID offset to verb string.\n     */\n    virtual void verb(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, \n        UINT command_id_offset, std::string& verb_out);\n    virtual void verb(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, \n        UINT command_id_offset, std::wstring& verb_out);\n\n    /**\n     * The shell is asking which item in the menu it should make default.\n     *\n     * Set the parameter to the desired ID and return true to set the default.\n     * Return false to ask the shell to choose the default itself.\n     *\n     * The default implementation returns true to make the shell choose the\n     * default.  Override this method in a subclass to change the behaviour.\n     */\n    virtual bool default_menu_item(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT& default_command_id);\n\n    // @}\n\n};\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/detail/command_state_conversion.hpp",
    "content": "/**\n    @file\n\n    Conversion between command state representations.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_DETAIL_COMMAND_STATE_CONVERSION_HPP\n#define SWISH_NSE_DETAIL_COMMAND_STATE_CONVERSION_HPP\n\n#include \"swish/nse/Command.hpp\"\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <stdexcept> // logic_error\n\n#include <shobjidl.h> // EXPCMDSTATE\n\nnamespace swish {\nnamespace nse {\nnamespace detail {\n\ninline EXPCMDSTATE command_state_to_expcmdstate(\n    BOOST_SCOPED_ENUM(Command::state) state_in)\n{\n    switch (state_in)\n    {\n    case Command::state::enabled:\n        return ECS_ENABLED;\n\n    case Command::state::disabled:\n        return ECS_DISABLED;\n\n    case Command::state::hidden:\n        // Add disabled flag as well just to be on the safe side.\n        // As the command button is hidden it shouldn't matter whether it \n        // is disabled\n        return ECS_HIDDEN | ECS_DISABLED;\n\n    default:\n        BOOST_THROW_EXCEPTION(\n            std::logic_error(\"Unrecognised command state\"));\n    }\n}\n\n}}}\n\n#endif\n"
  },
  {
    "path": "swish/nse/explorer_command.cpp",
    "content": "/**\n    @file\n\n    Explorer tool-bar command button implementation classes.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"explorer_command.hpp\"\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/enum.h> // stl_enumeration\n#include <comet/ptr.h> // com_ptr\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/foreach.hpp> // BOOST_FOREACH\n\n#include <Shlwapi.h> // SHStrDup\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::stl_enumeration;\nusing comet::uuid_t;\n\ntemplate<> struct comet::enumerated_type_of<IEnumExplorerCommand>\n{ typedef IExplorerCommand* is; };\n\ntemplate<> struct comet::impl::type_policy<IExplorerCommand*>\n{\n    template<typename S>\n    static void init(IExplorerCommand*& p, const S& s) \n    {  p = s.get(); p->AddRef(); }\n\n    static void clear(IExplorerCommand*& p) { p->Release(); }    \n};\n\nnamespace swish {\nnamespace nse {\n\n#pragma region CExplorerCommandProvider implementation\n\n/**\n * Create an ExplorerCommandProvider from exisiting ExplorerCommands.\n *\n * Store the ordered vector of commands and build a mapping from GUIDs\n * to IExplorerCommands for use when looking up via GetCommand.\n */\nCExplorerCommandProvider::CExplorerCommandProvider(\n    const ordered_commands& commands) : m_commands(commands)\n{\n    BOOST_FOREACH(comet::com_ptr<IExplorerCommand>& c, m_commands)\n    {\n        uuid_t guid;\n        HRESULT hr = c->GetCanonicalName(guid.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n        m_guid_mapping[guid] = c;\n    }\n}\n\nSTDMETHODIMP CExplorerCommandProvider::GetCommands(\n    IUnknown* /*punkSite*/, const IID& riid, void** ppv)\n{\n    if (ppv)\n        *ppv = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        com_ptr<IEnumExplorerCommand> commands =\n            stl_enumeration<IEnumExplorerCommand>::create(\n                m_commands, get_unknown());\n        return commands->QueryInterface(riid, ppv);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\nSTDMETHODIMP CExplorerCommandProvider::GetCommand(\n    const GUID& rguidCommandId, const IID& riid, void** ppv)\n{\n    if (ppv)\n        *ppv = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        command_map::const_iterator item = m_guid_mapping.find(rguidCommandId);\n        if (item == m_guid_mapping.end())\n            BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n        return item->second->QueryInterface(riid, ppv);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n#pragma endregion\n\n#pragma region CExplorerCommandErrorAdapter implementation\n\n/**\n * Return command's title string.\n *\n * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n *                           executed upon.\n * @param[out] ppszName      Location in which to return character buffer\n *                           allocated with CoTaskMemAlloc.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetTitle(\n    IShellItemArray* psiItemArray, wchar_t** ppszName)\n{\n    if (ppszName)\n        *ppszName = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        HRESULT hr = ::SHStrDup(title(psiItemArray).c_str(), ppszName);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Return command's icon descriptor.\n *\n * This takes the form \"shell32.dll,-249\" where 249 is the icon's resource ID.\n *\n * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n *                           executed upon.\n * @param[out] ppszIcon      Location in which to return character buffer\n *                           allocated with CoTaskMemAlloc.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetIcon(\n    IShellItemArray* psiItemArray, wchar_t** ppszIcon)\n{\n    if (ppszIcon)\n        *ppszIcon = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        HRESULT hr = ::SHStrDup(icon(psiItemArray).c_str(), ppszIcon);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Return command's tool tip.\n *\n * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n *                           executed upon.\n * @param[out] ppszInfotip   Location in which to return character buffer\n *                           allocated with CoTaskMemAlloc.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetToolTip(\n    IShellItemArray* psiItemArray, wchar_t** ppszInfotip)\n{\n    if (ppszInfotip)\n        *ppszInfotip = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        HRESULT hr = ::SHStrDup(tool_tip(psiItemArray).c_str(), ppszInfotip);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Return command's unique GUID.\n *\n * @param[out] pguidCommandName   Location in which to return GUID.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetCanonicalName(\n    GUID* pguidCommandName)\n{\n    if (pguidCommandName)\n        *pguidCommandName = GUID_NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        *pguidCommandName = canonical_name();\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Return the command's state given array of PIDLs.\n *\n * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n *                           executed upon.\n * @param[in]  fOkToBeSlow   Indicated whether slow operations can be used\n *                           when calculating the state.\n * @param[out] pCmdState     Location in which to return the state flags.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetState(\n    IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState)\n{\n    if (pCmdState)\n        *pCmdState = 0;\n    else\n        return E_POINTER;\n\n    try\n    {\n        *pCmdState = state(psiItemArray, (fOkToBeSlow) ? true : false);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n/**\n * Execute the code associated with this command instance.\n *\n * @param[in] psiItemArray  Optional array of PIDLs that command is\n *                          executed upon.\n * @param[in] pbc           Optional bind context.\n */\nSTDMETHODIMP CExplorerCommandErrorAdapter::Invoke(\n    IShellItemArray* psiItemArray, IBindCtx* pbc)\n{\n    try\n    {\n        invoke(psiItemArray, pbc);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\nSTDMETHODIMP CExplorerCommandErrorAdapter::GetFlags(EXPCMDFLAGS* pFlags)\n{\n    if (pFlags)\n        *pFlags = 0;\n    else\n        return E_POINTER;\n\n    try\n    {\n        *pFlags = flags();\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\nSTDMETHODIMP CExplorerCommandErrorAdapter::EnumSubCommands(\n    IEnumExplorerCommand** ppEnum)\n{\n    if (ppEnum)\n        *ppEnum = NULL;\n    else\n        return E_POINTER;\n\n    try\n    {\n        *ppEnum = subcommands().detach();\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    return S_OK;\n}\n\n#pragma endregion\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/explorer_command.hpp",
    "content": "/* Explorer tool-bar command button implementation classes.\n\n   Copyright (C) 2010, 2011, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_NSE_EXPLORER_COMMAND_HPP\n#define SWISH_NSE_EXPLORER_COMMAND_HPP\n#pragma once\n\n#include \"swish/nse/command_site.hpp\"\n#include \"swish/nse/detail/command_state_conversion.hpp\"\n                                               // command_state_to_expcmdstate\n\n#include <washer/object_with_site.hpp> // object_with_site\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n#include <comet/uuid_fwd.h> // uuid\n\n#include <boost/preprocessor.hpp> // creating variadic pass-through contructors\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <shobjidl.h> // IExplorerCommandProvider/IExplorerCommand\n\n#include <string>\n#include <map>\n#include <vector>\n\ntemplate<> struct comet::comtype<IExplorerCommand>\n{\n    static const IID& uuid() throw() { return IID_IExplorerCommand; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comet::comtype<IExplorerCommandProvider>\n{\n    static const IID& uuid() throw() { return IID_IExplorerCommandProvider; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comet::comtype<IEnumExplorerCommand>\n{\n    static const IID& uuid() throw() { return IID_IEnumExplorerCommand; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace nse {\n\nclass CExplorerCommandProvider :\n    public comet::simple_object<IExplorerCommandProvider>\n{\npublic:\n\n    typedef std::vector<comet::com_ptr<IExplorerCommand> > ordered_commands;\n    typedef std::map<comet::uuid_t, comet::com_ptr<IExplorerCommand> >\n        command_map;\n\n    CExplorerCommandProvider(const ordered_commands& commands);\n\n    /**\n     * Return an Explorer command instance.\n     *\n     * @param[in] punkSite  Optional, pointer through which to set a site.\n     * @param[in] riid      IID of the requested interface (typically\n     *                      IEnumExplorerCommand).\n     * @param[out] ppv      Location in which to return the requested\n     *                      interface.\n     */\n    IFACEMETHODIMP GetCommands(\n        IUnknown* punkSite, const IID& riid, void** ppv);\n\n    /**\n     * Return an enumerator of IExplorerCommand instances.\n     *\n     * @param[in] rguidCommandId  GUID of the requested command.\n     * @param[in] riid            IID of the requested interface (typically\n     *                            IExplorerCommand).\n     * @param[out] ppv            Location in which to return the requested\n     *                            interface.\n     */\n    IFACEMETHODIMP GetCommand(\n        const GUID& rguidCommandId, const IID& riid, void** ppv);\n\nprivate:\n\n    ordered_commands m_commands;\n    command_map m_guid_mapping;\n};\n\n/**\n * Abstract IExplorerCommand implementation wrapper.\n *\n * Wraps a C++ implementation of IExplorerCommand with code to convert it\n * to the external COM interface.  This is an NVI style approach.\n */\nclass CExplorerCommandErrorAdapter : public IExplorerCommand\n{\npublic:\n\n    typedef IExplorerCommand interface_is;\n\n    /** @name IExplorerCommand external COM methods. */\n    // @{\n\n    IFACEMETHODIMP GetTitle(\n        IShellItemArray* psiItemArray, wchar_t** ppszName);\n\n    IFACEMETHODIMP GetIcon(\n        IShellItemArray* psiItemArray, wchar_t** ppszIcon);\n\n    IFACEMETHODIMP GetToolTip(\n        IShellItemArray* psiItemArray, wchar_t** ppszInfotip);\n\n    IFACEMETHODIMP GetCanonicalName(GUID* pguidCommandName);\n\n    IFACEMETHODIMP GetState(\n        IShellItemArray* psiItemArray, BOOL fOkToBeSlow,\n        EXPCMDSTATE* pCmdState);\n\n    IFACEMETHODIMP Invoke(\n        IShellItemArray* psiItemArray, IBindCtx* pbc);\n\n    IFACEMETHODIMP GetFlags(EXPCMDFLAGS* pFlags);\n\n    IFACEMETHODIMP EnumSubCommands(IEnumExplorerCommand** ppEnum);\n\n    // @}\n\nprivate:\n\n    /** @name NVI internal interface.\n     *\n     * Implement this to create IExplorerCommand instances.\n     */\n    // @{\n    virtual const comet::uuid_t& canonical_name() const = 0;\n    virtual std::wstring title(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n    virtual std::wstring tool_tip(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n    virtual std::wstring icon(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n    virtual EXPCMDSTATE state(\n        const comet::com_ptr<IShellItemArray>& items,\n        bool ok_to_be_slow) const = 0;\n    virtual EXPCMDFLAGS flags() const = 0;\n    virtual comet::com_ptr<IEnumExplorerCommand> subcommands() const = 0;\n    virtual void invoke(\n        const comet::com_ptr<IShellItemArray>& items,\n        const comet::com_ptr<IBindCtx>& bind_ctx) const = 0;\n    // @}\n};\n\n\n#ifndef SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS\n#define SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS 10\n#endif\n\n#define EXPLORER_COMMAND_VARIADIC_CONSTRUCTOR(N, classname, initialiser) \\\n    BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename A)>) \\\n    explicit classname(BOOST_PP_ENUM_BINARY_PARAMS(N, A, a)) \\\n        : initialiser(BOOST_PP_ENUM_PARAMS(N, a)) {}\n\n/**\n * Implements IExplorerCommands by wrapping command functors.\n *\n * @param T  Functor which provides the same interface as Command.\n *\n * This also implements IObjectWithSite to give the command access to the window\n * it is in.\n */\ntemplate<typename T>\nclass CExplorerCommand :\n    public comet::simple_object<\n        CExplorerCommandErrorAdapter, washer::object_with_site>\n{\npublic:\n\n    typedef T command_type;\n\n// Define pass-through constructors with variable numbers of arguments\n#define BOOST_PP_LOCAL_MACRO(N) \\\n    EXPLORER_COMMAND_VARIADIC_CONSTRUCTOR(N, CExplorerCommand, m_command)\n\n#define BOOST_PP_LOCAL_LIMITS (0, SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS)\n#include BOOST_PP_LOCAL_ITERATE()\n\nprivate:\n\n    /**\n     * Return command's unique GUID.\n     */\n    const comet::uuid_t& canonical_name() const\n    {\n        return m_command.guid();\n    }\n\n    /**\n     * Return command's title string.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring title(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.title(items);\n    }\n\n    /**\n     * Return command's tool tip.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring tool_tip(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.tool_tip(items);\n    }\n\n    /**\n     * Return command's icon descriptor.\n     *\n     * This takes the form \"shell32.dll,-249\" where 249 is the icon's\n     * resource ID.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring icon(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.icon_descriptor(items);\n    }\n\n    /**\n     * Return the command's state given array of PIDLs.\n     *\n     * @param items          Optional array of PIDLs that command would be\n     *                       executed upon.\n     * @param ok_to_be_slow  Indicates whether slow operations can be used\n     *                       when calculating the state.  If false and slow\n     *                       operations are required, throw E_PENDING.\n     */\n    EXPCMDSTATE state(\n        const comet::com_ptr<IShellItemArray>& items, bool ok_to_be_slow)\n    const\n    {\n        return detail::command_state_to_expcmdstate(\n            m_command.state(items, ok_to_be_slow));\n    }\n\n    EXPCMDFLAGS flags() const\n    {\n        return 0;\n    }\n\n    comet::com_ptr<IEnumExplorerCommand> subcommands() const\n    {\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOTIMPL));\n        return NULL;\n    }\n\n    /**\n     * Execute the code associated with this command.\n     *\n     * @param items     Optional array of PIDLs that command is executed upon.\n     * @param bind_ctx  Optional bind context.\n     */\n    void invoke(\n        const comet::com_ptr<IShellItemArray>& items,\n        const comet::com_ptr<IBindCtx>& bind_ctx) const\n    {\n        m_command(items, command_site(m_ole_site), bind_ctx);\n    }\n\n    /**\n     * Let the site we have been embedded in pass us a reference to itself.\n     *\n     * Allows the commmand to use UI and other feature of the view.\n     */\n    void on_set_site(comet::com_ptr<IUnknown> ole_site)\n    {\n        m_ole_site = ole_site;\n    }\n\n    command_type m_command;\n    comet::com_ptr<IUnknown> m_ole_site;\n};\n\n#undef EXPLORER_COMMAND_VARIADIC_CONSTRUCTOR\n#undef SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/task_pane.hpp",
    "content": "/* Windows XP web view task pane expandos.\n\n   Copyright (C) 2010, 2013, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/**\n * @file\n * @todo  This file should probably move to the Washer project although\n *        CUICommand should stay here.\n */\n\n#ifndef SWISH_NSE_TASK_PANE_EXPANDOS_HPP\n#define SWISH_NSE_TASK_PANE_EXPANDOS_HPP\n#pragma once\n\n#include \"swish/nse/command_site.hpp\"\n#include \"swish/nse/detail/command_state_conversion.hpp\"\n                                           // command_state_to_expcmdstate\n#include \"swish/nse/UICommand.hpp\" // IUIElement, IUICommand\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n#include <washer/object_with_site.hpp> // object_with_site\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n\n#include <boost/preprocessor.hpp> // creating variadic pass-through contructors\n#include <boost/static_assert.hpp> // BOOST_STATIC_ASSERT\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/type_traits/is_base_of.hpp> // is_base_of\n\n#include <string>\n\n#include <Shlwapi.h> // SHStrDupW\n\nnamespace swish {\nnamespace nse {\n\n/**\n * Base class for implementations of interfaces the derive from IUIElement.\n *\n * The likely candidates are implementations of IUIElement itself and\n * IUICommand.\n *\n * This code has been factored into this templated base class as the\n * implementations must inherit from the most derived interface only.\n * Inheriting from both IUIElement and IUICommand will lead to an ambiguous\n * conversion error.\n *\n * Wraps a C++ implementation of IUIElement with code to convert it\n * to the external COM interface.  This is an NVI style approach.\n */\ntemplate<typename Interface>\nclass CUIElementErrorAdapterBase : public Interface\n{\n    BOOST_STATIC_ASSERT((boost::is_base_of<IUIElement, Interface>::value));\n\npublic:\n\n    typedef Interface interface_is;\n\n    /** @name IUIElement external COM methods. */\n    // @{\n\n    /**\n     * Return command's title string.\n     *\n     * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n     *                           executed upon.\n     * @param[out] ppszName      Location in which to return character buffer\n     *                           allocated with CoTaskMemAlloc.\n     */\n    virtual IFACEMETHODIMP get_Name(\n        IShellItemArray* psiItemArray, wchar_t** ppszName)\n    {\n        if (ppszName)\n            *ppszName = NULL;\n        else\n            return E_POINTER;\n\n        try\n        {\n            HRESULT hr = ::SHStrDupW(title(psiItemArray).c_str(), ppszName);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(comet::com_error(hr));\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n    /**\n     * Return command's icon descriptor.\n     *\n     * This takes the form \"shell32.dll,-249\" where 249 is the icon's resource ID.\n     *\n     * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n     *                           executed upon.\n     * @param[out] ppszIcon      Location in which to return character buffer\n     *                           allocated with CoTaskMemAlloc.\n     */\n    virtual IFACEMETHODIMP get_Icon(\n        IShellItemArray* psiItemArray, wchar_t** ppszIcon)\n    {\n        if (ppszIcon)\n            *ppszIcon = NULL;\n        else\n            return E_POINTER;\n\n        try\n        {\n            HRESULT hr = ::SHStrDupW(icon(psiItemArray).c_str(), ppszIcon);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(comet::com_error(hr));\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n    /**\n     * Return command's tool tip.\n     *\n     * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n     *                           executed upon.\n     * @param[out] ppszInfotip   Location in which to return character buffer\n     *                           allocated with CoTaskMemAlloc.\n     */\n    virtual IFACEMETHODIMP get_Tooltip(\n        IShellItemArray* psiItemArray, wchar_t** ppszInfotip)\n    {\n        if (ppszInfotip)\n            *ppszInfotip = NULL;\n        else\n            return E_POINTER;\n\n        try\n        {\n            HRESULT hr =\n                ::SHStrDup(tool_tip(psiItemArray).c_str(), ppszInfotip);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(comet::com_error(hr));\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n    // @}\n\n    /** @name NVI internal interface.\n     *\n     * Implement this to create IUIElement instances.\n     */\n    // @{\n\n    virtual std::wstring title(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n\n    virtual std::wstring icon(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n\n    virtual std::wstring tool_tip(\n        const comet::com_ptr<IShellItemArray>& items) const = 0;\n\n    // @}\n};\n\n/**\n * Abstract IUIElement implementation wrapper.\n *\n * Wraps a C++ implementation of IUIElement with code to convert it\n * to the external COM interface.  This is an NVI style approach.\n */\nclass CUIElementErrorAdapter :\n    public CUIElementErrorAdapterBase<IUIElement> {};\n\n/**\n * Abstract IUICommand implementation wrapper.\n *\n * Wraps a C++ implementation of IUICommand with code to convert it\n * to the external COM interface.  This is an NVI style approach.\n */\nclass CUICommandErrorAdapter : public CUIElementErrorAdapterBase<IUICommand>\n{\npublic:\n\n    /** @name IUICommand external COM methods. */\n    // @{\n\n    /**\n     * Return command's unique GUID.\n     *\n     * @param[out] pguidCommandName   Location in which to return GUID.\n     */\n    IFACEMETHODIMP get_CanonicalName(GUID* pguidCommandName)\n    {\n        if (pguidCommandName)\n            *pguidCommandName = GUID_NULL;\n        else\n            return E_POINTER;\n\n        try\n        {\n            *pguidCommandName = canonical_name();\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n    /**\n     * Return the command's state given array of PIDLs.\n     *\n     * @param[in]  psiItemArray  Optional array of PIDLs that command would be\n     *                           executed upon.\n     * @param[in]  fOkToBeSlow   Indicated whether slow operations can be used\n     *                           when calculating the state.\n     * @param[out] pCmdState     Location in which to return the state flags.\n     */\n    IFACEMETHODIMP get_State(\n        IShellItemArray* psiItemArray, int nRequested, //BOOL fOkToBeSlow,\n        EXPCMDSTATE* pCmdState)\n    {\n        if (pCmdState)\n            *pCmdState = 0;\n        else\n            return E_POINTER;\n\n        try\n        {\n            *pCmdState = state(psiItemArray, (nRequested) ? true : false);\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n\n    /**\n     * Execute the code associated with this command instance.\n     *\n     * @param[in] psiItemArray  Optional array of PIDLs that command is\n     *                          executed upon.\n     * @param[in] pbc           Optional bind context.\n     */\n    IFACEMETHODIMP Invoke(\n        IShellItemArray* psiItemArray, IBindCtx* pbc)\n    {\n        try\n        {\n            invoke(psiItemArray, pbc);\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n\n        return S_OK;\n    }\n\n    // @}\n\nprivate:\n\n    /** @name NVI internal interface.\n     *\n     * Implement this to create IUICommand instances.\n     */\n    // @{\n\n    virtual const comet::uuid_t& canonical_name() const = 0;\n\n    virtual EXPCMDSTATE state(\n        const comet::com_ptr<IShellItemArray>& items,\n        bool ok_to_be_slow) const = 0;\n\n    virtual void invoke(\n        const comet::com_ptr<IShellItemArray>& items,\n        const comet::com_ptr<IBindCtx>& bind_ctx) const = 0;\n\n    // @}\n};\n\n#ifndef SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS\n#define SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS 10\n#endif\n\n#define COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR(N, classname, initialiser) \\\n    BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename A)>) \\\n    explicit classname(BOOST_PP_ENUM_BINARY_PARAMS(N, A, a)) \\\n        : initialiser(BOOST_PP_ENUM_PARAMS(N, a)) {}\n\n/**\n * Implements IUICommand by wrapping command functors.\n *\n * @param T  Functor which provides the same interface as Command.\n *\n * This also implements IObjectWithSite to give the command access to the window\n * it is in.\n */\ntemplate<typename T>\nclass CUICommand :\n    public comet::simple_object<\n        CUICommandErrorAdapter, washer::object_with_site>\n{\npublic:\n\n    typedef T command_type;\n\n// Define pass-through contructors with variable numbers of arguments\n#define BOOST_PP_LOCAL_MACRO(N) \\\n    COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR(N, CUICommand, m_command)\n\n#define BOOST_PP_LOCAL_LIMITS (0, SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS)\n#include BOOST_PP_LOCAL_ITERATE()\n\nprivate:\n\n    /**\n     * Return command's unique GUID.\n     */\n    const comet::uuid_t& canonical_name() const\n    {\n        return m_command.guid();\n    }\n\n    /**\n     * Return command's title string.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring title(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.title(items);\n    }\n\n    /**\n     * Return command's tool tip.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring tool_tip(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.tool_tip(items);\n    }\n\n    /**\n     * Return command's icon descriptor.\n     *\n     * This takes the form \"shell32.dll,-249\" where 249 is the icon's\n     * resource ID.\n     *\n     * @param items  Optional array of PIDLs that command would be executed\n     *               upon.\n     */\n    std::wstring icon(const comet::com_ptr<IShellItemArray>& items) const\n    {\n        return m_command.icon_descriptor(items);\n    }\n\n    /**\n     * Return the command's state given array of PIDLs.\n     *\n     * @param items          Optional array of PIDLs that command would be\n     *                       executed upon.\n     * @param ok_to_be_slow  Indicates whether slow operations can be used\n     *                       when calculating the state.  If false and slow\n     *                       operations are required, throw E_PENDING.\n     */\n    EXPCMDSTATE state(\n        const comet::com_ptr<IShellItemArray>& items, bool ok_to_be_slow)\n    const\n    {\n        return detail::command_state_to_expcmdstate(\n            m_command.state(items, ok_to_be_slow));\n    }\n\n    /**\n     * Execute the code associated with this command.\n     *\n     * @param items     Optional array of PIDLs that command is executed upon.\n     * @param bind_ctx  Optional bind context.\n     */\n    void invoke(\n        const comet::com_ptr<IShellItemArray>& items,\n        const comet::com_ptr<IBindCtx>& bind_ctx) const\n    {\n        m_command(items, command_site(m_ole_site), bind_ctx);\n    }\n\n    /**\n     * Let the site we have been embedded in pass us a reference to itself.\n     *\n     * Allows the commmand to use UI and other feature of the view.\n     */\n    virtual void on_set_site(comet::com_ptr<IUnknown> ole_site)\n    {\n        m_ole_site = ole_site;\n    }\n\n    command_type m_command;\n    comet::com_ptr<IUnknown> m_ole_site;\n};\n\n\n#undef COMMAND_ADAPTER_VARIADIC_CONSTRUCTOR\n#undef SWISH_COMMAND_CONSTRUCTOR_MAX_ARGUMENTS\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/nse/view_callback.cpp",
    "content": "/**\n    @file\n\n    Explorer shell view windows callback handler.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"view_callback.hpp\"\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/error.h> // com_error\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n\nusing comet::com_error;\n\nnamespace {\n\n    /// @name  Undocumented messages\n    // @{\n    const UINT SFVM_SELECTIONCHANGED = 8;\n    const UINT SFVM_GET_WEBVIEW_CONTENT = 83;\n    const UINT SFVM_GET_WEBVIEW_TASKS = 84;\n    // @}\n\n}\n\nnamespace swish {\nnamespace nse {\n\nCViewCallback::~CViewCallback() {}\n\nSTDMETHODIMP CViewCallback::MessageSFVCB(\n        UINT message, WPARAM wparam, LPARAM lparam)\n{\n    try\n    {\n        bool handled = false;\n        switch (message)\n        {\n        case SFVM_WINDOWCREATED:\n            handled = on_window_created(reinterpret_cast<HWND>(wparam));\n            break;\n\n        case SFVM_GETNOTIFY:\n            if (wparam == 0 || lparam == 0)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n            handled = on_get_notify(\n                *reinterpret_cast<PCIDLIST_ABSOLUTE*>(wparam),\n                *reinterpret_cast<LONG*>(lparam));\n            break;\n\n        case SFVM_FSNOTIFY:\n            handled = on_fs_notify(\n                reinterpret_cast<PCIDLIST_ABSOLUTE>(wparam), lparam);\n            break;\n\n        case SFVM_MERGEMENU:\n            if (lparam == 0)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n            handled = on_merge_menu(*reinterpret_cast<QCMINFO*>(lparam));\n            break;\n          \n        case SFVM_SELECTIONCHANGED:\n            // wparam's meaning is unknown\n            if (lparam == 0)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n            handled = on_selection_changed(\n                *reinterpret_cast<SFV_SELECTINFO*>(lparam));\n            break;\n\n        case SFVM_INITMENUPOPUP:\n            handled = on_init_menu_popup(\n                LOWORD(wparam), HIWORD(wparam),\n                reinterpret_cast<HMENU>(lparam));\n            break;\n\n        case SFVM_INVOKECOMMAND:\n            handled = on_invoke_command(wparam);\n            break;\n\n        case SFVM_GETHELPTEXT:\n            handled = on_get_help_text(\n                LOWORD(wparam), HIWORD(wparam), \n                reinterpret_cast<LPTSTR>(lparam));\n            break;\n\n        case SFVM_GET_WEBVIEW_CONTENT:\n            if (lparam == 0)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n            handled = on_get_webview_content(\n                *reinterpret_cast<SFV_WEBVIEW_CONTENT_DATA*>(lparam));\n            break;\n\n        case SFVM_GET_WEBVIEW_TASKS:\n            if (lparam == 0)\n                BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n            handled = on_get_webview_tasks(\n                *reinterpret_cast<SFV_WEBVIEW_TASKSECTION_DATA*>(lparam));\n            break;\n\n        default:\n            handled = on_unknown_sfvm(message, wparam, lparam);\n            break;\n        }\n\n        if (handled)\n        {\n            return S_OK;\n        }\n        else\n        {\n            // special treatment for FSNOTIFY because it uses S_FALSE to\n            // suppress default processing.\n            if (message == SFVM_FSNOTIFY)\n                BOOST_THROW_EXCEPTION(com_error(S_FALSE));\n            else\n                BOOST_THROW_EXCEPTION(com_error(E_NOTIMPL));\n        }\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n\n    assert(false && \"Unreachable\");\n    return E_UNEXPECTED;\n}\n\nbool CViewCallback::on_unknown_sfvm(\n    UINT /*message*/, WPARAM /*wparam*/, LPARAM /*lparam*/)\n{ return false; }\n\nbool CViewCallback::on_window_created(HWND /*hwnd_view*/)\n{ return false; }\n\nbool CViewCallback::on_get_notify(\n    PCIDLIST_ABSOLUTE& /*pidl_monitor*/, LONG& /*events*/)\n{ return false; }\n\nbool CViewCallback::on_fs_notify(\n    PCIDLIST_ABSOLUTE /*pidl*/, LONG /*event*/)\n{ return false; }\n\nbool CViewCallback::on_merge_menu(QCMINFO& /*menu_info*/)\n{ return false; }\n\nbool CViewCallback::on_selection_changed(\n    SFV_SELECTINFO& /*selection_info*/)\n{ return false; }\n\nbool CViewCallback::on_init_menu_popup(\n    UINT /*first_command_id*/, int /*menu_index*/, HMENU /*menu*/)\n{ return false; }\n\nbool CViewCallback::on_invoke_command(UINT /*command_id*/)\n{ return false; }\n\nbool CViewCallback::on_get_help_text(\n    UINT /*command_id*/, UINT /*buffer_size*/, LPTSTR /*buffer*/)\n{ return false; }\n\nbool CViewCallback::on_get_webview_content(\n    SFV_WEBVIEW_CONTENT_DATA& /*content_out*/) { return false; }\n\nbool CViewCallback::on_get_webview_tasks(\n    SFV_WEBVIEW_TASKSECTION_DATA& /*tasks_out*/) { return false; }\n\n}} // namespace swish::nse\n"
  },
  {
    "path": "swish/nse/view_callback.hpp",
    "content": "/**\n    @file\n\n    Explorer shell view windows callback handler.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_NSE_VIEW_CALLBACK_HPP\n#define SWISH_NSE_VIEW_CALLBACK_HPP\n#pragma once\n\n#include \"swish/nse/UICommand.hpp\" // IUIElement, IEnumUICommand\n\n#include <comet/interface.h> // comtype\n\n#include <ShlObj.h> // IShellFolderViewCB\n\ntemplate<> struct comet::comtype<IShellFolderViewCB>\n{\n    static const IID& uuid() throw() { return IID_IShellFolderViewCB; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace nse {\n\nclass CViewCallback : public IShellFolderViewCB\n{\npublic:\n\n    typedef IShellFolderViewCB interface_is;\n\n    virtual ~CViewCallback();\n\npublic: // IShellFolderViewCB\n\n    /**\n     * Callback method for shell view to inform us as things happen.\n     *\n     * This is the way in which the default @c IShellView object that we\n     * created using @c SHCreateShellFolderView allows us to still have a say\n     * in what goes on.  As things happen in the view, messages are sent to\n     * this callback allowing us to react to them.\n     *\n     * @param message  The @c SFVM_* message type that the view is sending us.\n     * @param wparam   One of the possible parameters (varies with message type).\n     * @param lparam   Another possible parameter (varies with message type).\n     *\n     * @returns @c S_OK if we handled the message or @c E_NOTIMPL if we did not.\n     */\n    virtual IFACEMETHODIMP MessageSFVCB(\n        UINT message, WPARAM wparam, LPARAM lparam);\n\nprotected:\n\n    /**\n     * SFVM_SELECTIONCHANGED parameter.\n     *\n     * Undocumented by Microsoft.  Based on public domain code at\n     * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n     *\n     * Copyright (C) 1998-2003 Whirling Dervishes Software.\n     */\n    struct SFV_SELECTINFO\n    {\n        UINT uOldState; // 0\n        UINT uNewState; // LVIS_SELECTED, LVIS_FOCUSED,...\n        LPITEMIDLIST pidl;\n    };\n\n    /**\n     * SFVM_GET_WEBVIEW_CONTENT parameter.\n     *\n     * Undocumented by Microsoft.  Based on public domain code at\n     * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n     *\n     * Copyright (C) 1998-2003 Whirling Dervishes Software.\n     */\n    struct SFV_WEBVIEW_CONTENT_DATA\n    {\n        long l1;\n        long l2;\n        nse::IUIElement* pExtraTasksExpando; ///< Expando with dark title\n        nse::IUIElement* pFolderTasksExpando;\n        IEnumIDList* pEnumRelatedPlaces;\n    };\n\n    /**\n     * SFVM_GET_WEBVIEW_TASKS parameter.\n     *\n     * Undocumented by Microsoft.  Based on public domain code at\n     * http://www.whirlingdervishes.com/nselib/mfc/samples/source.php.\n     *\n     * Copyright (C) 1998-2003 Whirling Dervishes Software.\n     */\n    struct SFV_WEBVIEW_TASKSECTION_DATA\n    {\n        nse::IEnumUICommand *pEnumExtraTasks;\n        nse::IEnumUICommand *pEnumFolderTasks;\n    };\n\nprivate:\n\n    /// @name  SFVM_* message handlers\n    // @{\n\n    /**\n     * A message was sent to the callback that we don't know how to crack.\n     *\n     * The message is ignored by default but can be captured by the subclasses\n     * if they override on_unknown_sfvm.\n     */\n    virtual bool on_unknown_sfvm(UINT message, WPARAM wparam, LPARAM lparam);\n\n    /**\n     * The folder window is being created.\n     *\n     * The shell is notifying us of the folder view's window handle.\n     */\n    virtual bool on_window_created(HWND hwnd_view);\n\n    /**\n     * Which events should the shell monitor for changes?\n     *\n     * We are notified via SFVM_FSNOTIFY if any events indicated here occurr.\n     *\n     * @warning  The pidl we return in @a pidl_monitor remains owned by this\n     *           object and must remain valid until this object is destroyed.\n     */\n    virtual bool on_get_notify(PCIDLIST_ABSOLUTE& pidl_monitor, LONG& events);\n\n    /**\n     * An event has occurred affecting one of our items.\n     *\n     * The event is probably the result of a SHChangeNotify of some sort.\n     * Returning false prevents the default view from refreshing to reflect the\n     * change.\n     */\n    virtual bool on_fs_notify(PCIDLIST_ABSOLUTE pidl, LONG event);\n\n    /**\n     * The view is asking us if we want to merge any items into\n     * the menu it has created before it adds it to the Explorer window.\n     */\n    virtual bool on_merge_menu(QCMINFO& menu_info);\n\n    /**\n     * The view is telling us that something has changed about its selection\n     * state.\n     */\n    virtual bool on_selection_changed(SFV_SELECTINFO& selection_info);\n\n    /**\n     * The view is about to display a popup menu.\n     *\n     * This gives us the chance to modify the menu before it is displayed.\n     *\n     * @param first_command_id  First ID reserved for client commands.\n     * @param menu_index        Menu's index.\n     * @param menu              Menu's handle.\n     */\n    virtual bool on_init_menu_popup(\n        UINT first_command_id, int menu_index, HMENU menu);\n\n    /**\n     * The view is telling us that a menu or toolbar item has been invoked \n     * in the Explorer window and is giving us a chance to react to it.\n     */\n    virtual bool on_invoke_command(UINT command_id);\n    \n    /**\n     * Specify help text for menu or toolbar items.\n     */\n    virtual bool on_get_help_text(\n        UINT command_id, UINT buffer_size, LPTSTR buffer);\n    \n    /**\n     * The shell view is requesting our expando title info.\n     * Undocumented by Microsoft.\n     *\n     * @see http://www.codeproject.com/KB/shell/foldertasks.aspx\n     * @see http://www.eggheadcafe.com/forumarchives/platformsdkshell/Feb2006/post25949644.asp\n     */\n    virtual bool on_get_webview_content(SFV_WEBVIEW_CONTENT_DATA& content_out);\n    \n    /**\n     * The shell view is requesting our expando members.\n     * Undocumented by Microsoft.\n     *\n     * @see http://www.codeproject.com/KB/shell/foldertasks.aspx\n     * @see http://www.eggheadcafe.com/forumarchives/platformsdkshell/Feb2006/post25949644.asp\n     */\n    virtual bool on_get_webview_tasks(SFV_WEBVIEW_TASKSECTION_DATA& tasks_out);\n\n    // @}\n\n};\n\n}} // namespace swish::nse\n\n#endif\n"
  },
  {
    "path": "swish/port_conversion.hpp",
    "content": "/**\n    @file\n\n    Convert between port numbers and canonical strings.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n/**\n * @file\n * Use these functions instead of lexical_cast if the port number must\n * be canonical, e.g., 65535 rather than 65,535 or 65.535.  Locales affect\n * the result of lexical_casts but canonical port numbers must not depend\n * on the locale.\n *\n * Based on a fix provided by David J.\n */\n\n#ifndef SWISH_PORT_CONVERSION_HPP\n#define SWISH_PORT_CONVERSION_HPP\n#pragma once\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <locale> // locale::classic\n#include <sstream> // basic_ostringstream\n#include <stdexcept> // logic_error\n#include <string>\n\nnamespace swish {\n\n/**\n * Locale-independent port number to port string conversion.\n */\ntemplate<typename T>\ninline T basic_port_to_string(long port)\n{\n    std::basic_ostringstream<T::value_type> stream;\n    stream.imbue(std::locale::classic()); // force locale-independence\n    stream << port;\n    if (!stream)\n        BOOST_THROW_EXCEPTION(\n            std::logic_error(\"Unable to convert port number to string\"));\n\n    return stream.str();\n}\n\n/**\n * Locale-independent port number to narrow port string conversion.\n */\ninline std::string port_to_string(long port)\n{ return basic_port_to_string<std::string>(port); }\n\n/**\n * Locale-independent port number to wide port string conversion.\n */\ninline std::wstring port_to_wstring(long port)\n{ return basic_port_to_string<std::wstring>(port); }\n\n} // namespace swish\n\n#endif\n"
  },
  {
    "path": "swish/provider/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  libssh2_sftp_filesystem_item.cpp\n  Provider.cpp\n  libssh2_sftp_filesystem_item.hpp\n  Provider.hpp\n  sftp_filesystem_item.hpp\n  sftp_provider.hpp\n  ticketed_stream.hpp)\n\nadd_library(provider ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(provider\n  PUBLIC Comet::comet connection ${Boost_LIBRARIES}\n  PRIVATE ssh Washer::washer)\n"
  },
  {
    "path": "swish/provider/Provider.cpp",
    "content": "// Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"Provider.hpp\"\n\n#include \"swish/connection/authenticated_session.hpp\"\n#include \"swish/connection/session_manager.hpp\" // session_reservation\n#include \"swish/provider/libssh2_sftp_filesystem_item.hpp\"\n#include \"swish/provider/sftp_filesystem_item.hpp\"\n#include \"swish/remotelimits.h\"\n#include \"swish/trace.hpp\" // trace\n\n#include <comet/bstr.h>     // bstr_t\n#include <comet/datetime.h> // datetime_t\n#include <comet/enum.h>     // stl_enumeration\n#include <comet/error.h>    // com_error\n#include <comet/ptr.h>      // com_ptr\n#include <comet/server.h>   // simple_object for STL holder with AddRef lifetime\n#include <comet/stream.h>   // adapt_stream_pointer\n\n#include <ssh/filesystem.hpp> // directory_iterator\n#include <ssh/stream.hpp>     // ofstream, ifstream\n\n#include <boost/filesystem/path.hpp>          // path\n#include <boost/iterator/filter_iterator.hpp> // make_filter_iterator\n#include <boost/make_shared.hpp>              // make_shared\n#include <boost/move/move.hpp>                // BOOST_RV_REF\n#include <boost/throw_exception.hpp>          // BOOST_THROW_EXCEPTION\n#include <boost/system/system_error.hpp>      // system_error, system_category\n\n#include <cassert> // assert\n#include <exception>\n#include <stdexcept> // invalid_argument\n#include <string>\n#include <vector> // to hold listing\n\nusing swish::connection::authenticated_session;\nusing swish::connection::session_reservation;\nusing swish::tracing::trace;\n\nusing comet::adapt_stream_pointer;\nusing comet::bstr_t;\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::datetime_t;\nusing comet::stl_enumeration;\n\nusing boost::make_filter_iterator;\nusing boost::make_shared;\nnamespace errc = boost::system::errc;\nusing boost::system::system_category;\nusing boost::system::system_error;\n\nusing ssh::filesystem::directory_iterator;\nusing ssh::filesystem::file_attributes;\nusing ssh::filesystem::fstream;\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::overwrite_behaviour;\nusing ssh::filesystem::path;\nusing ssh::filesystem::sftp_filesystem;\nusing ssh::filesystem::sftp_file;\n\nusing std::exception;\nusing std::invalid_argument;\nusing std::string;\nusing std::wstring;\nusing std::vector;\n\nnamespace swish\n{\nnamespace provider\n{\n\nclass provider\n{\npublic:\n    explicit provider(BOOST_RV_REF(session_reservation) session_ticket);\n\n    directory_listing listing(const path& directory);\n\n    comet::com_ptr<IStream> get_file(const path& file_path,\n                                     std::ios_base::openmode open_mode);\n\n    VARIANT_BOOL rename(com_ptr<ISftpConsumer> consumer, const path& from_path,\n                        const path& to_path);\n\n    void remove_all(const path& path);\n\n    void create_new_directory(const path& path);\n\n    const path resolve_link(const path& path);\n\n    sftp_filesystem_item stat(const path& path, bool follow_links);\n\nprivate:\n    session_reservation m_ticket;\n};\n\nCProvider::CProvider(BOOST_RV_REF(session_reservation) session_ticket)\n{\n    m_provider = make_shared<provider>(boost::ref(session_ticket));\n}\n\ndirectory_listing CProvider::listing(const path& directory)\n{\n    return m_provider->listing(directory);\n}\n\ncomet::com_ptr<IStream> CProvider::get_file(const path& file_path,\n                                            std::ios_base::openmode open_mode)\n{\n    return m_provider->get_file(file_path, open_mode);\n}\n\nVARIANT_BOOL CProvider::rename(ISftpConsumer* consumer, const path& from_path,\n                               const path& to_path)\n{\n    return m_provider->rename(consumer, from_path, to_path);\n}\n\nvoid CProvider::remove_all(const path& path)\n{\n    m_provider->remove_all(path);\n}\n\nvoid CProvider::create_new_directory(const path& path)\n{\n    m_provider->create_new_directory(path);\n}\n\npath CProvider::resolve_link(const path& path)\n{\n    return m_provider->resolve_link(path);\n}\n\nsftp_filesystem_item CProvider::stat(const path& path, bool follow_links)\n{\n    return m_provider->stat(path, follow_links);\n}\n\n/**\n * Create libssh2-based data provider.\n */\nprovider::provider(BOOST_RV_REF(session_reservation) ticket) : m_ticket(ticket)\n{\n}\n\nnamespace\n{\n\nbool not_special_file(const sftp_file& file)\n{\n    return file.path().filename() != \".\" && file.path().filename() != \"..\";\n}\n}\n\n/**\n* Retrieves a file listing, @c ls, of a given directory.\n*\n* @param directory  Absolute path of the directory to list.\n*/\ndirectory_listing provider::listing(const path& directory)\n{\n    if (directory.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    sftp_filesystem& channel = m_ticket.session().get_sftp_filesystem();\n\n    vector<sftp_filesystem_item> files;\n    transform(\n        make_filter_iterator(not_special_file,\n                             channel.directory_iterator(directory)),\n        make_filter_iterator(not_special_file, channel.directory_iterator()),\n        back_inserter(files),\n        libssh2_sftp_filesystem_item::create_from_libssh2_file);\n\n    return files;\n}\n\ncom_ptr<IStream> provider::get_file(const path& file_path,\n                                    std::ios_base::openmode mode)\n{\n    if (file_path.empty())\n        BOOST_THROW_EXCEPTION(invalid_argument(\"File cannot be empty\"));\n\n    sftp_filesystem& channel = m_ticket.session().get_sftp_filesystem();\n\n    if (mode & std::ios_base::out && mode & std::ios_base::in)\n    {\n        return adapt_stream_pointer(\n            make_shared<fstream>(boost::ref(channel), file_path, mode),\n            file_path.filename().wstring());\n    }\n    else if (mode & std::ios_base::out)\n    {\n        return adapt_stream_pointer(\n            make_shared<ofstream>(boost::ref(channel), file_path, mode),\n            file_path.filename().wstring());\n    }\n    else if (mode & std::ios_base::in)\n    {\n        return adapt_stream_pointer(\n            make_shared<ifstream>(boost::ref(channel), file_path, mode),\n            file_path.filename().wstring());\n    }\n    else\n    {\n        BOOST_THROW_EXCEPTION(\n            std::invalid_argument(\"Stream must be input, output or both\"));\n    }\n}\n\nnamespace\n{\n\n/**\n * Rename file or directory and overwrite any obstruction non-atomically.\n *\n * This involves renaming the obstruction at the target to a temporary file,\n * renaming the source file to the target and then deleting the renamed\n * obstruction.  As this is not an atomic operation it is possible to fail\n * between any of these stages and is not a prefect solution.  It may, for\n * instance, leave the temporary file behind.\n *\n * @param from\n *     Absolute path of the file or directory to be renamed.\n * @param to\n *     Absolute path to rename `from` to.\n *\n * @throws  ssh_error if the operation fails.\n */\nvoid rename_non_atomic_overwrite(authenticated_session& session,\n                                 const string& from, const string& to)\n{\n    string temporary = to + \".swish_rename_temp\";\n\n    rename(session.get_sftp_filesystem(), to, temporary,\n           overwrite_behaviour::prevent_overwrite);\n\n    try\n    {\n        rename(session.get_sftp_filesystem(), from, to,\n               overwrite_behaviour::prevent_overwrite);\n    }\n    catch (const exception&)\n    {\n        // Rename failed, rename our temporary back to its old name\n        try\n        {\n            rename(session.get_sftp_filesystem(), from, to,\n                   overwrite_behaviour::prevent_overwrite);\n        }\n        catch (const exception&)\n        { /* Suppress to avoid nested exception */\n        }\n\n        throw;\n    }\n\n    // We ignore any failure to clean up the temporary backup as the rename\n    // has succeeded, whether or not cleanup fails.\n    //\n    // XXX: We could inform the user of this here.  Might make UI\n    // separation messy though.\n    try\n    {\n        remove_all(session.get_sftp_filesystem(), temporary);\n    }\n    catch (const exception&)\n    {\n    }\n}\n\n/**\n * Retry renaming after seeking permission to overwrite the obstruction at\n * the target.\n *\n * If this fails the file or directory really can't be renamed and the error\n * message from libssh2 is returned in @a error_out.\n *\n * @param pConsumer\n *     Callback for user confirmation.\n *\n * @param previous_error\n *     Error code of the previous rename attempt in order to determine if an\n *     overwrite has any chance of being successful.\n *\n * @param from\n *     Absolute path of the file or directory to be renamed.\n *\n * @param to\n *     Absolute path to rename @a from to.\n *\n * @returns `true` if the the rename operation succeeds as a result of\n *           retrying it,\n *          `false` if the the rename operation needed user permission for\n *           something and the user chose to abort the renaming.\n *\n * @throws `previous_error` if the situation is not caused by an obstruction\n *         at the target.  Retrying renaming is not going to help here.\n *\n * @bug  The strings aren't converted from UTF-8 to UTF-16 before displaying\n *       to the user.  Any unicode filenames will produce gibberish in the\n *       confirmation dialogues.\n */\nbool rename_retry_with_overwrite(authenticated_session& session,\n                                 ISftpConsumer* pConsumer,\n                                 const system_error& previous_error,\n                                 const string& from, const string& to)\n{\n    assert(previous_error.code() && \"Previous attempt succeeded; why retry?\");\n\n    if (previous_error.code() == errc::file_exists)\n    {\n        HRESULT hr =\n            pConsumer->OnConfirmOverwrite(bstr_t(from).in(), bstr_t(to).in());\n        if (FAILED(hr))\n            return false;\n\n        // Attempt rename again this time allowing it to atomically overwrite\n        // any obstruction.\n        // This will only work on a server supporting SFTP version 5 or above.\n\n        try\n        {\n            rename(session.get_sftp_filesystem(), from, to,\n                   overwrite_behaviour::atomic_overwrite);\n            return true;\n        }\n        catch (const system_error& e)\n        {\n            if (e.code() == errc::operation_not_supported)\n            {\n                rename_non_atomic_overwrite(session, from, to);\n                return true;\n            }\n            else\n            {\n                throw;\n            }\n        }\n    }\n    else\n    {\n        // The failure is an unspecified one. This isn't the end of the world.\n        // SFTP servers < v5 (i.e. most of them) return this error code if the\n        // file already exists as they don't explicitly support overwriting.\n        // We need to stat() the file to find out if this is the case and if\n        // the user confirms the overwrite we will have to explicitly delete\n        // the target file first (via a temporary) and then repeat the rename.\n        //\n        // NOTE: this is not a perfect solution due to the possibility\n        // for race conditions.\n\n        // We used to test for FX_FAILURE here, because that's what OpenSSH\n        // returns, but changed it because the v3 standard (v5 handled above)\n        // doesn't promise any particular error code so we might as well\n        // treat them all this way.\n\n        if (exists(session.get_sftp_filesystem(), to))\n        {\n            HRESULT hr = pConsumer->OnConfirmOverwrite(bstr_t(from).in(),\n                                                       bstr_t(to).in());\n            if (FAILED(hr))\n                return false;\n\n            rename_non_atomic_overwrite(session, from, to);\n            return true;\n        }\n        else\n        {\n            // Rethrow the last exception because it wasn't caused by an\n            // obstruction.\n            //\n            // RACE CONDITION: It might have been caused by an obstruction\n            // which was then cleared by the time we did the existence check\n            // above.  The result it just that we would fail when we could\n            // have succeeded.  Such an edge case that it doesn't matter.\n            throw previous_error;\n        }\n    }\n}\n}\n\n/**\n * Renames a file or directory.\n *\n * The source and target file or directory must be specified using absolute\n * paths for the remote filesystem.  The results of passing relative paths are\n * not guaranteed (though, libssh2 seems to default to operating in the home\n * directory) and may be dangerous.\n *\n * If a file or folder already exists at the target path, @a to_path,\n * we inform the front-end consumer through a call to OnConfirmOverwrite.\n * If confirmation is given, we attempt to overwrite the\n * obstruction with the source path, @a from_path, and if successful we\n * return @c VARIANT_TRUE.  This can be used by the caller to decide whether\n * or not to update a directory view.\n *\n * @remarks\n * Due to the limitations of SFTP versions 4 and below, most servers will not\n * allow atomic overwrite.  We attempt to do this non-atomically by:\n * -# appending @c \".swish_renaming_temp\" to the obstructing target's filename\n * -# renaming the source file to the old target name\n * -# deleting the renamed target\n * If step 2 fails, we try to rename the temporary file back to its old name.\n * It is possible that this last step may fail, in which case the temporary file\n * would remain in place.  It could be recovered by manually renaming it back.\n *\n * @warning\n * If either of the paths are not absolute, this function may cause files\n * in whichever directory libssh2 considers 'current' to be renamed or deleted\n * if they happen to have matching filenames.\n *\n * @param consumer   UI callback.\n * @param from_path  Absolute path of the file or directory to be renamed.\n * @param to_path    Absolute path that @a from_path should be renamed to.\n *\n * @returns  Whether or not we needed to overwrite an existing file or\n *           directory at the target path.\n */\nVARIANT_BOOL provider::rename(com_ptr<ISftpConsumer> consumer, const path& from,\n                              const path& to)\n{\n    if (from.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n    if (to.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    // NOP if filenames are equal\n    if (from == to)\n        return VARIANT_FALSE;\n\n    // Attempt to rename old path to new path\n    try\n    {\n        ssh::filesystem::rename(m_ticket.session().get_sftp_filesystem(), from,\n                                to, overwrite_behaviour::prevent_overwrite);\n\n        // Rename was successful without overwrite\n        return VARIANT_FALSE;\n    }\n    catch (const system_error& e)\n    {\n        if (rename_retry_with_overwrite(m_ticket.session(), consumer.get(), e,\n                                        from, to))\n        {\n            return VARIANT_TRUE;\n        }\n        else\n        {\n            BOOST_THROW_EXCEPTION(com_error(E_ABORT));\n        }\n    }\n}\n\nvoid provider::remove_all(const path& target)\n{\n    if (target.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    ssh::filesystem::remove_all(m_ticket.session().get_sftp_filesystem(),\n                                target);\n}\n\nvoid provider::create_new_directory(const path& path)\n{\n    if (path.empty())\n        BOOST_THROW_EXCEPTION(com_error(\n            \"Cannot create a directory without a name\", E_INVALIDARG));\n\n    create_directory(m_ticket.session().get_sftp_filesystem(), path);\n}\n\nconst path provider::resolve_link(const path& path)\n{\n    sftp_filesystem& channel = m_ticket.session().get_sftp_filesystem();\n    bstr_t target = channel.canonical_path(path).wstring();\n\n    return target.detach();\n}\n\n/**\n * Get the details of a file by path.\n *\n * The item returned by this function doesn't include a long entry or\n * owner and group names as string (these being derived from the long entry).\n */\nsftp_filesystem_item provider::stat(const path& path, bool follow_links)\n{\n    sftp_filesystem& channel = m_ticket.session().get_sftp_filesystem();\n\n    file_attributes stat_result =\n        channel.attributes(path, follow_links != FALSE);\n\n    return libssh2_sftp_filesystem_item::create_from_libssh2_attributes(\n        path, stat_result);\n}\n}\n} // namespace swish::provider\n"
  },
  {
    "path": "swish/provider/Provider.hpp",
    "content": "/**\n    @file\n\n    libssh2-based SFTP provider component.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#ifndef SWISH_PROVIDER_PROVIDER_HPP\n#define SWISH_PROVIDER_PROVIDER_HPP\n#pragma once\n\n#include \"swish/connection/session_manager.hpp\" // session_reservation\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <boost/move/move.hpp> // BOOST_RV_REF\n#include <boost/shared_ptr.hpp> // shared_ptr\n\nnamespace swish {\nnamespace provider {\n\nclass provider;\n\nclass CProvider : public sftp_provider\n{\npublic:\n\n    explicit CProvider(\n        BOOST_RV_REF(swish::connection::session_reservation) session_ticket);\n\n    virtual directory_listing listing(const ssh::filesystem::path& directory);\n\n    virtual comet::com_ptr<IStream> get_file(\n        const ssh::filesystem::path& file_path, std::ios_base::openmode open_mode);\n\n    virtual VARIANT_BOOL rename(\n        ISftpConsumer* consumer, const ssh::filesystem::path& from_path,\n        const ssh::filesystem::path& to_path);\n\n    virtual void remove_all(const ssh::filesystem::path& path);\n\n    virtual void create_new_directory(const ssh::filesystem::path& path);\n\n    virtual ssh::filesystem::path resolve_link(\n        const ssh::filesystem::path& link_path);\n\n    virtual sftp_filesystem_item stat(\n        const ssh::filesystem::path& path, bool follow_links);\n\nprivate:\n    boost::shared_ptr<provider> m_provider;\n};\n\n}} // namespace swish::provider\n\n#endif\n"
  },
  {
    "path": "swish/provider/libssh2_sftp_filesystem_item.cpp",
    "content": "/**\n    @file\n\n    SFTP filesystem item using libssh2 backend.\n\n    @if license\n\n    Copyright (C) 2012, 2015  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#include \"libssh2_sftp_filesystem_item.hpp\"\n\n#include \"swish/utils.hpp\" // Utf8StringToWideString\n\n#include <ssh/filesystem.hpp> // file_attributes, sftp_file\n\n#include <boost/regex.hpp> // Regular expressions\n#include <boost/shared_ptr.hpp>\n\nusing swish::utils::Utf8StringToWideString;\n\nusing comet::datetime_t;\n\nusing ssh::filesystem::file_attributes;\nusing ssh::filesystem::path;\nusing ssh::filesystem::sftp_file;\n\nusing boost::optional;\nusing boost::shared_ptr;\nusing boost::uint64_t;\n\nusing std::string;\nusing std::wstring;\n\nnamespace {\n\n    const boost::regex regex(\"\\\\S{10,}\\\\s+\\\\d+\\\\s+(\\\\S+)\\\\s+(\\\\S+)\\\\s+.+\");\n    const unsigned int USER_MATCH = 1;\n    const unsigned int GROUP_MATCH = 2;\n\n    /**\n     * Get the username part of an SFTP 'ls -l'-style long entry.\n     *\n     * According to the specification\n     * (http://www.openssh.org/txt/draft-ietf-secsh-filexfer-02.txt):\n     *\n     * The recommended format for the longname field is as follows:\n     *\n     *     -rwxr-xr-x   1 mjos     staff      348911 Mar 25 14:29 t-filexfer\n     *     1234567890 123 12345678 12345678 12345678 123456789012\n     *\n     * where the second line shows the *minimum* number of characters.\n     *\n     * @warning\n     * The spec specifically forbids parsing this long entry by it is the\n     * only way to get the user @b name rather than the user @b ID.\n     */\n    optional<wstring> parse_user_from_long_entry(const string& long_entry)\n    {\n        boost::smatch match;\n        if (regex_match(long_entry, match, regex) && match[USER_MATCH].matched)\n        {\n            return Utf8StringToWideString(match[USER_MATCH].str());\n        }\n        else\n        {\n            return optional<wstring>();\n        }\n    }\n\n    /**\n     * Get the group name part of an SFTP 'ls -l'-style long entry.\n     *\n     * @see parse_user_from_long_entry() for more information.\n     */\n    optional<wstring> parse_group_from_long_entry(const string& long_entry)\n    {\n        boost::smatch match;\n        if (regex_match(long_entry, match, regex) && match[GROUP_MATCH].matched)\n        {\n            return Utf8StringToWideString(match[GROUP_MATCH].str());\n        }\n        else\n        {\n            return optional<wstring>();\n        }\n    }\n\n}\n\nnamespace swish {\nnamespace provider {\n\nsftp_filesystem_item\nlibssh2_sftp_filesystem_item::create_from_libssh2_attributes(\n    const path& file_name, const file_attributes& attributes)\n{\n    return sftp_filesystem_item(\n        shared_ptr<sftp_filesystem_item_interface>(\n            new libssh2_sftp_filesystem_item(file_name, attributes)));\n}\n\nsftp_filesystem_item\nlibssh2_sftp_filesystem_item::create_from_libssh2_file(const sftp_file& file)\n{\n    return sftp_filesystem_item(\n        shared_ptr<sftp_filesystem_item_interface>(\n            new libssh2_sftp_filesystem_item(file)));\n}\n\n\nvoid libssh2_sftp_filesystem_item::common_init(\n    const path& file_name, const file_attributes& attributes)\n{\n    m_path = file_name;\n\n    switch (attributes.type())\n    {\n    case file_attributes::normal_file:\n        m_type = type::file;\n        break;\n\n    case file_attributes::directory:\n        m_type = type::directory;\n        break;\n\n    case file_attributes::symbolic_link:\n        m_type = type::link;\n        break;\n\n    default:\n        m_type = type::unknown;\n    }\n\n    if (attributes.permissions())\n    {\n        m_permissions = *attributes.permissions();\n    }\n\n    if (attributes.size())\n    {\n        m_size = *attributes.size();\n    }\n\n    if (attributes.uid())\n    {\n        m_uid = *attributes.uid();\n    }\n\n    if (attributes.gid())\n    {\n        m_gid = *attributes.gid();\n    }\n\n    if (attributes.last_accessed())\n    {\n        m_accessed.from_unixtime(\n            static_cast<time_t>(*attributes.last_accessed()),\n            datetime_t::utc_convert_mode::none);\n    }\n\n    if (attributes.last_modified())\n    {\n        m_modified.from_unixtime(\n            static_cast<time_t>(*attributes.last_modified()),\n            datetime_t::utc_convert_mode::none);\n    }\n}\n\nlibssh2_sftp_filesystem_item::libssh2_sftp_filesystem_item(\n    const sftp_file& file)\n    :\nm_type(type::unknown), m_permissions(0U), m_uid(0U), m_gid(0U), m_size(0U)\n{\n    file_attributes attributes = file.attributes();\n\n    common_init(file.path().filename(), attributes);\n\n    // Naughtily, we parse the long (ls -l) form of the file's attributes\n    // for the username and group.  The standard says we shouldn't but\n    // there's no other way to get them as text.  Although it contains a copy\n    // the filename, which may not be in UTF-8 encoding, we treat this\n    // long form as a UTF-8 string as the other info /should/ be UTF-8 and we\n    // don't use the filename.\n\n    // To be on the safe side assume that the long entry doesn't hold\n    // valid owner and group info if the UID and GID aren't valid\n\n    string utf8_long_entry = file.long_entry();\n\n    if (attributes.uid())\n    {\n        m_owner = parse_user_from_long_entry(utf8_long_entry);\n    }\n\n    if (attributes.gid())\n    {\n        m_group = parse_group_from_long_entry(utf8_long_entry);\n    }\n}\n\nlibssh2_sftp_filesystem_item::libssh2_sftp_filesystem_item(\n    const path& file_name, const file_attributes& attributes)\n    :\nm_type(type::unknown), m_permissions(0U), m_uid(0U), m_gid(0U), m_size(0U)\n{\n    common_init(file_name, attributes);\n}\n\nBOOST_SCOPED_ENUM(sftp_filesystem_item_interface::type)\nlibssh2_sftp_filesystem_item::type() const\n{\n    return m_type;\n}\n\nssh::filesystem::path libssh2_sftp_filesystem_item::filename() const\n{\n    return m_path.filename();\n}\n\nunsigned long libssh2_sftp_filesystem_item::permissions() const\n{\n    return m_permissions;\n}\n\noptional<wstring> libssh2_sftp_filesystem_item::owner() const\n{\n    return m_owner;\n}\n\nunsigned long libssh2_sftp_filesystem_item::uid() const\n{\n    return m_uid;\n}\n\noptional<wstring> libssh2_sftp_filesystem_item::group() const\n{\n    return m_group;\n}\n\nunsigned long libssh2_sftp_filesystem_item::gid() const\n{\n    return m_gid;\n}\n\nuint64_t libssh2_sftp_filesystem_item::size_in_bytes() const\n{\n    return m_size;\n}\n\ndatetime_t libssh2_sftp_filesystem_item::last_accessed() const\n{\n    return m_accessed;\n}\n\ndatetime_t libssh2_sftp_filesystem_item::last_modified() const\n{\n    return m_modified;\n}\n\n/*\nbool libssh2_sftp_filesystem_item::operator<(const libssh2_sftp_filesystem_item& other) const\n{\n    if (bstrFilename == 0)\n        return other.bstrFilename != 0;\n\n    if (other.bstrFilename == 0)\n        return false;\n\n    return ::VarBstrCmp(\n        bstrFilename, other.bstrFilename,\n        ::GetThreadLocale(), 0) == VARCMP_LT;\n}\n\nbool libssh2_sftp_filesystem_item::operator==(const libssh2_sftp_filesystem_item& other) const\n{\n    if (bstrFilename == 0 && other.bstrFilename == 0)\n        return true;\n\n    return ::VarBstrCmp(\n        bstrFilename, other.bstrFilename,\n        ::GetThreadLocale(), 0) == VARCMP_EQ;\n}\n\nbool libssh2_sftp_filesystem_item::operator==(const comet::bstr_t& name) const\n{\n    return bstrFilename == name;\n}\n*/\n\n}}\n"
  },
  {
    "path": "swish/provider/libssh2_sftp_filesystem_item.hpp",
    "content": "/**\n    @file\n\n    SFTP filesystem item using libssh2 backend.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#ifndef SWISH_PROVIDER_LIBSSH2_SFTP_FILESYSTEM_ITEM_HPP\n#define SWISH_PROVIDER_LIBSSH2_SFTP_FILESYSTEM_ITEM_HPP\n\n#include \"swish/provider/sftp_filesystem_item.hpp\"\n\n#include <boost/cstdint.hpp> // uint64_t\n#include <boost/optional.hpp>\n\n#include <comet/datetime.h> // datetime_t\n\n#include <string>\n\nnamespace ssh {\nnamespace filesystem {\n\nclass file_attributes;\nclass sftp_file;\n\n}\n}\n\nnamespace swish {\nnamespace provider {\n\n/**\n * An entry in an SFTP directory retrieved by the libssh2 backend.\n */\nclass libssh2_sftp_filesystem_item : public sftp_filesystem_item_interface\n{\npublic:\n\n    /**\n     * Create filesystem entry from libssh2 filesystem item representation.\n     */\n    static sftp_filesystem_item create_from_libssh2_file(\n        const ssh::filesystem::sftp_file& file);\n\n    /**\n     * Create filesystem entry from libssh2 filesystem item representation using\n     * only the attributes and filename.\n     *\n     * This constructor is for use with a stat-style situation where the full\n     * file info isn't available.\n     *\n     * Items created with this constructor will *not* be able to return the\n     * user name or group name as a string.\n     *\n     * @param file_name\n     *        Filename.\n     *\n     * @param attributes\n     *        Object containing the file's details.\n     */\n    static sftp_filesystem_item create_from_libssh2_attributes(\n        const ssh::filesystem::path& file_name,\n        const ssh::filesystem::file_attributes& attributes);\n\n    virtual BOOST_SCOPED_ENUM(type) type() const;\n    virtual ssh::filesystem::path filename() const;\n    virtual unsigned long permissions() const;\n    virtual boost::optional<std::wstring> owner() const;\n    virtual unsigned long uid() const;\n    virtual boost::optional<std::wstring> group() const;\n    virtual unsigned long gid() const;\n    virtual boost::uint64_t size_in_bytes() const;\n    virtual comet::datetime_t last_accessed() const;\n    virtual comet::datetime_t last_modified() const;\n\nprivate:\n\n    libssh2_sftp_filesystem_item(\n        const ssh::filesystem::sftp_file& file);\n\n    libssh2_sftp_filesystem_item(\n        const ssh::filesystem::path& file_name,\n        const ssh::filesystem::file_attributes& attributes);\n\n    void common_init(\n        const ssh::filesystem::path& file_name,\n        const ssh::filesystem::file_attributes& attributes);\n\n    BOOST_SCOPED_ENUM(type) m_type;\n    ssh::filesystem::path m_path;\n    unsigned long m_permissions;\n    boost::optional<std::wstring> m_owner;\n    boost::optional<std::wstring> m_group;\n    unsigned long m_uid;\n    unsigned long m_gid;\n    boost::uint64_t m_size;\n    comet::datetime_t m_modified;\n    comet::datetime_t m_accessed;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/provider/sftp_filesystem_item.hpp",
    "content": "/**\n    @file\n\n    SFTP backend filesystem item interface.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#ifndef SWISH_PROVIDER_SFTP_FILESYSTEM_ITEM_HPP\n#define SWISH_PROVIDER_SFTP_FILESYSTEM_ITEM_HPP\n\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/cstdint.hpp> // uint64_t\n#include <boost/detail/scoped_enum_emulation.hpp> // BOOST_SCOPED_ENUM\n#include <boost/optional.hpp>\n#include <boost/shared_ptr.hpp>\n\n#include <comet/datetime.h> // datetime_t\n\n#include <string>\n\n\nnamespace swish {\nnamespace provider {\n\n/**\n * Interface to Swish's representation of an SFTP file's properties.\n *\n * All attributes are technically optional according to the SFTP standard\n * (i.e. the server could set the flags to say the returned value isn't valid),\n * but, to simplify things inside Swish, we only make this optionality\n * explicit for `owner` and `group` as they are the only ones with a realistic\n * prospect of not being supported.  The others have sensible defaults.\n */\nclass sftp_filesystem_item_interface\n{\npublic:\n\n    BOOST_SCOPED_ENUM_START(type)\n    {\n        /// File that can be opened and whose contents can be accessed\n        /// (permissions permitting).\n        file,\n\n        /// This filesystem item can be listed for items under it.\n        directory,\n\n        /// This file is a link to another item\n        link,\n\n        /// An item of a type we don't recognise or the server didn't send any\n        /// information about the type\n        unknown\n    };\n    BOOST_SCOPED_ENUM_END();\n\n    /// Type of item represented by this object.\n    virtual BOOST_SCOPED_ENUM(type) type() const = 0;\n\n    /// Filename relative to directory (e.g. `README.txt`).\n    virtual ssh::filesystem::path filename() const = 0;\n\n    /// Unix file permissions.\n    virtual unsigned long permissions() const = 0;\n\n    /// The user name of the file's owner.\n    /// This may not exist if the server doesn't report named users.  This may\n    /// also be incorrect if the server responds in an unusual way so should\n    /// only be used for information.\n    virtual boost::optional<std::wstring> owner() const = 0;\n\n    /// Numeric ID of file's owner.\n    virtual unsigned long uid() const = 0;\n\n    /// The name of the user group to which the file belongs\n    /// This may not exist if the server doesn't report named groups.  This may\n    /// also be incorrect if the server responds in an unusual way so should\n    /// only be used for information.\n    virtual boost::optional<std::wstring> group() const = 0;\n\n    /// Numeric ID of group to which the file belongs.\n    virtual unsigned long gid() const = 0;\n\n    /// The file's size in bytes.\n    virtual boost::uint64_t size_in_bytes() const = 0;\n\n    /// The date and time at which the file was last accessed.\n    virtual comet::datetime_t last_accessed() const = 0;\n\n    /// The date and time at which the file was last modified.\n    virtual comet::datetime_t last_modified() const = 0;\n};\n\n/**\n * Type erasure interface to SFTP representation implementations.\n */\nclass sftp_filesystem_item : public sftp_filesystem_item_interface\n{\npublic:\n\n    BOOST_SCOPED_ENUM(type) type() const\n    { return m_inner->type(); }\n\n    ssh::filesystem::path filename() const\n    { return m_inner ->filename(); }\n\n    unsigned long permissions() const\n    { return m_inner->permissions(); }\n\n    boost::optional<std::wstring> owner() const\n    { return m_inner->owner(); }\n\n    unsigned long uid() const\n    { return m_inner->uid(); }\n\n    boost::optional<std::wstring> group() const\n    { return m_inner->group(); }\n\n    unsigned long gid() const\n    { return m_inner->gid(); }\n\n    boost::uint64_t size_in_bytes() const\n    { return m_inner->size_in_bytes(); }\n\n    comet::datetime_t last_accessed() const\n    { return m_inner->last_accessed(); }\n\n    comet::datetime_t last_modified() const\n    { return m_inner->last_modified(); }\n\n    explicit sftp_filesystem_item(\n        boost::shared_ptr<sftp_filesystem_item_interface> inner)\n        : m_inner(inner) {}\n\nprivate:\n    boost::shared_ptr<sftp_filesystem_item_interface> m_inner;\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/provider/sftp_provider.hpp",
    "content": "/**\n    @file\n\n    SFTP backend interfaces.\n\n    @if license\n\n    Copyright (C) 2010, 2011, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_PROVIDER_SFTP_PROVIDER_H\n#define SWISH_PROVIDER_SFTP_PROVIDER_H\n#pragma once\n\n#include \"swish/provider/sftp_filesystem_item.hpp\"\n\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/optional/optional.hpp>\n//#include <boost/range/any_range.hpp> USE ONCE WE UPGRADE BOOST\n\n#include <comet/interface.h> // comtype\n#include <comet/ptr.h> // com_ptr\n\n#include <string> // wstring\n#include <utility> // pair\n#include <vector>\n\nclass ISftpConsumer : public IUnknown\n{\npublic:\n\n    /**\n     * Get password from the user.\n     *\n     * @return\n     *     Uninitialised optional if authentication should be aborted.\n     *     String containing password, otherwise.\n     */\n    virtual boost::optional<std::wstring> prompt_for_password() = 0;\n\n    /**\n     * Get files containing private and public keys for public-key\n     * authentication.\n     *\n     * @return\n     *     Uninitialised optional if public-key authentication should not be\n     *     performed using file-based keys.\n     *     Pair of paths: private-key file first, public-key file second.\n     */\n    virtual boost::optional<\n        std::pair<boost::filesystem::path, boost::filesystem::path>>\n        key_files() = 0;\n\n    /**\n     * Perform a challenge-response interaction with the user.\n     *\n     * @return\n     *     Uninitialised optional if authentication should be aborted.\n     *     As many responses as there were prompts, otherwise.\n     */\n    virtual boost::optional<std::vector<std::string>> challenge_response(\n        const std::string& title, const std::string& instructions,\n        const std::vector<std::pair<std::string, bool>>& prompts) = 0;\n\n    virtual HRESULT OnConfirmOverwrite(\n        BSTR bstrOldFile,\n        BSTR bstrNewFile\n    ) = 0;\n    virtual HRESULT OnHostkeyMismatch(\n        BSTR bstrHostName,\n        BSTR bstrHostKey,\n        BSTR bstrHostKeyType\n    ) = 0;\n    virtual HRESULT OnHostkeyUnknown(\n        BSTR bstrHostName,\n        BSTR bstrHostKey,\n        BSTR bstrHostKeyType\n    ) = 0;\n};\n\nnamespace swish {\nnamespace provider {\n\n//typedef boost::any_range<\n//    sftp_filesystem_item, boost::forward_traversal_tag,\n//    sftp_filesystem_item&, std::ptrdiff_t>\n//    directory_listing;\ntypedef std::vector<sftp_filesystem_item> directory_listing;\n\nclass sftp_provider\n{\npublic:\n    virtual ~sftp_provider() {}\n\n    virtual directory_listing listing(\n        const ssh::filesystem::path& directory) = 0;\n\n    virtual comet::com_ptr<IStream> get_file(\n        const ssh::filesystem::path& file_path, std::ios_base::openmode mode) = 0;\n\n    virtual VARIANT_BOOL rename(\n        ISftpConsumer* consumer, const ssh::filesystem::path& from_path,\n        const ssh::filesystem::path& to_path) = 0;\n\n    virtual void remove_all(const ssh::filesystem::path& path) = 0;\n\n    virtual void create_new_directory(const ssh::filesystem::path& path) = 0;\n\n    /**\n     * Return the canonical path of the given non-canonical path.\n     *\n     * While generally used to resolve symlinks, it can also be used to\n     * convert paths relative to the startup directory into absolute paths.\n     */\n    virtual ssh::filesystem::path resolve_link(\n        const ssh::filesystem::path& link_path) = 0;\n\n    virtual sftp_filesystem_item stat(\n        const ssh::filesystem::path& path, bool follow_links) = 0;\n};\n\n}}\n\nnamespace comet {\n\ntemplate<> struct comtype<ISftpConsumer>\n{\n    static const IID& uuid() throw()\n    {\n        static comet::uuid_t iid(\"304982B4-4FB1-4C2E-A892-3536DF59ACF5\");\n        return iid;\n    }\n    typedef IUnknown base;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "swish/provider/ticketed_stream.hpp",
    "content": "/**\n    @file\n\n    Session-holding IStream.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#ifndef SWISH_PROVIDER_TICKETED_STREAM_HPP\n#define SWISH_PROVIDER_TICKETED_STREAM_HPP\n\n#include <swish/connection/session_manager.hpp> // session_reservation\n\n#include <comet/server.h> // simple_object\n\n#include <boost/move/move.hpp> // BOOST_RV_REF\n\nnamespace swish {\nnamespace provider {\n\n/**\n * IStream holding a session reservation ticket.\n *\n * The ticket ensures the session remains active for at least as long as the\n * IStream.\n */\nclass ticketed_stream : public comet::simple_object<IStream>\n{\npublic:\n\n    ticketed_stream(\n        com_ptr<IStream> stream,\n        BOOST_RV_REF(swish::connection::session_reservation) ticket)\n        : m_ticket(ticket), m_inner(stream) {}\n\n    virtual HRESULT STDMETHODCALLTYPE Read( \n        void* buffer, ULONG buffer_size, ULONG* read_count_out)\n    {\n        return m_inner->Read(buffer, buffer_size, read_count_out);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Write(\n        const void* data, ULONG data_size, ULONG* written_count_out)\n    {\n        return m_inner->Write(data, data_size, written_count_out);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Seek(\n        LARGE_INTEGER offset, DWORD origin, ULARGE_INTEGER* new_position_out)\n    {\n        return m_inner->Seek(offset, origin, new_position_out);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER new_size)\n    {\n        return m_inner->SetSize(new_size);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE CopyTo( \n        IStream* destination, ULARGE_INTEGER amount,\n        ULARGE_INTEGER* bytes_read_out, ULARGE_INTEGER* bytes_written_out)\n    {\n        return m_inner->CopyTo(\n            destination, amount, bytes_read_out, bytes_written_out);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD commit_flags)\n    {\n        return m_inner->Commit(commit_flags);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Revert()\n    {\n        return m_inner->Revert();\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE LockRegion(\n        ULARGE_INTEGER offset, ULARGE_INTEGER extent,\n        DWORD lock_type)\n    {\n        return m_inner->LockRegion(offset, extent, lock_type);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(\n        ULARGE_INTEGER offset, ULARGE_INTEGER extent,\n        DWORD lock_type)\n    {\n        return m_inner->UnlockRegion(offset, extent, lock_type);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Stat( \n        STATSTG* attributes_out, DWORD stat_flag)\n    {\n        return m_inner->Stat(attributes_out, stat_flag);\n    }\n\n    virtual HRESULT STDMETHODCALLTYPE Clone(IStream** stream_out)\n    {\n        return m_inner->Clone(stream_out);\n    }\n\nprivate:\n\n    swish::connection::session_reservation m_ticket;\n\n    comet::com_ptr<IStream> m_stream;\n};\n\n}} // namespace swish::provider\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(COMMAND_SOURCES\n  commands/commands.cpp\n  commands/commands.hpp\n  commands/delete.cpp\n  commands/delete.hpp\n  commands/NewFolder.cpp\n  commands/NewFolder.hpp)\n\nset(SOURCES\n  columns.cpp\n  context_menu_callback.cpp\n  filemode.c\n  Mode.cpp\n  pidl_connection.cpp\n  properties.cpp\n  ViewCallback.cpp\n  columns.hpp\n  context_menu_callback.hpp\n  filemode.h\n  Mode.h\n  pidl_connection.hpp\n  properties.hpp\n  remote_pidl.hpp\n  swish_pidl.hpp\n  ViewCallback.hpp\n  ${COMMAND_SOURCES})\n\nadd_library(remote_folder ${SOURCES})\n\ntarget_compile_definitions(remote_folder\n  PRIVATE ISOLATION_AWARE_ENABLED=1)\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(remote_folder\n  PRIVATE connection provider nse frontend shell_folder shell\n  PUBLIC Washer::washer Comet::comet ${Boost_LIBRARIES})\n"
  },
  {
    "path": "swish/remote_folder/Mode.cpp",
    "content": "/*  File permissions processing functions\n\n    Copyright (C) 2006, 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#include \"Mode.h\"\n\n#include <assert.h>\n\nusing namespace swish::remote_folder::mode;\n\nMode::Mode(mode_t mode) : mode(mode) {}\n\nbool Mode::isSymLink()\n{\n    assert(S_ISLNK(mode) ^ ( S_ISREG(mode) || S_ISDIR(mode) || S_ISCHR(mode)\n        || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISLNK(mode);\n}\n\nbool Mode::isRegular()\n{\n    assert(S_ISREG(mode) ^ ( S_ISLNK(mode) || S_ISDIR(mode) || S_ISCHR(mode)\n        || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISREG(mode);\n}\n\nbool Mode::isDirectory()\n{\n    assert(S_ISDIR(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISCHR(mode)\n        || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISDIR(mode);\n}\n\nbool Mode::isCharacter()\n{\n    assert(S_ISLNK(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISLNK(mode);\n}\n\nbool Mode::isBlock()\n{\n    assert(S_ISBLK(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISBLK(mode);\n}\n\nbool Mode::isFifo()\n{\n    assert(S_ISFIFO(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISFIFO(mode);\n}\n\nbool Mode::isSocket()\n{\n    assert(S_ISSOCK(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISDOOR(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISSOCK(mode);\n}\n\nbool Mode::isDoor()                                     /* Solaris door 'D' */\n{\n    assert(S_ISDOOR(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISNAM(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISDOOR(mode);\n}\n\nbool Mode::isNamed()                                /* XENIX named file 'x' */\n{\n    assert(S_ISNAM(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISMPB(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISNAM(mode);\n}\n\nbool Mode::isMultiplexedBlock()            /* multiplexed block special 'B' */\n{\n    assert(S_ISMPB(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPC(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISMPB(mode);\n}\n\nbool Mode::isMultiplexedChar()             /* multiplexed char special 'm' */\n{\n    assert(S_ISMPC(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISWHT(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISMPC(mode);\n}\n\nbool Mode::isWhiteout()                                 /* BSD whiteout 'w' */\n{\n    assert(S_ISWHT(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISMPC(mode) || S_ISNWK(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISWHT(mode);\n}\n\nbool Mode::isNetwork()                         /* HP-UX network special 'n' */\n{\n    assert(S_ISNWK(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISMPC(mode) || S_ISWHT(mode)\n        || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISNWK(mode);\n}\n\nbool Mode::isContiguous()                 /* contiguous - returns false 'C' */\n{\n    assert(S_ISCTG(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISMPC(mode) || S_ISWHT(mode)\n        || S_ISNWK(mode) || S_ISCTG(mode) || S_ISOFD(mode) || S_ISOFL(mode) ));\n    return S_ISCTG(mode);\n}\n\nbool Mode::isOffline()      /* Cray DMF offline no data - returns false 'M' */\n{\n    assert(S_ISOFL(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISMPC(mode) || S_ISWHT(mode)\n        || S_ISNWK(mode) || S_ISCTG(mode) || S_ISOFD(mode) ));\n    return S_ISOFL(mode);\n}\n\nbool Mode::isOfflineData()   /* Cray DMF offline + data - returns false 'M' */\n{\n    assert(S_ISOFD(mode) ^ ( S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)\n        || S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode)\n        || S_ISSOCK(mode) || S_ISDOOR(mode) || S_ISNAM(mode)\n        || S_ISMPB(mode) || S_ISMPC(mode) || S_ISWHT(mode)\n        || S_ISNWK(mode) || S_ISCTG(mode) || S_ISOFL(mode) ));\n    return S_ISOFD(mode);\n}\n\n\nbool Mode::isSUID()\n{\n    return ((mode & S_ISUID) == S_ISUID);\n}\n\nbool Mode::isSGID()\n{\n    return ((mode & S_ISGID) == S_ISGID);\n}\n\nbool Mode::isSticky()\n{\n    return ((mode & S_ISVTX) == S_ISVTX);\n}\n\nstd::string Mode::toString()\n{\n    const int MODE_STR_BUFFER_SIZE = 10;\n    char buf[MODE_STR_BUFFER_SIZE];\n    ::mode_string(mode, buf);\n    return std::string(buf, MODE_STR_BUFFER_SIZE);\n}"
  },
  {
    "path": "swish/remote_folder/Mode.h",
    "content": "/*  File permissions processing functions (header).\n\n    Copyright (C) 2006, 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_MODE_H\n#define SWISH_REMOTE_FOLDER_MODE_H\n#pragma once\n\n#include \"filemode.h\"\n\n#include <string>\n\nnamespace swish {\nnamespace remote_folder {\nnamespace mode {\n\nclass Mode\n{\npublic:\n    Mode(mode_t mode);\n\n    bool isRegular();          ///< regular                    '-'\n    bool isSymLink();          ///< Symbolic link              'l'\n    bool isDirectory();        ///< directory                  'd'\n    bool isCharacter();        ///< character special          'c'\n    bool isBlock();            ///< block special              'b'\n    bool isFifo();             ///< fifo                       'p'\n    bool isSocket();           ///< socket                     's'\n    bool isDoor();             ///< Solaris door               'D'\n    bool isNamed();            ///< XENIX named file           'x'\n    bool isMultiplexedBlock(); ///< multiplexed block special  'B'\n    bool isMultiplexedChar();  ///< multiplexed char special   'm'\n    bool isWhiteout();         ///< BSD whiteout               'w'\n    bool isNetwork();          ///< HP-UX network special      'n'\n    bool isContiguous();       ///< contiguous                 'C'\n    bool isOffline();          ///< Cray DMF offline no data   'M'\n    bool isOfflineData();      ///< Cray DMF offline + data    'M'\n\n    bool isSUID();\n    bool isSGID();\n    bool isSticky();\n\n    std::string toString();\n\nprivate:\n    mode_t mode;\n};\n\n}}} // namespace swish::remote_folder::mode\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/ViewCallback.cpp",
    "content": "/* Handler for remote folder's interaction with Explorer Shell Folder View.\n\n   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"ViewCallback.hpp\"\n\n#include \"swish/frontend/UserInteraction.hpp\" // CUserInteraction\n#include \"swish/remote_folder/commands/commands.hpp\" // NewFolder\n#include \"swish/remote_folder/pidl_connection.hpp\" // provider_from_pidl\n\n#include <washer/error.hpp> // last_error\n\n#include <boost/bind.hpp> // bind\n#include <boost/exception/info.hpp>\n#include <boost/exception/errinfo_api_function.hpp> // errinfo_api_function\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n\nusing swish::frontend::CUserInteraction;\nusing swish::nse::IEnumUICommand;\nusing swish::nse::IUIElement;\nusing swish::remote_folder::provider_from_pidl;\nusing swish::remote_folder::commands::remote_folder_task_pane_tasks;\nusing swish::remote_folder::commands::remote_folder_task_pane_titles;\n\nusing comet::com_ptr;\n\nusing washer::last_error;\nusing washer::shell::pidl::apidl_t;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing boost::bind;\nusing boost::enable_error_info;\nusing boost::errinfo_api_function;\n\nusing std::pair;\n\nnamespace swish {\nnamespace remote_folder {\n\nnamespace {\n\n    bool is_vista_or_greater()\n    {\n        OSVERSIONINFO version = OSVERSIONINFO();\n        version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\n        if (::GetVersionEx(&version) == FALSE)\n            BOOST_THROW_EXCEPTION(\n                enable_error_info(last_error()) << \n                errinfo_api_function(\"GetVersionEx\"));\n\n        return version.dwMajorVersion > 5;\n    }\n}\n\n/**\n * Create customisation callback object for Explorer default shell view.\n *\n * @param folder_pidl  Absolute PIDL to the folder for whom we are\n *                     creating this callback object.\n */\nCViewCallback::CViewCallback(const apidl_t& folder_pidl) :\n    m_folder_pidl(folder_pidl) {}\n\n/**\n * The folder window is being created.\n *\n * The shell is notifying us of the folder view's window handle.\n */\nbool CViewCallback::on_window_created(HWND hwnd_view)\n{\n    if (hwnd_view)\n    {\n        m_owning_folder_view = window<wchar_t>(\n            window_handle::foster_handle(hwnd_view));\n    }\n\n    return true;\n}\n\n/**\n * Tell the shell that we might notify it of update events that apply to this\n * folder (specified using our absolute PIDL).\n *\n * We are notified via SFVM_FSNOTIFY if any events indicated here occurr.\n *\n * @TODO: It's possible that @a events already has events set in its bitmask\n *        so maybe we should 'or' our extra events with it.\n */\nbool CViewCallback::on_get_notify(\n    PCIDLIST_ABSOLUTE& pidl_monitor, LONG& events)\n{\n    events = SHCNE_CREATE | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR |\n        SHCNE_UPDATEITEM | SHCNE_UPDATEDIR | SHCNE_RENAMEITEM |\n        SHCNE_RENAMEFOLDER;\n    pidl_monitor = m_folder_pidl.get(); // Owned by us\n    return true;\n}\n\n/**\n * The shell is telling us that an event (probably a SHChangeNotify of some\n * sort) has affected one of our item.  Just nod. If we don't it doesn't work.\n */\nbool CViewCallback::on_fs_notify(\n    PCIDLIST_ABSOLUTE /*pidl*/, LONG /*event*/)\n{\n    return true;\n}\n\nbool CViewCallback::on_get_webview_content(\n    SFV_WEBVIEW_CONTENT_DATA& content_out)\n{\n    assert(content_out.pFolderTasksExpando == NULL);\n    assert(content_out.pExtraTasksExpando == NULL);\n    assert(content_out.pEnumRelatedPlaces == NULL);\n\n    // HACK: webview conflicts with ExplorerCommands so we disable it if\n    //       ExplorerCommands are likely to be used.\n    if (is_vista_or_greater())\n        return false;\n\n    pair< com_ptr<IUIElement>, com_ptr<IUIElement> > tasks =\n        remote_folder_task_pane_titles(m_folder_pidl);\n\n    content_out.pExtraTasksExpando = tasks.first.detach();\n    content_out.pFolderTasksExpando = tasks.second.detach();\n    return true;\n}\n\nnamespace {\n    com_ptr<ISftpConsumer> consumer(HWND hwnd)\n    {\n        return new CUserInteraction(hwnd);\n    }\n}\n\nbool CViewCallback::on_get_webview_tasks(\n    SFV_WEBVIEW_TASKSECTION_DATA& tasks_out)\n{\n    //for some reason this fails on 64-bit\n    //assert(tasks_out.pEnumExtraTasks == NULL);\n\n    assert(tasks_out.pEnumFolderTasks == NULL);\n\n    // HACK: webview conflicts with ExplorerCommands so we disable it if\n    //       ExplorerCommands are likely to be used.\n    if (is_vista_or_greater())\n        return false;\n\n    HWND nasty_old_hwnd = (m_owning_folder_view) ?\n        m_owning_folder_view->hwnd() : NULL;\n\n    pair< com_ptr<IEnumUICommand>, com_ptr<IEnumUICommand> > commands =\n        remote_folder_task_pane_tasks(\n            m_folder_pidl, ole_site(),\n            bind(provider_from_pidl, m_folder_pidl, _1, _2),\n            bind(&consumer, nasty_old_hwnd));\n\n    tasks_out.pEnumExtraTasks = commands.first.detach();\n    tasks_out.pEnumFolderTasks = commands.second.detach();\n    return true;\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/remote_folder/ViewCallback.hpp",
    "content": "/* Handler for remote folder's interaction with Explorer Shell Folder View.\n\n   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_VIEW_CALLBACK_HPP\n#define SWISH_REMOTE_FOLDER_VIEW_CALLBACK_HPP\n#pragma once\n\n#include \"swish/nse/view_callback.hpp\" // CViewCallback\n\n#include <washer/object_with_site.hpp> // object_with_site\n#include <washer/shell/pidl.hpp> // apidl_t\n#include <washer/window/window.hpp>\n\n#include <comet/server.h> // simple_object\n\n#include <boost/optional/optional.hpp> // optional\n\nnamespace swish {\nnamespace remote_folder {\n\nclass CViewCallback :\n    public comet::simple_object<nse::CViewCallback, washer::object_with_site>\n{\npublic:\n\n    CViewCallback(const washer::shell::pidl::apidl_t& folder_pidl);\n\nprivate:\n\n    /// @name  SFVM_* message handlers\n    // @{\n\n    virtual bool on_window_created(HWND hwnd_view);\n    virtual bool on_get_notify(PCIDLIST_ABSOLUTE& pidl_monitor, LONG& events);\n    virtual bool on_fs_notify(PCIDLIST_ABSOLUTE pidl, LONG event);\n    virtual bool on_get_webview_content(SFV_WEBVIEW_CONTENT_DATA& content_out);\n    virtual bool on_get_webview_tasks(SFV_WEBVIEW_TASKSECTION_DATA& tasks_out);\n\n    // @}\n\n    boost::optional<washer::window::window<wchar_t>> m_owning_folder_view;\n    washer::shell::pidl::apidl_t m_folder_pidl;\n};\n\n}} // namespace remote::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/columns.cpp",
    "content": "/**\n    @file\n\n    Host folder detail columns.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"columns.hpp\"\n\n#include <washer/shell/format.hpp> // format_date_time,\n                                   // format_filesize_kilobytes\n#pragma warning(push)\n#pragma warning(disable: 4510 4610) // Cannot generate default constructor\n#include <boost/array.hpp> // array\n#pragma warning(pop)\n#include <boost/locale.hpp> // translate\n\n#include <string>\n\n#include <Commctrl.h> // LVCFMT_*\n#include <Propkey.h> // PKEY_ *\n\nusing washer::shell::format_date_time;\nusing washer::shell::format_filesize_kilobytes;\nusing washer::shell::property_key;\n\nusing comet::variant_t;\n\nusing boost::array;\nusing boost::locale::translate;\n\nusing std::wstring;\n\nnamespace swish {\nnamespace remote_folder {\n\nnamespace {\n\n    /**\n     * Convert the variant to a date string in the format normal for the shell.\n     */\n    wstring date_formatter(const variant_t& val)\n    {\n        return format_date_time<wchar_t>(val);\n    }\n\n    /**\n     * Format the number in the variant as a file size in KB.\n     */\n    wstring size_formatter(const variant_t& val)\n    {\n        return format_filesize_kilobytes<wchar_t>(val);\n    }\n\n    /**\n     * Static column information.\n     * Order of entries must correspond to the indices in columnIndices.\n     */\n    const boost::array<column_entry, 10> column_key_index = { {\n\n        { PKEY_ItemNameDisplay, translate(\"Property (filename/label)\", \"Name\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30 },\n\n        { PKEY_Size, translate(\"Property\", \"Size\"),\n          SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15,\n          size_formatter },\n\n        { PKEY_ItemTypeText, translate(\"Property\", \"Type\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20 },\n\n        { PKEY_DateModified, translate(\"Property\", \"Date Modified\"),\n          SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20,\n          date_formatter },\n\n        { PKEY_DateAccessed, translate(\"Property\", \"Date Accessed\"),\n          SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20, date_formatter },\n\n        { PKEY_Permissions, translate(\"Property\", \"Permissions\"),\n          SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12 },\n\n        { PKEY_FileOwner, translate(\"Property\", \"Owner\"),\n          SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 12 },\n\n        { PKEY_Group, translate(\"Property\", \"Group\"),\n          SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 12 },\n\n        { PKEY_OwnerId, translate(\"Property\", \"Owner ID\"),\n          SHCOLSTATE_TYPE_INT, LVCFMT_LEFT, 10 },\n\n        { PKEY_GroupId, translate(\"Property\", \"Group ID\"),\n          SHCOLSTATE_TYPE_INT, LVCFMT_LEFT, 10 }\n    } };\n\n}\n\n/**\n * Return a column entry.\n */\nconst column_entry& RemoteColumnEntries::entry(size_t index) const\n{\n    return column_key_index.at(index);\n}\n\n/**\n * Convert index to a corresponding PROPERTYKEY.\n */\nconst property_key& property_key_from_column_index(size_t index)\n{\n    return column_key_index.at(index).m_key;\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/remote_folder/columns.hpp",
    "content": "/**\n    @file\n\n    Host folder detail columns.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_COLUMNS_HPP\n#define SWISH_REMOTE_FOLDER_COLUMNS_HPP\n#pragma once\n\n#include \"properties.hpp\" // property_from_pidl, compare_pidls_by_property\n\n#include \"swish/nse/StaticColumn.hpp\" // StaticColumn\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <comet/variant.h> // variant_t\n\n#include <boost/locale/encoding_utf.hpp> // utf_to_utf\n#include <boost/locale.hpp> // message\n#include <boost/function.hpp> // function\n\n#include <ShTypes.h> // SHCOLSTATEF\n\n#include <string>\n\nnamespace swish {\nnamespace remote_folder {\n\n#pragma warning(push)\n#pragma warning(disable: 4510 4610) // Cannot generate default constructor\n\nstruct column_entry\n{\n    /**\n     * @name Fields.\n     *\n     * These can be set using an aggregate initialiser: { val1, val2, ... }\n     */\n    // @{\n    washer::shell::property_key m_key;\n    boost::locale::message m_title;\n    SHCOLSTATEF m_flags;\n    int m_format;\n    int m_avg_char_width;\n    boost::function<std::wstring (const comet::variant_t&)> m_stringifier;\n    // @}\n\n    std::wstring title() const\n    {\n        return boost::locale::conv::utf_to_utf<wchar_t>(m_title.str());\n    }\n\n    SHCOLSTATEF flags() const { return m_flags; }\n    int format() const { return m_format; }\n    int avg_char_width() const { return m_avg_char_width; }\n\n    /**\n     * Convert the column's property variant to a string.\n     *\n     * Transforms the output using m_stringifier, if any, otherwise performs\n     * simple wstring conversion.\n     */\n    std::wstring detail(const washer::shell::pidl::cpidl_t& pidl) const\n    {\n        comet::variant_t var = property_from_pidl(pidl, m_key);\n        if (m_stringifier)\n            return m_stringifier(var);\n        else\n            return var;\n    }\n\n    int compare(\n        const washer::shell::pidl::cpidl_t& lhs,\n        const washer::shell::pidl::cpidl_t& rhs) const\n    {\n        return compare_pidls_by_property(lhs, rhs, m_key);\n    }\n};\n\n#pragma warning(pop)\n\n/**\n * StaticColumn-compatible interface to the static column data.\n */\nclass RemoteColumnEntries\n{\nprotected:\n    const column_entry& entry(size_t index) const;\n};\n\ntypedef swish::nse::StaticColumn<RemoteColumnEntries> Column;\n\nconst washer::shell::property_key& property_key_from_column_index(\n    size_t index);\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/commands/NewFolder.cpp",
    "content": "/* Copyright (C) 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"NewFolder.hpp\"\n\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n#include \"swish/provider/sftp_filesystem_item.hpp\"\n#include \"swish/remote_folder/swish_pidl.hpp\" // absolute_path_from_swish_pidl\n#include \"swish/shell_folder/SftpDirectory.h\" // CSftpDirectory\n#include \"swish/shell/shell.hpp\" // put_view_item_into_rename_mode\n\n#include <ssh/filesystem/path.hpp>\n\n#include <washer/shell/services.hpp> // shell_browser, shell_view\n#include <washer/trace.hpp> // trace\n\n#include <comet/error.h> // com_error\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/lexical_cast.hpp> // lexical_cast\n#include <boost/locale.hpp> // translate\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/format.hpp> // wformat\n#include <boost/regex.hpp> // wregex, smatch, regex_match\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <exception>\n#include <string>\n#include <vector>\n\nusing swish::frontend::announce_last_exception;\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing swish::provider::sftp_filesystem_item;\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::absolute_path_from_swish_pidl;\nusing swish::shell::put_view_item_into_rename_mode;\n\nusing ssh::filesystem::path;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::shell_browser;\nusing washer::shell::shell_view;\nusing washer::window::window;\nusing washer::trace;\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::function;\nusing boost::shared_ptr;\nusing boost::lexical_cast;\nusing boost::locale::translate;\nusing boost::optional;\nusing boost::regex_match;\nusing boost::wformat;\nusing boost::wregex;\nusing boost::wsmatch;\n\nusing std::exception;\nusing std::vector;\nusing std::wstring;\n\nnamespace {\n\n/**\n * Find the first non-existent directory name that begins with @a initial_name.\n *\n * This may simply be initial_name, however, if an item of this name already\n * exists in the directory, return a name that begins with @a initial_name\n * followed by a space and a digit in brackets.  The digit should be the lowest\n * digit that will create a name that doesn't already exits.\n *\n * @todo  Investigate whether other locales require something other than an\n *        Arabic digit being appended or if it should be appended in a different\n *        place.\n */\nwstring prefix_if_necessary(\n    const wstring& initial_name, shared_ptr<sftp_provider> provider,\n    const path& directory)\n{\n    wregex new_folder_pattern(\n        str(wformat(L\"%1%|%1% \\\\((\\\\d+)\\\\)\") % initial_name));\n    wsmatch digit_suffix_match;\n\n    bool collision = false;\n    vector<unsigned long> suffixes;\n    BOOST_FOREACH(\n        const sftp_filesystem_item& lt, provider->listing(directory))\n    {\n        wstring filename = lt.filename().wstring();\n        if (regex_match(filename, digit_suffix_match, new_folder_pattern))\n        {\n            assert(digit_suffix_match.size() == 2); // complete + capture group\n            assert(digit_suffix_match[0].matched); // why are we here otherwise?\n\n            // We record whether an exact match was found with \"initial_name\"\n            // but keep going regardless: if it was, we will need to find\n            // the next available digit suffix; if not, it might be found on\n            // a future iteration so we still need the next available digit\n            if (digit_suffix_match[1].matched)\n            {\n                unsigned long suffix = lexical_cast<unsigned long>(\n                    wstring(digit_suffix_match[1]));\n                suffixes.push_back(suffix);\n            }\n            else if (digit_suffix_match[0].matched)\n            {\n                collision = true;\n            }\n            else\n            {\n                assert(false && \"Invariant violation: should never reach here\");\n                collision = false;\n                break;\n            }\n        }\n    }\n\n    if (!collision)\n        return initial_name;\n    else\n    {\n        unsigned long lowest = 2;\n        std::sort(suffixes.begin(), suffixes.end());\n        BOOST_FOREACH(unsigned long suffix, suffixes)\n        {\n            if (suffix == 1) // skip \"New Folder (1)\" as Windows doesn't use it\n                continue;    // so we won't either\n            if (suffix == lowest)\n                ++lowest;\n            if (suffix > lowest)\n                break;\n        }\n\n        return str(wformat(L\"%1% (%2%)\") % initial_name % lowest);\n    }\n}\n\n}\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\nnamespace {\n    const uuid_t NEW_FOLDER_COMMAND_ID(L\"b816a882-5022-11dc-9153-0090f5284f85\");\n}\n\nNewFolder::NewFolder(\n    const apidl_t& folder_pidl,\n    const provider_factory& provider,\n    const consumer_factory& consumer) :\n    Command(\n        translate(L\"New &folder\"), NEW_FOLDER_COMMAND_ID,\n        translate(L\"Create a new, empty folder in the folder you have open.\"),\n        L\"shell32.dll,-258\", L\"\",\n        translate(L\"Make a new folder\")),\n    m_folder_pidl(folder_pidl), m_provider_factory(provider),\n    m_consumer_factory(consumer) {}\n\nBOOST_SCOPED_ENUM(Command::state) NewFolder::state(\n    com_ptr<IShellItemArray>, bool /*ok_to_be_slow*/)\nconst\n{\n    return state::enabled;\n}\n\nvoid NewFolder::operator()(\n    com_ptr<IShellItemArray>, const command_site& site, com_ptr<IBindCtx>)\nconst\n{\n    try\n    {\n        shared_ptr<sftp_provider> provider = m_provider_factory(\n            m_consumer_factory(),\n            translate(\"Name of a running task\", \"Creating new folder\"));\n\n        CSftpDirectory directory(m_folder_pidl, provider);\n\n        // The default New Folder name may already exist in the folder. If it\n        // does, we append a number to it to make it unique\n        wstring initial_name = translate(L\"Initial name\", L\"New folder\");\n        initial_name = prefix_if_necessary(\n            initial_name, provider,\n            absolute_path_from_swish_pidl(m_folder_pidl));\n\n        cpidl_t pidl = directory.CreateDirectory(initial_name);\n\n        try\n        {\n            // A failure after this point is not worth reporting.  The folder\n            // was created even if we didn't allow the user a chance to pick a\n            // name.\n\n            com_ptr<IShellView> view = shell_view(shell_browser(site.ole_site()));\n            if (view)\n            {\n                put_view_item_into_rename_mode(view, pidl);\n            }\n        }\n        catch (const exception& e)\n        {\n            trace(\"WARNING: Couldn't put folder into rename mode: %s\") %\n                e.what();\n        }\n    }\n    catch (...)\n    {\n        try\n        {\n            optional<window<wchar_t>> view_window = site.ui_owner();\n            if (view_window)\n            {\n                announce_last_exception(\n                    view_window->hwnd(),\n                    translate(L\"Could not create a new folder\"),\n                    translate(L\"You might not have permission.\"));\n            }\n        } catch (...) {}\n\n        throw;\n    }\n}\n\n}}} // namespace swish::remote_folder::commands\n"
  },
  {
    "path": "swish/remote_folder/commands/NewFolder.hpp",
    "content": "/* Copyright (C) 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_COMMANDS_NEW_FOLDER_HPP\n#define SWISH_REMOTE_FOLDER_COMMANDS_NEW_FOLDER_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/nse/Command.hpp\" // Command\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <boost/function.hpp>\n#include <boost/shared_ptr.hpp>\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\ntypedef boost::function<\n    boost::shared_ptr<swish::provider::sftp_provider>(\n        comet::com_ptr<ISftpConsumer>, const std::string& task_name)> provider_factory;\n\ntypedef boost::function<comet::com_ptr<ISftpConsumer>()> consumer_factory;\n\nclass NewFolder : public swish::nse::Command\n{\npublic:\n    NewFolder(\n        const washer::shell::pidl::apidl_t& folder_pidl,\n        const provider_factory& provider,\n        const consumer_factory& consumer);\n\n    virtual BOOST_SCOPED_ENUM(state) state(\n        comet::com_ptr<IShellItemArray> selection,\n        bool ok_to_be_slow) const;\n\n    void operator()(\n        comet::com_ptr<IShellItemArray> selection,\n        const swish::nse::command_site& site,\n        comet::com_ptr<IBindCtx> bind_ctx) const;\n\nprivate:\n    washer::shell::pidl::apidl_t m_folder_pidl;\n    provider_factory m_provider_factory;\n    consumer_factory m_consumer_factory;\n};\n\n}}} // namespace swish::remote_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/commands/commands.cpp",
    "content": "/* Copyright (C) 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"commands.hpp\"\n\n#include \"swish/nse/explorer_command.hpp\" // CExplorerCommand*\n#include \"swish/nse/task_pane.hpp\" // CUIElementErrorAdapter, CUICommand\n#include \"swish/remote_folder/commands/NewFolder.hpp\"\n\n\n#include <comet/server.h> // simple_object\n#include <comet/smart_enum.h> // make_smart_enumeration\n\n#include <boost/bind.hpp> // bind, _1\n#include <boost/locale.hpp> // translate\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <exception>\n#include <string>\n#include <utility> // make_pair\n#include <vector>\n\nusing swish::nse::CExplorerCommandProvider;\nusing swish::nse::CExplorerCommand;\nusing swish::nse::CUIElementErrorAdapter;\nusing swish::nse::CUICommand;\nusing swish::nse::IEnumUICommand;\nusing swish::nse::IUICommand;\nusing swish::nse::IUIElement;\nusing swish::nse::WebtaskCommandTitleAdapter;\nusing swish::provider::sftp_provider;\n\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_ptr;\nusing comet::make_smart_enumeration;\nusing comet::simple_object;\n\nusing boost::bind;\nusing boost::function;\nusing boost::locale::translate;\nusing boost::make_shared;\nusing boost::shared_ptr;\n\nusing std::make_pair;\nusing std::vector;\nusing std::wstring;\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\ncom_ptr<IExplorerCommandProvider> remote_folder_command_provider(\n    const apidl_t& folder_pidl,\n    const provider_factory& provider,\n    const consumer_factory& consumer)\n{\n    CExplorerCommandProvider::ordered_commands commands;\n    commands.push_back(\n        new CExplorerCommand<NewFolder>(\n            folder_pidl, provider, consumer));\n    return new CExplorerCommandProvider(commands);\n}\n\nclass CSftpTasksTitle : public simple_object<CUIElementErrorAdapter>\n{\npublic:\n\n    virtual std::wstring title(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return translate(L\"File and Folder Tasks\");\n    }\n\n    virtual std::wstring icon(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return L\"shell32.dll,-319\";\n    }\n\n    virtual std::wstring tool_tip(\n        const comet::com_ptr<IShellItemArray>& /*items*/) const\n    {\n        return translate(L\"These tasks help you manage your remote files.\");\n    }\n};\n\nstd::pair<com_ptr<IUIElement>, com_ptr<IUIElement> >\nremote_folder_task_pane_titles(const apidl_t& /*folder_pidl*/)\n{\n    return make_pair(new CSftpTasksTitle(), com_ptr<IUIElement>());\n}\n\nstd::pair<com_ptr<IEnumUICommand>, com_ptr<IEnumUICommand> >\nremote_folder_task_pane_tasks(\n    const apidl_t& folder_pidl, com_ptr<IUnknown> ole_site,\n    const provider_factory& provider, const consumer_factory& consumer)\n{\n    typedef shared_ptr< vector< com_ptr<IUICommand> > > shared_command_vector;\n    shared_command_vector commands =\n        make_shared< vector< com_ptr<IUICommand> > >();\n\n    com_ptr<IUICommand> new_folder =\n        new CUICommand<WebtaskCommandTitleAdapter<NewFolder>>(\n        folder_pidl,\n        bind(\n            provider, _1,\n            translate(\"Name of a running task\", \"Creating new folder\")),\n        consumer);\n\n    com_ptr<IObjectWithSite> object_with_site = com_cast(new_folder);\n\n    // Explorer doesn't seem to call SetSite on the command object which is odd\n    // because any command that needs to change the view would need it.  We do\n    // it instead.\n    // XXX: We never unset the site.  Explorer normally does if it sets it.\n    //      I don't know if this is a problem.\n    if (object_with_site)\n        object_with_site->SetSite(ole_site.get());\n\n    commands->push_back(new_folder);\n\n    com_ptr<IEnumUICommand> e =\n        make_smart_enumeration<IEnumUICommand>(commands);\n\n    return make_pair(e, com_ptr<IEnumUICommand>());\n}\n\n}}} // namespace swish::remote_folder::commands\n"
  },
  {
    "path": "swish/remote_folder/commands/commands.hpp",
    "content": "/* Copyright (C) 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_COMMANDS_HPP\n#define SWISH_REMOTE_FOLDER_COMMANDS_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/nse/UICommand.hpp\" // IUIElement\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/function.hpp>\n#include <boost/shared_ptr.hpp>\n\n#include <shobjidl.h> // IExplorerCommandProvider\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\ntypedef boost::function<\n    boost::shared_ptr<swish::provider::sftp_provider>(\n        comet::com_ptr<ISftpConsumer>, const std::string& task_name)>\n    provider_factory;\n\ntypedef boost::function<comet::com_ptr<ISftpConsumer>()> consumer_factory;\n\ncomet::com_ptr<IExplorerCommandProvider> remote_folder_command_provider(\n    const washer::shell::pidl::apidl_t& folder_pidl,\n    const provider_factory& provider,\n    const consumer_factory& consumer);\n\nstd::pair<comet::com_ptr<nse::IUIElement>, comet::com_ptr<nse::IUIElement> >\nremote_folder_task_pane_titles(\n    const washer::shell::pidl::apidl_t& folder_pidl);\n\nstd::pair<\n    comet::com_ptr<nse::IEnumUICommand>,\n    comet::com_ptr<nse::IEnumUICommand> >\nremote_folder_task_pane_tasks(\n    const washer::shell::pidl::apidl_t& folder_pidl,\n    comet::com_ptr<IUnknown> ole_site,\n    const provider_factory& provider,\n    const consumer_factory& consumer);\n\n}}} // namespace swish::remote_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/commands/delete.cpp",
    "content": "/**\n    @file\n\n    Remote item deletion.\n\n    @if license\n\n    Copyright (C) 2011, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/remote_folder/commands/delete.hpp\"\n\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n#include \"swish/shell_folder/SftpDirectory.h\" // CSftpDirectory\n#include \"swish/shell/shell_item_array.hpp\"\n#include \"swish/shell/parent_and_item.hpp\"\n\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t, pidl_cast\n\n#include <boost/locale.hpp> // translate\n#include <boost/format.hpp> // wformat\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n#include <vector>\n\n#include <Windows.h> // IsolationAwareMessageBox\n\nusing swish::frontend::announce_last_exception;\nusing swish::provider::sftp_provider;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_cast;\n\nusing comet::com_ptr;\n\nusing boost::function;\nusing boost::locale::translate;\nusing boost::shared_ptr;\nusing boost::wformat;\n\nusing std::vector;\nusing std::wstring;\n\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\nnamespace {\n\n    /**\n     * Deletes files or folders.\n     *\n     * The list of items to delete is supplied as a list of PIDLs and may\n     * contain a mix of files and folder.\n     */\n    template<typename ProviderFactory, typename ConsumerFactory>\n    void do_delete(\n        HWND hwnd_view, const vector<com_ptr<IParentAndItem>>& death_row,\n        ProviderFactory provider_factory, ConsumerFactory consumer_factory)\n    {\n        com_ptr<ISftpConsumer> consumer = consumer_factory(hwnd_view);\n        shared_ptr<sftp_provider> provider = provider_factory(\n            consumer, translate(\"Name of a running task\", \"Deleting files\"));\n\n        // Delete each item and notify shell\n        vector<com_ptr<IParentAndItem>>::const_iterator it = death_row.begin();\n        while (it != death_row.end())\n        {\n            CSftpDirectory directory((*it)->parent_pidl(), provider);\n            directory.Delete((*it)->item_pidl());\n            ++it;\n        }\n    }\n\n    /**\n     * Displays dialog seeking confirmation from user to delete a single item.\n     *\n     * The dialog differs depending on whether the item is a file or a folder.\n     *\n     * @param hwnd_view  Handle to the window used for UI.\n     * @param filename   Name of the file or folder being deleted.\n     * @param is_folder  Is the item in question a file or a folder?\n     *\n     * @returns  Whether confirmation was given or denied.\n     */\n    bool confirm_deletion(\n        HWND hwnd_view, const wstring& filename, bool is_folder)\n    {\n        if (hwnd_view == NULL)\n            return false;\n\n        wstring message;\n        if (!is_folder)\n        {\n            message = L\"Are you sure you want to permanently delete '\";\n            message += filename;\n            message += L\"'?\";\n        }\n        else\n        {\n            message = L\"Are you sure you want to permanently delete the \"\n                L\"folder '\";\n            message += filename;\n            message += L\"' and all of its contents?\";\n        }\n\n        int ret = ::IsolationAwareMessageBoxW(\n            hwnd_view, message.c_str(),\n            (is_folder) ? L\"Confirm Folder Delete\" : L\"Confirm File Delete\",\n            MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1);\n\n        return (ret == IDYES);\n    }\n\n\n    /**\n     * Displays dialog seeking confirmation from user to delete multiple items.\n     *\n     * @param hwnd_view   Handle to the window used for UI.\n     * @param item_count  Number of items selected for deletion.\n     *\n     * @returns  Whether confirmation was given or denied.\n     */\n    bool confirm_multiple_deletion(HWND hwnd_view, size_t item_count)\n    {\n        if (hwnd_view == NULL)\n            return false;\n\n        wstring message =\n            str(wformat(\n                L\"Are you sure you want to permanently delete these %d items?\")\n                % item_count);\n\n        int ret = ::IsolationAwareMessageBoxW(\n            hwnd_view, message.c_str(), L\"Confirm Multiple Item Delete\",\n            MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1);\n\n        return (ret == IDYES);\n    }\n\n    /**\n     * Deletes files or directories after seeking confirmation from user.\n     *\n     * The list of items to delete is supplied as a list of PIDLs and may\n     * contain a mix of files and folders.\n     *\n     * If just one item is chosen, a specific confirmation message for that\n     * item is shown.  If multiple items are to be deleted, a general\n     * confirmation message is displayed asking if the number of items are\n     * to be deleted.\n     */\n    template<typename ProviderFactory, typename ConsumerFactory>\n    void execute_death_row(\n        HWND hwnd_view, const vector<com_ptr<IParentAndItem>>& death_row,\n        ProviderFactory provider_factory, ConsumerFactory consumer_factory)\n    {\n        size_t item_count = death_row.size();\n\n        BOOL go_ahead = false;\n        if (item_count == 1)\n        {\n            remote_itemid_view itemid(death_row[0]->item_pidl());\n            go_ahead = confirm_deletion(\n                hwnd_view, itemid.filename(), itemid.is_folder());\n        }\n        else if (item_count > 1)\n        {\n            go_ahead = confirm_multiple_deletion(hwnd_view, item_count);\n        }\n        else\n        {\n            assert(false);\n            return; // do nothing because no items were given\n        }\n\n        if (go_ahead)\n        {\n            do_delete(hwnd_view, death_row, provider_factory, consumer_factory);\n        }\n    }\n}\n\n\nDelete::Delete(\n    my_provider_factory provider_factory,\n    my_consumer_factory consumer_factory)\n    : m_provider_factory(provider_factory), m_consumer_factory(consumer_factory)\n{}\n\nvoid Delete::operator()(HWND hwnd_view, com_ptr<IShellItemArray> selection)\nconst\n{\n    try\n    {\n        vector<com_ptr<IParentAndItem>> death_row;\n        comet::wrap_t<IShellItemArray>::iterator_type it = selection->begin();\n        comet::wrap_t<IShellItemArray>::iterator_type end = selection->end();\n        while(it++ != end)\n        {\n            com_ptr<IShellItem> item = *it;\n            com_ptr<IParentAndItem> parent_and_item = try_cast(item);\n            death_row.push_back(parent_and_item);\n        }\n\n        execute_death_row(\n            hwnd_view, death_row, m_provider_factory, m_consumer_factory);\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd_view, translate(L\"Unable to delete the item\"),\n            translate(L\"You might not have permission.\"));\n        throw;\n    }\n}\n\n}}} // namespace swish::remote_folder::commands\n"
  },
  {
    "path": "swish/remote_folder/commands/delete.hpp",
    "content": "/**\n    @file\n\n    Swish remote folder commands.\n\n    @if license\n\n    Copyright (C) 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_COMMANDS_DELETE_HPP\n#define SWISH_REMOTE_FOLDER_COMMANDS_DELETE_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/function.hpp>\n#include <boost/shared_ptr.hpp>\n\n#include <shobjidl.h> // IShellItemArray\n\nnamespace swish {\nnamespace remote_folder {\nnamespace commands {\n\nclass Delete\n{\n    typedef boost::function<\n        boost::shared_ptr<swish::provider::sftp_provider>(\n        comet::com_ptr<ISftpConsumer>, const std::string& task_name)\n    > my_provider_factory;\n\n    typedef boost::function<comet::com_ptr<ISftpConsumer>(HWND)>\n        my_consumer_factory;\n\npublic:\n    Delete(\n        my_provider_factory provider_factory,\n        my_consumer_factory consumer_factory);\n\n    void operator()(\n        HWND hwnd_view, comet::com_ptr<IShellItemArray> selection) const;\n\nprivate:\n    my_provider_factory m_provider_factory;\n    my_consumer_factory m_consumer_factory;\n};\n\n}}} // namespace swish::remote_folder::commands\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/context_menu_callback.cpp",
    "content": "/**\n    @file\n\n    RemoteFolder context menu implementation (basically that what it does).\n\n    @if license\n\n    Copyright (C) 2011, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n#include \"swish/shell_folder/SftpDirectory.h\" // CSftpDirectory\n#include \"swish/shell/shell.hpp\" // ui_object_of_item\n#include \"swish/remote_folder/commands/delete.hpp\" // Delete\n#include \"swish/remote_folder/pidl_connection.hpp\" // provider_from_pidl\n#include \"swish/remote_folder/context_menu_callback.hpp\"\n                                                       // context_menu_callback\n#include \"swish/shell/parent_and_item.hpp\" // parent_and_item\n#include \"swish/shell/shell_item_array.hpp\" // shell_item_array_from_data_object\n\n#include <washer/error.hpp> // last_error\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t\n#include <washer/shell/shell.hpp> // pidl_from_parsing_name\n\n#include <comet/uuid.h> // uuid_t\n\n#include <boost/locale.hpp> // translate\n#include <boost/exception/errinfo_file_name.hpp> // errinfo_file_name\n#include <boost/filesystem/path.hpp> // path\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <stdexcept> // runtime_error\n#include <vector>\n\n#include <ShellApi.h> // ShellExecuteEx\n#include <Windows.h> // InsertMenu, SetMenuDefaultItem\n\nusing swish::frontend::announce_last_exception;\nusing swish::provider::sftp_provider;\nusing swish::shell::shell_item_array_from_data_object;\nusing swish::shell::ui_object_of_item;\n\nusing washer::last_error;\nusing washer::shell::pidl_from_parsing_name;\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_cast;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuid_t;\n\nusing boost::enable_error_info;\nusing boost::errinfo_api_function;\nusing boost::filesystem::path;\nusing boost::function;\nusing boost::locale::translate;\nusing boost::numeric_cast;\nusing boost::shared_ptr;\n\nusing std::runtime_error;\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\nnamespace comet {\n\ntemplate<> struct comtype<IDropTarget>\n{\n    static const IID& uuid() throw() { return IID_IDropTarget; }\n    typedef IUnknown base;\n};\n\n} // namespace comet\n\nnamespace swish {\nnamespace remote_folder {\n\nnamespace {\n\n    bool is_single_link(com_ptr<IShellItemArray> selection)\n    {\n        if (selection->size() != 1)\n        {\n            return false;\n        }\n        else\n        {\n            com_ptr<IParentAndItem> parent_and_item = com_cast(selection->at(0));\n            if (!parent_and_item)\n            {\n                return false;\n            }\n\n            return remote_itemid_view(parent_and_item->item_pidl()).is_link();\n        }\n    }\n\n    bool are_normal_files(com_ptr<IShellItemArray> selection)\n    {\n        if (selection->size() < 1U)\n            return false;\n\n        // TODO: fix comet enum_iterator so == is defined.  == is OK with\n        // read-once iteration (see istream_iterator for example).  Then we can\n        // use ranges\n        comet::wrap_t<IShellItemArray>::iterator_type it = selection->begin();\n        comet::wrap_t<IShellItemArray>::iterator_type end = selection->end();\n        while(it++ != end)\n        {\n            com_ptr<IShellItem> item = *it;\n            com_ptr<IParentAndItem> parent_and_item = com_cast(item);\n            if (!parent_and_item)\n            {\n                return false;\n            }\n\n            cpidl_t item_pidl = parent_and_item->item_pidl();\n            if (remote_itemid_view(item_pidl).is_link() ||\n                remote_itemid_view(item_pidl).is_folder())\n            {\n                return false;\n            }\n        }\n\n        // FIXME: failure to be a folder or a link does not mean you're a\n        // normal file.\n        return true;\n    }\n\n    const UINT MENU_OFFSET_OPEN = 0;\n\n}\n\ncontext_menu_callback::context_menu_callback(\n    my_provider_factory provider_factory,\n    my_consumer_factory consumer_factory)\n    : m_provider_factory(provider_factory),\n    m_consumer_factory(consumer_factory) {}\n\n/**\n * @todo  Take account of allowed changes flags.\n */\nbool context_menu_callback::merge_context_menu(\n    HWND hwnd_view, com_ptr<IDataObject> selection_data_object, HMENU hmenu,\n    UINT first_item_index, UINT& minimum_id, UINT maximum_id,\n    UINT allowed_changes_flags)\n{\n    com_ptr<IShellItemArray> selection =\n        shell_item_array_from_data_object(selection_data_object);\n\n    if (is_single_link(selection))\n    {\n        BOOL success = ::InsertMenuW(\n            hmenu, first_item_index, MF_BYPOSITION,\n            minimum_id + MENU_OFFSET_OPEN,\n            wstring(translate(L\"Open &link\")).c_str());\n        if (!success)\n            BOOST_THROW_EXCEPTION(\n                enable_error_info(last_error()) <<\n                errinfo_api_function(\"InsertMenuW\"));\n\n        // It's not worth aborting menu creation just because we can't\n        // set default so ignore return val\n        ::SetMenuDefaultItem(hmenu, minimum_id + MENU_OFFSET_OPEN, FALSE);\n\n        ++minimum_id;\n\n        // Return false so that Explorer won't add its own 'open'\n        // and 'explore' menu items.\n        // TODO: Find out what else we lose.\n        return false;\n    }\n    else if (are_normal_files(selection))\n    {\n        BOOL success = ::InsertMenuW(\n            hmenu, first_item_index, MF_BYPOSITION,\n            minimum_id + MENU_OFFSET_OPEN,\n            wstring(translate(L\"&Open\")).c_str());\n        if (!success)\n            BOOST_THROW_EXCEPTION(\n                enable_error_info(last_error()) <<\n                errinfo_api_function(\"InsertMenuW\"));\n\n        // It's not worth aborting menu creation just because we can't\n        // set default so ignore return val\n        ::SetMenuDefaultItem(hmenu, minimum_id + MENU_OFFSET_OPEN, FALSE);\n\n        ++minimum_id;\n\n        // Return false so that Explorer won't add its own 'open'\n        // and 'explore' menu items.\n        // TODO: Find out what else we lose.\n        return false;\n    }\n    else\n    {\n        return true; // Let Explorer provide standard verbs\n    }\n}\n\nvoid context_menu_callback::verb(\n    HWND hwnd_view, com_ptr<IDataObject> /*selection*/, UINT command_id_offset,\n    wstring& verb_out)\n{\n    if (command_id_offset != MENU_OFFSET_OPEN)\n        BOOST_THROW_EXCEPTION(\n            com_error(\"Unrecognised menu command ID\", E_FAIL));\n\n    verb_out = L\"open\";\n}\n\nvoid context_menu_callback::verb(\n    HWND hwnd_view, com_ptr<IDataObject> /*selection*/, UINT command_id_offset,\n    string& verb_out)\n{\n    if (command_id_offset != MENU_OFFSET_OPEN)\n        BOOST_THROW_EXCEPTION(\n            com_error(\"Unrecognised menu command ID\", E_FAIL));\n\n    verb_out = \"open\";\n}\n\nnamespace {\n\n    template<typename ProviderFactory, typename ConsumerFactory>\n    bool do_invoke_command(\n        ProviderFactory provider_factory,\n        ConsumerFactory consumer_factory,\n        HWND hwnd_view, com_ptr<IDataObject> selection_data_object,\n        UINT item_offset, const wstring& /*arguments*/, int window_mode)\n    {\n        com_ptr<IShellItemArray> selection =\n            shell_item_array_from_data_object(selection_data_object);\n\n        if (item_offset == DFM_CMD_DELETE)\n        {\n            commands::Delete deletion_command(\n                provider_factory, consumer_factory);\n            deletion_command(hwnd_view, selection);\n            return true;\n        }\n        else if (item_offset == MENU_OFFSET_OPEN && is_single_link(selection))\n        {\n            try\n            {\n                com_ptr<IShellItem> item = selection->at(0);\n                com_ptr<IParentAndItem> folder_and_pidl = try_cast(item);\n                apidl_t item_pidl = folder_and_pidl->absolute_item_pidl();\n\n                // Create SFTP Consumer for this HWNDs lifetime\n                com_ptr<ISftpConsumer> consumer = consumer_factory(hwnd_view);\n\n                shared_ptr<sftp_provider> provider = provider_from_pidl(\n                    folder_and_pidl->parent_pidl(), consumer,\n                    translate(\"Name of a running task\", \"Resolving link\"));\n\n                CSftpDirectory directory(folder_and_pidl->parent_pidl(), provider);\n\n                apidl_t target = directory.ResolveLink(folder_and_pidl->item_pidl());\n\n                SHELLEXECUTEINFO sei = SHELLEXECUTEINFO();\n                sei.cbSize = sizeof(SHELLEXECUTEINFO);\n                sei.fMask = SEE_MASK_IDLIST;\n                sei.hwnd = hwnd_view;\n                sei.nShow = window_mode;\n                sei.lpIDList =\n                    const_cast<void*>(\n                        reinterpret_cast<const void*>(target.get()));\n                sei.lpVerb = L\"open\";\n                if (!::ShellExecuteEx(&sei))\n                    BOOST_THROW_EXCEPTION(\n                        enable_error_info(last_error()) <<\n                        errinfo_api_function(\"ShellExecuteEx\"));\n            }\n            catch (...)\n            {\n                announce_last_exception(\n                    hwnd_view, translate(L\"Unable to open the link\"),\n                    translate(L\"You might not have permission.\"));\n                throw;\n            }\n\n            return true; // Even if the above fails, we don't want to invoke\n            // any default action provided by Explorer\n        }\n        // TODO: handle links so that links to files are resolved and the\n        // targets are opened\n        //\n        // FIXME: what if the selection contains a mix of items?\n        else if (item_offset == MENU_OFFSET_OPEN && are_normal_files(selection))\n        {\n            try\n            {\n                com_ptr<IShellItem> item = selection->at(0);\n                com_ptr<IParentAndItem> folder_and_pidl = try_cast(item);\n                apidl_t item_pidl = folder_and_pidl->absolute_item_pidl();\n\n                // XXX: We're only opening the first file even though we copy all\n                // of them.  Is this what we want?\n\n                vector<wchar_t> system_temp_dir(MAX_PATH + 1, L'\\0');\n                DWORD rc = ::GetTempPathW(\n                    numeric_cast<DWORD>(system_temp_dir.size()),\n                    &system_temp_dir[0]);\n                if (rc < 1)\n                    BOOST_THROW_EXCEPTION(last_error());\n\n                // We're using drag-and-drop to do the copy, so we don't\n                // want collisions to be possible as they will throw up\n                // confirmation dialogues.  We create a unique directory to\n                // copy the file into by generating a GUID.  If this already\n                // exists, the universe may be close to collapse in which case\n                // we should probably find our loved ones and stop worrying\n                // about file transfers.\n                path temp_dir = &system_temp_dir[0];\n                temp_dir /= uuid_t::create().w_str();\n                if (!create_directory(temp_dir))\n                    BOOST_THROW_EXCEPTION(\n                        runtime_error(\n                            \"Temporary download location already exists\"));\n\n                com_ptr<IDropTarget> drop_target =\n                    ui_object_of_item<IDropTarget>(\n                        pidl_from_parsing_name(temp_dir.wstring()).get());\n\n                POINTL pt = { 0, 0 };\n                DWORD dwEffect = DROPEFFECT_COPY;\n                HRESULT hr = drop_target->DragEnter(\n                    selection_data_object.get(), MK_LBUTTON, pt, &dwEffect);\n                if (FAILED(hr))\n                    BOOST_THROW_EXCEPTION(\n                        com_error_from_interface(drop_target, hr));\n\n                dwEffect &= DROPEFFECT_COPY;\n                if (dwEffect)\n                {\n                    hr = drop_target->Drop(\n                        selection_data_object.get(), MK_LBUTTON, pt, &dwEffect);\n                    if (FAILED(hr))\n                        BOOST_THROW_EXCEPTION(\n                            com_error_from_interface(drop_target, hr));\n                }\n                else\n                {\n                    hr = drop_target->DragLeave();\n                    if (FAILED(hr))\n                        BOOST_THROW_EXCEPTION(\n                            com_error_from_interface(drop_target, hr));\n                    BOOST_THROW_EXCEPTION(\n                        runtime_error(\n                            \"Permission refused to copy remote file to \"\n                            \"temporary location\"));\n                }\n\n                path target = temp_dir;\n                target /= remote_itemid_view(folder_and_pidl->item_pidl()).filename();\n                wstring target_windows_path = target.wstring();\n\n                // Before opening the file we make it read-only to discourage\n                // users from making changes and saving it to the temporary\n                // location - they're likely to forget and then lose their data.\n                // This should force most apps to invoke Save As.\n                ::SetFileAttributes(\n                    target_windows_path.c_str(),\n                    ::GetFileAttributes(target_windows_path.c_str())\n                        | FILE_ATTRIBUTE_READONLY);\n                // It isn't worth aborting the operation if this fails so we\n                // ignore any error\n\n                SHELLEXECUTEINFO sei = SHELLEXECUTEINFO();\n                sei.cbSize = sizeof(SHELLEXECUTEINFO);\n                //sei.fMask = SEE_\n                sei.hwnd = hwnd_view;\n                sei.nShow = window_mode;\n                sei.lpFile = target_windows_path.c_str();\n                sei.lpVerb = L\"open\";\n                if (!::ShellExecuteEx(&sei))\n                    BOOST_THROW_EXCEPTION(\n                        enable_error_info(last_error()) <<\n                        errinfo_api_function(\"ShellExecuteEx\"));\n            }\n            catch (...)\n            {\n                announce_last_exception(\n                    hwnd_view, translate(L\"Unable to open the file\"),\n                    translate(L\"You might not have permission.\"));\n                throw;\n            }\n\n            return true; // Even if the above fails, we don't want to invoke\n            // any default action provided by Explorer\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n}\n\nbool context_menu_callback::invoke_command(\n    HWND hwnd_view, com_ptr<IDataObject> selection, UINT item_offset,\n    const wstring& arguments)\n{\n    return do_invoke_command(\n        m_provider_factory, m_consumer_factory, hwnd_view, selection,\n        item_offset, arguments, SW_NORMAL);\n}\n\n/**\n * @todo  Take account of the behaviour flags.\n */\nbool context_menu_callback::invoke_command(\n    HWND hwnd_view, com_ptr<IDataObject> selection, UINT item_offset,\n    const wstring& arguments, DWORD /*behaviour_flags*/, UINT /*minimum_id*/,\n    UINT /*maximum_id*/, const CMINVOKECOMMANDINFO& invocation_details,\n    com_ptr<IUnknown> /*context_menu_site*/)\n{\n    return do_invoke_command(\n        m_provider_factory, m_consumer_factory, hwnd_view, selection,\n        item_offset, arguments, invocation_details.nShow);\n}\n\nbool context_menu_callback::default_menu_item(\n    HWND /*hwnd_view*/, com_ptr<IDataObject> selection_data_object,\n    UINT& default_command_id)\n{\n    com_ptr<IShellItemArray> selection =\n        shell_item_array_from_data_object(selection_data_object);\n\n    if (is_single_link(selection))\n    {\n        default_command_id = MENU_OFFSET_OPEN;\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/remote_folder/context_menu_callback.hpp",
    "content": "/**\n    @file\n\n    RemoteFolder context menu implementation (basically that what it does).\n\n    @if license\n\n    Copyright (C) 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_CONTEXT_MENU_CALLBACK_HPP\n#define SWISH_REMOTE_FOLDER_CONTEXT_MENU_CALLBACK_HPP\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/nse/default_context_menu_callback.hpp\"\n                                               // default_context_menu_callback\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/function.hpp>\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\n#include <ObjIdl.h> // IDataObject\n#include <ShObjIdl.h> // CMINVOKECOMMANDINFO\n#include <Windows.h> // HWND, HMENU\n\nnamespace swish {\nnamespace remote_folder {\n\nclass context_menu_callback : public swish::nse::default_context_menu_callback\n{\n    typedef boost::function<\n        boost::shared_ptr<swish::provider::sftp_provider>(\n        comet::com_ptr<ISftpConsumer>, const std::string&)\n    > my_provider_factory;\n\n    typedef boost::function<comet::com_ptr<ISftpConsumer>(HWND)>\n        my_consumer_factory;\n\npublic:\n    context_menu_callback(\n        my_provider_factory provider_factory,\n        my_consumer_factory consumer_factory);\n\nprivate:\n    bool merge_context_menu(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, HMENU hmenu,\n        UINT first_item_index, UINT& minimum_id, UINT maximum_id,\n        UINT allowed_changes_flags);\n\n    void verb(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT command_id_offset, std::wstring& verb_out);\n\n    void verb(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT command_id_offset, std::string& verb_out);\n\n    bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, UINT item_offset,\n        const std::wstring& arguments);\n\n    bool invoke_command(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection, UINT item_offset,\n        const std::wstring& arguments, DWORD behaviour_flags, UINT minimum_id,\n        UINT maximum_id, const CMINVOKECOMMANDINFO& invocation_details,\n        comet::com_ptr<IUnknown> context_menu_site);\n\n    bool default_menu_item(\n        HWND hwnd_view, comet::com_ptr<IDataObject> selection,\n        UINT& default_command_id);\n\n    my_provider_factory m_provider_factory;\n    my_consumer_factory m_consumer_factory;\n};\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/filemode.c",
    "content": "/* filemode.c -- make a string describing file modes\n   Copyright (C) 1985, 1990, 1993, 1998-2000, 2004 Free Software Foundation, Inc.\n   Copyright (C) 2006, 2009  Alexander Lamaison <awl03 (at) doc.ic.ac.uk>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2, or (at your option)\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program; if not, write to the Free Software Foundation,\n   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */\n\n/* Removed by Alexander Lamaison for swish project\n\n#if HAVE_CONFIG_H\n# include <config.h>\n#endif\n\n#include <sys/types.h>\n#include <sys/stat.h>\n\n#include \"filemode.h\"\n#include \"stat-macros.h\"\n\nRemoved 2006.08.20 */\n\n#include \"filemode.h\"\n\n/* Set the 's' and 't' flags in file attributes string CHARS,\n   according to the file mode BITS.  */\n\nstatic void\nsetst (mode_t bits, char *chars)\n{\n  if (bits & S_ISUID)\n    {\n      if (chars[3] != 'x')\n    /* Set-uid, but not executable by owner.  */\n    chars[3] = 'S';\n      else\n    chars[3] = 's';\n    }\n  if (bits & S_ISGID)\n    {\n      if (chars[6] != 'x')\n    /* Set-gid, but not executable by group.  */\n    chars[6] = 'S';\n      else\n    chars[6] = 's';\n    }\n  if (bits & S_ISVTX)\n    {\n      if (chars[9] != 'x')\n    /* Sticky, but not executable by others.  */\n    chars[9] = 'T';\n      else\n    chars[9] = 't';\n    }\n}\n\n/* Return a character indicating the type of file described by\n   file mode BITS:\n   'd' for directories\n   'D' for doors\n   'b' for block special files\n   'c' for character special files\n   'n' for network special files\n   'm' for multiplexor files\n   'M' for an off-line (regular) file\n   'l' for symbolic links\n   's' for sockets\n   'p' for fifos\n   'C' for contigous data files\n   'w' for BSD whiteout - Added by Alexander Lamaison for Swish project\n   'B' for multiplexor (block) - Added by Alexander Lamaison for Swish project\n   'x' for Xenix named files - Added by Alexander Lamaison for Swish project\n   '-' for regular files\n   '?' for any other file type.  */\n\nstatic char\nftypelet (mode_t bits)\n{\n  if (S_ISBLK (bits))\n    return 'b';\n  if (S_ISCHR (bits))\n    return 'c';\n  if (S_ISDIR (bits))\n    return 'd';\n  if (S_ISREG (bits))\n    return '-';\n  if (S_ISFIFO (bits))\n    return 'p';\n  if (S_ISLNK (bits))\n    return 'l';\n  if (S_ISSOCK (bits))\n    return 's';\n  if (S_ISMPC (bits))\n    return 'm';\n  if (S_ISNWK (bits))\n    return 'n';\n  if (S_ISDOOR (bits))\n    return 'D';\n  if (S_ISCTG (bits))\n    return 'C';\n/* Added by Alexander Lamaison for Swish project */\n  if (S_ISWHT (bits))\n    return 'w';\n  if (S_ISMPB (bits))\n    return 'B';\n  if (S_ISNAM (bits))\n    return 'x';\n/* Added 2006.08.20 */\n  /* The following two tests are for Cray DMF (Data Migration\n     Facility), which is a HSM file system.  A migrated file has a\n     `st_dm_mode' that is different from the normal `st_mode', so any\n     tests for migrated files should use the former.  */\n\n  if (S_ISOFD (bits))\n    /* off line, with data  */\n    return 'M';\n  /* off line, with no data  */\n  if (S_ISOFL (bits))\n    return 'M';\n  return '?';\n}\n\n/* Like filemodestring, but only the relevant part of the `struct stat'\n   is given as an argument.  */\n\nvoid\nmode_string (mode_t mode, char *str)\n{\n  str[0] = ftypelet (mode);\n  str[1] = mode & S_IRUSR ? 'r' : '-';\n  str[2] = mode & S_IWUSR ? 'w' : '-';\n  str[3] = mode & S_IXUSR ? 'x' : '-';\n  str[4] = mode & S_IRGRP ? 'r' : '-';\n  str[5] = mode & S_IWGRP ? 'w' : '-';\n  str[6] = mode & S_IXGRP ? 'x' : '-';\n  str[7] = mode & S_IROTH ? 'r' : '-';\n  str[8] = mode & S_IWOTH ? 'w' : '-';\n  str[9] = mode & S_IXOTH ? 'x' : '-';\n  setst (mode, str);\n}\n\n/* filemodestring - fill in string STR with an ls-style ASCII\n   representation of the st_mode field of file stats block STATP.\n   10 characters are stored in STR; no terminating null is added.\n   The characters stored in STR are:\n\n   0    File type.  'd' for directory, 'c' for character\n    special, 'b' for block special, 'm' for multiplex,\n    'l' for symbolic link, 's' for socket, 'p' for fifo,\n    '-' for regular, '?' for any other file type\n\n   1    'r' if the owner may read, '-' otherwise.\n\n   2    'w' if the owner may write, '-' otherwise.\n\n   3    'x' if the owner may execute, 's' if the file is\n    set-user-id, '-' otherwise.\n    'S' if the file is set-user-id, but the execute\n    bit isn't set.\n\n   4    'r' if group members may read, '-' otherwise.\n\n   5    'w' if group members may write, '-' otherwise.\n\n   6    'x' if group members may execute, 's' if the file is\n    set-group-id, '-' otherwise.\n    'S' if it is set-group-id but not executable.\n\n   7    'r' if any user may read, '-' otherwise.\n\n   8    'w' if any user may write, '-' otherwise.\n\n   9    'x' if any user may execute, 't' if the file is \"sticky\"\n    (will be retained in swap space after execution), '-'\n    otherwise.\n    'T' if the file is sticky but not executable.  */\n\n/* Removed by Alexander Lamaison for swish project\n\nvoid\nfilemodestring (struct stat *statp, char *str)\n{\n  mode_string (statp->st_mode, str);\n}\n\nRemoved 2006.08.20 */\n"
  },
  {
    "path": "swish/remote_folder/filemode.h",
    "content": "/* Make a string describing file modes.\n\n   Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc.\n   Copyright (C) 2006, 2009  Alexander Lamaison <awl03 (at) doc.ic.ac.uk>\n\n   This program is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; either version 2, or (at your option)\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program; if not, write to the Free Software Foundation,\n   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.*/\n\n#ifndef FILEMODE_H_\n\n#define S_IFMT        0170000 /* type of file */\n#define S_IFSOCK    0140000 /* socket                        's' */\n#define S_IFLNK        0120000 /* symbolic link                'l' */\n#define S_IFREG        0100000 /* regular                        '-' */\n#define S_IFBLK        0060000 /* block special                'b' */\n#define S_IFDIR        0040000 /* directory                    'd' */\n#define S_IFCHR        0020000 /* character special            'c' */\n#define S_IFIFO        0010000 /* fifo                            'p' */\n#define S_IFDOOR    0150000    /* Solaris door                 'D' */        \n#define S_IFNAM        0050000    /* XENIX named file                'x' */\n#define S_IFMPB        0070000    /* multiplexed block special     'B'    */\n#define S_IFMPC        0030000    /* multiplexed char special        'm' */\n#define S_IFWHT        0160000 /* BSD whiteout                    'w' */\n#define S_IFNWK        110000    /* HP-UX network special        'n' */ \n\n/* TODO: Add support for other obscure types\n *   S_IFCNT                Contiguous file                    'C'\n *   S_IFSHAD 130000        Solaris shadow inode for ACL (not seen by userspace)\n *   S_IFEVC                UNOS eventcount\n *   S_ISOFD                Cray DMF: off line with data    'M'\n *   S_ISOFL                Cray DMF: off line with no data 'M'\n */\n\n#define S_ISUID        0004000 /* set user id on execution */\n#define S_ISGID        0002000 /* set group id on execution */\n#define S_ISVTX        0001000 /* save swapped text even after use */\n\n#define S_IRWXU        (S_IRUSR | S_IWUSR | S_IXUSR)\n#define S_IRUSR        0000400 /* read permission, owner */\n#define S_IWUSR        0000200 /* write permission, owner */\n#define S_IXUSR        0000100 /* execute/search permission, owner */\n#define S_IRWXG        (S_IRGRP | S_IWGRP | S_IXGRP)\n#define S_IRGRP        0000040 /* read permission, group */\n#define S_IWGRP        0000020 /* write permission, grougroup */\n#define S_IXGRP        0000010 /* execute/search permission, group */\n#define S_IRWXO        (S_IROTH | S_IWOTH | S_IXOTH)\n#define S_IROTH        0000004 /* read permission, other */\n#define S_IWOTH        0000002 /* write permission, other */\n#define S_IXOTH        0000001 /* execute/search permission, other */\n\n#define S_ISLNK(m)    (((m) & S_IFMT) == S_IFLNK)\n#define S_ISREG(m)    (((m) & S_IFMT) == S_IFREG)\n#define S_ISDIR(m)    (((m) & S_IFMT) == S_IFDIR)\n#define S_ISCHR(m)    (((m) & S_IFMT) == S_IFCHR)\n#define S_ISBLK(m)    (((m) & S_IFMT) == S_IFBLK)\n#define S_ISFIFO(m)    (((m) & S_IFMT) == S_IFIFO)\n#define S_ISSOCK(m)    (((m) & S_IFMT) == S_IFSOCK)\n#define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR) /* Solaris 2.5 and up */\n#define S_ISNAM(m)  (((m) & S_IFMT) == S_IFNAM)  /* Xenix */\n#define S_ISMPB(m)  (((m) & S_IFMT) == S_IFMPB)  /* V7 */\n#define S_ISMPC(m)  (((m) & S_IFMT) == S_IFMPC)  /* V7 */\n#define S_ISWHT(m)  (((m) & S_IFMT) == S_IFWHT)  /* BSD whiteout */\n#define S_ISNWK(m)  (((m) & S_IFMT) == S_IFNWK)  /* HP/UX */\n/* contiguous */\n# ifndef S_ISCTG\n#  define S_ISCTG(m) ((void)(m), 0)\n# endif\n/* Cray DMF (data migration facility): offline, with data  */\n# ifndef S_ISOFD\n#  define S_ISOFD(m) ((void)(m), 0)\n# endif\n/* Cray DMF (data migration facility): offline, with no data  */\n# ifndef S_ISOFL\n#  define S_ISOFL(m) ((void)(m), 0)\n# endif\n\n/* typedef u32 mode_t; Changed by Alex Lamaison 2009.02.01 */\ntypedef unsigned long mode_t;\n\n#ifdef __cplusplus\nextern \"C\" void mode_string (mode_t mode, char *str);\n#else\nextern void mode_string (mode_t mode, char *str);\n#endif\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/pidl_connection.cpp",
    "content": "/**\n    @file\n\n    Relates PIDLs to SFTP connections.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"pidl_connection.hpp\"\n\n#include \"swish/connection/session_manager.hpp\"\n#include \"swish/host_folder/host_pidl.hpp\" // find_host_itemid, host_itemid_view\n#include \"swish/provider/Provider.hpp\" // CProvider\n\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\nusing swish::connection::connection_spec;\nusing swish::connection::session_manager;\nusing swish::host_folder::find_host_itemid;\nusing swish::host_folder::host_itemid_view;\nusing swish::provider::CProvider;\nusing swish::provider::sftp_provider;\n\nusing washer::shell::pidl::apidl_t;\n\nusing comet::com_ptr;\n\nusing boost::shared_ptr;\n\nusing std::string;\nusing std::wstring;\n\n\nnamespace swish {\nnamespace remote_folder {\n\nnamespace {\n\n    void params_from_pidl(\n        const apidl_t& pidl, wstring& user, wstring& host, int& port)\n    {\n        // Find HOSTPIDL part of this folder's absolute pidl to extract server\n        // info\n        host_itemid_view host_itemid(*find_host_itemid(pidl));\n        assert(host_itemid.valid());\n\n        user = host_itemid.user();\n        host = host_itemid.host();\n        port = host_itemid.port();\n        assert(!user.empty());\n        assert(!host.empty());\n    }\n\n}\n\nconnection_spec connection_from_pidl(const apidl_t& pidl)\n{\n    // Extract connection info from PIDL\n    wstring user, host, path;\n    int port;\n    params_from_pidl(pidl, user, host, port);\n\n    return connection_spec(host, user, port);\n}\n\nshared_ptr<sftp_provider> provider_from_pidl(\n    const apidl_t& pidl, com_ptr<ISftpConsumer> consumer,\n    const string& task_name)\n{\n    connection_spec specification = connection_from_pidl(pidl);\n\n    return shared_ptr<CProvider>(\n        new CProvider(\n            session_manager().reserve_session(\n                specification, consumer, task_name)));\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/remote_folder/pidl_connection.hpp",
    "content": "/**\n    @file\n\n    Relates PIDLs to SFTP connections.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_PIDL_CONNECTION_HPP\n#define SWISH_REMOTE_FOLDER_PIDL_CONNECTION_HPP\n#pragma once\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h>\n\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\nnamespace swish {\nnamespace remote_folder {\n\n/**\n * Converts a host PIDL into a connection specification.\n */\nswish::connection::connection_spec connection_from_pidl(\n    const washer::shell::pidl::apidl_t& pidl);\n\n/**\n * Creates lazy-connecting provider primed to connect for given PIDL.\n *\n * The session will be created from the information stored in this\n * folder's PIDL, @a pidl, if connection is required.\n */\nboost::shared_ptr<swish::provider::sftp_provider> provider_from_pidl(\n    const washer::shell::pidl::apidl_t& pidl,\n    comet::com_ptr<ISftpConsumer> consumer,\n    const std::string& task_name);\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/properties.cpp",
    "content": "/**\n    @file\n\n    Host folder property columns.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"properties.hpp\"\n#include \"Mode.h\"          // Unix-style permissions\n\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include <boost/assign.hpp> // map_list_of\n\n#include <boost/exception/info.hpp>\n#include <boost/exception/errinfo_api_function.hpp> // errinfo_api_function\n#include <boost/function.hpp> // function\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <comet/error.h> // com_error\n#include <comet/util.h> // autocoinit\n\n#include <cassert> // assert\n#include <map>\n\n#include <initguid.h> // Make DEFINE_PROPERTYKEY() actually define a key\n#include <Propkey.h> // PKEY_ *\n#include <ShellAPI.h> // SHGetFileInfo\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::property_key;\n\nusing comet::auto_coinit;\nusing comet::variant_t;\nusing comet::com_error;\n\nusing boost::assign::map_list_of;\nusing boost::enable_error_info;\nusing boost::errinfo_api_function;\nusing boost::locale::translate;\n\nusing std::map;\nusing std::wstring;\n\nnamespace swish {\nnamespace remote_folder {\n\nDEFINE_PROPERTYKEY(\n    PKEY_Group, \\\n    0xb816a851, 0x5022, 0x11dc, 0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, \\\n    0x4f, 0x85, PID_FIRST_USABLE);\nDEFINE_PROPERTYKEY(\n    PKEY_Permissions, \\\n    0xb816a851, 0x5022, 0x11dc, 0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, \\\n    0x4f, 0x85, PID_FIRST_USABLE + 1);\nDEFINE_PROPERTYKEY(\n    PKEY_OwnerId, \\\n    0xb816a851, 0x5022, 0x11dc, 0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, \\\n    0x4f, 0x85, PID_FIRST_USABLE + 2);\nDEFINE_PROPERTYKEY(\n    PKEY_GroupId, \\\n    0xb816a851, 0x5022, 0x11dc, 0x91, 0x53, 0x00, 0x90, 0xf5, 0x28, \\\n    0x4f, 0x85, PID_FIRST_USABLE + 3);\n\nnamespace {\n\n    /**\n     * Find the Windows friendly type name for the file given as a PIDL.\n     *\n     * This type name is the one used in Explorer details.  For example,\n     * something.txt is given the type name \"Text Document\" and a directory\n     * is called a \"File Folder\" regardless of its name.\n     */\n    std::wstring lookup_friendly_typename(const cpidl_t& pidl)\n    {\n        // Must call CoInitialize before SHGetFileInfo, otherwise it returns\n        // the wrong typename (something like \"TXT file\" instead of\n        // \"Text Document\")\n        auto_coinit com_scope;\n\n        DWORD dwAttributes =\n            (remote_itemid_view(pidl).is_folder()) ?\n                FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;\n\n        UINT uInfoFlags = SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME;\n\n        SHFILEINFOW shfi = SHFILEINFOW();\n        if (!::SHGetFileInfoW(\n            remote_itemid_view(pidl).filename().c_str(), dwAttributes,\n            &shfi, sizeof(shfi), uInfoFlags))\n\t\t{\n\t\t\tBOOST_THROW_EXCEPTION(\n\t\t\t\tenable_error_info(com_error(E_FAIL)) <<\n\t\t\t\terrinfo_api_function(\"SHGetFileInfoW\"));\n\t\t}\n\n        return shfi.szTypeName;\n    }\n\n    class unknown_property_error : public std::runtime_error\n    {\n    public:\n        unknown_property_error() : std::runtime_error(\"Unknown property\") {}\n    };\n\n    typedef map<\n        property_key,\n        boost::function<variant_t (const cpidl_t& pidl)> >\n        remote_property_map;\n\n    variant_t label_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).filename(); }\n\n    variant_t owner_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).owner(); }\n\n    variant_t group_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).group(); }\n\n    variant_t owner_id_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).owner_id(); }\n\n    variant_t group_id_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).group_id(); }\n\n    variant_t size_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).size(); }\n\n    variant_t modified_date_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).date_modified(); }\n\n    variant_t accessed_date_getter(const cpidl_t& pidl)\n    { return remote_itemid_view(pidl).date_accessed(); }\n\n    variant_t type_getter(const cpidl_t& pidl)\n    { return lookup_friendly_typename(pidl); }\n\n    variant_t permissions_getter(const cpidl_t& pidl)\n    {\n        DWORD dwPerms = remote_itemid_view(pidl).permissions();\n        return mode::Mode(dwPerms).toString();\n    }\n\n    const remote_property_map remote_property_getters = map_list_of\n        (PKEY_ItemNameDisplay, label_getter) // Display name (Label)\n        (PKEY_FileOwner, owner_getter) // Owner\n        (PKEY_Group, group_getter) // Group\n        (PKEY_OwnerId, owner_id_getter) // Owner ID (UID)\n        (PKEY_GroupId, group_id_getter) // Group ID (GID)\n        (PKEY_Permissions, permissions_getter) // File permissions: drwxr-xr-x\n        (PKEY_Size, size_getter) // File size in bytes\n        (PKEY_DateModified, modified_date_getter) // Last modified date\n        (PKEY_DateAccessed, accessed_date_getter) // Last accessed date\n        (PKEY_ItemTypeText, type_getter); // Friendly type name\n}\n\n/**\n * Get the requested property for a file based on its PIDL.\n *\n * Many of these will be standard system properties but some are custom\n * to Swish if an appropriate one did not already exist.\n */\nvariant_t property_from_pidl(const cpidl_t& pidl, const property_key& key)\n{\n    remote_property_map::const_iterator pos = remote_property_getters.find(key);\n    if (pos == remote_property_getters.end())\n        BOOST_THROW_EXCEPTION(unknown_property_error());\n\n    return (pos->second)(pidl.get());\n}\n\n/**\n * Compare two PIDLs by one of their properties.\n *\n * @param left   First PIDL in the comparison.\n * @param right  Second PIDL in the comparison.\n * @param key    Property on which to compare the two PIDLs.\n *\n * @retval -1 if left < right for chosen property.\n * @retval  0 if left == right for chosen property.\n * @retval  1 if left > right for chosen property.\n */\nint compare_pidls_by_property(\n    const cpidl_t& left, const cpidl_t& right, const property_key& key)\n{\n    if (property_from_pidl(left, key) == property_from_pidl(right, key))\n        return 0;\n    else if (property_from_pidl(left, key) < property_from_pidl(right, key))\n        return -1;\n\n    assert(property_from_pidl(left, key) > property_from_pidl(right, key));\n    return 1;\n}\n\n}} // namespace swish::remote_folder\n"
  },
  {
    "path": "swish/remote_folder/properties.hpp",
    "content": "/**\n    @file\n\n    Properties available for items in a host folder.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_PROPERTIES_HPP\n#define SWISH_REMOTE_FOLDER_PROPERTIES_HPP\n#pragma once\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <comet/variant.h> // variant_t\n\n#include <WTypes.h> // PROPERTYKEY\n\nnamespace swish {\nnamespace remote_folder {\n\ncomet::variant_t property_from_pidl(\n    const washer::shell::pidl::cpidl_t& pidl,\n    const washer::shell::property_key& key);\n\nint compare_pidls_by_property(\n    const washer::shell::pidl::cpidl_t& pidl_left,\n    const washer::shell::pidl::cpidl_t& pidl_right,\n    const washer::shell::property_key& key);\n\n/**\n * Custom properties (PKEYs) for Swish remote folder.\n *\n * Ideally, we want as few of these as possible.  If an appropriate\n * one already exists in propkey.h, that should be used instead.\n *\n * The Swish remote folder FMTID GUID which collects all the custom\n * properties together is @c {b816a851-5022-11dc-9153-0090f5284f85}.\n */\n// @{\nextern \"C\" const PROPERTYKEY PKEY_Group;\nextern \"C\" const PROPERTYKEY PKEY_Permissions;\nextern \"C\" const PROPERTYKEY PKEY_OwnerId;\nextern \"C\" const PROPERTYKEY PKEY_GroupId;\n// @}\n\n}} // namespace swish::host_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/remote_pidl.hpp",
    "content": "/**\n    @file\n\n    PIDL access particular to remote folder PIDLs.\n\n    @if license\n\n    Copyright (C) 2011, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_REMOTE_PIDL_HPP\n#define SWISH_REMOTE_FOLDER_REMOTE_PIDL_HPP\n#pragma once\n\n#include \"swish/remotelimits.h\"  // Text field limits\n\n#include <ssh/filesystem/path.hpp>\n\n#include <comet/datetime.h> // datetime_t\n\n#include <washer/shell/pidl.hpp> // pidl_t\n#include <washer/shell/pidl_iterator.hpp> // raw_pidl_iterator\n\n#include <boost/static_assert.hpp> // BOOST_STATIC_ASSERT\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#ifndef STRICT_TYPED_ITEMIDS\n#error Currently, swish requires strict PIDL types: define STRICT_TYPED_ITEMIDS\n#endif\n#include <ShTypes.h> // Raw PIDL types\n#include <StrAlign.h> // ua_wcslen, ua_wcscpy_s\n\n#include <cstring> // memset\n#include <exception>\n#include <string>\n#include <vector>\n\nnamespace swish {\nnamespace remote_folder {\n\nnamespace detail {\n\n#include <pshpack1.h>\n/**\n * Internal structure of the PIDLs representing items on the remote filesystem.\n */\nstruct remote_item_id\n{\n    USHORT cb;\n    DWORD dwFingerprint;\n    bool fIsFolder;\n    bool fIsLink;\n    WCHAR wszFilename[MAX_FILENAME_LENZ];\n    WCHAR wszOwner[MAX_USERNAME_LENZ];\n    WCHAR wszGroup[MAX_USERNAME_LENZ];\n    ULONG uUid;\n    ULONG uGid;\n    DWORD dwPermissions;\n    //WORD wPadding;\n    ULONGLONG uSize;\n    DATE dateModified;\n    DATE dateAccessed;\n\n    static const DWORD FINGERPRINT = 0x533aaf69;\n};\n#include <poppack.h>\n\nBOOST_STATIC_ASSERT((sizeof(remote_item_id) % sizeof(DWORD)) == 0);\n\ninline std::wstring copy_unaligned_string(const wchar_t __unaligned* source)\n{\n    std::vector<wchar_t> buffer(::ua_wcslen(source) + 1);\n    ::ua_wcscpy_s(&buffer[0], buffer.size(), source);\n    return std::wstring(&buffer[0]);\n}\n\n}\n\n/**\n * View internal fields of remote folder PIDLs.\n *\n * The viewer doesn't take ownership of the PIDL it's passed so it must remain\n * valid for the duration of the viewer's use.\n */\nclass remote_itemid_view\n{\npublic:\n    // We have to take the PIDL as a template, rather than that as a pidl_t\n    // as the PIDL passed might be a cpidl_t or an apidl_t.  In this case\n    // the pidl would be converted to a pidl_t using a temporary which is\n    // destroyed immediately after the constructor returns, thereby\n    // invalidating the PIDL we've stored a reference to.\n    template<typename T, typename Alloc>\n    explicit remote_itemid_view(\n        const washer::shell::pidl::basic_pidl<T, Alloc>& pidl)\n        : m_itemid(reinterpret_cast<const detail::remote_item_id*>(pidl.get()))\n    {}\n\n    explicit remote_itemid_view(PCUIDLIST_RELATIVE pidl)\n        : m_itemid(reinterpret_cast<const detail::remote_item_id*>(pidl)) {}\n\n    bool valid() const\n    {\n        if (m_itemid == NULL)\n            return false;\n\n        return ((m_itemid->cb == sizeof(detail::remote_item_id)) &&\n            (m_itemid->dwFingerprint == detail::remote_item_id::FINGERPRINT));\n    }\n\n    std::wstring filename() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return detail::copy_unaligned_string(m_itemid->wszFilename);\n    }\n\n    std::wstring owner() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return detail::copy_unaligned_string(m_itemid->wszOwner);\n    }\n\n    std::wstring group() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return detail::copy_unaligned_string(m_itemid->wszGroup);\n    }\n\n    ULONG owner_id() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->uUid;\n    }\n\n    ULONG group_id() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->uGid;\n    }\n\n    bool is_folder() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->fIsFolder;\n    }\n\n    bool is_link() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->fIsLink;\n    }\n\n    DWORD permissions() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->dwPermissions;\n    }\n\n    ULONGLONG size() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return m_itemid->uSize;\n    }\n\n    comet::datetime_t date_modified() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return comet::datetime_t(m_itemid->dateModified);\n    }\n\n    comet::datetime_t date_accessed() const\n    {\n        if (!valid())\n            BOOST_THROW_EXCEPTION(std::exception(\"PIDL is not a remote item\"));\n        return comet::datetime_t(m_itemid->dateAccessed);\n    }\n\nprivate:\n    const detail::remote_item_id __unaligned* m_itemid;\n};\n\nnamespace detail {\n\n#include <pshpack1.h>\n    struct remote_item_template\n    {\n        remote_item_id id;\n        SHITEMID terminator;\n    };\n#include <poppack.h>\n\n}\n\n/**\n * Create a new wrapped PIDL holding a remote_item_id with given parameters.\n *\n * @param filename       Name of file or directory on the remote filesystem.\n * @param is_folder      Is file a folder?\n * @param is_link        Is file a symlink?\n * @param owner          Name of file owner on remote system.\n * @param group          Name of file group on remote system.\n * @param owner_id       UID of file owner on remote system.\n * @param group_id       GID of file group on remote system.\n * @param permissions    The file's Unix permissions bits.\n * @param size           Size of file in bytes.\n * @param date_modified  Date that file was last modified.\n * @param date_accessed  Date that file was last accessed.\n */\ninline washer::shell::pidl::cpidl_t create_remote_itemid(\n    const std::wstring& filename, bool is_folder, bool is_link,\n    const std::wstring& owner, const std::wstring& group, ULONG owner_id,\n    ULONG group_id, DWORD permissions, ULONGLONG size,\n    const comet::datetime_t date_modified,\n    const comet::datetime_t date_accessed)\n{\n    // We create the item on the stack and then clone it into\n    // a CoTaskMemAllocated pidl when we return it as a cpidl_t\n    detail::remote_item_template item;\n    std::memset(&item, 0, sizeof(item));\n\n    item.id.cb = sizeof(item.id);\n    item.id.dwFingerprint = detail::remote_item_id::FINGERPRINT;\n\n#pragma warning(push)\n#pragma warning(disable:4996)\n    filename.copy(item.id.wszFilename, MAX_FILENAME_LENZ);\n    item.id.wszFilename[MAX_HOSTNAME_LENZ - 1] = wchar_t();\n\n    owner.copy(item.id.wszOwner, MAX_USERNAME_LENZ);\n    item.id.wszOwner[MAX_USERNAME_LENZ - 1] = wchar_t();\n\n    group.copy(item.id.wszGroup, MAX_USERNAME_LENZ);\n    item.id.wszGroup[MAX_USERNAME_LENZ - 1] = wchar_t();\n#pragma warning(pop)\n\n    item.id.fIsFolder = is_folder;\n    item.id.fIsLink = is_link;\n\n    item.id.uUid = owner_id;\n    item.id.uGid = group_id;\n    item.id.dwPermissions = permissions;\n    item.id.uSize = size;\n\n    item.id.dateModified = date_modified.get();\n    item.id.dateAccessed = date_accessed.get();\n\n    assert(item.terminator.cb == 0);\n\n    return washer::shell::pidl::cpidl_t(\n        reinterpret_cast<PCITEMID_CHILD>(&item));\n}\n\n/**\n * Return the relative path made by the items in this PIDL.\n * e.g.\n * - A child PIDL returns:     \"filename.ext\"\n * - A relative PIDL returns:  \"dir2/dir2/dir3/filename.ext\"\n * - An absolute PIDL returns: \"dir2/dir2/dir3/filename.ext\"\n */\ninline ssh::filesystem::path path_from_remote_pidl(\n    const washer::shell::pidl::pidl_t& remote_pidl)\n{\n    // Walk over RemoteItemIds and append each filename to form the path\n    washer::shell::pidl::raw_pidl_iterator it(remote_pidl.get());\n\n    ssh::filesystem::path path;\n    while (it != washer::shell::pidl::raw_pidl_iterator())\n    {\n        remote_itemid_view itemid(*it);\n        if (!itemid.valid())\n            break; // should never happen\n\n        path /= itemid.filename();\n\n        ++it;\n    }\n\n    return path;\n}\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/remote_folder/swish_pidl.hpp",
    "content": "/**\n    @file\n\n    Operations over complete Swish PIDLs.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_REMOTE_FOLDER_SWISH_PIDL_HPP\n#define SWISH_REMOTE_FOLDER_SWISH_PIDL_HPP\n#pragma once\n\n#include \"swish/host_folder/host_pidl.hpp\" // find_host_itemid, host_itemid_view\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include <washer/shell/pidl.hpp> // apidl_t\n#include <washer/shell/pidl_iterator.hpp> // raw_pidl_iterator\n\n#include <ssh/filesystem/path.hpp>\n\nnamespace swish {\nnamespace remote_folder {\n\n/**\n * Return the absolute path made by the items in this PIDL.\n * e.g. \"/path/dir2/dir2/dir3/filename.ext\"\n * The PIDL must contain a host itemid an after that can contain any number of\n * remote item ids, but doesn't have to.\n */\ninline ssh::filesystem::path absolute_path_from_swish_pidl(\n    const washer::shell::pidl::apidl_t& pidl)\n{\n    washer::shell::pidl::raw_pidl_iterator item_pos =\n        swish::host_folder::find_host_itemid(pidl);\n\n    ssh::filesystem::path path =\n        swish::host_folder::host_itemid_view(*item_pos).path();\n\n    if (++item_pos != washer::shell::pidl::raw_pidl_iterator())\n    {\n        if (remote_itemid_view(*item_pos).valid())\n        {\n            path /= path_from_remote_pidl(*item_pos);\n        }\n    }\n\n    return path;\n}\n\n}} // namespace swish::remote_folder\n\n#endif\n"
  },
  {
    "path": "swish/remotelimits.h",
    "content": "/*  Defines constant limits given that resources are not local and\n    therefore do not follow OS-defined constants\n\n    Copyright (C) 2007  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#ifndef REMOTELIMITS_H\n#define REMOTELIMITS_H\n\n#if _MSC_VER > 1000\n#pragma once\n#endif // _MSC_VER > 1000\n\n// String limits should not include the terminating NULL\n// TODO: Not sure if this is the case currently\n\n#define MAX_USERNAME_LEN          32\n#define MAX_USERNAME_LENZ         33\n\n// Under libc-5 the utmp and wtmp files only allow 8 chars for username\n#define MAX_USERNAME_LEN_UNIX      8\n// Under libc-6 this is increased to 32 characters\n#define MAX_USERNAME_LEN_LINUX    32\n// Apparently Win2k can have names up to 104 characters: \n// http://www.microsoft.com/technet/prodtechnol/windows2000serv/deploy/confeat/08w2kada.mspx\n#define MAX_USERNAME_LEN_WIN      20\n\n#define MAX_HOSTNAME_LEN         255 // http://en.wikipedia.org/wiki/Hostname\n#define MAX_HOSTNAME_LENZ        256\n\n#define MAX_PATH_LEN            1023\n#define MAX_PATH_LENZ           1024\n#define MAX_PATH_LEN_WIN         248 // 260 including filename\n#define MAX_PATH_LEN_LINUX      4096\n\n#define MAX_FILENAME_LEN         255 // Choosing lower val as Windows FAT is\n#define MAX_FILENAME_LENZ        256 // also limited to 255. Makes things easier\n#define MAX_FILENAME_LEN_WIN     256 \n#define MAX_FILENAME_LEN_LINUX   255\n\n#define MIN_PORT                   0\n#define MAX_PORT               65535\n#define MAX_PORT_STR_LEN           5 // length of '65535' as a string\n\n#define PROTOCOL_LEN               7 // length of 'sftp://' as a string\n\n// Complete connection description of the form:\n//     sftp://username@hostname:port/path\n#define MAX_CANONICAL_LEN \\\n    (PROTOCOL_LEN + MAX_USERNAME_LEN + MAX_HOSTNAME_LEN \\\n    + MAX_PATH_LEN + MAX_PORT_STR_LEN + 2)\n\n#define MAX_LABEL_LEN             30 // Arbitrary - chosen to be easy to display\n#define MAX_LABEL_LENZ            31 \n\n#define SFTP_DEFAULT_PORT         22\n\n#define MAX_LONGENTRY_LEN        127 // 128 should be long enough to fit any\n#define MAX_LONGENTRY_LENZ       128 // long entry (certainly what we need)\n\n#endif REMOTELIMITS_H\n"
  },
  {
    "path": "swish/shell/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  parent_and_item.hpp\n  shell.hpp\n  shell.cpp\n  shell_item.hpp\n  shell_item_array.hpp)\n\nadd_library(shell ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(shell\n  PUBLIC ${Boost_LIBRARIES} Washer::washer Comet::comet)\n"
  },
  {
    "path": "swish/shell/parent_and_item.hpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_SHELL_PARENT_AND_ITEM_HPP\n#define SWISH_SHELL_PARENT_AND_ITEM_HPP\n\n#include <washer/shell/pidl.hpp>\n\n#include <comet/ptr.h>\n\n#include <shobjidl.h>\n\nnamespace comet {\n\ntemplate<> struct comtype<IParentAndItem>\n{\n    static const IID& uuid() throw()\n    { return IID_IParentAndItem; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct wrap_t<IParentAndItem>\n{\n    washer::shell::pidl::apidl_t parent_pidl()\n    {\n        washer::shell::pidl::apidl_t parent;\n        HRESULT hr = raw(this)->GetParentAndItem(parent.out(), NULL, NULL);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n        return parent;\n    }\n\n    washer::shell::pidl::cpidl_t item_pidl()\n    {\n        washer::shell::pidl::cpidl_t item;\n        HRESULT hr = raw(this)->GetParentAndItem(NULL, NULL, item.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n        return item;\n    }\n\n    washer::shell::pidl::apidl_t absolute_item_pidl()\n    {\n        washer::shell::pidl::apidl_t parent;\n        washer::shell::pidl::cpidl_t item;\n        HRESULT hr = raw(this)->GetParentAndItem(parent.out(), NULL, item.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n        return parent + item;\n    }\n\n    comet::com_ptr<IShellFolder> parent_folder()\n    {\n        comet::com_ptr<IShellFolder> folder;\n        HRESULT hr = raw(this)->GetParentAndItem(NULL, folder.out(), NULL);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n        return folder;\n    }\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "swish/shell/shell.cpp",
    "content": "/**\n    @file\n\n    Utility functions to work with the Windows Shell Namespace.\n\n    @if license\n\n    Copyright (C) 2009, 2011, 2012, 2015\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"shell.hpp\"\n\n#include <washer/shell/services.hpp> // shell_browser, shell_view\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n\n#include <comet/error.h> // com_error\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <exception>\n\n#include <shlobj.h>  // SHILCreateFromPath, ILFree\n#include <Winerror.h>  // FAILED\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl_shell_item;\nusing washer::shell::shell_browser;\nusing washer::shell::shell_view;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\n\nusing boost::filesystem::path;\nusing boost::filesystem::directory_iterator;\nusing boost::optional;\nusing boost::shared_ptr;\n\nusing std::invalid_argument;\n\nnamespace swish {\nnamespace shell {\n\npath path_from_pidl(PIDLIST_ABSOLUTE pidl)\n{\n    return pidl_shell_item(pidl).parsing_name();\n}\n\nshared_ptr<ITEMIDLIST_ABSOLUTE> pidl_from_path(\n    const path& filesystem_path)\n{\n    PIDLIST_ABSOLUTE pidl;\n    HRESULT hr = ::SHILCreateFromPath(\n        filesystem_path.wstring().c_str(), &pidl, NULL);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error(hr));\n\n    return shared_ptr<ITEMIDLIST_ABSOLUTE>(pidl, ::ILFree);\n}\n\ncom_ptr<IDataObject> data_object_for_file(const path& file)\n{\n    return data_object_for_files(&file, &file + 1);\n}\n\ncom_ptr<IDataObject> data_object_for_directory(const path& directory)\n{\n    if (!is_directory(directory))\n        BOOST_THROW_EXCEPTION(\n            invalid_argument(\"The path must be to a directory.\"));\n\n    return data_object_for_files(\n        directory_iterator(directory), directory_iterator());\n}\n\nvoid put_view_item_into_rename_mode(\n    comet::com_ptr<IShellView> view,\n    const washer::shell::pidl::cpidl_t& item)\n{\n    HRESULT hr = view->SelectItem(\n        item.get(),\n        SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS |\n        SVSI_ENSUREVISIBLE | SVSI_FOCUSED);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error_from_interface(view, hr));\n}\n\noptional<window<wchar_t>> window_for_ole_site(com_ptr<IUnknown> ole_site)\n{\n    try\n    {\n        com_ptr<IShellView> view = shell_view(shell_browser(ole_site));\n        HWND hwnd = NULL;\n        if (view->GetWindow(&hwnd) == S_OK && hwnd)\n        {\n            return window<wchar_t>(window_handle::foster_handle(hwnd));\n        }\n    } catch(const std::exception&) {}\n\n    return optional<window<wchar_t>>();\n}\n\n}} // namespace swish::shell\n"
  },
  {
    "path": "swish/shell/shell.hpp",
    "content": "/**\n    @file\n\n    Utility functions to work with the Windows Shell Namespace.\n\n    @if license\n\n    Copyright (C) 2009, 2011, 2012, 2015\n    Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/windows_api.hpp\" // SHBindToParent\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n#include <washer/window/window.hpp>\n\n#include <comet/error.h> // com_error\n#include <comet/interface.h>  // uuidof, comtype\n#include <comet/ptr.h>  // com_ptr\n\n#include <boost/filesystem.hpp>  // path\n#include <boost/numeric/conversion/cast.hpp>  // numeric_cast\n#include <boost/optional/optional.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n#include <boost/iterator/indirect_iterator.hpp>\n\n#include <shobjidl.h>  // IShellFolder\n#include <ObjIdl.h>  // IDataObject\n\n#include <vector>\n#include <algorithm>  // transform\n#include <stdexcept>  // invalid_argument\n\nnamespace { // private\n\n    /**\n     * Function adapter for ILFindLastID to work with transform algorithm\n     * in ui_object_of_items.\n     */\n    PUITEMID_CHILD find_last_ID(const ITEMIDLIST_RELATIVE& idl)\n    {\n        return ::ILFindLastID(&idl);\n    }\n}\n\nnamespace comet {\n\ntemplate<> struct comtype<IDataObject>\n{\n    static const IID& uuid() throw() { return IID_IDataObject; }\n    typedef IUnknown base;\n};\n\n}\n\nnamespace swish {\nnamespace shell {\n\n/**\n * Return the filesystem path represented by the given PIDL.\n *\n * @warning\n * The PIDL must be a PIDL to a filesystem item.  If it isn't this\n * function is likely but not guaranteed to throw an exception\n * when it converts the parsing name to a path.  If the parsing\n * name looks sufficiently path-like, however, it may silently\n * succeed and return a bogus path.\n */\nboost::filesystem::path path_from_pidl(PIDLIST_ABSOLUTE pidl);\n\n/**\n * Return an absolute PIDL to the item in the filesystem at the given\n * path.\n */\nboost::shared_ptr<ITEMIDLIST_ABSOLUTE> pidl_from_path(\n    const boost::filesystem::path& filesystem_path);\n\n/**\n * Return an IDataObject representing several files in the same folder.\n *\n * The files are passed as a half-open range of fully-qualified paths to\n * each file.\n *\n * @templateparam It  An iterator type whose items are convertible to path\n *                    by the path constructor (e.g. could be paths or\n *                    strings).\n */\ntemplate<typename It>\ncomet::com_ptr<IDataObject> data_object_for_files(It begin, It end)\n{\n    std::vector<boost::shared_ptr<ITEMIDLIST_ABSOLUTE> > pidls;\n    transform(begin, end, back_inserter(pidls), pidl_from_path);\n\n    return ui_object_of_items<IDataObject>(pidls.begin(), pidls.end());\n}\n\n/**\n * Return an IDataObject representing a file on the local filesystem.\n */\ncomet::com_ptr<IDataObject> data_object_for_file(\n    const boost::filesystem::path& file);\n\n/**\n * Return an IDataObject representing all the files in a directory.\n */\ncomet::com_ptr<IDataObject> data_object_for_directory(\n    const boost::filesystem::path& directory);\n\n/**\n * Return the associated object of several items.\n *\n * This is a convenience function that binds to the items' parent and then\n * asks the parent for the associated object.  The items are passed as a\n * half-open range of absolute PIDLs.\n *\n * Analogous to GetUIObjectOf().\n *\n * @warning\n * In order for this to work all items MUST HAVE THE SAME PARENT (i.e. they\n * must all be in the same folder).\n */\ntemplate<typename T, typename It>\ncomet::com_ptr<T> ui_object_of_items(It begin, It end)\n{\n    //\n    // All the items we're passed have to have the same parent folder so\n    // we just bind to the parent of the *first* item in the collection.\n    //\n\n    if (begin == end)\n        BOOST_THROW_EXCEPTION(std::invalid_argument(\"Empty range given\"));\n\n    comet::com_ptr<IShellFolder> parent;\n    HRESULT hr = swish::windows_api::SHBindToParent(\n        // &* strips smart pointer, if any\n        &**begin, comet::uuidof(parent.in()),\n        reinterpret_cast<void**>(parent.out()), NULL);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    std::vector<ITEMID_CHILD __unaligned*> child_pidls;\n    std::transform(\n        boost::make_indirect_iterator(begin),\n        boost::make_indirect_iterator(end),\n        back_inserter(child_pidls), find_last_ID);\n\n    comet::com_ptr<T> ui_object;\n    hr = parent->GetUIObjectOf(\n        NULL, boost::numeric_cast<UINT>(child_pidls.size()),\n        (child_pidls.empty()) ? NULL : &child_pidls[0],\n        comet::uuidof<T>(), NULL, reinterpret_cast<void**>(ui_object.out()));\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error_from_interface(parent, hr));\n\n    return ui_object;\n}\n\n/**\n * Return the associated object of an item.\n *\n * This is a convenience function that binds to the item's parent\n * and then asks the parent for the associated object.  The type of\n * associated object is determined by the template parameter.\n *\n * Analogous to GetUIObjectOf().\n *\n * @templateparam T  Type of associated object to return.\n */\ntemplate<typename T>\ncomet::com_ptr<T> ui_object_of_item(PCIDLIST_ABSOLUTE pidl)\n{\n    return ui_object_of_items<T>(&pidl, &pidl + 1);\n}\n\nvoid put_view_item_into_rename_mode(\n    comet::com_ptr<IShellView> view,\n    const washer::shell::pidl::cpidl_t& item);\n\n/**\n * Get the window for the give OLE site, if available.\n */\nboost::optional<washer::window::window<wchar_t>> window_for_ole_site(\n    comet::com_ptr<IUnknown> ole_site);\n\n}} // namespace swish::shell\n"
  },
  {
    "path": "swish/shell/shell_item.hpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_SHELL_SHELL_ITEM_HPP\n#define SWISH_SHELL_SHELL_ITEM_HPP\n\n#include <comet/ptr.h>\n\n#include <shobjidl.h>\n\nnamespace swish {\nnamespace shell {\n\ntemplate struct comet::wrap_t<IShellItem>\n{\n\n\n};\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/shell/shell_item_array.hpp",
    "content": "/* Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef SWISH_SHELL_SHELL_ITEM_ARRAY_HPP\n#define SWISH_SHELL_SHELL_ITEM_ARRAY_HPP\n\n#include <washer/shell/pidl_array.hpp>\n\n#include <comet/enum_iterator.h>\n#include <comet/error.h>\n#include <comet/ptr.h>\n\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n\n#include <shobjidl.h>\n\nnamespace comet {\n\ntemplate<> struct comtype<IShellItemArray>\n{\n    static const IID& uuid() throw() { return IID_IShellItemArray; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comtype<IShellItem>\n{\n    static const IID& uuid() throw() { return IID_IShellItem; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct comet::impl::type_policy<IShellItem*>\n{\n    static void init(IShellItem*& destination, IShellItem* source)\n    {\n        destination = source;\n        destination->AddRef();\n    }\n\n    static void clear(IShellItem*& p)\n    {\n        p->Release();\n    }\n};\n\n\ntemplate<> struct enumerated_type_of<IEnumShellItems>\n{\n    typedef IShellItem* is;\n};\n\ntemplate<> struct wrap_t<IShellItemArray>\n{\n    typedef comet::enum_iterator<IEnumShellItems> iterator_type;\n\n    size_t size()\n    {\n        DWORD array_size = 0;\n        HRESULT hr = raw(this)->GetCount(&array_size);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n\n        return array_size;\n    }\n\n    comet::com_ptr<IShellItem> at(size_t index)\n    {\n        comet::com_ptr<IShellItem> item;\n        HRESULT hr = raw(this)->GetItemAt(\n            boost::numeric_cast<DWORD>(index), item.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n\n        return item;\n    }\n\n    comet::com_ptr<IShellItem> operator[](size_t index)\n    {\n        return at(index);\n    }\n\n    iterator_type begin()\n    {\n        com_ptr<IEnumShellItems> enumerator;\n        HRESULT hr = raw(this)->EnumItems(enumerator.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error_from_interface(raw(this), hr));\n        return iterator_type(enumerator);\n    }\n\n    iterator_type end()\n    {\n        return iterator_type();\n    }\n};\n\n}\n\nnamespace swish {\nnamespace shell {\n\ninline comet::com_ptr<IShellItemArray> shell_item_array_from_folder_items(\n    comet::com_ptr<IShellFolder> parent_folder,\n    UINT pidl_count, PCUITEMID_CHILD_ARRAY item_pidls)\n{\n    comet::com_ptr<IShellItemArray> item_array;\n    // Not passing the folder PIDL, so this relies on the folder\n    // implementing IPersistFolder2\n    HRESULT hr = ::SHCreateShellItemArray(\n        NULL, parent_folder.in(), pidl_count, item_pidls,\n        item_array.out());\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return item_array;\n}\n\ninline comet::com_ptr<IShellItemArray> shell_item_array_from_folder_pidl_and_items(\n    PCIDLIST_ABSOLUTE parent_folder_pidl,\n    UINT pidl_count, PCUITEMID_CHILD_ARRAY item_pidls)\n{\n    comet::com_ptr<IShellItemArray> item_array;\n    HRESULT hr = ::SHCreateShellItemArray(\n        parent_folder_pidl, NULL, pidl_count, item_pidls,\n        item_array.out());\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return item_array;\n}\n\ninline comet::com_ptr<IShellItemArray> shell_item_array_from_pidls(\n    UINT cidl, PCIDLIST_ABSOLUTE_ARRAY rgpidl)\n{\n    comet::com_ptr<IShellItemArray> item_array;\n    HRESULT hr = ::SHCreateShellItemArrayFromIDLists(\n        cidl, rgpidl, item_array.out());\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return item_array;\n}\n\ninline comet::com_ptr<IShellItemArray> shell_item_array_from_data_object(\n    comet::com_ptr<IDataObject> data_object)\n{\n    comet::com_ptr<IShellItemArray> item_array;\n    HRESULT hr = ::SHCreateShellItemArrayFromDataObject(\n        data_object.in(), item_array.iid(),\n        reinterpret_cast<void**>(item_array.out()));\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return item_array;\n}\n\n}}\n\n#endif\n"
  },
  {
    "path": "swish/shell_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(GENERATED_SOURCES\n  ${CMAKE_CURRENT_BINARY_DIR}/shell_folder.dir/${CMAKE_CFG_INTDIR}/Swish.h\n  ${CMAKE_CURRENT_BINARY_DIR}/shell_folder.dir/${CMAKE_CFG_INTDIR}/Swish_i.c)\nset_source_files_properties(${GENERATED_SOURCES} PROPERTIES GENERATED TRUE)\n\nset(SOURCES\n  DataObject.h\n  Folder.h\n  IconExtractor.h\n  KbdInteractiveDialog.h\n  HostFolder.h\n  locale_setup.hpp\n  Pidl.h\n  Registry.h\n  RemoteFolder.h\n  resource.h\n  SftpDataObject.h\n  SftpDirectory.h\n  SnitchingDataObject.hpp\n  SwishFolder.hpp\n  wtl.hpp\n  DataObject.cpp\n  HostFolder.cpp\n  IconExtractor.cpp\n  KbdInteractiveDialog.cpp\n  Registry.cpp\n  RemoteFolder.cpp\n  SftpDataObject.cpp\n  SftpDirectory.cpp\n  data_object/FileGroupDescriptor.hpp\n  data_object/GlobalLocker.hpp\n  data_object/ShellDataObject.hpp\n  data_object/ShellDataObject.cpp\n  data_object/StorageMedium.hpp\n  Swish.idl\n  ${GENERATED_SOURCES}\n  shell_folder.rc)\n\nadd_library(shell_folder ${SOURCES})\n\n# This exposes the include path of the directory that CMake tells midl\n# to output the header file into.  Ideally, it would be\n# \"${CMAKE_CURRENT_BINARY_DIR}/\\$(IntDir)\", but that is no good if\n# used by another project because $(IntDir) expands to the\n# intermediate directory of that project, not this one.\ntarget_include_directories(shell_folder\n  PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/shell_folder.dir/${CMAKE_CFG_INTDIR})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(shell_folder\n  PRIVATE host_folder remote_folder frontend nse drop_target\n  urlmon ${Boost_LIBRARIES}\n  PUBLIC Washer::washer Comet::comet)\n"
  },
  {
    "path": "swish/shell_folder/DataObject.cpp",
    "content": "/**\n    @file\n\n    Wrapper around shell-created IDataObject adding support for FILECONTENTS.\n\n    @if license\n\n    Copyright (C) 2009, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"DataObject.h\"\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/error.h> // com_error_from_interface\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\n\nnamespace comet {\n\ntemplate<> struct comtype<IDataObject>\n{\n    static const IID& uuid() { return IID_IDataObject; }\n    typedef ::IUnknown base;\n};\n\n}\n\nnamespace {\n\n    com_ptr<IDataObject> shell_data_object_from_pidls(\n        UINT cPidl, PCUITEMID_CHILD_ARRAY aPidl, \n        PCIDLIST_ABSOLUTE pidlCommonParent)\n    {\n        // Create the default shell IDataObject implementation which we \n        // are wrapping.\n        com_ptr<IDataObject> data_object;\n        HRESULT hr = ::CIDLData_CreateFromIDArray(\n            pidlCommonParent, cPidl, \n            reinterpret_cast<PCUIDLIST_RELATIVE_ARRAY>(aPidl),\n            data_object.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n\n        return data_object;\n    }\n\n}\n\n/**\n * Construct the DataObject with the top-level PIDLs.\n *\n * These PIDLs represent, for instance, the current group of files and\n * directories which have been selected in an Explorer window.  This list\n * should not include any sub-items of any of the directories.\n *\n * @param cPidl             Number of PIDLs in the selection.\n * @param aPidl             The selected PIDLs.\n * @param pidlCommonParent  PIDL to the common parent of all the PIDLs.\n */\nCDataObject::CDataObject(\n    UINT cPidl, PCUITEMID_CHILD_ARRAY aPidl, \n    PCIDLIST_ABSOLUTE pidlCommonParent) :\n    m_cfFileDescriptor(static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR))),\n    m_cfFileContents(static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILECONTENTS))),\n    m_inner(shell_data_object_from_pidls(cPidl, aPidl, pidlCommonParent))\n{\n}\n\n/*----------------------------------------------------------------------------*\n * IDataObject methods\n *----------------------------------------------------------------------------*/\n\nSTDMETHODIMP CDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)\n{\n    HRESULT hr = S_OK;\n    try\n    {\n        if (pformatetcIn->cfFormat == m_cfFileContents)\n        {\n            // Validate FORMATETC\n            if (!(pformatetcIn->tymed & TYMED_ISTREAM))\n                BOOST_THROW_EXCEPTION(com_error(DV_E_TYMED));\n            if (pformatetcIn->dwAspect != DVASPECT_CONTENT)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_DVASPECT));\n            if (pformatetcIn->ptd)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_DVTARGETDEVICE));\n\n            long lindex = pformatetcIn->lindex;\n\n            // Handle incorrect lindex if possible\n            if (lindex == -1)\n            {\n                if (m_streams.size() != 1)\n                    BOOST_THROW_EXCEPTION(com_error(DV_E_LINDEX));\n                lindex = 0;\n            }\n\n            // Ensure that the item is actually in our (sparse) local store\n            if (m_streams.find(lindex) == m_streams.end())\n                BOOST_THROW_EXCEPTION(com_error(DV_E_LINDEX));\n\n            // Fill STGMEDIUM with IStream\n            pmedium->pUnkForRelease = NULL;\n            m_streams[lindex].CopyTo(&(pmedium->pstm));\n            pmedium->tymed = TYMED_ISTREAM;\n        }\n        else\n        {\n            // Delegate all other requests to the inner IDataObject\n            hr = m_inner->GetData(pformatetcIn, pmedium);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error_from_interface(m_inner, hr));\n        }\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n    return hr;\n}\n\n/**\n * Set a format in the DataObject.\n *\n * Which item to set is specified as a FORMATETC and the item is passed in\n * a STGMEDIUM.  If an item already exists with the specified parameters, it\n * is replaced.\n *\n * @param pformatetc  Pointer to FORMATETC specifying the format type and, for\n *                    CFSTR_FILECONTENTS formats only, the index of the item\n *                    in the DataObject.\n * @param pmedium     Pointer to STGMEDIUM holding the item to be added to the\n *                    DataObject.\n * @param fRelease    Indicates who owns the contents of the STGMEDIUM after a\n *                    call to this method.  If true, this object does.  If \n *                    false, the caller retains ownership.\n */\nSTDMETHODIMP CDataObject::SetData( \n    FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)\n{\n    HRESULT hr = S_OK;\n    try\n    {\n        if (pformatetc->cfFormat == m_cfFileContents)\n        {\n            // Validate FORMATETC\n            if (pformatetc->tymed != TYMED_ISTREAM)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_TYMED));\n            if (pformatetc->dwAspect != DVASPECT_CONTENT)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_DVASPECT));\n            if (pformatetc->ptd)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_DVTARGETDEVICE));\n            if (pformatetc->lindex < 0)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_LINDEX));\n\n            // Validate STGMEDIUM\n            if (pmedium->tymed != pformatetc->tymed)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_TYMED));\n            if (!pmedium->pstm)\n                BOOST_THROW_EXCEPTION(com_error(DV_E_STGMEDIUM));\n\n            // Add IStream to our local store\n            m_streams[pformatetc->lindex] = pmedium->pstm;\n\n            if (fRelease) // Release STGMEDIUM if we own it now\n            {\n                ::ReleaseStgMedium(pmedium);\n            }\n\n            // Prod inner IDataObject with empty CFSTR_FILECONTENTS format\n            hr = ProdInnerWithFormat(pformatetc->cfFormat, pformatetc->tymed);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error_from_interface(m_inner, hr));\n        }\n        else\n        {\n            // Delegate all other requests to the inner IDataObject\n            hr = m_inner->SetData(pformatetc, pmedium, fRelease);\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error_from_interface(m_inner, hr));\n        }\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n    return hr;\n}\n\nSTDMETHODIMP CDataObject::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)\n{\n    return m_inner->GetDataHere(pformatetc, pmedium);\n}\n\nSTDMETHODIMP CDataObject::QueryGetData(FORMATETC *pformatetc)\n{\n    return m_inner->QueryGetData(pformatetc);\n}\n\nSTDMETHODIMP CDataObject::GetCanonicalFormatEtc( \n    FORMATETC *pformatetcIn, FORMATETC *pformatetcOut)\n{\n    return m_inner->GetCanonicalFormatEtc(pformatetcIn, pformatetcOut);\n}\nSTDMETHODIMP CDataObject::EnumFormatEtc( \n    DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)\n{\n    return m_inner->EnumFormatEtc(dwDirection, ppenumFormatEtc);\n}\n\nSTDMETHODIMP CDataObject::DAdvise( \n    FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, \n    DWORD *pdwConnection)\n{\n    return m_inner->DAdvise(pformatetc, advf, pAdvSink, pdwConnection);\n}\n\nSTDMETHODIMP CDataObject::DUnadvise(DWORD dwConnection)\n{\n    return m_inner->DUnadvise(dwConnection);\n}\n\nSTDMETHODIMP CDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)\n{\n    return m_inner->EnumDAdvise(ppenumAdvise);\n}\n\n\n/*----------------------------------------------------------------------------*\n * Protected methods\n *----------------------------------------------------------------------------*/\n\n/**\n * Prod the inner DataObject with the given format.\n *\n * This sets an empty item in the inner DataObject which causes it to register\n * the existence of the format.  This ensures that calls to QueryGetData() and\n * the IEnumFORMATETC enumeration---both of which are delegated to the inner\n * object---respond correctly.\n */\nHRESULT CDataObject::ProdInnerWithFormat(CLIPFORMAT nFormat, DWORD tymed)\nthrow()\n{\n    CFormatEtc fetc(nFormat, tymed);\n\n    STGMEDIUM stgEmpty = STGMEDIUM();\n\n    // The tymeds in the FORMATETC and STGMEDIUM must match.  This was tripping\n    // up Windows 8 (Consumer Preview) which is stricter about this and was\n    // returning E_OUTOFMEMORY from SetData.\n    stgEmpty.tymed = tymed;\n\n    return m_inner->SetData(&fetc, &stgEmpty, true);\n}"
  },
  {
    "path": "swish/shell_folder/DataObject.h",
    "content": "/**\n    @file\n\n    Wrapper around shell-created IDataObject adding support for FILECONTENTS.\n\n    @if license\n\n    Copyright (C) 2009, 2011, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/atl.hpp\"\n\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n\n#include <shlobj.h>  // Windows Shell API\n\n#include <map>  // Associative container for IStream store\n\n/**\n * Pseudo-subclass of IDataObject created by CIDLData_CreateFromIDArray().\n * \n * The shell-created DataObject is lacking in one respect: it doesn't\n * allow the storage of more than one item with the same format but\n * different @p lindex value.  This rules out using it as-is for the common\n * shell scenario where the contents of a number of selected files\n * are stored in the same IDataObject: only the last file is stored regardless\n * of the value of @p lindex passed in the FORMATETC into SetData().\n *\n * This class works around the problem by intercepting calls to the\n * shell DataObject (stored in @p m_inner) and performing custom \n * processing for CFSTR_FILECONTENTS formats.  All other requests are simply \n * forwarded to the inner IDataObject.\n *\n * As the locally stored CFSTR_FILECONTENTS formats may be set with any\n * @p lindex value (not necessarily a continuous series), a std::map is\n * used as a sparse array.\n */\nclass CDataObject :\n    public comet::simple_object<IDataObject>\n{\npublic:\n\n    typedef IDataObject interface_is;\n\n    CDataObject(\n        UINT cPidl, __in_ecount_opt(cPidl) PCUITEMID_CHILD_ARRAY aPidl,\n        __in PCIDLIST_ABSOLUTE pidlCommonParent);\n\npublic: // IDataObject methods\n\n    IFACEMETHODIMP GetData( \n        __in FORMATETC *pformatetcIn,\n        __out STGMEDIUM *pmedium);\n\n    IFACEMETHODIMP GetDataHere( \n        __in FORMATETC *pformatetc,\n        __inout STGMEDIUM *pmedium);\n\n    IFACEMETHODIMP QueryGetData( \n        __in_opt FORMATETC *pformatetc);\n\n    IFACEMETHODIMP GetCanonicalFormatEtc( \n        __in_opt FORMATETC *pformatetcIn,\n        __out FORMATETC *pformatetcOut);\n\n    IFACEMETHODIMP SetData( \n        __in FORMATETC *pformatetc,\n        __in STGMEDIUM *pmedium,\n        __in BOOL fRelease);\n\n    IFACEMETHODIMP EnumFormatEtc( \n        __in DWORD dwDirection,\n        __deref_out_opt IEnumFORMATETC **ppenumFormatEtc);\n\n    IFACEMETHODIMP DAdvise( \n        __in FORMATETC *pformatetc,\n        __in DWORD advf,\n        __in_opt IAdviseSink *pAdvSink,\n        __out DWORD *pdwConnection);\n\n    IFACEMETHODIMP DUnadvise( \n        __in DWORD dwConnection);\n\n    IFACEMETHODIMP EnumDAdvise( \n        __deref_out_opt IEnumSTATDATA **ppenumAdvise);\n\nprotected:\n    \n    HRESULT ProdInnerWithFormat(CLIPFORMAT nFormat, DWORD tymed) throw();\n\nprivate:\n\n    /** @name Stores */\n    // @{\n    typedef std::map<long, ATL::CComPtr<IStream> > StreamStore;\n    StreamStore m_streams;                ///< Local FILECONTENTS IStream store\n    comet::com_ptr<IDataObject> m_inner;  ///< Wrapped inner DataObject\n    // @}\n\n    /** @name Explicitly recognised CLIPFORMATS */\n    // @{\n    CLIPFORMAT m_cfFileDescriptor;    ///< CFSTR_FILEDESCRIPTOR\n    CLIPFORMAT m_cfFileContents;      ///< CFSTR_FILECONTENTS\n    // @}\n\n};\n\n\nclass CStorageMedium : public STGMEDIUM\n{\npublic:\n    CStorageMedium() throw()\n    {\n        tymed = TYMED_NULL;\n        hBitmap = 0;\n        hMetaFilePict = 0;\n        hEnhMetaFile = 0;\n        hGlobal = 0;\n        lpszFileName = NULL;\n        pstm = NULL;\n        pstg = NULL;\n        pUnkForRelease = NULL;\n    }\n\n    ~CStorageMedium() throw()\n    {\n        ::ReleaseStgMedium(this);\n    }\n};\n\nclass CFormatEtc : public FORMATETC\n{\npublic:\n    CFormatEtc(\n        CLIPFORMAT cfFormat, DWORD tymed = TYMED_HGLOBAL, LONG lIndex = -1, \n        DWORD dwAspect = DVASPECT_CONTENT, DVTARGETDEVICE *ptd = NULL)\n        throw()\n    {\n        _Construct(cfFormat, tymed, lIndex, dwAspect, ptd);\n    }\n\n    CFormatEtc(\n        UINT nFormat, DWORD tymed = TYMED_HGLOBAL, LONG lIndex = -1, \n        DWORD dwAspect = DVASPECT_CONTENT, DVTARGETDEVICE *ptd = NULL)\n        throw()\n    {\n        _Construct(static_cast<CLIPFORMAT>(nFormat), \n            tymed, lIndex, dwAspect, ptd);\n    }\n\n    CFormatEtc(\n        PCWSTR pszFormat, DWORD tymed = TYMED_HGLOBAL, LONG lIndex = -1, \n        DWORD dwAspect = DVASPECT_CONTENT, DVTARGETDEVICE *ptd = NULL)\n        throw(...)\n    {\n        UINT nFormat = ::RegisterClipboardFormatW(pszFormat);\n        ATLENSURE_THROW(nFormat, E_INVALIDARG);\n\n        _Construct(static_cast<CLIPFORMAT>(nFormat), \n            tymed, lIndex, dwAspect, ptd);\n    }\n\nprivate:\n    inline void _Construct(\n        CLIPFORMAT cfFormat, DWORD tymed, LONG lIndex, DWORD dwAspect, \n        DVTARGETDEVICE *ptd) throw()\n    {\n        this->cfFormat = cfFormat;\n        this->tymed = tymed;\n        this->lindex = lIndex;\n        this->dwAspect = dwAspect;\n        this->ptd = ptd;\n    }\n};\n\n\nclass CGlobalLock\n{\npublic:\n    CGlobalLock() throw() :\n        m_hGlobal(NULL), m_pMem(NULL)\n    {}\n    CGlobalLock(__in HGLOBAL hGlobal) throw() : \n        m_hGlobal(hGlobal), m_pMem(::GlobalLock(m_hGlobal))\n    {}\n\n    ~CGlobalLock() throw()\n    {\n        _Clear();\n    }\n\n    /**\n     * Disable copy constructor.  If the object were copied, the old one would\n     * be destroyed which unlocks the global memory but the new copy would\n     * not be re-locked.\n     */\n    CGlobalLock(const CGlobalLock& lock) throw();\n    CGlobalLock& operator=(const CGlobalLock&) throw();\n\n    void Attach(__in HGLOBAL hGlobal) throw()\n    {\n        _Clear();\n\n        m_hGlobal = hGlobal;\n        m_pMem = ::GlobalLock(m_hGlobal);\n    }\n\n    HGLOBAL Detach() throw()\n    {\n        HGLOBAL hGlobal = m_hGlobal;\n        _Clear();\n        return hGlobal;\n    }\n\n    CIDA* GetCida()\n    {\n        return static_cast<CIDA *>(m_pMem);\n    }\n\n    FILEGROUPDESCRIPTOR& GetFileGroupDescriptor()\n    {\n        return *static_cast<FILEGROUPDESCRIPTOR*>(m_pMem);\n    }\n\n    DWORD& GetDword()\n    {\n        return *static_cast<DWORD *>(m_pMem);\n    }\n\nprivate:\n\n    void _Clear() throw()\n    {\n        m_pMem = NULL;\n        if (m_hGlobal)\n            ::GlobalUnlock(m_hGlobal);\n        m_hGlobal = NULL;\n    }\n\n    HGLOBAL m_hGlobal;\n    PVOID m_pMem;\n};"
  },
  {
    "path": "swish/shell_folder/Folder.h",
    "content": "/**\n    @file\n\n    Base class for IShellFolder implementations.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2012\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"Pidl.h\"\n\n#include \"swish/atl.hpp\" // Common ATL setup\n#include \"swish/debug.hpp\" // METHOD_TRACE\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n#include <washer/shell/folder_error_adapters.hpp> // folder2_error_adapter\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t\n#include <washer/shell/property_key.hpp> // property_key\n#include <washer/shell/shell.hpp> // string_to_strret\n\n#include <comet/error.h> // com_error\n#include <comet/variant.h> // variant_t\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <shlobj.h>     // Windows Shell API\n\n#include <cstring> // memset, memcpy\n#include <stdexcept> // range_error\n\n#ifndef __IPersistIDList_INTERFACE_DEFINED__\n#define __IPersistIDList_INTERFACE_DEFINED__\n\n    EXTERN_C const IID IID_IPersistIDList;\n    \n    MIDL_INTERFACE(\"1079acfc-29bd-11d3-8e0d-00c04f6837d5\")\n    IPersistIDList : public IPersist\n    {\n    public:\n        virtual HRESULT STDMETHODCALLTYPE SetIDList( \n            /* [in] */ __RPC__in PCIDLIST_ABSOLUTE pidl) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE GetIDList( \n            /* [out] */ __RPC__deref_out_opt PIDLIST_ABSOLUTE *ppidl) = 0;\n        \n    };\n#endif\n\nnamespace comet {\n\ntemplate<> struct comtype<IPersistFolder2>\n{\n    static const IID& uuid() { return IID_IPersistFolder; }\n    typedef IPersistFolder base;\n};\n\n}\n\nnamespace swish {\nnamespace shell_folder {\nnamespace folder {\n\nnamespace detail {\n\n    /**\n     * Return the parent IShellFolder of the last item in the PIDL.\n     *\n     * Optionally, return the a pointer to the last item as well.  This\n     * function emulates the Vista-specific SHBindToFolderIDListParent API\n     * call.\n     */\n    inline HRESULT BindToParentFolderOfPIDL(\n        IShellFolder *psfRoot, PCUIDLIST_RELATIVE pidl, REFIID riid, \n        __out void **ppvParent, __out_opt PCUITEMID_CHILD *ppidlChild)\n    {\n        *ppvParent = NULL;\n        if (ppidlChild)\n            *ppidlChild = NULL;\n\n        // Equivalent to \n        //     ::SHBindToFolderIDListParent(\n        //         psfRoot, pidl, riid, ppvParent, ppidlChild);\n        \n        // Create PIDL to penultimate item (parent)\n        PIDLIST_RELATIVE pidlParent = ::ILClone(pidl);\n        ATLVERIFY(::ILRemoveLastID(pidlParent));\n\n        // Bind to the penultimate PIDL's folder (parent folder)\n        HRESULT hr = psfRoot->BindToObject(pidlParent, NULL, riid, ppvParent);\n        ::ILFree(pidlParent);\n        \n        if (SUCCEEDED(hr) && ppidlChild)\n        {\n            *ppidlChild = ::ILFindLastID(pidl);\n        }\n\n        return hr;\n    }\n}\n\ntemplate<typename ColumnType>\nclass CFolder :\n    public ATL::CComObjectRoot,\n    public washer::shell::folder2_error_adapter,\n    public washer::shell::shell_details_error_adapter,\n    public IPersistFolder3,\n    public IPersistIDList\n{\npublic:\n\n    BEGIN_COM_MAP(CFolder)\n        COM_INTERFACE_ENTRY(IPersistFolder3)\n        COM_INTERFACE_ENTRY(IShellFolder2)\n        COM_INTERFACE_ENTRY(IShellDetails)\n        COM_INTERFACE_ENTRY(IPersistIDList)\n        COM_INTERFACE_ENTRY2(IPersist,        IPersistFolder3)\n        COM_INTERFACE_ENTRY2(IPersistFolder,  IPersistFolder3)\n        COM_INTERFACE_ENTRY2(IPersistFolder2, IPersistFolder3)\n        COM_INTERFACE_ENTRY2(IShellFolder,    IShellFolder2)\n    END_COM_MAP()\n\n    CFolder() {}\n\n    virtual ~CFolder() {}\n\n    const washer::shell::pidl::apidl_t& root_pidl() const    \n    {\n        return m_root_pidl;\n    }\n\npublic: // IPersist methods\n\n    /**\n     * Get the class identifier (CLSID) of the object.\n     * \n     * @implementing IPersist\n     *\n     * @param[in] pClassID  Location in which to return the CLSID.\n     */\n    IFACEMETHODIMP GetClassID(CLSID* pClassID)\n    {\n        ATLENSURE_RETURN_HR(pClassID, E_POINTER);\n\n        *pClassID = clsid();\n\n        return S_OK;\n    }\n\npublic: // IPersistFolder methods\n\n    /**\n     * Assign an @b absolute PIDL to be the root of this folder.\n     *\n     * @implementing IPersistFolder\n     *\n     * This function tells a folder its place in the system namespace. \n     * If the folder implementation needs to construct a fully qualified PIDL\n     * to elements that it contains, the PIDL passed to this method is \n     * used to construct these.\n     *\n     * @param pidl  PIDL that specifies the absolute location of this folder.\n     */\n    IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl)\n    {\n        ATLENSURE_RETURN_HR(!::ILIsEmpty(pidl), E_INVALIDARG);\n        ATLENSURE_RETURN_HR(!m_root_pidl, E_UNEXPECTED); // Multiple init\n\n        m_root_pidl = pidl;\n        return S_OK;\n    }\n\npublic: // IPersistFolder2 methods\n\n    /**\n     * Get the root of this folder - the absolute PIDL relative to the\n     * desktop.\n     *\n     * @implementing IPersistFolder2\n     *\n     * @param[out] ppidl  Location in which to return the copied PIDL.  \n     *                    If the folder hasn't been initialised yet, this \n     *                    value will be NULL.\n     *\n     * @returns  S_FALSE if Initialize() hasn't been called.\n     */\n    IFACEMETHODIMP GetCurFolder(PIDLIST_ABSOLUTE* ppidl)\n    {\n        ATLENSURE_RETURN_HR(ppidl, E_POINTER);\n\n        *ppidl = NULL;\n\n        try\n        {\n            if (!root_pidl()) // Legal to call this before Initialize()\n                return S_FALSE;\n\n            // Copy the PIDL that was passed to us in Initialize()\n            root_pidl().copy_to(*ppidl);\n        }\n        WASHER_COM_CATCH_INTERFACE(IPersistFolder2);\n        \n        return S_OK;\n    }\n\npublic: // IPersistFolder3 methods\n    \n    IFACEMETHODIMP InitializeEx(\n        IBindCtx* /*pbc*/, PCIDLIST_ABSOLUTE pidlRoot, \n        const PERSIST_FOLDER_TARGET_INFO* /*ppfti*/)\n    {\n        ATLENSURE_RETURN_HR(pidlRoot, E_POINTER);\n\n        return Initialize(pidlRoot);\n    }\n    \n    IFACEMETHODIMP GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO* ppfti)\n    {\n        ATLENSURE_RETURN_HR(ppfti, E_POINTER);\n\n        ::ZeroMemory(ppfti, sizeof(PERSIST_FOLDER_TARGET_INFO));\n        return E_NOTIMPL;\n    }\n\npublic: // IPersistIDList methods\n\n    IFACEMETHODIMP SetIDList(PCIDLIST_ABSOLUTE pidl)\n    {\n        return Initialize(pidl);\n    }\n        \n    IFACEMETHODIMP GetIDList(PIDLIST_ABSOLUTE* ppidl)\n    {\n        return GetCurFolder(ppidl);\n    }\n\npublic: // IShellFolder methods\n\n    virtual void bind_to_storage(\n        PCUIDLIST_RELATIVE /*pidl*/, IBindCtx* /*bind_ctx*/,\n        const IID& /*iid*/, void** /*interface_out*/)\n    {\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOTIMPL));\n    }\n\n    /**\n     * Caller is requesting a subobject of this folder.\n     *\n     * @implementing folder_error_adapter\n     *\n     * Create and initialise an instance of the subitem represented by @a pidl\n     * and return the interface asked for in @a iid.\n     *\n     * Typically this is an IShellFolder although it may be an IStream.  \n     * Whereas create_view_object() and get_ui_object_of() request 'associated\n     * objects' of items in the hierarchy, calls to bind_to_object()\n     * are for the objects representing the items themselves.  E.g.,\n     * IShellFolder for folders and IStream for files.\n     *\n     * @todo  Find out more about how IStreams are dealt with and what it \n     *        gains us.\n     *\n     * @param[in]  pidl           PIDL to the requested object @b relative to\n     *                            this folder.\n     * @param[in]  bind_ctx       Binding context.\n     * @param[in]  iid            IID of the interface being requested.\n     * @param[out] interface_out  Location in which to return the requested\n     *                            interface.\n     *\n     * @note  Corresponds to IShellFolder::BindToObject.\n     */\n    virtual void bind_to_object(\n        PCUIDLIST_RELATIVE pidl, IBindCtx* bind_ctx, const IID& iid,\n        void** interface_out)\n    {\n        if (::ILIsEmpty(pidl))\n            BOOST_THROW_EXCEPTION(comet::com_error(E_INVALIDARG));\n\n        // TODO: We can optimise this function by immediately throwing\n        // E_NOTIMPL for any riid that we know our children and grandchildren\n        // don't provide. This is not in the spirit of COM QueryInterface but\n        // it may be a performance boost.\n\n        // First item in pidl must be of our type\n        validate_pidl(pidl);\n\n        if (::ILIsChild(pidl)) // Our child subfolder is the target\n        {\n            comet::com_ptr<IShellFolder> folder = subfolder(\n                reinterpret_cast<PCUITEMID_CHILD>(pidl));\n\n            // Create absolute PIDL to the subfolder by combining with \n            // our root\n            HRESULT hr = folder->QueryInterface(iid, interface_out);\n            if (FAILED(hr))\n                comet::throw_com_error(folder.get(), hr);\n        }\n        else\n        // One of our grandchildren is the target - delegate to its parent\n        {\n            comet::com_ptr<IShellFolder> folder;\n\n            PCUITEMID_CHILD pidl_grandchild = NULL;\n            HRESULT hr = detail::BindToParentFolderOfPIDL(\n                this, pidl, IID_PPV_ARGS(folder.out()), &pidl_grandchild);\n            if (FAILED(hr))\n                comet::throw_com_error(\n                    comet::com_ptr<IShellFolder>(this).get(), hr);\n\n            hr = folder->BindToObject(\n                pidl_grandchild, bind_ctx, iid, interface_out);\n            if (FAILED(hr))\n                comet::throw_com_error(folder.get(), hr);\n        }\n    }\n\n    /**\n     * Determine the relative order of two items in or below this folder.\n     *\n     * @implementing folder_error_adapter\n     *\n     * Given their item identifier lists, compare the two objects and return a\n     * value indicating the result of the comparison:\n     * - Negative: pidl1 < pidl2\n     * - Positive: pidl1 > pidl2\n     * - Zero:     pidl1 == pidl2\n     *\n     * @note  Corresponds to IShellFolder::CompareIDs.\n     */\n    int compare_ids( \n        LPARAM lparam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)\n    {\n        int column = LOWORD(lparam);\n\n        bool compare_all_fields = (HIWORD(lparam) == SHCIDS_ALLFIELDS);\n        bool canonical = (HIWORD(lparam) == SHCIDS_CANONICALONLY);\n\n        assert(!compare_all_fields || column == 0);\n        assert(!canonical || !compare_all_fields);\n\n        // Handle empty PIDL cases\n        if (::ILIsEmpty(pidl1) && ::ILIsEmpty(pidl2)) // Both empty - equal\n            return 0;\n        else if (::ILIsEmpty(pidl1))                  // Only pidl1 empty <\n            return -1;\n        else if (::ILIsEmpty(pidl2))                  // Only pidl2 empty >\n            return 1;\n        \n        // Explorer can pass us invalid PIDLs from its cache if our PIDL \n        // representation changes.  We catch that here to stop us asserting\n        // later.\n        validate_pidl(pidl1);\n        validate_pidl(pidl2);\n\n        // The casts here are OK.  We are aware compare_pidls only compares\n        // a single item.  We recurse later if needed.\n        int result = compare_pidls(\n            static_cast<PCUITEMID_CHILD>(pidl1),\n            static_cast<PCUITEMID_CHILD>(pidl2),\n            column, compare_all_fields, canonical);\n\n        if ((::ILIsChild(pidl1) && ::ILIsChild(pidl2)) || result != 0)\n        {\n            return result;\n        }\n        else\n        {\n            // The first items are equal and there are more items to\n            // compare.\n            // Delegate the rest of the comparison to our child.\n\n            comet::com_ptr<IShellFolder> folder;\n\n            washer::shell::pidl::cpidl_t child;\n            child.attach(::ILCloneFirst(pidl1));        \n            bind_to_object(child.get(), NULL, IID_PPV_ARGS(folder.out()));\n\n            HRESULT hr = folder->CompareIDs(\n                lparam, ::ILNext(pidl1), ::ILNext(pidl2));\n            if (FAILED(hr))\n                comet::throw_com_error(folder.get(), hr);\n\n            return (short)HRESULT_CODE(hr);\n        }\n    }\n\n    /**\n     * Create an object associated with @b this folder.\n     *\n     * @implementing folder_error_adapter\n     *\n     * The types of object which can be requested include IShellView,\n     * IContextMenu, IExtractIcon, IQueryInfo, IShellDetails or IDropTarget.\n     * This method is in contrast to get_ui_object_of() which performs the same\n     * task but for an item contained *within* the current folder rather than\n     * the folder itself.\n     *\n     * @param[in]   hwnd           Handle to the parent window, if any, of any\n     *                             UI that may be needed to complete the \n     *                             request.\n     * @param[in]   iid            Interface UUID for the object being\n     *                             requested.\n     * @param[out]  interface_out  Return value.\n     *\n     * @note  Corresponds to IShellFolder::CreateViewObject.\n     */\n    void create_view_object(HWND hwnd_owner, REFIID iid, void** interface_out)\n    {\n        comet::com_ptr<IUnknown> object = folder_object(hwnd_owner, iid);\n\n        HRESULT hr = object.get()->QueryInterface(iid, interface_out);\n        if (FAILED(hr))\n            comet::throw_com_error(object.get(), hr);\n    }\n\n    /**\n     * Create an object associated with an item in the current folder.\n     *\n     * @implementing folder_error_adapter\n     *\n     * Callers will request an associated object, such as a context menu, for \n     * items in the folder by calling this method with the IID of the object \n     * they want and the PIDLs of the items they want it for.  In addition, \n     * if the don't pass any PIDLs then they are requesting an associated \n     * object of this folder.\n     *\n     * We deal with the request as follows:\n     * - If the request is for an object associated with this folder, we\n     *   call folder_object() with the requested IID.  You should override\n     *   that method if your folder supports any associated objects.\n     *\n     * - If the request is for items in this in this folder we call\n     *   folder_item_object() with the IID and the PIDLs.  Again, you should\n     *   override that method if you want to support associated objects for\n     *   your folder's contents.\n     *\n     * - If the previous step fails to find the associated object and there is\n     *   only a single PIDL, we attempt to bind to the item in the PIDL as\n     *   an IShellFolder.  If this succeeds we delegate the object lookup to\n     *   this subfolder by calling its IShellFolder::CreateViewObject() method.\n     *\n     * The idea is that a given folder implementation answers object queries\n     * for itself and the non-folder items within it.  Additionally, it can\n     * answer queries for sub-folders if it chooses but it doesn't have to. If\n     * it doesn't, the request will be delegated to the subfolder\n     * implementation.\n     * \n     * create_view_object() performs the same task as get_ui_object_of() but\n     * only for the folder, not for items within it.\n     *\n     * @param[in]  hwnd_owner     Handle to the parent window, if any, of any\n     *                            UI that may be needed to complete the\n     *                            request.\n     * @param[in]  riid           Interface UUID for the object being\n     *                            requested.\n     * @param[out] interface_out  Return value.\n     *\n     * @note  Corresponds to IShellFolder::GetUIObjectIf.\n     */\n    void get_ui_object_of(\n        HWND hwnd_owner, UINT pidl_count, PCUITEMID_CHILD_ARRAY pidl_array,\n        const IID& iid, void** interface_out)\n    {\n        comet::com_ptr<IUnknown> object;\n\n        if (pidl_count == 0)\n        {\n            // Equivalent to CreateViewObject\n            object = folder_object(hwnd_owner, iid);\n        }\n        else\n        {\n            try\n            {\n                object = folder_item_object(\n                    hwnd_owner, iid, pidl_count, pidl_array);\n            }\n            catch (const comet::com_error& e)\n            {\n                if (e.hr() == E_NOINTERFACE && pidl_count == 1)\n                    object = _delegate_object_lookup_to_subfolder(\n                        hwnd_owner, iid, pidl_array[0]);\n                else\n                    throw;\n            }\n        }\n\n        HRESULT hr = object.get()->QueryInterface(iid, interface_out);\n        if (FAILED(hr))\n            comet::throw_com_error(object.get(), hr);\n    }\n\npublic: // IShellDetails methods\n\n    /**\n     * Sort by a given column of the folder view.\n     *\n     * @implementing shell_details_base_interface\n     *\n     * @return false to instruct the shell to perform the sort itself.\n     */\n    bool column_click(UINT /*column_index*/)\n    {\n        return false;\n    }\n    \npublic: // IShellFolder2 methods\n\n    /**\n     * Detailed information about an item in a folder.\n     *\n     * The desired detail is specified by a column index.\n     *\n     * @implementing folder_base_interface2\n     *\n     * This function operates in two distinctly different ways:\n     *  - if pidl is NULL:\n     *       Retrieve the the names of the columns themselves.\n     *  - if pidl is not NULL:\n     *       Retrieve  information for the item in the given pidl.\n     *\n     * The caller indicates which detail they want by specifying a column index\n     * in column_index.  If this column does not exist, throw an error.\n     *\n     * @retval  A SHELLDETAILS structure holding the requested detail as a\n     *          string along with various metadata.\n     *\n     * @note  Typically, a folder view calls this method repeatedly,\n     *        incrementing the column index each time.  The first column for\n     *        which we throw an error, marks the end of the columns in this\n     *        folder.\n     */\n    SHELLDETAILS get_details_of(PCUITEMID_CHILD pidl, UINT column_index)\n    {\n        ColumnType col(column_index);\n\n        SHELLDETAILS details;\n        std::memset(&details, 0, sizeof(SHELLDETAILS));\n\n        if (!pidl)\n        {\n            details.cxChar = col.average_width_in_chars();\n            details.fmt = col.format();\n            details.str = washer::shell::string_to_strret(col.header());\n        }\n        else\n        {\n            details.str = washer::shell::string_to_strret(col.detail(pidl));\n        }\n\n        return details;\n    }\n\n    /**\n     * GUID of the search to invoke when the user clicks on the search\n     * toolbar button.\n     *\n     * @implementing folder_base_interface2\n     *\n     * We do not support search objects so this method is not implemented.\n     */\n    GUID get_default_search_guid()\n    {\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOTIMPL));\n        return GUID();\n    }\n\n    /**\n     * Enumeration of all searches supported by this folder\n     *\n     * @implementing folder_base_interface2\n     *\n     * We do not support search objects so this method is not implemented.\n     */\n    IEnumExtraSearch* enum_searches()\n    {\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOTIMPL));\n        return NULL;\n    }\n\n    /**\n     * Default sorting and display columns.\n     *\n     * @implementing folder_base_interface2\n     *\n     * This is a default implementation which simply return the 1st (zeroth)\n     * column for both sorting and display.  Derived classes can override this\n     * if they need custom behaviour.\n     */\n    void get_default_column(ULONG* sort_out, ULONG* display_out)\n    {\n        *sort_out = 0;\n        *display_out = 0;\n    }\n\n    /**\n     * Default UI state (hidden etc.) and type (string, integer, etc.) for the\n     * column specified by column_index.\n     *\n     * @implementing folder_base_interface2\n     */\n    SHCOLSTATEF get_default_column_state(UINT column_index)\n    {\n        ColumnType col(column_index);\n        return col.state();\n    }\n    \n    /**\n     * Detailed information about an item in a folder.\n     *\n     * The desired detail is specified by PROPERTYKEY.\n     *\n     * @implementing folder_base_interface2\n     */\n    VARIANT get_details_ex(PCUITEMID_CHILD pidl, const SHCOLUMNID* pscid)\n    {\n        if (::ILIsEmpty(pidl))\n            BOOST_THROW_EXCEPTION(comet::com_error(E_INVALIDARG));\n\n        return property(*pscid, pidl).detach();\n    }\n\nprotected:\n\n    /**\n     * Return the folder implementation's CLSID.\n     *\n     * This allows callers to identify the type of folder for persistence\n     * etc.\n     */\n    virtual CLSID clsid() const = 0;\n\n    /**\n     * Check if a PIDL is recognised.\n     *\n     * Explorer has a tendency to pass our folders PIDLs that don't belong to\n     * them to see if we are paying attention (or, more likely, to see if it\n     * can use some PIDL data that it cached earlier.  We need to disbelieve \n     * everything Explorer tells us an validate each PIDL it gives us.\n     *\n     * Implementation should sniff the PIDLs passed to this method and\n     * throw an exception of they don't recognise them.\n     */\n    virtual void validate_pidl(PCUIDLIST_RELATIVE pidl) const = 0;\n\n    /**\n     * Determine the relative order of two file objects or folders.\n     *\n     * @returns\n     * - Negative: pidl1 < pidl2\n     * - Positive: pidl1 > pidl2\n     * - Zero:     pidl1 == pidl2\n     *\n     * This is one of the most important methods to get right.  When\n     * implementing it, take care that if two PIDLs don't represent the \n     * same filesystem item you don't return 0 from this method!  Explorer\n     * uses this to test if an item it has cached and if you falsely\n     * claim that it is Explorer is likely not to bother looking at your\n     * item becuase it thinks it already has it.\n     *\n     * If compare_all_fields is false, the PIDLs are compared by the\n     * data that corresponds to the given column index.  Otherwise, the PIDLs\n     * are compared by all the data the contain.\n     *\n     * @todo  We aren't actually comparing raw PIDLs here when\n     *        compare_all_fields is true.  We should be.\n     *\n     * @todo  Do something with canonical flag.\n     */\n    virtual int compare_pidls(\n        PCUITEMID_CHILD pidl1, PCUITEMID_CHILD pidl2,\n        int column, bool compare_all_fields, bool /*canonical*/) const\n        // TODO: This should be a PURE virtual method.  We're putting the\n        //       impl here temporarily.  Move it somewhere better!\n    {\n        if (compare_all_fields)\n        {\n            // FIXME:  This should ignore columns completely and do a raw PIDL\n            //         comparison\n            try\n            {\n                int i = 0;\n                while (true)\n                {\n                    int result = compare_pidls(pidl1, pidl2, i, false, false);\n                    if (result != 0)\n                        return result;\n                }\n            }\n            catch (const std::range_error&)\n            {\n                return 0;\n            }\n        }\n        else\n        {\n            ColumnType col(column);\n            return col.compare(pidl1, pidl2);\n        }\n    }\n\n    /**\n     * The caller is requesting an object associated with the current folder.\n     *\n     * The default implementation throws an E_NOINTERFACE exception which\n     * indicates to the caller that the object doesn't exist.  You almost \n     * certainly want to override this method in your folder.\n     *\n     * Examples of the type of object that Explorer may request include \n     *  - IShellView, the GUI representation of your folder\n     *  - IDropTarget, in order to support drag-and-drop into your GUI window\n     *  - IContextMenu, if your folder has a context menu\n     *\n     * This method corresponds roughly to CreateViewObject() but also\n     * deals with the unusual case where GetUIObjectOf() is called without \n     * any PIDLs.\n     */\n    virtual ATL::CComPtr<IUnknown> folder_object(HWND hwnd, REFIID riid) = 0;\n\n    /**\n     * The caller is requesting an object associated with one of more items\n     * in the current folder.\n     *\n     * The default implementation throws an E_NOINTERFACE exception which\n     * indicates to the caller that the object doesn't exist.  You almost \n     * certainly want to override this method in your folder to return\n     * associated objects for @b non-folder item.  It is also common to\n     * handle some objects for sub-folder items too.  For example, handling\n     * IDropTarget requests here avoids the need to bind to an instance of\n     * the subfolder implementation first.\n     *\n     * If a request isn't handled here (this method throws E_NOINTERFACE)\n     * and it's possible to bind to the item's IShellFolder interface then \n     * the request is delegated to the folder's CreateViewObject method and, \n     * assuming that folder is implemented with this class, will end up at \n     * the subfolder's folder_object() method.  However if this method throws\n     * a different error, the request is not delegated.\n     *\n     * This method corresponds roughly to GetUIObjectOf().\n     */\n    virtual ATL::CComPtr<IUnknown> folder_item_object(\n        HWND hwnd, REFIID riid, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl) = 0;\n\n    /**\n     * The caller is asking for an IShellFolder handler for a subfolder.\n     *\n     * The pidl passed to this method will be the @b child item whose folder\n     * is being requested.  It may not end in one of our own PIDLs if this\n     * is the root folder of our own hierarchy.  In that case it will be\n     * the PIDL of the external hosting folder such as 'Desktop' or\n     * 'My Computer'\n     *\n     * Respond to the request by creating an instance of the subfolder\n     * handler object (which may well be another instance of your folder\n     * class) and initialise it with the PIDL.\n     *\n     * This method corresonds to BindToFolder() where the item is directly\n     * in the current folder (not a grandchild).\n     */\n    virtual ATL::CComPtr<IShellFolder> subfolder(\n        const washer::shell::pidl::cpidl_t& pidl) = 0;\n\n    /**\n     * The caller is asking for some property of an item in this folder.\n     *\n     * Which property is indicated by the given PROPERTYKEY (a GUID, aka\n     * SHCOLUMNID).  Common PROPERTYKEYs are in Propkey.h.\n     *\n     * The return value is a variant so can be any type that VARIANTs\n     * support.\n     */\n    virtual comet::variant_t property(\n        const washer::shell::property_key& key,\n        const washer::shell::pidl::cpidl_t& pidl) = 0;\n\nprivate:\n\n    /**\n     * Delegate associated object lookup to subfolder item's CreateViewObject.\n     *\n     * Attempts to bind to the item, given in the PIDL, as an IShellFolder.\n     * If this succeeds, that folder is queried for its associated object by\n     * a call to IShellFolder::CreateViewObject().\n     */\n    ATL::CComPtr<IUnknown> CFolder::_delegate_object_lookup_to_subfolder(\n        HWND hwnd, REFIID riid, PCUITEMID_CHILD pidl)\n    {\n        HRESULT hr;\n        ATL::CComPtr<IShellFolder> subfolder;\n        hr = BindToObject(pidl, NULL, IID_PPV_ARGS(&subfolder));\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n        ATL::CComPtr<IUnknown> object;\n        hr = subfolder->CreateViewObject(\n            hwnd, riid, reinterpret_cast<void**>(&object));\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n        return object;\n    }\n\n    washer::shell::pidl::apidl_t m_root_pidl;\n};\n\n}}} // namespace swish::shell_folder::folder\n"
  },
  {
    "path": "swish/shell_folder/HostFolder.cpp",
    "content": "/* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"HostFolder.h\"\n\n#include \"RemoteFolder.h\"\n#include \"Registry.h\"             // For saved connection details\n#include \"swish/debug.hpp\"\n#include \"swish/frontend/UserInteraction.hpp\" // CUserInteraction\n#include \"swish/host_folder/columns.hpp\" // property_key_from_column_index\n#include \"swish/host_folder/commands/Rename.hpp\"\n#include \"swish/host_folder/commands/Remove.hpp\"\n#include \"swish/host_folder/commands/commands.hpp\" // host_folder_commands\n#include \"swish/host_folder/context_menu_callback.hpp\"\n#include \"swish/host_folder/extract_icon.hpp\"\n#include \"swish/host_folder/host_management.hpp\"\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view,\n                                           // find_host_itemid\n                                           // url_from_host_itemid\n#include \"swish/host_folder/overlay_icon.hpp\"\n#include \"swish/host_folder/properties.hpp\" // property_from_pidl\n#include \"swish/host_folder/host_management.hpp\" // RemoveConnectionFromRegistry\n#include \"swish/host_folder/ViewCallback.hpp\" // CViewCallback\n#include \"swish/remotelimits.h\"   // Text field limits\n#include \"swish/shell/shell_item_array.hpp\" // shell_item_array_from_folder_items\n#include \"swish/windows_api.hpp\" // SHBindToParent\n#include \"swish/trace.hpp\" // trace\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_INTERFACE\n#include <washer/shell/shell.hpp> // strret_to_string\n#include <washer/window/window.hpp>\n#include <washer/window/window_handle.hpp>\n\n#include <comet/smart_enum.h> // make_smart_enumeration\n#include <comet/error.h> // com_error\n\n#include <strsafe.h>  // For StringCchCopy\n\n#include <boost/locale.hpp> // translate\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/optional/optional.hpp>\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <cstring> // memset\n#include <string>\n#include <vector>\n\nusing ATL::CComPtr;\nusing ATL::CComObject;\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\nusing comet::make_smart_enumeration;\nusing comet::throw_com_error;\nusing comet::variant_t;\n\nusing boost::locale::translate;\nusing boost::make_shared;\nusing boost::optional;\nusing boost::shared_ptr;\n\nusing std::vector;\nusing std::wstring;\n\nusing swish::frontend::CUserInteraction;\nusing swish::host_folder::CViewCallback;\nusing swish::host_folder::commands::host_folder_command_provider;\nusing swish::host_folder::commands::Rename;\nusing swish::host_folder::commands::Remove;\nusing swish::host_folder::context_menu_callback;\nusing swish::host_folder::create_host_itemid;\nusing swish::host_folder::extract_icon_co;\nusing swish::host_folder::find_host_itemid;\nusing swish::host_folder::host_itemid_view;\nusing swish::host_folder::host_management::FindConnectionInRegistry;\nusing swish::host_folder::host_management::LoadConnectionsFromRegistry;\nusing swish::host_folder::host_management::RenameConnectionInRegistry;\nusing swish::host_folder::overlay_icon;\nusing swish::host_folder::property_from_pidl;\nusing swish::host_folder::property_key_from_column_index;\nusing swish::host_folder::url_from_host_itemid;\nusing swish::nse::Command;\nusing swish::shell::shell_item_array_from_folder_items;\nusing swish::tracing::trace;\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::property_key;\nusing washer::shell::strret_to_string;\nusing washer::shell::string_to_strret;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nnamespace comet {\n\ntemplate<> struct comtype<::IEnumIDList>\n{\n    static const ::IID& uuid() throw() { return ::IID_IEnumIDList; }\n    typedef ::IUnknown base;\n};\n\ntemplate<> struct enumerated_type_of<IEnumIDList>\n{ typedef PITEMID_CHILD is; };\n\ntemplate<> struct comtype<IQueryAssociations>\n{\n    static const IID& uuid() { return IID_IQueryAssociations; }\n    typedef ::IUnknown base;\n};\n\ntemplate<> struct comtype<::IExtractIconW>\n{\n    static const ::IID& uuid() throw() { return ::IID_IExtractIconW; }\n    typedef ::IUnknown base;\n};\n\n/**\n * Copy policy used to create IEnumIDList from cpidl_t.\n */\ntemplate<> struct impl::type_policy<PITEMID_CHILD>\n{\n    static void init(PITEMID_CHILD& raw_pidl, const cpidl_t& pidl)\n    {\n        pidl.copy_to(raw_pidl);\n    }\n\n    static void clear(PITEMID_CHILD& raw_pidl) { ::CoTaskMemFree(raw_pidl); }\n};\n\n\ntemplate<> struct comtype<::IShellIconOverlay>\n{\n    static const ::IID& uuid() throw() { return ::IID_IShellIconOverlay; }\n    typedef ::IUnknown base;\n};\n\n}\n\n/*--------------------------------------------------------------------------*/\n/*      Functions implementing IShellFolder via folder_error_adapter.       */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Create an IEnumIDList which enumerates the items in this folder.\n *\n * @implementing folder_error_adapter\n *\n * @param hwnd   Optional window handle used if enumeration requires user\n *               input.\n * @param flags  Flags specifying which types of items to include in the\n *               enumeration. Possible flags are from the @c SHCONT enum.\n *\n * @retval S_FALSE if the are no matching items to enumerate.\n */\nIEnumIDList* CHostFolder::enum_objects(HWND hwnd, SHCONTF flags)\n{\n    UNREFERENCED_PARAMETER(hwnd); // No UI required to access registry\n\n    // This folder only contains folders\n    if (!(flags & SHCONTF_FOLDERS) ||\n        (flags & (SHCONTF_NETPRINTERSRCH | SHCONTF_SHAREABLE)))\n        return NULL;\n\n    // Load connections from HKCU\\Software\\Swish\\Connections\n    return make_smart_enumeration<IEnumIDList>(\n        make_shared< vector<cpidl_t> >(LoadConnectionsFromRegistry())).detach();\n}\n\n/**\n * Convert path string relative to this folder into a PIDL to the item.\n *\n * @implementing folder_error_adapter\n *\n * @todo  Handle the attributes parameter.  Should just return\n * GetAttributesOf() the PIDL we create but it is a bit hazy where the\n * host PIDL's responsibilities end and the remote PIDL's start because\n * of the path embedded in the host PIDL.\n */\nPIDLIST_RELATIVE CHostFolder::parse_display_name(\n    HWND hwnd, IBindCtx* bind_ctx, const wchar_t* display_name,\n    ULONG* attributes_inout)\n{\n    trace(__FUNCTION__\" called (display_name=%s)\") % display_name;\n\n    // The string we are trying to parse should be of the form:\n    //    sftp://username@hostname:port/path\n\n    wstring strDisplayName(display_name);\n    if (strDisplayName.empty())\n    {\n        PIDLIST_RELATIVE pidl;\n        root_pidl().copy_to(pidl);\n        return pidl;\n    }\n\n    // Must start with sftp://\n    if (strDisplayName.substr(0, 7) != L\"sftp://\")\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    // Must have @ to separate username from hostname\n    wstring::size_type nAt = strDisplayName.find_first_of(L'@', 7);\n    if (nAt == wstring::npos)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    // Must have : to separate hostname from port number\n    wstring::size_type nColon = strDisplayName.find_first_of(L':', 7);\n    if (nAt == wstring::npos)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n    if (nColon <= nAt)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    // Must have / to separate port number from path\n    wstring::size_type nSlash = strDisplayName.find_first_of(L'/', 7);\n    if (nAt == wstring::npos)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n    if (nColon <= nAt)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    wstring strUser = strDisplayName.substr(7, nAt - 7);\n    wstring strHost = strDisplayName.substr(nAt+1, nColon - (nAt+1));\n    wstring strPort = strDisplayName.substr(nColon+1, nAt - (nSlash+1));\n    wstring strPath = strDisplayName.substr(nSlash+1);\n    if (strUser.empty() || strHost.empty() || strPort.empty() ||\n        strPath.empty())\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    int nPort = _wtoi(strPort.c_str());\n    if (nPort < MIN_PORT || nPort > MAX_PORT)\n        BOOST_THROW_EXCEPTION(com_error(E_FAIL));\n\n    // Create child PIDL for this path segment\n    cpidl_t pidl = create_host_itemid(strHost, strUser, strPath, nPort);\n\n    com_ptr<IShellFolder> subfolder;\n    bind_to_object(\n        pidl.get(), bind_ctx, subfolder.iid(),\n        reinterpret_cast<void**>(subfolder.out()));\n\n    wchar_t wszPath[MAX_PATH];\n    ::StringCchCopyW(wszPath, ARRAYSIZE(wszPath), strPath.c_str());\n\n    pidl_t pidl_path;\n    HRESULT hr = subfolder->ParseDisplayName(\n        hwnd, bind_ctx, wszPath, NULL, pidl_path.out(), attributes_inout);\n    if (FAILED(hr))\n        throw_com_error(subfolder.get(), hr);\n\n    pidl_t pidl_out = root_pidl() + pidl_path;\n    return pidl_out.detach();\n}\n\n/**\n * Retrieve the display name for the specified file object or subfolder.\n *\n * @implementing folder_error_adapter\n */\nSTRRET CHostFolder::get_display_name_of(PCUITEMID_CHILD pidl, SHGDNF flags)\n{\n    if (::ILIsEmpty(pidl))\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    wstring name;\n\n    if (flags & SHGDN_FORPARSING)\n    {\n        if (!(flags & SHGDN_INFOLDER))\n        {\n            // Bind to parent\n            com_ptr<IShellFolder> parent;\n            PCUITEMID_CHILD pidlThisFolder = NULL;\n            HRESULT hr = swish::windows_api::SHBindToParent(\n                root_pidl().get(), parent.iid(),\n                reinterpret_cast<void**>(parent.out()), &pidlThisFolder);\n\n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error(hr));\n\n            STRRET strret;\n            std::memset(&strret, 0, sizeof(strret));\n            hr = parent->GetDisplayNameOf(pidlThisFolder, flags, &strret);\n            if (FAILED(hr))\n                throw_com_error(parent.get(), hr);\n\n            name = strret_to_string<wchar_t>(\n                strret, pidlThisFolder) + L'\\\\';\n        }\n\n        name += url_from_host_itemid(pidl, true);\n    }\n    else if (flags == SHGDN_NORMAL || flags & SHGDN_FORADDRESSBAR)\n    {\n        name = url_from_host_itemid(pidl, false);\n    }\n    else if (flags == SHGDN_INFOLDER || flags & SHGDN_FOREDITING)\n    {\n        name = host_itemid_view(pidl).label();\n    }\n    else\n    {\n        UNREACHABLE;\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n    }\n\n    return string_to_strret(name);\n}\n\nnamespace {\n\n    void notify_shell_that_rename_occurred(\n        const apidl_t& old_pidl, const apidl_t& new_pidl)\n    {\n        ::SHChangeNotify(\n            SHCNE_RENAMEFOLDER, SHCNF_IDLIST, old_pidl.get(), new_pidl.get());\n    }\n}\n\n/**\n * Rename item.\n */\nPITEMID_CHILD CHostFolder::set_name_of(\n    HWND /*hwnd*/, PCUITEMID_CHILD pidl, const wchar_t* new_label,\n    SHGDNF /*flags*/)\n{\n    wstring from_label = host_itemid_view(pidl).label();\n    RenameConnectionInRegistry(from_label, new_label);\n    optional<cpidl_t> connection = FindConnectionInRegistry(new_label);\n    if (connection) {\n        notify_shell_that_rename_occurred(\n            root_pidl() + pidl, root_pidl() + *connection);\n        return connection->detach();\n    } else {\n        return NULL;\n    }\n}\n\n/**\n * Returns the attributes for the items whose PIDLs are passed in.\n *\n * @implementing folder_error_adapter\n */\nvoid CHostFolder::get_attributes_of(\n    UINT pidl_count, PCUITEMID_CHILD_ARRAY pidl_array,\n    SFGAOF* attributes_inout)\n{\n    com_ptr<IShellItemArray> selection = \n        shell_item_array_from_folder_items(this, pidl_count, pidl_array);\n\n    DWORD dwAttribs = 0;\n    dwAttribs |= SFGAO_FOLDER;\n    dwAttribs |= SFGAO_HASSUBFOLDER;\n\n    // This adds a 'rename' item to the default context menu that SetNameOf\n    // directly on the IShellFolder\n    Rename rename_command;\n    if (rename_command.state(selection, false) == Command::state::enabled)\n    {\n        dwAttribs |= SFGAO_CANRENAME;\n    }\n\n    // This adds an 'delete' item to the default context menu that calls the\n    // menu handler with ID DFM_CMD_DELETE\n    Remove remove_command(root_pidl());\n    if (remove_command.state(selection, false) == Command::state::enabled)\n    {\n        dwAttribs |= SFGAO_CANDELETE;\n    }\n\n    *attributes_inout &= dwAttribs;\n}\n\n/*--------------------------------------------------------------------------*/\n/*     Functions implementing IShellFolder2 via folder2_error_adapter.      */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Convert column index to matching PROPERTYKEY, if any.\n *\n * @implementing folder2_error_adapter\n */\nSHCOLUMNID CHostFolder::map_column_to_scid(UINT column_index)\n{\n    return property_key_from_column_index(column_index).get();\n}\n\n/*--------------------------------------------------------------------------*/\n/*                    Functions implementing IShellIconOverlay              */\n/*--------------------------------------------------------------------------*/\n\n\nSTDMETHODIMP CHostFolder::GetOverlayIndex(PCUITEMID_CHILD item, int* index)\n{\n    try\n    {\n        overlay_icon overlay(item);\n\n        if (overlay.has_overlay())\n        {\n            *index = overlay.index();\n            return S_OK;\n        }\n        else\n        {\n            return S_FALSE;\n        }\n    }\n    WASHER_COM_CATCH_INTERFACE(IShellIconOverlay)\n}\n\nSTDMETHODIMP CHostFolder::GetOverlayIconIndex(\n    PCUITEMID_CHILD item, int* icon_index)\n{\n    try\n    {\n        overlay_icon overlay(item);\n\n        if (overlay.has_overlay())\n        {\n            *icon_index = overlay.icon_index();\n            return S_OK;\n        }\n        else\n        {\n            return S_FALSE;\n        }\n    }\n    WASHER_COM_CATCH_INTERFACE(IShellIconOverlay)\n}\n\n/*--------------------------------------------------------------------------*/\n/*                     CFolder NVI internal interface.                      */\n/* These method implement the internal interface of the CFolder abstract    */\n/* class                                                                    */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Return the folder's registered CLSID\n *\n * @implementing CFolder\n */\nCLSID CHostFolder::clsid() const\n{\n    return __uuidof(this);\n}\n\n/**\n * Sniff PIDLs to determine if they are of our type.  Throw if not.\n *\n * @implementing CFolder\n */\nvoid CHostFolder::validate_pidl(PCUIDLIST_RELATIVE pidl) const\n{\n    if (pidl == NULL)\n        BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n    if (!host_itemid_view(pidl).valid())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n}\n\nnamespace {\n\n    com_ptr<ISftpConsumer> consumer_factory(HWND hwnd)\n    {\n        return new CUserInteraction(hwnd);\n    }\n}\n\n/**\n * Create and initialise new folder object for subfolder.\n *\n * @implementing CFolder\n *\n * Create CRemoteFolder initialised with its root PIDL.  CHostFolders\n * don't have any other types of subfolder.\n */\nCComPtr<IShellFolder> CHostFolder::subfolder(const cpidl_t& pidl)\n{\n    CComPtr<IShellFolder> folder = CRemoteFolder::Create(\n        (root_pidl() + pidl).get(), consumer_factory);\n    ATLENSURE_THROW(folder, E_NOINTERFACE);\n\n    return folder;\n}\n\n/**\n * Return a property, specified by PROERTYKEY, of an item in this folder.\n */\nvariant_t CHostFolder::property(const property_key& key, const cpidl_t& pidl)\n{\n    return property_from_pidl(pidl, key);\n}\n\n/*--------------------------------------------------------------------------*/\n/*                    CSwishFolder internal interface.                      */\n/* These method override the (usually no-op) implementations of some        */\n/* in the CSwishFolder base class                                           */\n/*--------------------------------------------------------------------------*/\n\n\n/**\n * Create a toolbar command provider for the folder.\n */\nCComPtr<IExplorerCommandProvider> CHostFolder::command_provider(\n    HWND /*owning_hwnd*/)\n{\n    TRACE(\"Request: IExplorerCommandProvider\");\n\n    return host_folder_command_provider(root_pidl()).get();\n}\n\n/**\n * Create an icon extraction helper object for the selected item.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IExtractIconW> CHostFolder::extract_icon_w(\n    HWND hwnd_view, PCUITEMID_CHILD pidl)\n{\n    optional<window<wchar_t>> owning_view;\n    if (hwnd_view)\n        owning_view = window<wchar_t>(window_handle::foster_handle(hwnd_view));\n\n    return new extract_icon_co(owning_view, pidl);\n}\n\n/**\n * Create a file association handler for host items.\n *\n * @implementing CSwishFolder\n *\n * We don't need to look at the PIDLs as all host items are the same.\n */\nCComPtr<IQueryAssociations> CHostFolder::query_associations(\n    HWND /*hwnd*/, UINT /*cpidl*/, PCUITEMID_CHILD_ARRAY /*apidl*/)\n{\n    TRACE(\"Request: IQueryAssociations\");\n\n    CComPtr<IQueryAssociations> spAssoc;\n    HRESULT hr = ::AssocCreate(\n        CLSID_QueryAssociations, IID_PPV_ARGS(&spAssoc));\n    ATLENSURE_SUCCEEDED(hr);\n\n    // Get CLSID in {DWORD-WORD-WORD-WORD-WORD.DWORD} form\n    LPOLESTR posz;\n    ::StringFromCLSID(__uuidof(CHostFolder), &posz);\n    shared_ptr<OLECHAR> clsid(posz, ::CoTaskMemFree);\n    posz = NULL;\n\n    // Initialise default assoc provider to use Swish CLSID key for data.\n    // This is necessary to pick up properties and TileInfo etc.\n    hr = spAssoc->Init(0, clsid.get(), NULL, NULL);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error_from_interface(spAssoc.p, hr));\n\n    return spAssoc;\n}\n\nnamespace {\n\n    HRESULT CALLBACK menu_callback(\n        IShellFolder* folder, HWND hwnd_view, IDataObject* selection,\n        UINT message_id, WPARAM wparam, LPARAM lparam)\n    {\n        CRemoteFolder* remote_folder = static_cast<CRemoteFolder*>(folder);\n\n        context_menu_callback callback(remote_folder->root_pidl());\n        return callback(hwnd_view, selection, message_id, wparam, lparam);\n    }\n\n}\n\n/**\n * Create a context menu for the selected items.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IContextMenu> CHostFolder::context_menu(\n    HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n{\n    TRACE(\"Request: IContextMenu\");\n    assert(cpidl > 0);\n\n    // Get keys associated with filetype from registry.\n    // We only take into account the item that was right-clicked on\n    // (the first array element) even if this was a multi-selection.\n    //\n    // This article says that we don't need to specify the keys:\n    // http://groups.google.com/group/microsoft.public.platformsdk.shell/\n    // browse_thread/thread/6f07525eaddea29d/\n    // but we do for the context menu to appear in versions of Windows\n    // earlier than Vista.\n    HKEY *akeys; UINT ckeys;\n    ATLENSURE_THROW(SUCCEEDED(\n        CRegistry::GetHostFolderAssocKeys(&ckeys, &akeys)),\n        E_UNEXPECTED  // Might fail if registry is corrupted\n    );\n\n    CComPtr<IShellFolder> spThisFolder = this;\n    ATLENSURE_THROW(spThisFolder, E_OUTOFMEMORY);\n\n    // Create default context menu from list of PIDLs\n    CComPtr<IContextMenu> spMenu;\n    HRESULT hr = ::CDefFolderMenu_Create2(\n        root_pidl().get(), hwnd, cpidl, apidl, spThisFolder,\n        menu_callback, ckeys, akeys, &spMenu);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error(hr));\n\n    return spMenu;\n}\n\n/**\n * Create a data object for the selected items.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IDataObject> CHostFolder::data_object(\n    HWND /*hwnd*/, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n{\n    TRACE(\"Request: IDataObject\");\n    assert(cpidl > 0);\n\n    // A DataObject is required in order for the call to\n    // CDefFolderMenu_Create2 (above) to succeed on versions of Windows\n    // earlier than Vista\n\n    CComPtr<IDataObject> spdo;\n    HRESULT hr = ::CIDLData_CreateFromIDArray(\n        root_pidl().get(), cpidl,\n        reinterpret_cast<PCUIDLIST_RELATIVE_ARRAY>(apidl), &spdo);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error(hr));\n\n    return spdo;\n}\n\n/**\n * Create an instance of our Shell Folder View callback handler.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IShellFolderViewCB> CHostFolder::folder_view_callback(HWND /*hwnd*/)\n{\n    return new CViewCallback(root_pidl());\n}\n"
  },
  {
    "path": "swish/shell_folder/HostFolder.h",
    "content": "/**\n    @file\n\n    Explorer folder to handle SFTP connection items.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"SwishFolder.hpp\"       // Superclass\n\n#include \"swish/host_folder/columns.hpp\" // Column\n\n#include \"resource.h\"            // main symbols\n#include \"Swish.h\" // For CHostFolder UUID\n#include \"swish/CoFactory.hpp\"   // CComObject factory\n\n#include \"swish/atl.hpp\"         // Common ATL setup\n\n#include <vector>\n\nclass ATL_NO_VTABLE CHostFolder :\n    public IShellIconOverlay,\n    public swish::shell_folder::folder::CSwishFolder<\n        swish::host_folder::Column>\n{\npublic:\n\n    BEGIN_COM_MAP(CHostFolder)\n        COM_INTERFACE_ENTRY(IShellIconOverlay)\n        COM_INTERFACE_ENTRY_CHAIN(CSwishFolder)\n    END_COM_MAP()\n\nprotected:\n\n    CLSID clsid() const;\n\n    void validate_pidl(PCUIDLIST_RELATIVE pidl) const;\n\n    ATL::CComPtr<IShellFolder> subfolder(\n        const washer::shell::pidl::cpidl_t& pidl);\n    \n    comet::variant_t property(\n        const washer::shell::property_key& key,\n        const washer::shell::pidl::cpidl_t& pidl);\n\n    ATL::CComPtr<IExplorerCommandProvider> command_provider(HWND hwnd);\n\n    ATL::CComPtr<IExtractIconW> extract_icon_w(\n        HWND hwnd, PCUITEMID_CHILD pidl);\n    ATL::CComPtr<IQueryAssociations> query_associations(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n    ATL::CComPtr<IContextMenu> context_menu(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n    ATL::CComPtr<IDataObject> data_object(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n\n    ATL::CComPtr<IShellFolderViewCB> folder_view_callback(HWND hwnd);\n\npublic:\n\n    // IShellFolder (via folder_error_adapter)\n    virtual IEnumIDList* enum_objects(HWND hwnd, SHCONTF flags);\n    \n    virtual void get_attributes_of(\n        UINT pidl_count, PCUITEMID_CHILD_ARRAY pidl_array,\n        SFGAOF* flags_inout);\n\n    virtual STRRET get_display_name_of(\n        PCUITEMID_CHILD pidl, SHGDNF uFlags);\n\n    virtual PIDLIST_RELATIVE parse_display_name(\n        HWND hwnd, IBindCtx* bind_ctx, const wchar_t* display_name,\n        ULONG* attributes_inout);\n\n    virtual PITEMID_CHILD set_name_of(\n        HWND hwnd, PCUITEMID_CHILD pidl, const wchar_t* name,\n        SHGDNF flags);\n\n    // IShellFolder2 (via folder_error_adapter2)\n    virtual SHCOLUMNID map_column_to_scid(UINT column_index);\n\n    // IShellIconOverlay\n    STDMETHOD(GetOverlayIndex)(PCUITEMID_CHILD item, int* index);\n    STDMETHOD(GetOverlayIconIndex)(PCUITEMID_CHILD item, int* icon_index);\n};\n"
  },
  {
    "path": "swish/shell_folder/IconExtractor.cpp",
    "content": "/*  Implementation of icon extraction handler.\n\n    Copyright (C) 2008  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#include \"IconExtractor.h\"\n\n#include <shellapi.h>         // For SHGetFileInfo\n#include <strsafe.h>          // For StringCchCopy\n\nusing ATL::CComPtr;\n\n/*static*/ CComPtr<IExtractIconW> CIconExtractor::Create(\n    PCTSTR szFilename, bool fIsFolder)\n{\n    CComPtr<CIconExtractor> sp = CIconExtractor::CreateCoObject();\n    sp->Initialize(szFilename, fIsFolder);\n    return sp.p;\n}\n\n/**\n * Sets file or folder that this IconExtractor is being used for.\n *\n * @param szFilename The filename of the file or folder whose icon we want.\n * @param fIsFolder  Flag whether this is a file (@c false) or folder (@c true).\n */\nvoid CIconExtractor::Initialize(PCTSTR szFilename, bool fIsFolder)\n{\n    m_fForFolder = fIsFolder;\n    m_strFilename = szFilename;\n}\n\n/**\n * Retrieves location of the appropriate icon as an index into the system list.\n *\n * @implementing IExtractIconW\n *\n * @param[in]  uFlags        Flags that determine what type of icon is being\n *                           requested.\n * @param[out] pwszIconFile  The name of the file to find the icon in. In our \n *                           case we return '*' to indicate that the icon is in \n *                           the system index and the value returned in \n *                           @p piIndex is the index to it.\n * @param[in]  cchMax        The size of the buffer passed as @p szIconFile.\n * @param[out] piIndex       The index to the icon in the system list.\n * @param[out] pwFlags       Output flags. In our case set to indicate that \n *                           @p szIconFile is not a real filename.\n *             \n */\nSTDMETHODIMP CIconExtractor::GetIconLocation(\n    UINT uFlags, PWSTR pwszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags )\n{\n    ATLTRACE(\"CIconExtractor::GetIconLocation called\\n\");\n\n    // Look for icon's index into system list\n    int index = _GetIconIndex(uFlags);\n    if (index < 0)\n        return S_FALSE; // None found - make shell use Unknown\n\n    // Output * as filename to indicate icon is in system list\n    ATLVERIFY(SUCCEEDED(\n        ::StringCchCopyW(pwszIconFile, cchMax, L\"*\")));\n    *pwFlags = GIL_NOTFILENAME;\n    *piIndex = index;\n\n    return S_OK;\n}\n\n/**\n * @overload\n * @implementing IExtractIconA\n */\nSTDMETHODIMP CIconExtractor::GetIconLocation(\n    UINT uFlags, LPSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)\n{\n    ATLTRACE(\"CIconExtractor::GetIconLocation called\\n\");\n\n    // Look for icon's index into system list\n    int index = _GetIconIndex(uFlags);\n    if (index < 0)\n        return S_FALSE; // None found - make shell use Unknown\n\n    // Output * as filename to indicate icon is in system list\n    ATLVERIFY(SUCCEEDED(\n        ::StringCchCopyA(szIconFile, cchMax, \"*\")));\n    *pwFlags = GIL_NOTFILENAME;\n    *piIndex = index;\n\n    return S_OK;\n}\n\n/**\n * Extract an icon bitmap given the information passed.\n *\n * @implementing IExtractIconW\n *\n * @returns S_FALSE to tell the shell to extract the icons itself.\n */\nSTDMETHODIMP CIconExtractor::Extract(PCWSTR, UINT, HICON *, HICON *, UINT)\n{\n    ATLTRACE(\"CIconExtractor::Extract called\\n\");\n    return S_FALSE;\n}\n\n/**\n * @overload\n * @implementing IExtractIconA\n */\nSTDMETHODIMP CIconExtractor::Extract(PCSTR, UINT, HICON *, HICON *, UINT)\n{\n    ATLTRACE(\"CIconExtractor::Extract (A) called\\n\");\n    return S_FALSE;\n}\n\nint CIconExtractor::_GetIconIndex(UINT uFlags)\n{\n    if (uFlags & GIL_DEFAULTICON)\n        return 0;\n\n    DWORD dwAttributes = \n        (m_fForFolder) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;\n\n    UINT uInfoFlags = SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX;\n    if (uFlags & GIL_OPENICON)\n        uInfoFlags |= SHGFI_OPENICON;\n\n    // Look up index to default icon in current system list\n    {\n        // Get index to default icon in system list\n        SHFILEINFO shfi;\n        if(::SHGetFileInfo(m_strFilename, dwAttributes, &shfi, \n            sizeof(shfi), uInfoFlags))\n        {\n            return shfi.iIcon;\n        }\n        else\n            return -1; // None found\n    }\n}\n\n// CIconExtractor"
  },
  {
    "path": "swish/shell_folder/IconExtractor.h",
    "content": "/**\n    @file\n\n    Component allowing icon extraction based on file extension.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/CoFactory.hpp\" // CoObject factory mixin\n\n#include \"swish/atl.hpp\"  // Common ATL setup\n#include <atlstr.h>     // CString\n\n#include <shlobj.h>     // Windows Shell API\n\nclass ATL_NO_VTABLE CIconExtractor :\n    public IExtractIconW,\n    public IExtractIconA,\n    public ATL::CComObjectRoot,\n    public swish::CCoFactory<CIconExtractor>\n{\npublic:\n\n    BEGIN_COM_MAP(CIconExtractor)\n        COM_INTERFACE_ENTRY(IExtractIconW)\n        COM_INTERFACE_ENTRY(IExtractIconA)\n    END_COM_MAP()\n\n    CIconExtractor() : m_fForFolder(false) {}\n\n    static ATL::CComPtr<IExtractIconW> Create(\n        PCTSTR szFilename, bool fIsFolder);\n    void Initialize(PCTSTR szFilename, bool fIsFolder);\n\n    // IExtractIconW\n    IFACEMETHODIMP GetIconLocation(\n        UINT uFlags, __out_ecount(cchMax) PWSTR pwszIconFile, UINT cchMax,\n        __out int *piIndex, __out UINT *pwFlags);\n\n    IFACEMETHODIMP Extract(\n        PCWSTR pszFile, UINT nIconIndex, __out_opt HICON *phiconLarge,\n        __out_opt HICON *phiconSmall, UINT nIconSize);\n\n    // IExtractIconA\n    IFACEMETHODIMP GetIconLocation(\n        UINT uFlags, __out_ecount(cchMax) LPSTR szIconFile, UINT cchMax,\n        __out int *piIndex, __out UINT *pwFlags);\n\n    IFACEMETHODIMP Extract(\n        PCSTR pszFile, UINT nIconIndex, __out_opt HICON *phiconLarge,\n        __out_opt HICON *phiconSmall, UINT nIconSize);\n\nprivate:\n    int _GetIconIndex(UINT uFlags);\n\n    bool m_fForFolder;           ///< Are we trying to extract the icon \n                                 ///< for a Folder?\n    ATL::CString m_strFilename;  ///< File to get default icon for.\n};"
  },
  {
    "path": "swish/shell_folder/KbdInteractiveDialog.cpp",
    "content": "/*  WTL dialog box for keyboard-interactive requests.\n\n    Copyright (C) 2008, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#include \"KbdInteractiveDialog.h\"\n\n#include \"swish/utils.hpp\"\n\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/locale.hpp> // translate\n\n#include \"wtl.hpp\"          // WTL\n#include <atlctrls.h>       // WTL control wrappers\n#include <atlstr.h> // CString\n\nusing swish::utils::Utf8StringToWideString;\nusing swish::utils::WideStringToUtf8String;\nusing boost::locale::translate;\n\nusing WTL::CStatic;\nusing WTL::CEdit;\nusing WTL::CButton;\nusing WTL::CClientDC;\nusing WTL::CFontHandle;\n\nusing ATL::CString;\n\nusing std::pair;\nusing std::string;\nusing std::vector;\n\n#define SEPARATION 10\n#define MINI_SEPARATION 3\n#define RESPONSE_BOX_HEIGHT 22\n\n\nCKbdInteractiveDialog::CKbdInteractiveDialog(\n    const std::string& title, const std::string& instructions,\n    const std::vector<std::pair<std::string, bool>>& prompts)\n    :\nm_title(title), m_instructions(instructions), m_prompts(prompts) {}\n\nvector<string> CKbdInteractiveDialog::GetResponses()\n{\n    return m_responses;\n}\n\n/*----------------------------------------------------------------------------*\n * Event Handlers\n *----------------------------------------------------------------------------*/\n\nLRESULT CKbdInteractiveDialog::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)\n{\n    // If server specifies a title use it as the dialogue title\n    if (!m_title.empty())\n        this->SetWindowText(\n            Utf8StringToWideString(m_title).c_str());\n    else\n        this->SetWindowText(\n            translate(L\"Keyboard-interactive request\").str().c_str());\n\n    // Get size of this dialogue box\n    CRect rectDialog;\n    this->GetClientRect(rectDialog);\n\n    // Control drawing 'cursor' - increment each time we move down the window\n    CPoint point(0,0);\n\n    // Draw instruction label\n    CRect rect = _DrawInstruction(rectDialog);\n    point.Offset(rect.left, rect.Height()+2*SEPARATION);\n\n    // Draw prompts and response boxes\n    typedef pair<string, bool> prompt_pair;\n    BOOST_FOREACH(const prompt_pair& prompt, m_prompts)\n    {\n        CRect rectPrompt = _DrawPrompt(prompt.first, point, rectDialog);\n\n        // Increment point by height of prompt text plus small separation\n        point.Offset(0, rectPrompt.Height() + MINI_SEPARATION);\n\n        CRect rectResponse = _DrawResponseBox(\n            !prompt.second, point, rectDialog);\n\n        // Increment point by height of response box plus separation\n        point.Offset(0, rectResponse.Height() + SEPARATION);\n    }\n\n    // Move OK and Cancel below prompts\n    CRect rectOKCancel = _DrawOKCancel(point, rectDialog);\n\n    // Expand dialogue downward to include all controls\n    rectDialog.bottom = rectOKCancel.bottom + SEPARATION;\n    ATLVERIFY( this->ResizeClient(rectDialog.Width(), rectDialog.Height()) );\n\n    // Place dialogue and set focus\n    CenterWindow();\n    if (m_vecResponseWindows.size() && m_vecResponseWindows[0])\n        ::SetFocus(m_vecResponseWindows[0]);\n\n    return 0;\n}\n\nLRESULT CKbdInteractiveDialog::OnOK(WORD, WORD wID, HWND, BOOL&)\n{\n    _ExchangeData();\n    EndDialog(wID);\n    return 0;\n}\n\nLRESULT CKbdInteractiveDialog::OnCancel(WORD, WORD wID, HWND, BOOL&)\n{\n    EndDialog(wID);\n    return 0;\n}\n\n/*----------------------------------------------------------------------------*\n * Private functions\n *----------------------------------------------------------------------------*/\n\nCRect CKbdInteractiveDialog::_DrawInstruction(CRect rectDialog)\n{\n    // Fix instruction text's width to 20px fewer than the dialog and centre\n    CRect rect(0, 0, rectDialog.Width()-20, 0);\n    rect.OffsetRect(10, 10);\n\n    // Always draw the instruction label even if empty to override resource text\n    CStatic instruction(GetDlgItem(IDC_INSTRUCTION));\n    instruction.SetWindowText(Utf8StringToWideString(m_instructions).c_str());\n\n    CFontHandle font, fontOld;\n    font = instruction.GetFont();\n\n    // Calculate neccessary size of instruction label\n    CClientDC dc(instruction);\n    fontOld = dc.SelectFont(font);\n    dc.DrawText(Utf8StringToWideString(m_instructions).c_str(), -1, rect,\n        DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX);\n    dc.SelectFont(fontOld);\n\n    // Set instruction size, position and text\n    instruction.MoveWindow(rect);\n    instruction.SetWindowText(Utf8StringToWideString(m_instructions).c_str());\n\n    // Return instruction's rectangle\n    return rect;\n}\n\nCRect CKbdInteractiveDialog::_DrawPrompt(\n    const string& prompt, CPoint point, CRect rectDialog)\n{\n    // Use same font as instruction label\n    CStatic instruction(GetDlgItem(IDC_INSTRUCTION));\n    CFontHandle font, fontOld;\n    font = instruction.GetFont();\n\n    // Prompt label\n    CStatic prompt_label;\n    prompt_label.Create(*this, NULL, NULL, \n        WS_VISIBLE | WS_CHILD | SS_WORDELLIPSIS | SS_NOPREFIX);\n\n    // Fix prompt text's width to 20px fewer than the dialog\n    CRect rect(0, 0, rectDialog.Width()-20, 0);\n\n    // Calculate necessary (vertical) size of prompt label\n    CClientDC dcPrompt(prompt_label);\n    fontOld = dcPrompt.SelectFont(font);\n    dcPrompt.DrawText(\n        Utf8StringToWideString(prompt).c_str(), -1, rect,\n        DT_CALCRECT | DT_WORD_ELLIPSIS | DT_NOPREFIX);\n    dcPrompt.SelectFont(fontOld);\n\n    // Set prompt size, position, font and text\n    rect.OffsetRect(point);\n    prompt_label.MoveWindow(rect);\n    prompt_label.SetFont(font);\n    prompt_label.SetWindowText(Utf8StringToWideString(prompt).c_str());\n\n    // Return prompt's rectangle\n    return rect;\n}\n\nCRect CKbdInteractiveDialog::_DrawResponseBox(\n    bool fHideResponse, CPoint point, CRect rectDialog)\n{\n    // Use same font as instruction label\n    CStatic instruction(GetDlgItem(IDC_INSTRUCTION));\n    CFontHandle font = instruction.GetFont();\n\n    // Response text box\n    CEdit edit;\n    DWORD dwWindowFlags = WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL;\n    if (fHideResponse)\n    {\n        dwWindowFlags |= ES_PASSWORD;\n    }\n    DWORD dwWindowFlagsEx = WS_EX_CLIENTEDGE;\n    edit.Create(*this, NULL, NULL, dwWindowFlags, dwWindowFlagsEx);\n    m_vecResponseWindows.push_back(edit); // Push onto list of response boxes\n\n    // Fix response box's width to 20px fewer than the dialog\n    CRect rect(0, 0, rectDialog.Width()-20, RESPONSE_BOX_HEIGHT);\n\n    // Set response size, position and font\n    rect.MoveToXY(point.x, point.y);\n    edit.MoveWindow(rect);\n    edit.SetFont(font);\n\n    // Return response box's rectangle\n    return rect;\n}\n\nCRect CKbdInteractiveDialog::_DrawOKCancel(\n    CPoint point, CRect rectDialog)\n{\n    CButton btnOK(GetDlgItem(IDOK)), btnCancel(GetDlgItem(IDCANCEL));\n\n    CRect rectOK, rectCancel;\n    ATLVERIFY( btnOK.GetClientRect(rectOK) );\n    ATLVERIFY( btnCancel.GetClientRect(rectCancel) );\n    rectCancel.MoveToXY(\n        rectDialog.right - rectCancel.Width() - SEPARATION,\n        point.y + SEPARATION);\n    rectOK.MoveToXY(\n        rectDialog.right - rectCancel.Width() - rectOK.Width() - 2*SEPARATION,\n        point.y + SEPARATION);\n    ATLVERIFY( btnOK.MoveWindow(rectOK) );\n    ATLVERIFY( btnCancel.MoveWindow(rectCancel) );\n\n    // Return the union of the two buttons' rectangles\n    return rectOK | rectCancel;\n}\n\n/**\n * Copy data from response Win32 edit boxes into member variable.\n *\n * This is necessary as the dialogue and its text boxed are destroyed when OK\n * or Cancel is clicked.  Therefore, this function must be called in the\n * OK button click event handler.  The responses can be retrieved from the\n * member variable using the GetResonses() function after the\n * dialogue window has been destroyed.\n */\nvoid CKbdInteractiveDialog::_ExchangeData()\n{\n    m_responses.clear();\n\n    for each (HWND hwnd in m_vecResponseWindows)\n    {\n        CEdit edit(hwnd);\n        CString strResponse;\n        edit.GetWindowText(strResponse);\n        m_responses.push_back(WideStringToUtf8String(strResponse.GetString()));\n    }\n}\n"
  },
  {
    "path": "swish/shell_folder/KbdInteractiveDialog.h",
    "content": "/**\n    @file\n\n    WTL dialog box for keyboard-interactive requests.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"resource.h\"   // main symbols\n\n#include \"swish/atl.hpp\"  // Common ATL setup\n#include <atlwin.h>     // ATL windowing classes\n#include <atltypes.h>   // For CRect and CPoint\n\n#include <string>\n#include <utility> // pair\n#include <vector>\n\nclass CKbdInteractiveDialog : \n    public ATL::CDialogImpl<CKbdInteractiveDialog>\n{\npublic:\n\n    /** Dialog box resource identifier */\n    enum { IDD = IDD_KBDINTERACTIVEDIALOG };\n\n    CKbdInteractiveDialog(\n        const std::string& title, const std::string& instructions,\n        const std::vector<std::pair<std::string, bool>>& prompts);\n\n    std::vector<std::string> GetResponses();\n\n    BEGIN_MSG_MAP(CKbdInteractiveDialog)\n        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)\n        COMMAND_HANDLER(IDOK, BN_CLICKED, OnOK)\n        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnCancel)\n    END_MSG_MAP()\n\nprivate:\n    /** @name Message handlers */\n    // @{\n    LRESULT __callback OnInitDialog(UINT, WPARAM, LPARAM, BOOL&);\n    // @}\n\n    /** @name Command handlers */\n    // @{\n    LRESULT __callback OnOK(WORD, WORD, HWND, BOOL&);\n    LRESULT __callback OnCancel(WORD, WORD, HWND, BOOL&);\n    // @}\n\n    /** @name GUI drawing */\n    // @{\n    CRect _DrawInstruction(__in CRect rectDialog);\n    CRect _DrawPrompt(\n        const std::string&, __in CPoint point, __in CRect rectDialog);\n    CRect _DrawResponseBox(\n        bool fHideResponse, __in CPoint point, __in CRect rectDialog);\n    CRect _DrawOKCancel(__in CPoint point, __in CRect rectDialog);\n    // @}\n\n    void _ExchangeData();\n\n    // Input\n    std::string m_title;\n    std::string m_instructions;\n    std::vector<std::pair<std::string, bool>> m_prompts;\n\n    // Output\n    std::vector<HWND> m_vecResponseWindows;\n    std::vector<std::string> m_responses;\n};\n"
  },
  {
    "path": "swish/shell_folder/Pidl.h",
    "content": "/**\n    @file\n\n    PIDL wrapper classes.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include <comet/error.h> // com_error\n\n#include <shlobj.h>  // Native PIDL handling\n\n/**\n * Base class of all PIDL classes which wrap a non-const PIDL.\n */\ntemplate <typename IdListType>\nclass CPidlData\n{\nprotected:\n    /** @name PIDL Types */\n    // @{\n    typedef IdListType *PidlType;            ///< @b Non-const PIDL type\n    typedef const IdListType *ConstPidlType; ///< @b Const PIDL type\n    typedef PidlType Type; ///< Which of const or non-const @b this PIDL is\n    // @}\n\n    CPidlData() : m_pidl(NULL) {}\n    explicit CPidlData( __in_opt Type pidl ) throw() : m_pidl(pidl) {}\n    explicit CPidlData( __in const CPidlData& pidl ) throw() : m_pidl(pidl) {}\n\npublic:\n    inline bool operator!() const throw()\n    {\n        return m_pidl == NULL;\n    }\n\n    /**\n     * Implicitly convert wrapped PIDL to another type.\n     *\n     * This template delegates to Type (the type of the raw PIDL) decisions \n     * about which types this PIDL can be converted to.  The code will not \n     * compile if @p IdListSupertype is not compatible with Type.  Used in:\n     *    @code const IdListSupertype p = Type m_pidl @endcode\n     *\n     * @tparam IdListSupertype  Target type of conversion. Does not have to be\n     *                          supertype of raw PIDL type.  May be same type.\n     */\n    template<typename IdListSupertype>\n    operator const IdListSupertype() const throw()\n    {\n        return m_pidl;\n    }\n\n    Type m_pidl;\n};\n\n/**\n * Base class of all PIDL classes which wrap a const PIDL.\n */\ntemplate <typename IdListType>\nclass CPidlConstData\n{\nprotected:\n    /** @name PIDL Types */\n    // @{\n    typedef IdListType *PidlType;            ///< @b Non-const PIDL type\n    typedef const IdListType *ConstPidlType; ///< @b Const PIDL type\n    typedef ConstPidlType Type; ///< Which of const or non-const @b this PIDL is\n    // @}\n\n    CPidlConstData() : m_pidl(NULL) {}\n    CPidlConstData( __in_opt Type pidl ) throw() : m_pidl(pidl) {}\n    CPidlConstData( __in const CPidlConstData& pidl ) throw() : m_pidl(pidl) {}\n\npublic:\n    inline bool operator!() const throw()\n    {\n        return m_pidl == NULL;\n    }\n\n    /**\n     * Implicitly convert wrapped PIDL to another type.\n     *\n     * This template delegates to Type (the type of the raw PIDL) decisions \n     * about which types this PIDL can be converted to.  The code will not \n     * compile if @p IdListSupertype is not compatible with Type.  Used in:\n     *    @code const IdListSupertype p = Type m_pidl @endcode\n     *\n     * @tparam IdListSupertype  Target type of conversion. Does not have to be\n     *                          supertype of raw PIDL type.  May be same type.\n     */\n    template<typename IdListSupertype>\n    operator const IdListSupertype() const throw()\n    {\n        return m_pidl;\n    }\n\n    Type m_pidl;\n};\n\n/**\n * Class adding operations common to all types of PIDL: const or non-const,\n * relative, absolute or child.\n *\n * The template takes the type of the ITEMIDLIST; either relative \n * (ITEMIDLIST_RELATIVE), absolute (ITEMIDLIST_ABSOLUTE) or child (ITEMID_CHILD). \n * This enhances type-safety when using the PIDL with functions, etc.  The only \n * state stored by this wrapper is the PIDL itself and so can be used anywhere \n * a PIDL can.\n *\n * @param IdListType  The type of ITEMIDLIST whose pointer to be wrapped; \n *                    either ITEMIDLIST_RELATIVE, ITEMIDLIST_ABSOLUTE or \n *                    ITEMID_CHILD.\n *\n * @attention This class requires STRICT_TYPED_ITEMIDS to be defined in order\n *            to make the different types of PIDL distinct.  Without it, all\n *            three PIDL types appear to the compiler as LPITEMIDLIST.\n */\ntemplate <typename DataT>\nclass CPidlBase : public DataT\n{\nprotected:\n    typedef typename DataT::PidlType PidlType;\n    typedef typename DataT::ConstPidlType ConstPidlType;\n\npublic:\n    CPidlBase() {}\n    CPidlBase( __in_opt typename DataT::Type pidl ) throw() : DataT(pidl) {}\n    CPidlBase( __in const CPidlBase& pidl ) throw() : DataT(pidl) {}\n\n    PidlType CopyTo() const throw(...)\n    {\n        return Clone(m_pidl);\n    }\n\n    PidlType CopyParent() const throw(...)\n    {\n        PidlType pidl = CopyTo();\n        if (!::ILRemoveLastID(pidl))\n\t\t{\n\t\t\tassert(false);\n\t\t\tthrow comet::com_error(E_FAIL);\n\t\t}\n        return pidl;\n    }\n\n    PCUIDLIST_RELATIVE GetNext() const throw()\n    {\n        if (m_pidl == NULL)\n            return NULL;\n\n        PCUIDLIST_RELATIVE pidl = ::ILNext(m_pidl);\n        return (::ILIsEmpty(pidl)) ? NULL : pidl;\n    }\n\n    PCUITEMID_CHILD GetLast() const throw(...)\n    {\n        return ::ILFindLastID(m_pidl);\n    }\n\n    inline bool IsEmpty() const throw()\n    {\n        return ((m_pidl == NULL) || (m_pidl->mkid.cb == 0));\n    }\n\n    static PidlType Clone( __in_opt ConstPidlType pidl ) throw(...)\n    {\n        if (pidl == NULL)\n            return NULL;\n        \n        PidlType pidlOut = static_cast<PidlType>(::ILClone(pidl));\n        if (pidlOut == NULL)\n            AtlThrow(E_OUTOFMEMORY);\n\n        return pidlOut;\n    }\n};\n\n/** @name PIDL Handle type declarations.\n *\n * These types create PIDL wrappers with @b unmanaged lifetimes.  The PIDLs\n * are also const so cannot be modified or destroyed.\n *\n * This effect is achieved by deriving the common PIDL operations base\n * class from a const-PIDL data wrapper.\n */\ntypedef CPidlBase< CPidlConstData<ITEMIDLIST_RELATIVE> > CRelativePidlHandle;\ntypedef CPidlBase< CPidlConstData<ITEMIDLIST_ABSOLUTE> > CAbsolutePidlHandle;\ntypedef CPidlBase< CPidlConstData<ITEMID_CHILD> > CChildPidlHandle;\n\n/**\n * Wrapper for a PIDL with a @b managed lifetime.\n *\n * This class augments the commmon operations provided in CPidlBase with\n * those that are specific to non-const PIDLs such as creation and destruction\n * as well as concatenation.\n *\n * The template (and those from which it is derived) takes the type of \n * the ITEMIDLIST; either relative (ITEMIDLIST_RELATIVE), absolute \n  *(ITEMIDLIST_ABSOLUTE) or child (ITEMID_CHILD). This enhances \n * type-safety when using the PIDL with functions, etc.  The only state \n * stored by this wrapper is the PIDL itself and so can be used anywhere \n * a PIDL can.\n *\n * Most methods that take a PIDL argument, including the constructors, make a\n * copy of the PIDL first, although the class can take ownership of an\n * exisiting PIDL with Attach().\n *\n * Several of the methods return a reference to the current CPidl so that\n * operations can be chained, e.g.\n * @code pidl.Attach(old).Append(item).Detach() @endcode\n *\n * @param IdListType  The type of ITEMIDLIST whose pointer to be wrapped; \n *                    either ITEMIDLIST_RELATIVE, ITEMIDLIST_ABSOLUTE or \n *                    ITEMID_CHILD.\n *\n * @attention This class requires STRICT_TYPED_ITEMIDS to be defined in order\n *            to make the different types of PIDL distinct.  Without it, all\n *            three PIDL types appear to the compiler as LPITEMIDLIST.\n */\ntemplate <typename IdListType>\nclass CPidl : public CPidlBase< CPidlData<IdListType> >\n{\npublic:\n    CPidl() {}\n    CPidl( __in_opt ConstPidlType pidl ) throw(...) : CPidlBase(Clone(pidl)) {}\n    CPidl( __in const CPidl& pidl ) throw(...) : CPidlBase(Clone(pidl)) {}\n\n    CPidl& operator=( __in const CPidl& pidl ) throw(...)\n    {\n        if (m_pidl != pidl.m_pidl)\n        {\n            CopyFrom(pidl);\n        }\n        return *this;\n    }\n\n    /**\n     * Concatenation constructor.\n     */\n    explicit CPidl(\n        __in_opt ConstPidlType pidl1, __in_opt PCUIDLIST_RELATIVE pidl2 )\n    throw(...)\n    {\n        if (::ILIsEmpty(pidl1) && ::ILIsEmpty(pidl2))\n            return;\n\n        m_pidl = reinterpret_cast<PidlType>(::ILCombine(\n            reinterpret_cast<PCIDLIST_ABSOLUTE>(pidl1), pidl2));\n        if (!m_pidl)\n\t\t\tthrow comet::com_error(E_OUTOFMEMORY);\n    }\n    \n    /**\n     * Return raw address of PIDL to allow use as an out-parameter.\n     *\n     * Any existing PIDL will first be destroyed.\n     */\n    PidlType* operator&() throw()\n    {\n        Delete();\n        return &m_pidl;\n    }\n\n    ~CPidl() throw()\n    {\n        Delete();\n    }\n\n    CPidl& Attach( __in_opt PidlType pidl ) throw()\n    {\n        Delete();\n        m_pidl = pidl;\n        return *this;\n    }\n\n    CPidl& CopyFrom( __in_opt ConstPidlType pidl ) throw(...)\n    {\n        return Attach(Clone(pidl));\n    }\n\n    PidlType Detach() throw()\n    {\n        PidlType pidl = m_pidl;\n        m_pidl = NULL;\n        return pidl;\n    }\n\n    void Delete()\n    {\n        ::ILFree(m_pidl);\n        m_pidl = NULL;\n    }\n\n    CPidl& Append(__in_opt PCUIDLIST_RELATIVE pidl) throw(...)\n    {\n        if (::ILIsEmpty(pidl))\n            return *this;\n\n        return Attach(CPidl(m_pidl, pidl).Detach());\n    }\n};\n\n/**\n * Wrapper around a @b relative PIDL with a @b managed lifetime.\n */\ntypedef CPidl<ITEMIDLIST_RELATIVE> CRelativePidl;\n\n/**\n * Wrapper around an @b absolute PIDL with a @b managed lifetime.\n */\ntypedef CPidl<ITEMIDLIST_ABSOLUTE> CAbsolutePidl;\n\n/**\n * Wrapper around a @b child PIDL with a @b managed lifetime.\n */\ntypedef CPidl<ITEMID_CHILD> CChildPidl;"
  },
  {
    "path": "swish/shell_folder/Registry.cpp",
    "content": "/**\n    @file\n\n    Helper class for Swish registry access.\n\n    @if license\n\n    Copyright (C) 2008, 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"Registry.h\"\n\n#include \"swish/debug.hpp\"\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <boost/filesystem/path.hpp> // path\n\n#include <string>\n\nusing swish::remote_folder::remote_itemid_view;\n\nusing ATL::CRegKey;\nusing ATL::CString;\n\nusing boost::filesystem::path;\n\nusing std::vector;\nusing std::wstring;\n\n/**\n * Get registry keys for HostFolder connection association info.\n *\n * This list is not required for Windows Vista but, on any earlier \n * version, it must be passed to CDefFolderMenu_Create2 in order to display \n * the default context menu.\n *\n * Host connection items are treated as folders so the list of keys is:  \n *   HKCU\\\\Directory\n *   HKCU\\\\Directory\\\\Background\n *   HKCU\\\\Folder\n *   HKCU\\\\*\n *   HKCU\\\\AllFileSystemObjects\n *\n * @param[out] pcKeys  Number of HKEYS allocated in the array @p paKeys.\n * @param[out] paKeys  Location in which to return th allocated array.\n */\n/* static */ HRESULT CRegistry::GetHostFolderAssocKeys(\n    UINT *pcKeys, HKEY **paKeys)\nthrow()\n{\n    try\n    {\n        return _GetHKEYArrayFromKeynames(\n            _GetHostFolderAssocKeynames(), pcKeys, paKeys);\n    }\n    WASHER_COM_CATCH();\n}\n\n/**\n * Get registry keys for HostFolder connection association info.\n *\n * This list is not required for Windows Vista but, on any earlier \n * version, it must be passed to CDefFolderMenu_Create2 in order to display \n * the default context menu.\n *\n * A (ficticious) example might include:\n *   HKCU\\\\.ppt\n *   HKCU\\\\PowerPoint.Show\n *   HKCU\\\\PowerPoint.Show.12\n *   HKCU\\\\SystemFileAssociations\\\\.ppt\n *   HKCU\\\\SystemFileAssociations\\\\presentation\n *   HKCU\\\\*\n *   HKCU\\\\AllFileSystemObjects\n * for a file and:\n *   HKCU\\\\Directory\n *   HKCU\\\\Directory\\\\Background\n *   HKCU\\\\Folder\n *   HKCU\\\\AllFileSystemObjects\n * for a folder.\n *\n * @param[in]  itemid  Remote PIDL representing the file whose association \n *                     information is being requested.\n * @param[out] pcKeys  Number of HKEYS allocated in the array @p paKeys.\n * @param[out] paKeys  Location in which to return th allocated array.\n */\nHRESULT CRegistry::GetRemoteFolderAssocKeys(\n    remote_itemid_view itemid, UINT *pcKeys, HKEY **paKeys)\n{\n    try\n    {\n        return _GetHKEYArrayFromKeynames(\n            _GetRemoteFolderAssocKeynames(itemid), pcKeys, paKeys);\n    }\n    WASHER_COM_CATCH();\n}\n\nnamespace {\n\nCRegistry::KeyNames remote_folder_background_key_names() \n{\n    CRegistry::KeyNames names;\n\n    names.push_back(L\"Directory\\\\Background\");\n\n    return names;\n}\n\n}\n\nHRESULT CRegistry::GetRemoteFolderBackgroundAssocKeys(\n    UINT *pcKeys, HKEY **paKeys)\n{\n    try\n    {\n        return _GetHKEYArrayFromKeynames(\n            remote_folder_background_key_names(), pcKeys, paKeys);\n    }\n    WASHER_COM_CATCH();\n}\n\n\n/*----------------------------------------------------------------------------*\n * Private functions\n *----------------------------------------------------------------------------*/\n\n/**\n * Get names of registry keys which provide association info for Folder items.\n *\n * Such a list is required by Windows XP and earlier in order to display the \n * default context menu. Only 'HKCR\\\\Folder' is relevant as the Swish hosts are\n * virtual folder items with no filesystem parallel.  'HKCR\\\\Directory' and\n * 'HKCR\\AllFileSystemObjects' are for real filesystem items.  'HKCR\\\\*' is\n * not for folders at all.\n */\n/* static */ CRegistry::KeyNames CRegistry::_GetHostFolderAssocKeynames()\nthrow()\n{\n    KeyNames vecNames;\n\n    // Add virtual folder specific items\n    vecNames.push_back(L\"Folder\");\n\n    return vecNames;\n}\n\n/**\n * Get a list names of registry keys for the types of the selected file.\n *\n * A (ficticious) example might include:\n *   HKCU\\\\.ppt\n *   HKCU\\\\PowerPoint.Show\n *   HKCU\\\\PowerPoint.Show.12\n *   HKCU\\\\SystemFileAssociations\\\\.ppt\n *   HKCU\\\\SystemFileAssociations\\\\presentation\n *   HKCU\\\\*\n *   HKCU\\\\AllFileSystemObjects\n * for a file and:\n *   HKCU\\\\Folder\n *   HKCU\\\\Directory\n *   HKCU\\\\Directory\\\\Background\n *   HKCU\\\\AllFileSystemObjects\n * for a folder.\n *\n * @param itemid  Remote PIDL representing the file whose association \n *                information is being requested.\n */\n/* static */ CRegistry::KeyNames CRegistry::_GetRemoteFolderAssocKeynames(\n    remote_itemid_view itemid)\nthrow(...)\n{\n    KeyNames vecNames;\n\n    // If this is a directory, add directory-specific items\n    if (itemid.is_folder())\n    {\n        vecNames = _GetKeynamesForFolder();\n    }\n    else\n    {\n        wstring extension = path(itemid.filename()).extension().wstring();\n        wstring::size_type dot_index = extension.find(L\".\");\n        if (dot_index != wstring::npos)\n            extension.erase(dot_index);\n\n        // Get extension-specific keys\n        // We don't want to add the {.ext} key itself to the list\n        // of keys but rather, we should use it's default value to \n        // look up its file class.\n        // e.g:\n        //   HKCR\\.txt => (Default) txtfile\n        // so we look up the following key\n        //   HKCR\\txtfile\n        vecNames = _GetKeynamesForExtension(extension.c_str());\n    }\n\n    // Add names of keys that apply to items of all types\n    KeyNames vecCommon = _GetKeynamesCommonToAll();\n    vecNames.insert(vecNames.end(), vecCommon.begin(), vecCommon.end());\n\n    return vecNames;\n}\n\n/**\n * Get list of directory-specific association key names.\n */\n/* static */ CRegistry::KeyNames CRegistry::_GetKeynamesForFolder()\nthrow()\n{\n    KeyNames vecKeynames;\n\n    vecKeynames.push_back(L\"Folder\");\n    vecKeynames.push_back(L\"Directory\");\n    vecKeynames.push_back(L\"Directory\\\\Background\");\n\n    return vecKeynames;\n}\n\n/**\n * Get list of directory-specific association key names.\n */\n/* static */ CRegistry::KeyNames CRegistry::_GetKeynamesCommonToAll()\nthrow()\n{\n    KeyNames vecKeynames;\n\n    vecKeynames.push_back(L\"AllFilesystemObjects\");\n\n    return vecKeynames;\n}\n\n/**\n * Get the list of names of registry keys related to a specific file extension.\n *\n * @param pwszExtension  File extension whose keys will be returned.\n *\n * @todo  Some files, e.g. PDFs, need\n *        HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.ext\n */\n/* static */ CRegistry::KeyNames CRegistry::_GetKeynamesForExtension(\n    __in PCWSTR pwszExtension)\nthrow()\n{\n    KeyNames vecKeynames;\n    CString strExtension = CString(L\".\") + pwszExtension;\n\n    // Start digging at HKCR\\.{szExtension}\n    CRegKey reg;\n    if (reg.Open(HKEY_CLASSES_ROOT, strExtension, KEY_READ)    == ERROR_SUCCESS)\n    {\n        vecKeynames.push_back(strExtension);\n\n        // Try to get registered file class key (extensions's default val)\n        wchar_t wszClass[2048]; ULONG cchClass = 2048;\n        if (reg.QueryStringValue(L\"\", wszClass, &cchClass) == ERROR_SUCCESS\n         && cchClass > 1\n         && reg.Open(HKEY_CLASSES_ROOT, wszClass, KEY_READ) == ERROR_SUCCESS)\n        {\n            vecKeynames.push_back(wszClass);\n\n            // Does this class contain a CurVer subkey pointing to another\n            // version of this file.\n            //   e.g.: PowerPoint.Show\\CurVer => PowerPoint.Show.12\n            CString strCurVer = wszClass;\n            strCurVer += L\"\\\\CurVer\";\n            if (reg.Open(HKEY_CLASSES_ROOT, strCurVer, KEY_READ)\n                == ERROR_SUCCESS)\n            {\n                // Does this CurVer exist?\n                wchar_t wszCurVer[2048]; ULONG cchCurVer = 2048;\n                if (reg.QueryStringValue(L\"\", wszCurVer, &cchCurVer) ==\n                    ERROR_SUCCESS && cchCurVer > 1\n                 && reg.Open(HKEY_CLASSES_ROOT, wszCurVer, KEY_READ) == \n                    ERROR_SUCCESS)\n                {\n                    vecKeynames.push_back(wszCurVer);\n                }\n            }\n        }\n    }\n\n    // Dig again at HKCR\\SystemFileAssociations\\.{sxExtension}\n    CString strSysFileAssocExt = L\"SystemFileAssociations\\\\\" + strExtension;\n    if (reg.Open(HKEY_CLASSES_ROOT, strSysFileAssocExt, KEY_READ)\n        == ERROR_SUCCESS)\n    {\n        vecKeynames.push_back(strSysFileAssocExt);\n    }\n\n    // Dig again at HKCR\\.{szExtension}\\PerceivedType\n    if (reg.Open(HKEY_CLASSES_ROOT, strExtension, KEY_READ)\n        == ERROR_SUCCESS)\n    {\n        wchar_t wszPerceivedType[2048]; ULONG cchPerceivedType = 2048;\n        if (reg.QueryStringValue(\n                L\"PerceivedType\", wszPerceivedType, &cchPerceivedType)\n            == ERROR_SUCCESS && cchPerceivedType > 1)\n        {\n            CString strPerceivedType = \n                CString(L\"SystemFileAssociations\\\\\") + wszPerceivedType;\n\n            if (reg.Open(HKEY_CLASSES_ROOT, strPerceivedType, KEY_READ)\n                == ERROR_SUCCESS)\n                vecKeynames.push_back(strPerceivedType);\n        }\n    }\n\n    if (!vecKeynames.size())\n        vecKeynames.push_back(L\"Unknown\");\n\n    vecKeynames.push_back(L\"*\");\n\n    ATLASSERT( vecKeynames.size() <= 6 ); \n    return vecKeynames;\n}\n\n/**\n * Create SHAlloced array of HKEYs from a list of registry key names.\n */\n/* static */ HRESULT CRegistry::_GetHKEYArrayFromKeynames(\n    const KeyNames vecNames, UINT *pcKeys, HKEY **paKeys)\nthrow()\n{\n    vector<HKEY> vecKeys = _GetKeysFromKeynames(vecNames);\n    return _GetHKEYArrayFromVector(vecKeys, pcKeys, paKeys);\n}\n\n/**\n * Create SHAlloced array of HKEYs from a list of HKEYs.\n */\n/* static */ HRESULT CRegistry::_GetHKEYArrayFromVector(\n    const vector<HKEY> vecKeys, UINT *pcKeys, HKEY **paKeys)\nthrow()\n{\n    ATLASSERT( vecKeys.size() <= 16 ); // CDefFolderMenu_Create2's maximum\n\n    HKEY *aKeys = (HKEY *)::SHAlloc(vecKeys.size() * sizeof HKEY); \n    for (UINT i = 0; i < vecKeys.size(); i++)\n        aKeys[i] = vecKeys[i];\n\n    *pcKeys = static_cast<UINT>(vecKeys.size());\n    *paKeys = aKeys;\n\n    return S_OK;\n}\n\n/**\n * Create list of registry handles from list of keys names.\n */\n/* static */ vector<HKEY> CRegistry::_GetKeysFromKeynames(\n    const KeyNames vecKeynames)\nthrow()\n{\n    LSTATUS rc = ERROR_SUCCESS;\n    vector<HKEY> vecKeys;\n    \n    for each (CString strKeyname in vecKeynames)\n    {\n        CRegKey reg;\n        rc = reg.Open(HKEY_CLASSES_ROOT, strKeyname, KEY_READ);\n        ATLASSERT(rc == ERROR_SUCCESS);\n        if (rc == ERROR_SUCCESS)\n            vecKeys.push_back(reg.Detach());\n    }\n\n    return vecKeys;\n}\n"
  },
  {
    "path": "swish/shell_folder/Registry.h",
    "content": "/**\n    @file\n\n    Helper class for Swish registry access.\n\n    @if license\n\n    Copyright (C) 2008, 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/atl.hpp\"\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include <vector>\n\n#include <AtlStr.h> // CString\n\nclass CRegistry\n{\n\npublic:\n    static HRESULT GetHostFolderAssocKeys(\n        __out UINT *pcKeys, __deref_out_ecount(pcKeys) HKEY **paKeys);\n    static HRESULT GetRemoteFolderAssocKeys(\n        swish::remote_folder::remote_itemid_view itemid, \n        __out UINT *pcKeys, __deref_out_ecount(pcKeys) HKEY **paKeys);\n    static HRESULT GetRemoteFolderBackgroundAssocKeys(\n        UINT *pcKeys, HKEY **paKeys);\n    typedef std::vector<ATL::CString> KeyNames;\n\nprivate:\n\n    static KeyNames _GetHostFolderAssocKeynames();\n    static KeyNames _GetRemoteFolderAssocKeynames(\n        swish::remote_folder::remote_itemid_view itemid);\n\n    static KeyNames _GetKeynamesForFolder();\n    static KeyNames _GetKeynamesCommonToAll();\n    static KeyNames _GetKeynamesForExtension(__in PCWSTR pwszExtension)\n        throw();\n\n    static HRESULT _GetHKEYArrayFromKeynames(\n        __in const KeyNames vecNames, \n        __out UINT *pcKeys, __deref_out_ecount(pcKeys) HKEY **paKeys);\n\n    static HRESULT _GetHKEYArrayFromVector(\n        __in const std::vector<HKEY> vecKeys, \n        __out UINT *pcKeys, __deref_out_ecount(pcKeys) HKEY **paKeys);\n\n    static std::vector<HKEY> _GetKeysFromKeynames(\n        __in const KeyNames vecKeynames);\n};\n"
  },
  {
    "path": "swish/shell_folder/RemoteFolder.cpp",
    "content": "/* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"RemoteFolder.h\"\n\n#include \"SftpDirectory.h\"\n#include \"SftpDataObject.h\"\n#include \"IconExtractor.h\"\n#include \"Registry.h\"\n#include \"swish/debug.hpp\"\n#include \"swish/drop_target/DropTarget.hpp\" // CDropTarget\n#include \"swish/drop_target/DropUI.hpp\" // DropUI\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n#include \"swish/remote_folder/columns.hpp\" // property_key_from_column_index\n#include \"swish/remote_folder/commands/commands.hpp\"\n                                           // remote_folder_command_provider\n#include \"swish/remote_folder/pidl_connection.hpp\" // provider_from_pidl\n#include \"swish/remote_folder/context_menu_callback.hpp\"\n                                                       // context_menu_callback\n#include \"swish/remote_folder/properties.hpp\" // property_from_pidl\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n                                               // create_remote_itemid\n#include \"swish/remote_folder/ViewCallback.hpp\" // CViewCallback\n#include \"swish/shell_folder/SnitchingDataObject.hpp\" // CSnitchingDataObject\n#include \"swish/trace.hpp\" // trace\n#include \"swish/windows_api.hpp\" // SHBindToParent\n\n#include <washer/shell/shell.hpp> // string_to_strret\n#include <washer/window/window.hpp>\n\n#include <comet/datetime.h> // datetime_t\n#include <comet/regkey.h>\n\n#include <boost/bind.hpp> // bind\n#include <boost/exception/diagnostic_information.hpp> // diagnostic_information\n#include <boost/filesystem/path.hpp> // path\n#include <boost/locale.hpp> // translate\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n#include <string>\n\nusing swish::drop_target::CDropTarget;\nusing swish::drop_target::DropUI;\nusing swish::frontend::announce_last_exception;\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::CViewCallback;\nusing swish::remote_folder::commands::remote_folder_command_provider;\nusing swish::remote_folder::context_menu_callback;\nusing swish::remote_folder::create_remote_itemid;\nusing swish::remote_folder::property_from_pidl;\nusing swish::remote_folder::property_key_from_column_index;\nusing swish::remote_folder::provider_from_pidl;\nusing swish::remote_folder::remote_itemid_view;\nusing swish::tracing::trace;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::property_key;\nusing washer::shell::string_to_strret;\nusing washer::window::window;\nusing washer::window::window_handle;\n\nusing comet::com_ptr;\nusing comet::com_error;\nusing comet::datetime_t;\nusing comet::regkey;\nusing comet::throw_com_error;\nusing comet::variant_t;\n\nusing boost::bind;\nusing boost::filesystem::path;\nusing boost::locale::translate;\nusing boost::make_shared;\nusing boost::optional;\nusing boost::shared_ptr;\n\nusing ATL::CComPtr;\n\nusing std::string;\nusing std::wstring;\n\n\nnamespace comet {\n\ntemplate<> struct comtype<::IContextMenu>\n{\n    static const ::IID& uuid() throw() { return ::IID_IContextMenu; }\n    typedef ::IUnknown base;\n};\n\n}\n\n\nnamespace {\n\n    cpidl_t create_filename_only_pidl(const wstring& filename)\n    {\n        return create_remote_itemid(\n            filename, false, false, L\"\", L\"\", 0, 0, 0, 0, datetime_t(),\n            datetime_t());\n    }\n\n    /**\n     * Remove the extension from the remote item's filename *if appropriate*.\n     */\n    wstring filename_without_extension(const cpidl_t remote_item)\n    {\n        remote_itemid_view itemid(remote_item);\n        wstring full_name = itemid.filename();\n\n        if (full_name.empty() || itemid.is_folder())\n        {\n            return full_name;\n        }\n        else\n        {\n            if (full_name[0] != L'.')\n            {\n                return path(full_name).stem().wstring();\n            }\n            else\n            {\n                // File might look something like '.hidden.txt' or it might\n                // just be '.hidden'.  In the first case we only want to remove\n                // the '.txt' extension.  In the second case we don't want\n                // to remove anything.\n                wstring bit_after_initial_dot = full_name.substr(1);\n                return L'.' + path(bit_after_initial_dot).stem().wstring();\n            }\n        }\n    }\n}\n\n/*--------------------------------------------------------------------------*/\n/*      Functions implementing IShellFolder via folder_error_adapter.       */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Create an IEnumIDList which enumerates the items in this folder.\n *\n * @implementing folder_error_adapter\n *\n * @param hwnd   Optional window handle used if enumeration requires user\n *               input.\n * @param flags  Flags specifying which types of items to include in the\n *               enumeration. Possible flags are from the @c SHCONT enum.\n */\nIEnumIDList* CRemoteFolder::enum_objects(HWND hwnd, SHCONTF flags)\n{\n    try\n    {\n        com_ptr<ISftpConsumer> consumer = m_consumer_factory(hwnd);\n\n        // TODO: get the name of the directory and embed in the task name\n        shared_ptr<sftp_provider> provider = provider_from_pidl(\n            root_pidl(), consumer, translate(\n                \"Name of a running task\", \"Reading a directory\"));\n\n        // Create directory handler and get listing as PIDL enumeration\n        CSftpDirectory directory(root_pidl(), provider);\n        return directory.GetEnum(flags).detach();\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd, translate(L\"Unable to access the directory\"),\n            translate(L\"You might not have permission.\"));\n        throw;\n    }\n}\n\n/**\n * Convert path string relative to this folder into a PIDL to the item.\n *\n * @implementing folder_error_adapter\n *\n * @todo  Handle the attributes parameter.  Will need to contact server\n * as the PIDL we create is fake and will not have correct folderness, etc.\n */\nPIDLIST_RELATIVE CRemoteFolder::parse_display_name(\n    HWND hwnd, IBindCtx* bind_ctx, const wchar_t* display_name,\n    ULONG* attributes_inout)\n{\n    try\n    {\n        trace(__FUNCTION__\" called (display_name=%s)\") % display_name;\n        if (*display_name == L'\\0')\n            BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n        // The string we are trying to parse should be of the form:\n        //    directory/directory/filename\n        // or \n        //    filename\n        wstring strDisplayName(display_name);\n\n        // May have / to separate path segments\n        wstring::size_type nSlash = strDisplayName.find_first_of(L'/');\n        wstring strSegment;\n        if (nSlash == 0) // Unix machine - starts with folder called /\n        {\n            strSegment = strDisplayName.substr(0, 1);\n        }\n        else\n        {\n            strSegment = strDisplayName.substr(0, nSlash);\n        }\n\n        // Create child PIDL for this path segment\n        cpidl_t pidl = create_filename_only_pidl(strSegment);\n\n        // Bind to subfolder and recurse if there were other path segments\n        if (nSlash != wstring::npos)\n        {\n            wstring strRest = strDisplayName.substr(nSlash+1);\n\n            com_ptr<IShellFolder> subfolder;\n            bind_to_object(\n                pidl.get(), bind_ctx, subfolder.iid(),\n                reinterpret_cast<void**>(subfolder.out()));\n\n            wchar_t wszRest[MAX_PATH];\n            ::wcscpy_s(wszRest, ARRAYSIZE(wszRest), strRest.c_str());\n\n            pidl_t rest;\n            HRESULT hr = subfolder->ParseDisplayName(\n                hwnd, bind_ctx, wszRest, NULL, rest.out(), attributes_inout);\n            if (FAILED(hr))\n                throw_com_error(subfolder.get(), hr);\n\n            return (pidl + rest).detach();\n        }\n        else\n        {\n            return pidl.detach();\n        }\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd, translate(L\"Path not recognised\"),\n            translate(L\"Check that the path was entered correctly.\"));\n        throw;\n    }\n}\n\nnamespace {\n\n    bool extension_hiding_disabled_in_registry()\n    {\n        if (regkey user_settings = regkey(HKEY_CURRENT_USER).open_nothrow(\n            L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\\"\n            L\"Advanced\"))\n        {\n            regkey::mapped_type extension_setting =\n                user_settings[L\"HideFileExt\"];\n\n            if (extension_setting.exists())\n            {\n                return extension_setting == 0U;\n            }\n        }\n        \n        // We only reach here if the user settings didn't exist, not if\n        // they just said \"no\".  This means the global settings don't \n        // override the user settings, which seems the right way round.\n        if (regkey global_settings = regkey(HKEY_LOCAL_MACHINE).open_nothrow(\n            L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\\"\n            L\"Advanced\\\\Folder\\\\HideFileExt\"))\n        {\n            regkey::mapped_type extension_setting =\n                global_settings[L\"DefaultValue\"];\n\n            if (extension_setting.exists())\n            {\n                return extension_setting == 0U;\n            }\n        }\n\n        // It's unlikely that neither will be set but we're prepared for it\n        // anyway\n        return false;\n    }\n\n}\n\n\n\nbool CRemoteFolder::show_extension(PCUITEMID_CHILD pidl)\n{\n    if (extension_hiding_disabled_in_registry())\n        return true;\n\n    HKEY raw_class_key = NULL;\n    com_ptr<IQueryAssociations> associations = query_associations(\n        NULL, 1, &pidl);\n    HRESULT hr = associations->GetKey(\n        0, ASSOCKEY_CLASS, NULL, &raw_class_key);\n    regkey class_key(raw_class_key);\n\n    // Failing to find the key indicates an unknown file type.  As the\n    // user setting say 'Hide extensions for *known* filetypes' we\n    // show the extension if the file is unknown.\n    if FAILED(hr)\n    {\n        return true;\n    }\n    else\n    {\n        // In practice, Explorer returns the \"Unknown\" key for unregistered\n        // file types.  But that's ok; it contains an AlwaysShowExt value\n        // so we obey that and it all comes out in the wash.\n        return class_key[L\"AlwaysShowExt\"].exists();\n    }\n}\n\n/**\n * Retrieve the display name for the specified file object or subfolder.\n *\n * @implementing folder_error_adapter\n */\nSTRRET CRemoteFolder::get_display_name_of(PCUITEMID_CHILD pidl, SHGDNF flags)\n{\n    if (::ILIsEmpty(pidl))\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    wstring name;\n\n    bool fForParsing = (flags & SHGDN_FORPARSING) != 0;\n\n    if (fForParsing || (flags & SHGDN_FORADDRESSBAR))\n    {\n        if (!(flags & SHGDN_INFOLDER))\n        {\n            // Bind to parent\n            com_ptr<IShellFolder> parent;\n            PCUITEMID_CHILD pidlThisFolder = NULL;\n            HRESULT hr = swish::windows_api::SHBindToParent(\n                root_pidl().get(), parent.iid(),\n                reinterpret_cast<void**>(parent.out()), &pidlThisFolder);\n            \n            if (FAILED(hr))\n                BOOST_THROW_EXCEPTION(com_error(hr));\n\n            STRRET strret;\n            std::memset(&strret, 0, sizeof(strret));\n            hr = parent->GetDisplayNameOf(pidlThisFolder, flags, &strret);\n            if (FAILED(hr))\n                throw_com_error(parent.get(), hr);\n\n            ATLASSERT(strret.uType == STRRET_WSTR);\n\n            name += strret.pOleStr;\n            name += L'/';\n        }\n\n        // Add child path - include extension if FORPARSING\n        if (fForParsing)\n            name += remote_itemid_view(pidl).filename();\n        else\n            name += filename_without_extension(pidl);\n\n    }\n    else if (flags & SHGDN_FOREDITING)\n    {\n        name = remote_itemid_view(pidl).filename();\n    }\n    else\n    {\n        ATLASSERT(flags == SHGDN_NORMAL || flags == SHGDN_INFOLDER);\n\n        if (show_extension(pidl))\n        {\n            // The table of SHGDN examples on MSDN implies that the\n            // presence of the SHGDN_FORPARSING flag means include the file\n            // extension and its absence means remove it.\n            // But that's not the full story.  The SHGDN_FORPARSING flag\n            // indeed means include the file extension, but its absence means\n            // do what the user wants.  In other words, remove the extension\n            // if their Explorer settings say that's what they want.\n            // Checking the Explorer settings is up to the individual\n            // namespace extension.\n\n            name = remote_itemid_view(pidl).filename();\n        }\n        else\n        {\n            name = filename_without_extension(pidl);\n        }\n    }\n\n    return string_to_strret(name);\n}\n\n/**\n * Rename item.\n *\n * @implementing folder_error_adapter\n */\nPITEMID_CHILD CRemoteFolder::set_name_of(\n    HWND hwnd, PCUITEMID_CHILD pidl, const wchar_t* name, SHGDNF /*flags*/)\n{\n    try\n    {\n        // TODO: embed the name of the file in the task name\n        com_ptr<ISftpConsumer> consumer = m_consumer_factory(hwnd);\n        shared_ptr<sftp_provider> provider =\n            provider_from_pidl(root_pidl(), consumer, translate(\n                \"Name of a running task\", \"Renaming a file\"));\n\n        // Rename file\n        CSftpDirectory directory(root_pidl(), provider);\n        bool fOverwritten = directory.Rename(pidl, name, consumer);\n\n        // Create new PIDL from old one with new filename\n        remote_itemid_view itemid(pidl);\n        cpidl_t new_file = create_remote_itemid(\n            name, itemid.is_folder(), itemid.is_link(),\n            itemid.owner(), itemid.group(), itemid.owner_id(),\n            itemid.group_id(), itemid.permissions(), itemid.size(),\n            itemid.date_modified(), itemid.date_accessed());\n\n        // A failure to notify the shell shouldn't prevent us returning the PIDL\n        try\n        {\n            // Make PIDLs absolute\n            apidl_t old_pidl = root_pidl() + pidl;\n            apidl_t new_pidl = root_pidl() + new_file;\n\n            // Update the shell by passing both PIDLs\n            if (fOverwritten)\n            {\n                ::SHChangeNotify(\n                    SHCNE_DELETE, SHCNF_IDLIST | SHCNF_FLUSH, new_pidl.get(),\n                    NULL);\n            }\n            ::SHChangeNotify(\n                (remote_itemid_view(pidl).is_folder()) ?\n                    SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,\n                SHCNF_IDLIST | SHCNF_FLUSH, old_pidl.get(), new_pidl.get());\n        }\n        catch (const std::exception& e)\n        {\n            trace(\"Exception thrown while notifying shell of rename:\");\n            trace(\"%s\") % boost::diagnostic_information(e);\n        }\n\n        return new_file.detach();\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd, translate(L\"Unable to rename the item\"),\n            translate(L\"You might not have permission.\"));\n        throw;\n    }\n}\n\n/**\n * Returns the attributes for the items whose PIDLs are passed in.\n *\n * @implementing folder_error_adapter\n */\nvoid CRemoteFolder::get_attributes_of(\n    UINT pidl_count, PCUITEMID_CHILD_ARRAY pidl_array,\n    SFGAOF* attributes_inout)\n{\n    // Search through all PIDLs and check if they are all folders\n    bool fAllAreFolders = true;\n    for (UINT i = 0; i < pidl_count; i++)\n    {\n        if (!remote_itemid_view(pidl_array[i]).is_folder())\n        {\n            fAllAreFolders = false;\n            break;\n        }\n    }\n\n    // Search through all PIDLs and check if they are all links\n    bool fAreAllLinks = true;\n    for (UINT i = 0; i < pidl_count; i++)\n    {\n        if (!remote_itemid_view(pidl_array[i]).is_link())\n        {\n            fAreAllLinks = false;\n            break;\n        }\n    }\n\n    // Search through all PIDLs and check if they are all 'dot' files\n    bool fAllAreDotFiles = true;\n    for (UINT i = 0; i < pidl_count; i++)\n    {\n        wstring filename = remote_itemid_view(pidl_array[i]).filename();\n        if (filename[0] != L'.')\n        {\n            fAllAreDotFiles = false;\n            break;\n        }\n    }\n\n    DWORD dwAttribs = 0;\n    if (fAllAreFolders)\n    {\n        dwAttribs |= SFGAO_FOLDER;\n        dwAttribs |= SFGAO_HASSUBFOLDER;\n        dwAttribs |= SFGAO_DROPTARGET;\n    }\n    if (fAllAreDotFiles)\n    {\n        dwAttribs |= SFGAO_GHOSTED;\n        dwAttribs |= SFGAO_HIDDEN;\n    }\n    if (fAreAllLinks)\n    {\n        dwAttribs |= SFGAO_LINK;\n    }\n    dwAttribs |= SFGAO_CANRENAME;\n    dwAttribs |= SFGAO_CANDELETE;\n    dwAttribs |= SFGAO_CANCOPY;\n\n    *attributes_inout &= dwAttribs;\n}\n\n/*--------------------------------------------------------------------------*/\n/*     Functions implementing IShellFolder2 via folder2_error_adapter.      */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Convert column index to matching PROPERTYKEY, if any.\n *\n * @implementing folder2_error_adapter\n */\nSHCOLUMNID CRemoteFolder::map_column_to_scid(UINT column_index)\n{\n    return property_key_from_column_index(column_index).get();\n}\n\n/*--------------------------------------------------------------------------*/\n/*                     CFolder NVI internal interface.                      */\n/* These method implement the internal interface of the CFolder abstract    */\n/* class                                                                    */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Return the folder's registered CLSID\n *\n * @implementing CFolder\n */\nCLSID CRemoteFolder::clsid() const\n{\n    return __uuidof(this);\n}\n\n/**\n * Sniff PIDLs to determine if they are of our type.  Throw if not.\n *\n * @implementing CFolder\n */\nvoid CRemoteFolder::validate_pidl(PCUIDLIST_RELATIVE pidl) const\n{\n    if (pidl == NULL)\n        BOOST_THROW_EXCEPTION(com_error(E_POINTER));\n\n    if (!remote_itemid_view(pidl).valid())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n}\n\n/**\n * Create and initialise new folder object for subfolder.\n *\n * @implementing CFolder\n *\n * Create new CRemoteFolder initialised with its root PIDL.  CRemoteFolder\n * only have instances of themselves as subfolders.\n */\nCComPtr<IShellFolder> CRemoteFolder::subfolder(const cpidl_t& pidl)\n{\n    apidl_t new_root = root_pidl() + pidl;\n\n    // Create CRemoteFolder initialised with its root PIDL\n    CComPtr<IShellFolder> folder = CRemoteFolder::Create(\n        new_root.get(), m_consumer_factory);\n    ATLENSURE_THROW(folder, E_NOINTERFACE);\n\n    return folder;\n}\n\n/**\n * Return a property, specified by PROERTYKEY, of an item in this folder.\n */\nvariant_t CRemoteFolder::property(const property_key& key, const cpidl_t& pidl)\n{\n    return property_from_pidl(pidl, key);\n}\n\n/*--------------------------------------------------------------------------*/\n/*                    CSwishFolder internal interface.                      */\n/* These method override the (usually no-op) implementations of some        */\n/* in the CSwishFolder base class                                           */\n/*--------------------------------------------------------------------------*/\n\n/**\n * Create a toolbar command provider for the folder.\n */\nCComPtr<IExplorerCommandProvider> CRemoteFolder::command_provider(\n    HWND owning_hwnd)\n{\n    TRACE(\"Request: IExplorerCommandProvider\");\n\n    return remote_folder_command_provider(\n        root_pidl(),\n        bind(&provider_from_pidl, root_pidl(), _1, _2),\n        bind(m_consumer_factory, owning_hwnd)).get();\n}\n\n/**\n * Create an icon extraction helper object for the selected item.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IExtractIconW> CRemoteFolder::extract_icon_w(\n    HWND /*hwnd*/, PCUITEMID_CHILD pidl)\n{\n    TRACE(\"Request: IExtractIconW\");\n\n    remote_itemid_view itemid(pidl);\n    return CIconExtractor::Create(\n        itemid.filename().c_str(), itemid.is_folder());\n}\n\n/**\n * Create a file association handler for the selected items.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IQueryAssociations> CRemoteFolder::query_associations(\n    HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n{\n    TRACE(\"Request: IQueryAssociations\");\n    ATLENSURE(cpidl > 0);\n\n    CComPtr<IQueryAssociations> spAssoc;\n    HRESULT hr = ::AssocCreate(\n        CLSID_QueryAssociations, IID_PPV_ARGS(&spAssoc));\n    ATLENSURE_SUCCEEDED(hr);\n\n    remote_itemid_view itemid(apidl[0]);\n    \n    if (itemid.is_folder())\n    {\n        // Initialise default assoc provider for Folders\n        hr = spAssoc->Init(\n            ASSOCF_INIT_DEFAULTTOFOLDER, L\"Folder\", NULL, hwnd);\n        ATLENSURE_SUCCEEDED(hr);\n    }\n    else\n    {\n        // Initialise default assoc provider for given file extension\n        wstring extension = path(itemid.filename()).extension().wstring();\n        if (extension.empty())\n            extension = L\".\";\n        hr = spAssoc->Init(\n            ASSOCF_INIT_DEFAULTTOSTAR, extension.c_str(), NULL, hwnd);\n        ATLENSURE_SUCCEEDED(hr);\n    }\n\n    return spAssoc;\n}\n\nHRESULT CALLBACK CRemoteFolder::menu_callback(\n    IShellFolder* folder, HWND hwnd_view, IDataObject* selection, \n    UINT message_id, WPARAM wparam, LPARAM lparam)\n{\n    assert(folder);\n    if (!folder)\n        return E_POINTER;\n\n    return static_cast<CRemoteFolder*>(folder)->MenuCallback(\n        hwnd_view, selection, message_id, wparam, lparam);\n}\n\n/**\n * Create a context menu for the selected items.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IContextMenu> CRemoteFolder::context_menu(\n    HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n{\n    TRACE(\"Request: IContextMenu\");\n    assert(cpidl > 0);\n\n    // Get keys associated with filetype from registry.\n    // We only take into account the item that was right-clicked on \n    // (the first array element) even if this was a multi-selection.\n    //\n    // This article says that we don't need to specify the keys:\n    // http://groups.google.com/group/microsoft.public.platformsdk.shell/\n    // browse_thread/thread/6f07525eaddea29d/\n    // but we do for the context menu to appear in versions of Windows \n    // earlier than Vista.\n    HKEY *akeys = NULL;\n    UINT ckeys = 0;\n    if (cpidl > 0)\n    {\n        ATLENSURE_THROW(SUCCEEDED(\n            CRegistry::GetRemoteFolderAssocKeys(\n                remote_itemid_view(apidl[0]), &ckeys, &akeys)),\n            E_UNEXPECTED  // Might fail if registry is corrupted\n        );\n    }\n\n    CComPtr<IShellFolder> spThisFolder = this;\n    ATLENSURE_THROW(spThisFolder, E_OUTOFMEMORY);\n\n    // Create default context menu from list of PIDLs\n    CComPtr<IContextMenu> spMenu;\n    HRESULT hr = ::CDefFolderMenu_Create2(\n        root_pidl().get(), hwnd, cpidl, apidl, spThisFolder, \n        menu_callback, ckeys, akeys, &spMenu);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error(hr));\n\n    return spMenu;\n}\n\nCComPtr<IContextMenu> CRemoteFolder::background_context_menu(HWND hwnd)\n{\n    TRACE(\"Request: IContextMenu\");\n\n    // Get keys associated with directory background menus from registry.\n    //\n    // This article says that we don't need to specify the keys:\n    // http://groups.google.com/group/microsoft.public.platformsdk.shell/\n    // browse_thread/thread/6f07525eaddea29d/\n    // but we do for the context menu to appear in versions of Windows \n    // earlier than Vista.\n    HKEY *akeys; UINT ckeys;\n    ATLENSURE_THROW(SUCCEEDED(\n        CRegistry::GetRemoteFolderBackgroundAssocKeys(&ckeys, &akeys)),\n        E_UNEXPECTED  // Might fail if registry is corrupted\n        );\n\n    CComPtr<IShellFolder> spThisFolder = this;\n    ATLENSURE_THROW(spThisFolder, E_OUTOFMEMORY);\n\n    // Create default context menu from list of PIDLs\n    CComPtr<IContextMenu> spMenu;\n    HRESULT hr = ::CDefFolderMenu_Create2(\n        root_pidl().get(), hwnd, 0, NULL, spThisFolder, \n        menu_callback, ckeys, akeys, &spMenu);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error(hr));\n\n    return spMenu;\n}\n\n/**\n * Create a data object for the selected items.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IDataObject> CRemoteFolder::data_object(\n    HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n{\n    TRACE(\"Request: IDataObject\");\n    assert(cpidl > 0);\n\n    try\n    {\n        // TODO: pass a provider factory instead of the provider to the\n        // data object and create more specific reservations when needed\n        com_ptr<ISftpConsumer> consumer = m_consumer_factory(hwnd);\n        shared_ptr<sftp_provider> provider =\n            provider_from_pidl(root_pidl(), consumer, translate(\n                \"Name of a running task\", \"Accessing files\"));\n\n        return new swish::shell_folder::CSnitchingDataObject(\n            new CSftpDataObject(\n                cpidl, apidl, root_pidl().get(), provider));\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd,\n            (cpidl > 1) ? translate(L\"Unable to access the item\") :\n                          translate(L\"Unable to access the items\"),\n            translate(L\"You might not have permission.\"));\n        throw;\n    }\n}\n\n/**\n * Create a drop target handler for the folder.\n *\n * @implementing CSwishFolder\n */\nCComPtr<IDropTarget> CRemoteFolder::drop_target(HWND hwnd)\n{\n    TRACE(\"Request: IDropTarget\");\n\n    try\n    {\n        // TODO: pass a provider factory instead of the provider to the\n        // drop target and create more specific reservations when needed\n        com_ptr<ISftpConsumer> consumer = m_consumer_factory(hwnd);\n        shared_ptr<sftp_provider> provider =\n            provider_from_pidl(root_pidl(), consumer, translate(\n                \"Name of a running task\", \"Copying to directory\"));\n\n        optional< window<wchar_t> > owner;\n        if (hwnd)\n            owner = window<wchar_t>(window_handle::foster_handle(hwnd));\n\n        // HACKish:\n        // UI happens via the given owner window given here.  We used to do it\n        // via the window of the OLE site instead, but this is incompatible\n        // with asynchronous operations because the shell clears the site\n        // when Drop returns (at which point the operation is still running\n        // and may need an owner window for UI).\n        //\n        // We could hang on to a copy of the site but that seems .. impolite.\n        // After all, the shell presumably cleared the site for a reason.\n        //\n        // That said, what we're doing now seems pretty naughty too. We use the\n        // window we were passed as an owner window when we were created.  This\n        // window is probably the one the shell passed to our folder's\n        // GetUIObjectOf or CreateViewObject methods.  MSDN documents this\n        // window as the 'owner' to be used for UI but doesn't make clear how\n        // long the window is guarantted to remain alive: until the\n        // GetUIObjectOf/CreateViewObject call returns or for as long as this\n        // drop target is in use.  Nevertheless, this seems to work so it's\n        // what we're doing for now.\n        return new CDropTarget(\n            provider, root_pidl(), make_shared<DropUI>(owner));\n    }\n    catch (...)\n    {\n        announce_last_exception(\n            hwnd, translate(L\"Unable to access the folder\"),\n            translate(L\"You might not have permission.\"));\n        throw;\n    }\n}\n\n/**\n * Create an instance of our Shell Folder View callback handler.\n */\nCComPtr<IShellFolderViewCB> CRemoteFolder::folder_view_callback(HWND /*hwnd*/)\n{\n    return new CViewCallback(root_pidl());\n}\n\nHRESULT CRemoteFolder::MenuCallback(\n    HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam )\n{\n    context_menu_callback callback(\n        bind(&provider_from_pidl, root_pidl(), _1, _2), m_consumer_factory);\n    return callback(hwnd, pdtobj, uMsg, wParam, lParam);\n}\n"
  },
  {
    "path": "swish/shell_folder/RemoteFolder.h",
    "content": "/*  Explorer folder handling remote files and folders in a directory.\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#pragma once\n\n#include \"SwishFolder.hpp\"      // Superclass\n\n#include \"swish/CoFactory.hpp\"  // CComObject factory\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/remote_folder/columns.hpp\" // Column\n#include \"Swish.h\" // For CRemoteFolder UUID\n\n#include \"swish/atl.hpp\"        // Common ATL setup\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/function.hpp> // function\n\n#include <vector>\n\nclass ATL_NO_VTABLE CRemoteFolder :\n    public swish::shell_folder::folder::CSwishFolder<\n        swish::remote_folder::Column>,\n    private swish::CCoFactory<CRemoteFolder>\n{\npublic:\n\n    BEGIN_COM_MAP(CRemoteFolder)\n        COM_INTERFACE_ENTRY(IShellFolder)\n        COM_INTERFACE_ENTRY_CHAIN(CSwishFolder)\n    END_COM_MAP()\n\n    /*\n    We can assume that the PIDLs contained in this folder (i.e. any PIDL\n    relative to it) contain one or more REMOTEPIDLs representing the \n    file-system hierarchy of the target file or folder and may be a child of\n    either a HOSTPIDL or another REMOTEPIDL:\n\n        <Relative (HOST|REMOTE)PIDL>/REMOTEPIDL[/REMOTEPIDL]*\n    */\n\n    /**\n     * Create initialized instance of the CRemoteFolder class.\n     *\n     * @param pidl  Absolute PIDL at which to root the folder instance (passed\n     *              to Initialize).\n     * @param consumer_factory  Callable that returns a consumer instance to\n     *                          use for a single request.\n     *\n     * @returns Smart pointer to the CRemoteFolder's IShellFolder interface.\n     * @throws  com_error if creation fails.\n     */\n    static ATL::CComPtr<IShellFolder> Create(\n        PCIDLIST_ABSOLUTE pidl,\n        boost::function<comet::com_ptr<ISftpConsumer>(HWND)> consumer_factory)\n    throw(...)\n    {\n        ATL::CComPtr<CRemoteFolder> spObject = spObject->CreateCoObject();\n\n        spObject->set_consumer_factory(consumer_factory);\n        \n        HRESULT hr = spObject->Initialize(pidl);\n        ATLENSURE_SUCCEEDED(hr);\n        return spObject.p;\n    }\n\nprotected:\n\n    CLSID clsid() const;\n\n    void validate_pidl(PCUIDLIST_RELATIVE pidl) const;\n\n    ATL::CComPtr<IShellFolder> subfolder(\n        const washer::shell::pidl::cpidl_t& pidl);\n\n    comet::variant_t property(\n        const washer::shell::property_key& key,\n        const washer::shell::pidl::cpidl_t& pidl);\n\n    ATL::CComPtr<IExplorerCommandProvider> command_provider(HWND hwnd);\n    ATL::CComPtr<IContextMenu> background_context_menu(HWND hwnd);\n    ATL::CComPtr<IExtractIconW> extract_icon_w(\n        HWND hwnd, PCUITEMID_CHILD pidl);\n    ATL::CComPtr<IQueryAssociations> query_associations(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n    ATL::CComPtr<IContextMenu> context_menu(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n    ATL::CComPtr<IDataObject> data_object(\n        HWND hwnd, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl);\n    ATL::CComPtr<IDropTarget> drop_target(HWND hwnd);\n\n    ATL::CComPtr<IShellFolderViewCB> folder_view_callback(HWND hwnd);\n\npublic:\n\n    // IShellFolder (via folder_error_adapter)\n    virtual IEnumIDList* enum_objects(HWND hwnd, SHCONTF flags);\n    \n    virtual void get_attributes_of(\n        UINT pidl_count, PCUITEMID_CHILD_ARRAY pidl_array,\n        SFGAOF* flags_inout);\n\n    virtual STRRET get_display_name_of(\n        PCUITEMID_CHILD pidl, SHGDNF uFlags);\n\n    virtual PIDLIST_RELATIVE parse_display_name(\n        HWND hwnd, IBindCtx* bind_ctx, const wchar_t* display_name,\n        ULONG* attributes_inout);\n\n    virtual PITEMID_CHILD set_name_of(\n        HWND hwnd, PCUITEMID_CHILD pidl, const wchar_t* name,\n        SHGDNF flags);\n\n    // IShellFolder2 (via folder_error_adapter2)\n    virtual SHCOLUMNID map_column_to_scid(UINT column_index);\n\nprivate:\n    boost::function<comet::com_ptr<ISftpConsumer>(HWND)> m_consumer_factory;\n    comet::com_ptr<ISftpConsumer> m_consumer;\n\n    void set_consumer_factory(\n        boost::function<comet::com_ptr<ISftpConsumer>(HWND)> consumer_factory)\n    {\n        m_consumer_factory = consumer_factory;\n    }\n\n\n    /** @name Default Context Menu event handler */\n    // @{\n    static HRESULT CALLBACK menu_callback(\n        IShellFolder* folder, HWND hwnd_view, IDataObject* selection, \n        UINT message_id, WPARAM wparam, LPARAM lparam);\n    HRESULT MenuCallback( HWND hwnd, IDataObject *pdtobj, \n        UINT uMsg, WPARAM wParam, LPARAM lParam );\n    // @}\n\n    \n    bool CRemoteFolder::show_extension(PCUITEMID_CHILD pidl);\n\n};"
  },
  {
    "path": "swish/shell_folder/SftpDataObject.cpp",
    "content": "/*  DataObject creating FILE_DESCRIPTOR/FILE_CONTENTS formats from remote data.\n\n    Copyright (C) 2009, 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#include \"SftpDataObject.h\"\n\n#include \"SftpDirectory.h\"\n#include \"data_object/StorageMedium.hpp\"  // StorageMedium\n\n#include \"swish/provider/sftp_provider.hpp\"\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n                                               // path_from_remote_pidl\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n#include <washer/shell/pidl.hpp> // cpidl_t, pidl_t\n#include <washer/shell/pidl_iterator.hpp> // raw_pidl_iterator\n\n#pragma warning(push)\n#pragma warning(disable:4244) // conversion from uint64_t to uint32_t\n#include <boost/date_time/posix_time/conversion.hpp> // from_ftime\n#pragma warning(pop)\n#include <boost/iterator/transform_iterator.hpp> // transform_iterator\n#include <boost/mem_fn.hpp> // mem_fn\n#include <boost/shared_ptr.hpp>\n\n#include <boost/utility.hpp> // next\n\n#include <algorithm> // replace\n#include <stdexcept> // runtime_error\n#include <string>\n\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::path_from_remote_pidl;\nusing swish::remote_folder::remote_itemid_view;\nusing swish::shell_folder::data_object::FileGroupDescriptor;\nusing swish::shell_folder::data_object::Descriptor;\nusing swish::shell_folder::data_object::StorageMedium;\nusing swish::shell_folder::data_object::group_descriptor_from_range;\n\nusing washer::shell::pidl::basic_pidl;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl::raw_pidl_iterator;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing boost::make_transform_iterator;\nusing boost::mem_fn;\nusing boost::next;\nusing boost::shared_ptr;\n\nusing std::replace;\nusing std::runtime_error;\nusing std::wstring;\n\nnamespace comet {\n\ntemplate<> struct comtype<IDataObject>\n{\n    static const IID& uuid() { return IID_IDataObject; }\n    typedef ::IUnknown base;\n};\n\n}\n\n/**\n * Create the DataObject with the top-level PIDLs.\n *\n * These PIDLs represent, for instance, the current group of files and\n * directories which have been selected in an Explorer window.  This list\n * should not include any sub-items of any of the directories.\n *\n * @param cPidl             Number of PIDLs in the selection.\n * @param aPidl             The selected PIDLs.\n * @param pidlCommonParent  PIDL to the common parent of all the PIDLs.\n * @param pProvider         Backend to communicate with remote server.\n */\nCSftpDataObject::CSftpDataObject(\n    UINT cPidl, PCUITEMID_CHILD_ARRAY aPidl,\n    PCIDLIST_ABSOLUTE pidlCommonParent, shared_ptr<sftp_provider> provider)\n    : CDataObject(cPidl, aPidl, pidlCommonParent),\n    // Make a copy of the PIDLs.  These are used to delay-render the\n    // CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS format in GetData().\n    m_pidlCommonParent(pidlCommonParent),\n    m_provider(provider),\n    m_fExpandedPidlList(false),\n    m_fRenderedDescriptor(false),\n    m_cfPreferredDropEffect(static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT))),\n    m_cfFileDescriptor(static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR))),\n    m_cfFileContents(static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILECONTENTS)))\n{\n    std::copy(aPidl, aPidl + cPidl, std::back_inserter(m_pidls));\n\n    // Prod the inner object with the formats whose data we will delay-\n    // render in GetData()\n    if (cPidl > 0)\n    {\n        HRESULT hr;\n        hr = ProdInnerWithFormat(m_cfFileDescriptor, TYMED_HGLOBAL);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n        hr = ProdInnerWithFormat(m_cfFileContents, TYMED_ISTREAM);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n    }\n\n    // Set preferred drop effect.  This prevents any calls to GetData of FGD or\n    // FILECONTENTS until drag is complete, thereby preventing interruptions\n    // caused by delay-rendering.\n    _RenderCfPreferredDropEffect();\n}\n\n/*----------------------------------------------------------------------------*\n * IDataObject methods\n *----------------------------------------------------------------------------*/\n\nSTDMETHODIMP CSftpDataObject::GetData(\n    FORMATETC *pformatetcIn, STGMEDIUM *pmedium)\n{\n    ::ZeroMemory(pmedium, sizeof(STGMEDIUM));\n\n    // Delay-render data if necessary\n    try\n    {\n        if (pformatetcIn->cfFormat == m_cfFileDescriptor)\n        {\n            // Delay-render CFSTR_FILEDESCRIPTOR format into this IDataObject\n            _DelayRenderCfFileGroupDescriptor();\n        }\n        else if (pformatetcIn->cfFormat == m_cfFileContents)\n        {\n            // Delay-render CFSTR_FILECONTENTS format directly.  Do not store.\n            *pmedium = _DelayRenderCfFileContents(pformatetcIn->lindex);\n            return S_OK;\n        }\n\n        // Delegate all non-FILECONTENTS requests to the superclass\n        return __super::GetData(pformatetcIn, pmedium);\n    }\n    WASHER_COM_CATCH_AUTO_INTERFACE();\n}\n\n/*----------------------------------------------------------------------------*\n * Private methods\n *----------------------------------------------------------------------------*/\n\nvoid CSftpDataObject::_RenderCfPreferredDropEffect()\nthrow(...)\n{\n    // Create DROPEFFECT_COPY in global memory\n    HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));\n    CGlobalLock glock(hGlobal);\n    DWORD &dwDropEffect = glock.GetDword();\n    dwDropEffect = DROPEFFECT_COPY;\n\n    // Save in IDataObject\n    CFormatEtc fetc(m_cfPreferredDropEffect);\n    STGMEDIUM stg;\n    stg.tymed = TYMED_HGLOBAL;\n    stg.hGlobal = glock.Detach();\n    stg.pUnkForRelease = NULL;\n    HRESULT hr = SetData(&fetc, &stg, true);\n    if (FAILED(hr))\n    {\n        ::ReleaseStgMedium(&stg);\n    }\n    ATLENSURE_SUCCEEDED(hr);\n}\n\n/**\n * Delay render CFSTR_FILEDESCRIPTOR format for PIDLs passed to Initialize().\n *\n * Unlike the CFSTR_SHELLIDLIST format, the file group descriptor should\n * include not only the top-level items but also any subitems within and\n * directories.  This enables Explorer to copy or move an entire directory\n * tree.\n *\n * As this operation can be very expensive when the directory tree is deep,\n * it isn't appropriate to do this when the IDataObject is created.  This\n * would lead to large delays when simply opening a directory---an operation\n * that also requires an IDataObject.  Instead, this format is delay-rendered\n * from the list of PIDLs cached during Initialize() the first time it is\n * requested.\n *\n * @see _DelayRenderCfFileContents()\n *\n * @throws  com_error on error.\n */\nvoid CSftpDataObject::_DelayRenderCfFileGroupDescriptor()\nthrow(...)\n{\n    if (!m_fRenderedDescriptor && !m_pidls.empty())\n    {\n        // Create FILEGROUPDESCRIPTOR format from the cached PIDL list\n        HGLOBAL hglobal = _CreateFileGroupDescriptor();\n#ifdef DEBUG\n        FileGroupDescriptor fgd(hglobal);\n        ATLASSERT(fgd.size() > 0);\n#endif\n\n        // Insert the descriptor into the IDataObject\n        CFormatEtc fetc(m_cfFileDescriptor);\n        STGMEDIUM stg;\n        stg.tymed = TYMED_HGLOBAL;\n        stg.hGlobal = hglobal;\n        stg.pUnkForRelease = NULL;\n        HRESULT hr = SetData(&fetc, &stg, true);\n        if (FAILED(hr))\n        {\n            ::ReleaseStgMedium(&stg);\n        }\n        ATLENSURE_SUCCEEDED(hr);\n\n        m_fRenderedDescriptor = true;\n    }\n}\n\n/**\n * Delay-render a CFSTR_FILECONTENTS format for a PIDL passed to Initialize().\n *\n * Unlike the CFSTR_SHELLIDLIST format, the file contents formats should\n * include not only the top-level items but also any subitems within any\n * directories.  This enables Explorer to copy or move an entire directory\n * tree.\n *\n * As this operation can be very expensive when the directory tree is deep,\n * it isn't appropriate to do this when the IDataObject is created.  This\n * would lead to large delays when simply opening a directory---an operation\n * that also requires an IDataObject.  Instead, these formats are individually\n * delay-rendered from the list of PIDLs cached during Initialize() each time\n * one is requested.\n *\n * @see _DelayRenderCfFileGroupDescriptor()\n *\n * @throws  com_error on error.\n */\nSTGMEDIUM CSftpDataObject::_DelayRenderCfFileContents(long lindex)\nthrow(...)\n{\n    STGMEDIUM stg;\n    ::ZeroMemory(&stg, sizeof(STGMEDIUM));\n\n    if (!m_pidls.empty())\n    {\n        // Create an IStream from the cached PIDL list\n        com_ptr<IStream> stream = _CreateFileContentsStream(lindex);\n        ATLENSURE(stream);\n\n        // Pack into a STGMEDIUM which will be returned to the client\n        stg.tymed = TYMED_ISTREAM;\n        stg.pstm = stream.detach();\n    }\n    else\n    {\n        AtlThrow(DV_E_LINDEX);\n    }\n\n    return stg;\n}\n\n/**\n * Create CFSTR_FILEDESCRIPTOR format from cached PIDLs.\n */\nHGLOBAL CSftpDataObject::_CreateFileGroupDescriptor()\n{\n    ExpandedList descriptors;\n    _ExpandPidlsInto(descriptors);\n\n    typedef ExpandedList::value_type descriptor_type;\n\n    return group_descriptor_from_range(\n        make_transform_iterator(\n            descriptors.begin(), mem_fn(&descriptor_type::get)),\n        make_transform_iterator(\n            descriptors.end(), mem_fn(&descriptor_type::get)));\n}\n\n/**\n * Create an IStream for relative path stored in the lindexth FILEDESCRIPTOR.\n *\n * @p lindex corresponds to an item in the File Group Descriptor (which\n * we created in _DelayRenderCfFileGroupDescriptor) with the same index.\n *\n * @note Asking for an IStream to folder may not break (libssh2 can do\n * this) but it is a waste of effort. Explorer won't use it, nor should it.\n */\ncom_ptr<IStream> CSftpDataObject::_CreateFileContentsStream(long lindex)\nthrow(...)\n{\n    ATLENSURE(m_fRenderedDescriptor);\n\n    // Pull the FILEGROUPDESCRIPTOR we made earlier out of the DataObject\n    CFormatEtc fetc(m_cfFileDescriptor);\n    StorageMedium medium;\n    HRESULT hr = GetData(&fetc, medium.out());\n    ATLENSURE_SUCCEEDED(hr);\n    FileGroupDescriptor fgd(medium.get().hGlobal);\n\n    // Get stream from relative path stored in the lindexth FILEDESCRIPTOR\n    CSftpDirectory dir(m_pidlCommonParent, m_provider);\n\n    // UNOBVIOUS: FGDs store paths with backslashes so we need to convert\n    // those here\n    wstring path = fgd[lindex].path();\n    replace(path.begin(), path.end(), L'\\\\', L'/');\n    return dir.GetFileByPath(path, false);\n}\n\n/**\n * Expand all top-level PIDLs into a list of Descriptors with relative paths.\n *\n * There should be a file descriptor for every item in the directory\n * heirarchies.  Once expanded, this should not need to be done again for this\n * DataObject as the descriptors will be saved in the superclass.\n *\n * In an attempt to reduce the memory footprint of this very expensive\n * operation to an absolute minimum, all expansion is done by appending to a\n * single container by reference.\n */\nvoid CSftpDataObject::_ExpandPidlsInto(ExpandedList& descriptors)\nconst throw(...)\n{\n    for (UINT i = 0; i < m_pidls.size(); ++i)\n    {\n        _ExpandTopLevelPidlInto(m_pidls[i], descriptors);\n    }\n}\n\nnamespace {\n\n    using namespace boost::posix_time;\n\n    const int SHOW_PROGRESS_THRESHOLD = 10000;\n\n    template<typename T, typename U>\n    remote_itemid_view view_of_last_item(const basic_pidl<T, U>& pidl)\n    {\n        raw_pidl_iterator it(pidl.get());\n        while (it != raw_pidl_iterator())\n        {\n            if (next(it) == raw_pidl_iterator())\n                return remote_itemid_view(*it);\n            else\n                ++it;\n        }\n\n        BOOST_THROW_EXCEPTION(runtime_error(\"Empty iterator\"));\n    }\n\n    template<typename T, typename U>\n    Descriptor make_descriptor(const basic_pidl<T, U>& pidl, bool dialogue)\n    {\n        Descriptor d;\n\n        // Filename\n        d.path(path_from_remote_pidl(pidl).wstring());\n\n        // The PIDL we have been passed may be multilevel, representing a\n        // path to the file.  Get last item in PIDL to get properties of the\n        // file itself.\n\n        remote_itemid_view itemid = view_of_last_item(pidl);\n\n        // Size\n        d.file_size(itemid.size());\n\n        // Date\n        SYSTEMTIME st;\n        ATLVERIFY(itemid.date_modified().to_systemtime(&st));\n        FILETIME ftLastWriteTime;\n        ATLVERIFY(::SystemTimeToFileTime(&st, &ftLastWriteTime));\n        d.last_write_time(from_ftime<ptime>(ftLastWriteTime));\n\n        // Show progress UI?\n        if (d.file_size() > SHOW_PROGRESS_THRESHOLD || dialogue)\n            d.want_progress(true);\n\n        // Attributes\n        DWORD dwFileAttributes = 0;\n        if (itemid.is_folder())\n            dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;\n        else\n            dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;\n\n        if (itemid.filename()[0] == L'.')\n            dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;\n\n        d.attributes(dwFileAttributes);\n\n        return d;\n    }\n\n}\n\n/**\n * Expand one of the selected PIDLs to include any descendents.\n *\n * If the given PIDL is a simple item, the returned list just contains this\n * PIDL.  However, if it a directory it will contain the PIDL followed by\n * all the items in and below the directory.\n */\nvoid CSftpDataObject::_ExpandTopLevelPidlInto(\n    const TopLevelPidl& pidl, ExpandedList& descriptors)\nconst throw(...)\n{\n    // Add file descriptor from PIDL - common case\n    ATLENSURE_THROW(\n        descriptors.size() < descriptors.max_size() - 1, E_OUTOFMEMORY);\n    descriptors.push_back(make_descriptor(pidl, _WantProgressDialogue()));\n\n    // Explode the contents of subfolders into the list\n    if (remote_itemid_view(pidl).is_folder())\n    {\n        _ExpandDirectoryTreeInto(m_pidlCommonParent, pidl.get(), descriptors);\n    }\n}\n\n/**\n * Flattens the filesystem tree rooted at this directory into a list of PIDLs.\n *\n * The list includes this directory, all the items in this directory and all\n * items below any of those which are directories.\n *\n * Although called 'flat', all the PIDL are returned relative to this\n * directory's parent and therefore, actually do maintain a record of the\n * directory structure.\n *//*\nvector<CRelativePidl> CSftpDataObject::FlattenDirectoryTree()\nthrow(...)\n{\n    vector<CRelativePidl> pidls;\n    _FlattenDirectoryTreeInto(pidls, NULL);\n    ATLASSERT(pidls.size() > 0);\n    return pidls;\n}*/\n\n/**\n * Return a list of all the PIDLs in this directory and below as a single list.\n *\n * The PIDLs are returned appended to the end of the @p vecPidls inout\n * parameter which reduces the amount of copying.\n *\n * All the PIDL (which are relative to this directory's parent) are prefixed with\n * a given parent PIDL. This allows this method to be used recursively and still\n * produce a list of PIDLs relative to a common root.\n *\n * @param[in,out] vecPidl  List of flattened PIDLs to append our flattened\n *                         PIDLs.\n * @param[in] pidlPrefix   PIDL with which to prefix the PIDLs below this\n *                         folder. If NULL, the returned list is relative to\n *                         this folder.\n */\nvoid CSftpDataObject::_ExpandDirectoryTreeInto(\n    const CAbsolutePidl& pidlParent, const CRelativePidl& pidlDirectory,\n    ExpandedList& descriptors)\nconst throw(...)\n{\n    com_ptr<IEnumIDList> listing = _GetEnumAll(\n        CAbsolutePidl(pidlParent, pidlDirectory));\n\n    // Add all items below this directory (this directory added by caller)\n    HRESULT hr;\n    while(true)\n    {\n        cpidl_t pidl;\n        hr = listing->Next(1, pidl.out(), NULL);\n        if (hr != S_OK)\n            break;\n\n        // Create version of pidl relative to the common root (pidlParent)\n        pidl_t relative_pidl = pidlDirectory.m_pidl + pidl;\n\n        // Add simple item - common case\n        ATLENSURE_THROW(\n            descriptors.size() < descriptors.max_size() - 1, E_OUTOFMEMORY);\n        descriptors.push_back(make_descriptor(relative_pidl, true));\n\n        // Explode the contents of subfolders into the list\n        if (remote_itemid_view(pidl).is_folder())\n        {\n            pidl = NULL; // Reduce recursion footprint\n            _ExpandDirectoryTreeInto(\n                pidlParent, relative_pidl.get(), descriptors);\n        }\n    }\n    ATLENSURE(hr == S_FALSE);\n}\n\ncom_ptr<IEnumIDList> CSftpDataObject::_GetEnumAll(const CAbsolutePidl& pidl)\nconst throw(...)\n{\n    CSftpDirectory dir(pidl, m_provider);\n    return dir.GetEnum(\n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN);\n}\n\n/**\n * We want a progress dialogue unless the entire selection consists of a\n * single non-directory.  This is for the FD_PROGRESSUI flag.\n *\n * @rant WHY did MS put this flag in the descriptors!!??\n */\ninline bool CSftpDataObject::_WantProgressDialogue()\nconst throw()\n{\n    return m_pidls.size() > 1\n        || (m_pidls.size() == 1 && remote_itemid_view(m_pidls[0]).is_folder());\n}\n"
  },
  {
    "path": "swish/shell_folder/SftpDataObject.h",
    "content": "/**\n    @file\n\n    DataObject creating FILE_DESCRIPTOR/FILE_CONTENTS formats from remote data.\n\n    @if license\n\n    Copyright (C) 2009, 2010, 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"DataObject.h\"\n#include \"data_object/FileGroupDescriptor.hpp\"  // FileGroupDescriptor\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider\n#include \"swish/shell_folder/Pidl.h\"\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/shared_ptr.hpp>\n\n#include <vector>\n\n/**\n * Subclass of CDataObject which, additionally, creates CFSTR_FILEDESCRIPTOR\n * and CFSTR_FILECONTENTS from remote data on demand.\n *\n * This class creates the CFSTR_FILEDESCRIPTOR HGLOBAL data and delegates its\n * storage to the superclass (which will, in turn, delegate it to the inner\n * object provided by the system).  \n *\n * This class also creates CFSTR_FILECONTENTS data (as IStreams) as they are \n * requested.  Although the superclass (CDataObject) can---as with the file\n * group descriptor---store these for later, we no longer use this as doing \n * so keeps a file-handle open to every file ever requested. This would\n * cause a large transfer to fail part way through.  Instead, we create the\n * IStreams afresh on every request.  These file-handles will close when the\n * client Releases the IStream.\n *\n * These operations are expensive---they require the DataObject to contact\n * the remote server via an swish::provider::sftp_provider to retrieve file data---and may not\n * be needed if the client simply wants, say, a CFSTR_SHELLIDLIST format,\n * so delay-rendering is employed to postpone this expense until we are sure\n * it is required (GetData() is called with for one of the two formats).\n *\n * If the CFSTR_FILEDESCRIPTOR format is requested and any of the initial\n * PIDLs are directories, the PIDLs are expanded to include every item\n * anywhere with those directory trees.  Unfortunately, this is a @b very\n * expensive operation but the shell design doesn't give any way to provide\n * a partial file group descriptor.\n */\nclass CSftpDataObject : public CDataObject\n{\npublic:\n\n    CSftpDataObject(\n        UINT cPidl, __in_ecount_opt(cPidl) PCUITEMID_CHILD_ARRAY aPidl,\n        __in PCIDLIST_ABSOLUTE pidlCommonParent,\n        boost::shared_ptr<swish::provider::sftp_provider> provider);\n\npublic: // IDataObject methods\n\n    IFACEMETHODIMP GetData( \n        __in FORMATETC *pformatetcIn,\n        __out STGMEDIUM *pmedium);\n    \nprivate:\n    /**\n     * Top-level PIDL types. These represent currently-selected items\n     * and will always be single-level children of m_pidlCommonParent.\n     */\n    // @{\n    typedef washer::shell::pidl::cpidl_t TopLevelPidl;\n    typedef std::vector<TopLevelPidl> TopLevelList;\n    // @}\n\n    /**\n     * Expanded types.  The are the types that the top-level PIDLs are expanded\n     * into when a file group descriptor is requested.  They can represent all\n     * the items in or below the top-level and are needed in order to store \n     * entire directory trees in an IDataObject.\n     */\n    // @{\n    typedef swish::shell_folder::data_object::Descriptor ExpandedItem;\n    typedef std::vector<ExpandedItem> ExpandedList;\n    // @}\n\n    boost::shared_ptr<swish::provider::sftp_provider> m_provider;\n                                                     ///< Connection to backend\n\n    /** @name Cached PIDLs */\n    // @{\n    CAbsolutePidl m_pidlCommonParent;    ///< Parent of PIDLs in m_pidls\n    TopLevelList m_pidls;                ///< Top-level PIDLs (the selection)\n    // @}\n\n    /** @name Registered CLIPFORMATS */\n    // @{\n    CLIPFORMAT m_cfPreferredDropEffect;  ///< CFSTR_PREFERREDDROPEFFECT\n    CLIPFORMAT m_cfFileDescriptor;       ///< CFSTR_FILEDESCRIPTOR\n    CLIPFORMAT m_cfFileContents;         ///< CFSTR_FILECONTENTS\n    // @}\n\n    void _RenderCfPreferredDropEffect() throw(...);\n\n    /** @name Delay-rendering */\n    //@{\n    bool m_fExpandedPidlList;         ///< Have we expanded top-level PIDLs?\n    bool m_fRenderedDescriptor;       ///< Have we rendered FileGroupDescriptor\n\n    void _DelayRenderCfFileGroupDescriptor() throw(...);\n    STGMEDIUM _DelayRenderCfFileContents(long lindex) throw(...);\n\n    HGLOBAL _CreateFileGroupDescriptor();\n    comet::com_ptr<IStream> _CreateFileContentsStream(long lindex) throw(...);\n\n    void _ExpandPidlsInto(__inout ExpandedList& descriptors) const throw(...);\n    void _ExpandTopLevelPidlInto(\n        const TopLevelPidl& pidl, __inout ExpandedList& descriptors)\n        const throw(...);\n    void _ExpandDirectoryTreeInto(\n        const CAbsolutePidl& pidlParent, const CRelativePidl& pidlDirectory,\n        __inout ExpandedList& descriptors) const throw(...);\n    comet::com_ptr<IEnumIDList> _GetEnumAll(const CAbsolutePidl& pidl)\n        const throw(...);\n    inline bool _WantProgressDialogue() const throw();\n    // @}\n};\n"
  },
  {
    "path": "swish/shell_folder/SftpDirectory.cpp",
    "content": "/*  Manage remote directory as a collection of PIDLs.\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/\n\n#include \"SftpDirectory.h\"\n\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view, create_host_item\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view,\n                                               // create_remote_itemid\n#include \"swish/remote_folder/swish_pidl.hpp\" // absolute_path_from_swish_pidl\n\n#include <washer/shell/pidl_iterator.hpp> // pidl_iterator, find_host_itemid\n#include <washer/trace.hpp> // trace\n\n#include <comet/datetime.h> // datetime_t\n#include <comet/error.h> // com_error\n#include <comet/interface.h> // comtype\n#include <comet/smart_enum.h> // make_smart_enumeration\n\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/function.hpp>\n#include <boost/lambda/bind.hpp>\n#include <boost/lambda/lambda.hpp> // _1\n#include <boost/make_shared.hpp> // make_shared\n#include <boost/range/adaptor/filtered.hpp>\n#include <boost/range/adaptor/transformed.hpp>\n#include <boost/range/algorithm/copy.hpp>\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <algorithm> // transform\n#include <exception> // exception\n#include <vector>\n\nusing ssh::filesystem::path;\n\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::absolute_path_from_swish_pidl;\nusing swish::remote_folder::create_remote_itemid;\nusing swish::remote_folder::remote_itemid_view;\nusing swish::provider::sftp_filesystem_item;\n\nusing swish::host_folder::create_host_itemid;\nusing swish::host_folder::find_host_itemid;\nusing swish::host_folder::host_itemid_view;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_iterator;\nusing washer::shell::pidl::raw_pidl_iterator;\nusing washer::trace;\n\nusing comet::auto_attach;\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\nusing comet::datetime_t;\nusing comet::make_smart_enumeration;\n\nusing boost::adaptors::filtered;\nusing boost::adaptors::transformed;\nusing boost::function;\nusing namespace boost::lambda;\nusing boost::make_shared;\nusing boost::ref;\nusing boost::shared_ptr;\n\nusing std::exception;\nusing std::vector;\nusing std::wstring;\n\nnamespace comet {\n\ntemplate<> struct comtype<IEnumIDList>\n{\n    static const IID& uuid() throw() { return IID_IEnumIDList; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct enumerated_type_of<IEnumIDList>\n{ typedef PITEMID_CHILD is; };\n\n/**\n * Copy-policy for use by enumerators of child PIDLs.\n */\ntemplate<> struct impl::type_policy<PITEMID_CHILD>\n{\n    static void init(PITEMID_CHILD& t, const cpidl_t& s)\n    {\n        s.copy_to(t);\n    }\n\n    static void clear(PITEMID_CHILD& t)\n    {\n        ::ILFree(t);\n    }\n};\n\n}\n\n/**\n * Creates and initialises directory instance from a PIDL.\n *\n * @param directory_pidl  PIDL to the directory this object represents.  Must\n *                        start at or before a HostItemId.\n */\nCSftpDirectory::CSftpDirectory(\n    const apidl_t& directory_pidl, shared_ptr<sftp_provider> provider)\n:\nm_provider(provider), m_directory_pidl(directory_pidl),\nm_directory(absolute_path_from_swish_pidl(directory_pidl)) {}\n\nnamespace {\n\n    bool is_link(const sftp_filesystem_item& lt)\n    {\n        return lt.type() == sftp_filesystem_item::type::link;\n    }\n\n    bool is_directory(\n        const sftp_filesystem_item& file, const path& directory,\n        sftp_provider& provider)\n    {\n        if (is_link(file))\n        {\n            // Links don't indicate anything about their target such as\n            // whether it is a file or folder so we have to interrogate\n            // its target\n            path link_path = directory / file.filename();\n\n            try\n            {\n                sftp_filesystem_item target = provider.stat(link_path, TRUE);\n\n                // TODO: consider what other properties we might want to\n                // take from the target instead of the link.  Currently\n                // we only take on folderness.\n                return target.type() == sftp_filesystem_item::type::directory;\n            }\n            catch(const exception&)\n            {\n                // Broken links are treated like files.  There isn't really\n                // anything else sensible to do with them.\n                return false;\n            }\n        }\n        else\n        {\n            return file.type() == sftp_filesystem_item::type::directory;\n        }\n    }\n\n    bool is_dotted(const sftp_filesystem_item& file)\n    {\n        wstring filename = file.filename().wstring();\n        return filename[0] == L'.';\n    }\n\n    cpidl_t convert_directory_entry_to_pidl(\n        const sftp_filesystem_item& file, const path& directory,\n        sftp_provider& provider)\n    {\n        return create_remote_itemid(\n            file.filename().wstring(),\n            is_directory(file, directory, provider),\n            is_link(file),\n            (file.owner()) ? *file.owner() : wstring(),\n            (file.group()) ? *file.group() : wstring(),\n            file.uid(), file.gid(), file.permissions(), file.size_in_bytes(),\n            file.last_modified(), file.last_accessed());\n    }\n\n    /**\n     * Notify the shell that a new directory was created.\n     *\n     * Primarily, this will cause Explorer to show the new folder in any\n     * windows displaying the parent folder.\n     *\n     * IMPORTANT: this will only happen if the parent folder is listening for\n     * SHCNE_MKDIR notifications.\n     *\n     * We wait for the event to flush because setting the edit text afterwards\n     * depends on this.\n     */\n    void notify_shell_created_directory(const apidl_t& folder_pidl)\n    {\n        assert(folder_pidl);\n\n        ::SHChangeNotify(\n            SHCNE_MKDIR, SHCNF_IDLIST | SHCNF_FLUSH, folder_pidl.get(), NULL);\n    }\n\n    /**\n     * Notify the shell that a file or directory was deleted.\n     *\n     * Primarily, this will cause Explorer to remove the item from the parent\n     * folder view.\n     *\n     * This function works out whether it was a file or folder removed by\n     * extracting the flag from the remote item ID.\n     */\n    void notify_shell_of_deletion(\n        const apidl_t& parent_folder, const cpidl_t& file_or_folder)\n    {\n        bool is_folder = remote_itemid_view(file_or_folder).is_folder();\n        ::SHChangeNotify(\n            (is_folder) ? SHCNE_RMDIR : SHCNE_DELETE,\n            SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,\n            (parent_folder + file_or_folder).get(), NULL);\n    }\n}\n\n/**\n * Retrieve an IEnumIDList to enumerate this directory's contents.\n *\n * This function returns an enumerator which can be used to iterate through\n * the contents of this directory as a series of PIDLs.  This listing is a\n * @b copy of the one obtained from the server and will not update to reflect\n * changes.  In order to obtain an up-to-date listing, this function must be\n * called again to get a new enumerator.\n *\n * @param flags  Flags specifying nature of files to fetch.\n *\n * @returns  Smart pointer to the IEnumIDList.\n * @throws  com_error if an error occurs.\n */\ncom_ptr<IEnumIDList> CSftpDirectory::GetEnum(SHCONTF flags)\n{\n    // Interpret supported SHCONTF flags\n    bool include_folders = (flags & SHCONTF_FOLDERS) != 0;\n    bool include_non_folders = (flags & SHCONTF_NONFOLDERS) != 0;\n    bool include_hidden = (flags & SHCONTF_INCLUDEHIDDEN) != 0;\n\n    vector<sftp_filesystem_item> directory_enum = m_provider->listing(\n        m_directory);\n\n    // XXX: PERFORMANCE:\n    // For a link, we look its target details up 3 times!  Once to see if it is\n    // a directory, once to see if it isn't a directory and once to see if its\n    // a directory whilst converting to PIDL.  This info should be cached in\n    // the sftp_filesystem_item\n\n    function<bool(const sftp_filesystem_item&)> hidden_filter =\n        include_hidden || !bind(is_dotted, _1);\n\n    function<bool(const sftp_filesystem_item&)> directory_filter =\n        include_folders ||\n        !bind(is_directory, _1, m_directory, ref(*m_provider));\n\n    function<bool(const sftp_filesystem_item&)> non_directory_filter =\n        include_non_folders ||\n        bind(is_directory, _1, m_directory, ref(*m_provider));\n\n    function<cpidl_t(const sftp_filesystem_item&)> pidl_converter =\n        bind(\n            convert_directory_entry_to_pidl, _1, m_directory, ref(*m_provider));\n\n    shared_ptr< vector<cpidl_t> > pidls = make_shared< vector<cpidl_t> >();\n    boost::copy(\n        directory_enum |\n        filtered(hidden_filter) |\n        filtered(directory_filter) |\n        filtered(non_directory_filter) |\n        transformed(pidl_converter),\n        back_inserter(*pidls));\n\n    return make_smart_enumeration<IEnumIDList>(pidls);\n}\n\n/**\n * Get instance of CSftpDirectory for a subdirectory of this directory.\n *\n * @param directory  Child PIDL of directory within this directory.\n *\n * @returns  Instance of the subdirectory.\n * @throws  com_error if error.\n */\nCSftpDirectory CSftpDirectory::GetSubdirectory(const cpidl_t& directory)\n{\n    if (!remote_itemid_view(directory).is_folder())\n        BOOST_THROW_EXCEPTION(com_error(E_INVALIDARG));\n\n    apidl_t sub_directory = m_directory_pidl + directory;\n    return CSftpDirectory(sub_directory, m_provider);\n}\n\nnamespace {\n\n    std::ios_base::openmode writeable_to_openmode(bool writeable)\n    {\n        if (writeable)\n        {\n            return std::ios_base::out;\n        }\n        else\n        {\n            return std::ios_base::in;\n        }\n    }\n\n}\n\n/**\n * Get IStream interface to the remote file specified by the given PIDL.\n *\n * This 'file' may also be a directory but the IStream does not give access\n * to its subitems.\n *\n * @param file  Child PIDL of item within this directory.\n *\n * @returns  Smart pointer of an IStream interface to the file.\n * @throws  com_error if error.\n */\ncom_ptr<IStream> CSftpDirectory::GetFile(const cpidl_t& file, bool writeable)\n{\n    path file_path = m_directory / remote_itemid_view(file).filename();\n\n    return m_provider->get_file(file_path, writeable_to_openmode(writeable));\n}\n\n/**\n * Get IStream interface to the remote file specified by a relative path.\n *\n * This 'file' may also be a directory but the IStream does not give access\n * to its subitems.\n *\n * @param file  Path of item relative to this directory (may be at a level\n *              below this directory).\n *\n * @returns  Smart pointer of an IStream interface to the file.\n * @throws  com_error if error.\n */\ncom_ptr<IStream> CSftpDirectory::GetFileByPath(\n    const path& file, bool writeable)\n{\n    return m_provider->get_file(\n        m_directory / file, writeable_to_openmode(writeable));\n}\n\nbool CSftpDirectory::exists(const cpidl_t& file)\n{\n    path file_path = m_directory / remote_itemid_view(file).filename();\n\n    try\n    {\n        // std::ios_base::in makes it fail if file doesn't exist\n        m_provider->get_file(file_path, std::ios_base::in);\n    }\n    catch (const exception&)\n    {\n        return false;\n    }\n\n    return true;\n}\n\nbool CSftpDirectory::Rename(\n    const cpidl_t& old_file, const wstring& new_filename,\n    com_ptr<ISftpConsumer> consumer)\n{\n    path old_file_path = m_directory / remote_itemid_view(old_file).filename();\n    path new_file_path = m_directory / new_filename;\n\n    return m_provider->rename(consumer.in(), old_file_path, new_file_path)\n        == VARIANT_TRUE;\n}\n\nvoid CSftpDirectory::Delete(const cpidl_t& file)\n{\n    path target_path = m_directory / remote_itemid_view(file).filename();\n\n    m_provider->remove_all(target_path);\n\n    try\n    {\n        // Must not report a failure after this point.  The item was deleted\n        // even if notifying the shell fails.\n\n        notify_shell_of_deletion(m_directory_pidl, file);\n    }\n    catch (const exception& e)\n    {\n        trace(\"WARNING: Couldn't notify shell of deletion: %s\") % e.what();\n    }\n\n}\n\ncpidl_t CSftpDirectory::CreateDirectory(const wstring& name)\n{\n    path target_path = m_directory / name;\n\n    cpidl_t sub_directory = create_remote_itemid(\n        name, true, false, L\"\", L\"\", 0, 0, 0, 0, datetime_t::now(),\n        datetime_t::now());\n\n    m_provider->create_new_directory(target_path);\n\n    try\n    {\n        // Must not report a failure after this point.  The folder was created\n        // even if notifying the shell fails.\n\n        // TODO: stat new folder for actual parameters\n\n        notify_shell_created_directory(m_directory_pidl + sub_directory);\n    }\n    catch (const exception& e)\n    {\n        trace(\"WARNING: Couldn't notify shell of new folder: %s\") % e.what();\n    }\n\n    return sub_directory;\n}\n\napidl_t CSftpDirectory::ResolveLink(const cpidl_t& item)\n{\n    remote_itemid_view symlink(item);\n    path link_path = m_directory / symlink.filename();\n    path target_path = m_provider->resolve_link(link_path);\n\n    // XXX: HACK:\n    // Currently, we create the new PIDL for the resolved path by copying all\n    // the items up to (not including) the host itemid, then appending a new\n    // host itemid containing the full resolved path.  This is a horrible hack\n    // and is likely to fail miserably if the resolved target is a file rather\n    // than a directory.\n    //\n    // The proper solution would be to have three types of Item ID (PIDL items):\n    // - Server items that just maintain the details of the server connection.\n    //   They don't store any path information.\n    // - Remote items that hold the details of one segment of the remote path.\n    //   Combined in a list after a server item, they identify an absolute\n    //   path to a file or directory on a remote server.\n    // - Host items that are just shortcuts that resolve to a server item and\n    //   one or more remote items.  They hold server information and a starting\n    //   path.\n    // ... or something like this.  A host item could, in some magical, way hold\n    // an absolute PIDL that contains a server items followed by several remote\n    // items.  Symlink items could even be a fourth type of item.\n\n    pidl_iterator itemids(m_directory_pidl);\n    apidl_t pidl_to_link_target;\n    raw_pidl_iterator host_itemid = find_host_itemid(m_directory_pidl);\n    while (itemids != host_itemid)\n    {\n        pidl_to_link_target += *itemids++;\n    }\n\n    host_itemid_view old_item(*host_itemid);\n    cpidl_t new_host_item = create_host_itemid(\n        old_item.host(), old_item.user(), L\"\", old_item.port(),\n        old_item.label());\n\n    apidl_t resolved_target = pidl_to_link_target + new_host_item;\n    BOOST_FOREACH(const path& segment, target_path)\n    {\n        resolved_target += create_remote_itemid(\n            segment.filename().wstring(), true, false, L\"\", L\"\", 0, 0, 0, 0,\n            datetime_t(), datetime_t());\n    }\n\n    return resolved_target;\n}\n"
  },
  {
    "path": "swish/shell_folder/SftpDirectory.h",
    "content": "/**\n    @file\n\n    Manage remote directory as a collection of PIDLs.\n\n    @if license\n\n    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider, ISftpConsumer\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\nclass CSftpDirectory\n{\npublic:\n    CSftpDirectory(\n        const washer::shell::pidl::apidl_t& directory,\n        boost::shared_ptr<swish::provider::sftp_provider> provider);\n\n    comet::com_ptr<IEnumIDList> GetEnum(SHCONTF flags);\n    CSftpDirectory GetSubdirectory(\n        const washer::shell::pidl::cpidl_t& directory);\n    comet::com_ptr<IStream> GetFile(\n        const washer::shell::pidl::cpidl_t& file, bool writeable);\n    comet::com_ptr<IStream> GetFileByPath(\n        const ssh::filesystem::path& file, bool writeable);\n\n    bool exists(const washer::shell::pidl::cpidl_t& file);\n\n    bool Rename(\n        const washer::shell::pidl::cpidl_t& old_file,\n        const std::wstring& new_filename,\n        comet::com_ptr<ISftpConsumer> consumer);\n    void Delete(\n        const washer::shell::pidl::cpidl_t& file);\n    washer::shell::pidl::cpidl_t CreateDirectory(const std::wstring& name);\n    washer::shell::pidl::apidl_t ResolveLink(\n        const washer::shell::pidl::cpidl_t& item);\n\nprivate:\n    boost::shared_ptr<swish::provider::sftp_provider> m_provider;\n                                                      ///< Backend data provider\n    ssh::filesystem::path m_directory; ///< Absolute path to this directory.\n    const washer::shell::pidl::apidl_t m_directory_pidl;\n                                 ///< Absolute PIDL to this directory.\n};\n"
  },
  {
    "path": "swish/shell_folder/SnitchingDataObject.hpp",
    "content": "/**\n    @file\n\n    Wrap a data object to show errors to user.\n\n    @if license\n\n    Copyright (C) 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_SHELL_FOLDER_SNITCHING_DATA_OBJECT_HPP\n#define SWISH_SHELL_FOLDER_SNITCHING_DATA_OBJECT_HPP\n#pragma once\n\n#include \"swish/frontend/announce_error.hpp\" // announce_last_exception\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/error.h> // com_error_from_interface\n#include <comet/ptr.h> // com_ptr\n#include <comet/server.h> // simple_object\n\n#include <boost/locale.hpp> // translate\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <ObjIdl.h> // IDataObject\n\nnamespace comet {\n\ntemplate<> struct comtype<::IDataObject>\n{\n    static const ::IID& uuid() throw() { return ::IID_IDataObject; }\n    typedef ::IUnknown base;\n};\n\n}\n\nnamespace swish {\nnamespace shell_folder {\n\n/**\n * Layer around a data object that reports errors to the user.\n *\n * This keeps UI out of CDropTarget.  \n */\nclass CSnitchingDataObject : public comet::simple_object<IDataObject>\n{\npublic:\n    CSnitchingDataObject(comet::com_ptr<IDataObject> wrapped_data_object)\n        : m_inner(wrapped_data_object),\n          m_file_descriptor_format_w(static_cast<CLIPFORMAT>(\n              ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))),\n          m_file_descriptor_format_a(static_cast<CLIPFORMAT>(\n              ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA))),\n          m_file_contents_format(static_cast<CLIPFORMAT>(\n              ::RegisterClipboardFormat(CFSTR_FILECONTENTS))),\n          m_error_cycle_marker(FORMATETC())\n    {\n    }\n\npublic: // IDataObject methods\n\n    virtual HRESULT STDMETHODCALLTYPE GetData( \n        FORMATETC *pformatetcIn, STGMEDIUM *pmedium)\n    {\n        HRESULT hr = m_inner->GetData(pformatetcIn, pmedium);\n        try\n        {\n            try\n            {\n                // Only capture the delay rendered formats.  Everything else\n                // should have been caught when this data object was created\n                // and for some formats an error from GetData is standard\n                // operating procedure, not something that we should report.\n                if (pformatetcIn->cfFormat == m_file_descriptor_format_w ||\n                    pformatetcIn->cfFormat == m_file_descriptor_format_a ||\n                    pformatetcIn->cfFormat == m_file_contents_format)\n                {\n                    if (FAILED(hr))\n                        BOOST_THROW_EXCEPTION(\n                            comet::com_error_from_interface(m_inner, hr));\n                }\n            }\n            catch (...)\n            {\n                // DV_E_FORMATETC is used when we might have the data, just\n                // not in the requested format.  It should not be reported.\n                if (hr == DV_E_FORMATETC)\n                    throw;\n\n                // HACK:\n                // The shell asks for different versions of the same format\n                // (such as CFSTR_FILEDESCRIPTORA/CFSTR_FILEDESCRIPTORW) and\n                // different DVASPECTs.  As one fails it tries the next.\n                // However, we only want to report the error once\n                // so we record what the first failing case was and won't\n                // show the error message again unless we see that exact format\n                // requested again.\n                // The theory being that the calling code is not going to try\n                // a format again that we already said no to unless the user\n                // initiated the operation again in which case we *do* want to\n                // show the error message again.\n                // Yes, this is a hack; a different sequence of format requests\n                // might cause some weird behaviour.  However, it we mustn't\n                // display the error message repeatedly and this approach is a\n                // slight improvement on showing the message strictly once only.\n                if (m_error_cycle_marker.cfFormat == 0)\n                {\n                    m_error_cycle_marker = *pformatetcIn;\n                }\n                else if (\n                    pformatetcIn->cfFormat != m_error_cycle_marker.cfFormat ||\n                    pformatetcIn->dwAspect != m_error_cycle_marker.dwAspect ||\n                    pformatetcIn->lindex != m_error_cycle_marker.lindex ||\n                    pformatetcIn->ptd != m_error_cycle_marker.ptd ||\n                    pformatetcIn->tymed != m_error_cycle_marker.tymed)\n                {\n                    throw;\n                }\n\n                // HACK HACK HACK:\n                // Yes, we are creating a dialogue here even though we don't know\n                // if UI is even allowed.  Yes, our UI won't have a proper\n                // parent window.  Yes, it is disgusting.  No, there doesn't\n                // seem to be an alternative if we want to report\n                // a drag-and-drop error to the user.\n                // The shell doesn't give us an HWND when creating this data object.\n                // It doesn't do anything with IObjectWithSite while using this\n                // data object.  SFVM_DIDDRAGDROP is only called is the\n                // drag-and-drop *succeeded*.\n                // I'm out of options.  Let's just hope the shell doesn't often\n                // need no-UI drag-and-drop.\n                swish::frontend::announce_last_exception(\n                    NULL,\n                    boost::locale::translate(L\"Unable to access the item\"),\n                    boost::locale::translate(L\"You might not have permission.\"),\n                    true);\n                throw;\n            }\n        }\n        WASHER_COM_CATCH_AUTO_INTERFACE();\n        return hr;\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE GetDataHere( \n        FORMATETC *pformatetc, STGMEDIUM *pmedium)\n    {\n        return m_inner->GetDataHere(pformatetc, pmedium);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE QueryGetData(FORMATETC *pformatetc)\n    {\n        return m_inner->QueryGetData(pformatetc);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc( \n        FORMATETC *pformatectIn, FORMATETC *pformatetcOut)\n    {\n        return m_inner->GetCanonicalFormatEtc(pformatectIn, pformatetcOut);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE SetData( \n        FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)\n    {\n        return m_inner->SetData(pformatetc, pmedium, fRelease);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc( \n        DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)\n    {\n        return m_inner->EnumFormatEtc(dwDirection, ppenumFormatEtc);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE DAdvise( \n        FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,\n        DWORD *pdwConnection)\n    {\n        return m_inner->DAdvise(pformatetc, advf, pAdvSink, pdwConnection);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection)\n    {\n        return m_inner->DUnadvise(dwConnection);\n    }\n    \n    virtual HRESULT STDMETHODCALLTYPE EnumDAdvise(IEnumSTATDATA **ppenumAdvise)\n    {\n        return m_inner->EnumDAdvise(ppenumAdvise);\n    }\n\nprivate:\n    comet::com_ptr<IDataObject> m_inner;\n\n    /** @name Registered CLIPFORMATS */\n    // @{\n    CLIPFORMAT m_file_descriptor_format_w;     ///< CFSTR_FILEDESCRIPTORW\n    CLIPFORMAT m_file_descriptor_format_a;     ///< CFSTR_FILEDESCRIPTORA\n    CLIPFORMAT m_file_contents_format;         ///< CFSTR_FILECONTENTS\n    // @}\n\n    FORMATETC m_error_cycle_marker;\n};\n\n}} // namespace swish::shell_folder\n\n#endif"
  },
  {
    "path": "swish/shell_folder/Swish.idl",
    "content": "import \"oaidl.idl\";\r\nimport \"ocidl.idl\";\r\nimport \"shobjidl.idl\";\r\n\r\n[\r\n\tuuid(b816a838-5022-11dc-9153-0090f5284f85),\r\n\tversion(0.3),\r\n\thelpstring(\"Swish 0.3 Type Library\")\r\n]\r\nlibrary SwishLib\r\n{\r\n\timportlib(\"stdole32.tlb\");\r\n\timportlib(\"stdole2.tlb\");\r\n\r\n\t[\r\n\t\tuuid(b816a83a-5022-11dc-9153-0090f5284f85),\r\n\t\thelpstring(\"HostFolder Class\")\r\n\t]\r\n\tcoclass CHostFolder\r\n\t{\r\n\t\t[default] interface IShellFolder2;\r\n\t};\r\n\r\n\t[\r\n\t\tuuid(b816a83c-5022-11dc-9153-0090f5284f85),\r\n\t\thelpstring(\"RemoteFolder Class\")\r\n\t]\r\n\tcoclass CRemoteFolder\r\n\t{\r\n\t\t[default] interface IShellFolder2;\r\n\t};\r\n}"
  },
  {
    "path": "swish/shell_folder/SwishFolder.hpp",
    "content": "/**\n    @file\n\n    Base-class implementing functionality common to all Swish folders\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"Folder.h\" // Superclass\n\n#include \"swish/atl.hpp\" // Common ATL setup\n#include \"swish/debug.hpp\" // METHOD_TRACE\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/error.h> // com_error\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <cassert> // assert\n\nnamespace swish {\nnamespace shell_folder {\nnamespace folder {\n\ntemplate<typename ColumnType>\nclass CSwishFolder : public swish::shell_folder::folder::CFolder<ColumnType>\n{\npublic:\n\n    BEGIN_COM_MAP(CSwishFolder)\n        COM_INTERFACE_ENTRY(IShellFolder)\n        COM_INTERFACE_ENTRY_CHAIN(CFolder)\n    END_COM_MAP()\n\nprotected:\n\n    /**\n     * Create one of the objects associated with the current folder.\n     *\n     * Currently, only requests for the current objects are dispatched to the\n     * subclasses:\n     * - IShellView\n     * - IShellDetails\n     * - IDropTarget\n     * - IExplorerCommandProvider\n     * - IContextMenu\n     */\n    ATL::CComPtr<IUnknown> folder_object(HWND hwnd, REFIID riid)\n    {\n        ATL::CComPtr<IUnknown> object;\n\n        if (riid == __uuidof(IShellView))\n        {\n            object = folder_view(hwnd);\n        }\n        else if (riid == __uuidof(IShellDetails))\n        {\n            object = shell_details(hwnd);\n        }\n        else if (riid == __uuidof(IDropTarget))\n        {\n            object = drop_target(hwnd);\n        }\n        else if (riid == __uuidof(IExplorerCommandProvider))\n        {\n            object = command_provider(hwnd);\n        }\n        else if (riid == __uuidof(IContextMenu))\n        {\n            object = background_context_menu(hwnd);\n        }\n\n        // QueryInterface could fail at any point above and it *doesn't* throw\n        // an exception.  We have to check for NULL once we are sure it can't\n        // fail again: IUnknown returned as IUnknown shouldn't be able to fail.\n        if (!object)\n            BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n\n        return object;\n    }\n\n    /**\n     * Create one of the objects associated with an item in the current folder.\n     *\n     * Currently, only requests for the current objects are displatched to the\n     * subclasses:\n     * - IContextMenu\n     * - IDataObject\n     * - IQueryAssociations\n     * - IExtractIconW/IExtractIconA\n     */\n    ATL::CComPtr<IUnknown> folder_item_object(\n        HWND hwnd, REFIID riid, UINT cpidl, PCUITEMID_CHILD_ARRAY apidl)\n    {\n        assert(cpidl > 0);\n\n        ATL::CComPtr<IUnknown> object;\n\n        if (riid == __uuidof(IContextMenu))\n        {\n            object = context_menu(hwnd, cpidl, apidl);\n        }\n        else if (riid == __uuidof(IDataObject))\n        {\n            object = data_object(hwnd, cpidl, apidl);\n        }\n        else if (riid == __uuidof(IQueryAssociations))\n        {\n            object = query_associations(hwnd, cpidl, apidl);\n        }\n        else if (riid == __uuidof(IExtractIconW))\n        {\n            assert(cpidl == 1);\n            if (cpidl == 1)\n                object = extract_icon_w(hwnd, apidl[0]);\n        }\n        else if (riid == __uuidof(IExtractIconA))\n        {\n            assert(cpidl == 1);\n            if (cpidl == 1)\n                object = extract_icon_a(hwnd, apidl[0]);\n        }\n        \n        // QueryInterface could fail at any point above and it *doesn't* throw\n        // an exception.  We have to check for NULL once we are sure it can't\n        // fail again: IUnknown returned as IUnknown shouldn't be able to fail.\n        if (!object)\n            BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n\n        return object;\n    }\n\n    /** @name Objects associated with the current folder. */\n    // @{\n\n    /**\n     * Caller has requested the IShellView object associated with this folder.\n     */\n    virtual ATL::CComPtr<IShellView> folder_view(HWND hwnd)\n    {\n        TRACE(\"Request: IShellView\");\n\n        SFV_CREATE sfvdata = { sizeof(sfvdata), 0 };\n\n        // Create a pointer to this IShellFolder to pass to view\n        ATL::CComPtr<IShellFolder> this_folder = this;\n        ATLENSURE_THROW(this_folder, E_NOINTERFACE);\n        sfvdata.pshf = this_folder;\n\n        // Get the callback object for this folder view, if any.\n        // Must hold reference to it in this CComPtr over the\n        // ::SHCreateShellFolderView() call in case folder_view_callback()\n        // also creates it (hands back the only pointer to it).\n        ATL::CComPtr<IShellFolderViewCB> callback = folder_view_callback(hwnd);\n        sfvdata.psfvcb = callback;\n\n        // Create Default Shell Folder View object\n        ATL::CComPtr<IShellView> view;\n        HRESULT hr = ::SHCreateShellFolderView(&sfvdata, &view);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n        return view;\n    }\n\n    /**\n     * Caller has requested the IShellDetail object associated with this\n     * folder.\n     *\n     * By default, that is this folder itself.\n     */\n    virtual ATL::CComPtr<IShellDetails> shell_details(HWND /*hwnd*/)\n    {\n        TRACE(\"Request: IShellDetails\");\n\n        return this;\n    }\n\n    /** Create a drop target handler for the folder. */\n    virtual ATL::CComPtr<IDropTarget> drop_target(HWND /*hwnd*/)\n    {\n        TRACE(\"Request: IDropTarget\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /** Create a toolbar command provider for the folder. */\n    virtual ATL::CComPtr<IExplorerCommandProvider> command_provider(\n        HWND /*hwnd*/)\n    {\n        TRACE(\"Request: IExplorerCommandProvider\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /**\n     * Create a context menu for the folder background.\n     * Pasting into a Swish window requires this.\n     */\n    virtual ATL::CComPtr<IContextMenu> background_context_menu(HWND /*hwnd*/)\n    {\n        TRACE(\"Request: IContextMenu\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    // @}\n\n\n    /** @name Objects associated with items contained in folder. */\n    // @{\n\n    /** Create an icon extraction helper object for the selected item. */\n    virtual ATL::CComPtr<IExtractIconW> extract_icon_w(\n        HWND /*hwnd*/, PCUITEMID_CHILD /*pidl*/)\n    {\n        TRACE(\"Request: IExtractIconW\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /**\n     * Create an icon extraction helper object for the selected item. \n     * This is the ASCII version of the interface and, by default, requests are\n     * delegated to the same object as IExtractIconW.  Override this to\n     * change the behaviour.\n     */\n    virtual ATL::CComPtr<IExtractIconA> extract_icon_a(\n        HWND hwnd, PCUITEMID_CHILD pidl)\n    {\n        TRACE(\"Request: IExtractIconA\");\n        ATL::CComPtr<IExtractIconA> extractor;\n        extractor = extract_icon_w(hwnd, pidl);\n        return extractor;\n    }\n\n    /** Create a context menu for the selected items. */\n    virtual ATL::CComPtr<IContextMenu> context_menu(\n        HWND /*hwnd*/, UINT /*cpidl*/, PCUITEMID_CHILD_ARRAY /*apidl*/)\n    {\n        TRACE(\"Request: IContextMenu\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /** Create a file association handler for the selected items. */\n    virtual ATL::CComPtr<IQueryAssociations> query_associations(\n        HWND /*hwnd*/, UINT /*cpidl*/, PCUITEMID_CHILD_ARRAY /*apidl*/)\n    {\n        TRACE(\"Request: IQueryAssociations\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /** Create a data object for the selected items. */\n    virtual ATL::CComPtr<IDataObject> data_object(\n        HWND /*hwnd*/, UINT /*cpidl*/, PCUITEMID_CHILD_ARRAY /*apidl*/)\n    {\n        TRACE(\"Request: IDataObject\");\n        BOOST_THROW_EXCEPTION(comet::com_error(E_NOINTERFACE));\n        return NULL;\n    }\n\n    /**\n     * Return any folder view callback object that should be used when creating\n     * the default view.\n     */\n    virtual ATL::CComPtr<IShellFolderViewCB> folder_view_callback(\n        HWND /*hwnd*/)\n    {\n        return NULL;\n    }\n    \n    // @}\n};\n\n}}} // namespace swish::shell_folder::folder\n"
  },
  {
    "path": "swish/shell_folder/com_dll/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(RESOURCES\n  com_dll.rc\n  HostFolder.rgs\n  RemoteFolder.rgs\n  Swish.rgs)\n\nset(SOURCES\n  resource.h\n  com_dll.def\n  SwishCoClasses.cpp\n  SwishModule.cpp\n  ${RESOURCES})\n\nadd_library(shell_folder-com_dll SHARED ${SOURCES})\n\nhunter_add_package(WTL)\n\ntarget_link_libraries(shell_folder-com_dll\n  PRIVATE shell_folder versions WTL::wtl) # WTL needed for atlres.h\n\ninstall(TARGETS shell_folder-com_dll RUNTIME DESTINATION .)\n\n# This step is a dev-time convenience and not guaranteed to produce the same\n# result as the installer\nadd_custom_target(RegisterDllFromBuildTree\n  COMMAND regsvr32 $<TARGET_FILE:shell_folder-com_dll>\n  COMMENT \"Registering Swish DLL from build tree\" VERBATIM)"
  },
  {
    "path": "swish/shell_folder/com_dll/HostFolder.rgs",
    "content": "HKCR\r\n{\r\n\tNoRemove CLSID\r\n\t{\r\n\t\tForceRemove {b816a83a-5022-11dc-9153-0090f5284f85} = s 'Swish'\r\n\t\t{\r\n\t\t\tProgID = s 'Swish.HostFolder.1'\r\n\t\t\tVersionIndependentProgID = s 'Swish.HostFolder'\r\n\r\n\t\t\tInprocServer32 = s '%MODULE%'\r\n\t\t\t{\r\n\t\t\t\tval ThreadingModel = s 'Apartment'\r\n\t\t\t}\r\n\r\n\t\t\tval AppID = s '%APPID%'\r\n\t\t\tTypeLib = s '%APPID%'\r\n\r\n\t\t\tval InfoTip = s 'Remote file-system access via SFTP'\r\n\t\t\tval TileInfo = s 'prop:{28636AA6-953D-11D2-B5D6-00C04FD918D0} 5;{b816a850-5022-11dc-9153-0090f5284f85} 2;{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 7'\r\n\r\n\t\t\tShellFolder\r\n\t\t\t{\r\n\t\t\t\tval Attributes = d '2684354560'\r\n\t\t\t}\r\n\r\n\t\t\tDefaultIcon = s 'shell32.dll,9'\r\n\t\t}\r\n\t}\r\n\tSwish.HostFolder.1 = s 'CHostFolder Class'\r\n\t{\r\n\t\tCLSID = s '{b816a83a-5022-11dc-9153-0090f5284f85}'\r\n\t}\r\n\tSwish.HostFolder = s 'CHostFolder Class'\r\n\t{\r\n\t\tCLSID = s '{b816a83a-5022-11dc-9153-0090f5284f85}'\r\n\t\tCurVer = s 'Swish.HostFolder.1'\r\n\t}\r\n}\r\n\r\nHKLM\r\n{\r\n\tNoRemove Software\r\n\t{\r\n\t\tNoRemove Microsoft\r\n\t\t{\r\n\t\t\tNoRemove Windows\r\n\t\t\t{\r\n\t\t\t\tNoRemove CurrentVersion\r\n\t\t\t\t{\r\n\t\t\t\t\tNoRemove Explorer\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tNoRemove MyComputer\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNoRemove NameSpace\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tForceRemove {b816a83a-5022-11dc-9153-0090f5284f85} = s 'Swish'\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tval 'Removal Message' = s 'Please don''t remove Swish this way - uninstall it.'\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}\r\n\r\n\t\t\t\t\tNoRemove 'Shell Extensions'\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tNoRemove Approved\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tval {b816a83a-5022-11dc-9153-0090f5284f85} = s 'Swish HostFolder'\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}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "swish/shell_folder/com_dll/RemoteFolder.rgs",
    "content": "HKCR\r\n{\r\n\tNoRemove CLSID\r\n\t{\r\n\t\tForceRemove {b816a83c-5022-11dc-9153-0090f5284f85} = s 'CRemoteFolder Class'\r\n\t\t{\r\n\t\t\tProgID = s 'Swish.RemoteFolder.1'\r\n\t\t\tVersionIndependentProgID = s 'Swish.RemoteFolder'\r\n\r\n\t\t\tInprocServer32 = s '%MODULE%'\r\n\t\t\t{\r\n\t\t\t\tval ThreadingModel = s 'Apartment'\r\n\t\t\t}\r\n\r\n\t\t\tval AppID = s '%APPID%'\r\n\t\t\tTypeLib = s '%APPID%'\r\n\r\n\t\t\tval InfoTip = s 'Remote file-system access via SFTP'\r\n\t\t\tval TileInfo = s 'prop:{B725F130-47EF-101A-A5F1-02608C9EEBAC}, 12;{B725F130-47EF-101A-A5F1-02608C9EEBAC, 14}'\r\n\r\n\t\t\tShellFolder\r\n\t\t\t{\r\n\t\t\t\tval Attributes = d '2684354560'\r\n\t\t\t}\r\n\r\n\t\t\tDefaultIcon = s 'shell32.dll,9'\r\n\t\t}\r\n\t}\r\n\tSwish.RemoteFolder.1 = s 'CRemoteFolder Class'\r\n\t{\r\n\t\tCLSID = s '{b816a83c-5022-11dc-9153-0090f5284f85}'\r\n\t}\r\n\tSwish.RemoteFolder = s 'CRemoteFolder Class'\r\n\t{\r\n\t\tCLSID = s '{b816a83c-5022-11dc-9153-0090f5284f85}'\r\n\t\tCurVer = s 'Swish.RemoteFolder.1'\r\n\t}\r\n}\r\n\r\nHKLM\r\n{\r\n\tNoRemove Software\r\n\t{\r\n\t\tNoRemove Microsoft\r\n\t\t{\r\n\t\t\tNoRemove Windows\r\n\t\t\t{\r\n\t\t\t\tNoRemove CurrentVersion\r\n\t\t\t\t{\r\n\t\t\t\t\tNoRemove 'Shell Extensions'\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tNoRemove Approved\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tval {b816a83c-5022-11dc-9153-0090f5284f85} = s 'Swish SFTP Folder'\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}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "swish/shell_folder/com_dll/Swish.rgs",
    "content": "HKCR\r\n{\r\n\tNoRemove AppID\r\n\t{\r\n\t\t'%APPID%' = s 'Swish'\r\n\t\t'Swish.DLL'\r\n\t\t{\r\n\t\t\tval AppID = s '%APPID%'\r\n\t\t}\r\n\t}\r\n}\r\n\r\n"
  },
  {
    "path": "swish/shell_folder/com_dll/SwishCoClasses.cpp",
    "content": "/**\n    @file\n\n    Externally COM-creatable aspects of Swish.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/shell_folder/HostFolder.h\"\n#include \"swish/shell_folder/RemoteFolder.h\"\n#include \"Swish.h\"  // CHost/RemoteFolder UUIDs\n\n#include \"resource.h\"\n\nnamespace swish {\nnamespace shell_folder {\nnamespace com_dll {\n\nusing namespace ATL;\n\n/**\n * COM factory for externally created instances of CHostFolder.\n */\nclass ATL_NO_VTABLE CHostFolderCoClass :\n    public CHostFolder,\n    public CComCoClass<CHostFolderCoClass, &CLSID_CHostFolder>\n{\npublic:\n\n    DECLARE_REGISTRY_RESOURCEID(IDR_HOSTFOLDER)\n\n    BEGIN_COM_MAP(CHostFolderCoClass)\n        COM_INTERFACE_ENTRY(IShellFolder)\n        COM_INTERFACE_ENTRY_CHAIN(CHostFolder)\n    END_COM_MAP()\n};\n\nOBJECT_ENTRY_AUTO(__uuidof(CHostFolder), CHostFolderCoClass)\n\n/**\n * COM factory for externally created instances of CRemoteFolder.\n */\nclass ATL_NO_VTABLE CRemoteFolderCoClass :\n    public CRemoteFolder,\n    public CComCoClass<CRemoteFolderCoClass, &CLSID_CRemoteFolder>\n{\npublic:\n\n    DECLARE_REGISTRY_RESOURCEID(IDR_REMOTEFOLDER)\n\n    BEGIN_COM_MAP(CRemoteFolderCoClass)\n        COM_INTERFACE_ENTRY(IShellFolder)\n        COM_INTERFACE_ENTRY_CHAIN(CRemoteFolder)\n    END_COM_MAP()\n};\n\nOBJECT_ENTRY_AUTO(__uuidof(CRemoteFolder), CRemoteFolderCoClass)\n\n}}} // namespace swish::shell_folder::com_dll"
  },
  {
    "path": "swish/shell_folder/com_dll/SwishModule.cpp",
    "content": "/**\n    @file\n\n    DLL exports for in-proc COM server.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"resource.h\"                  // main symbols\n#include <swish/shell_folder/locale_setup.hpp> // LocaleSetup lifetime manager\n#include \"Swish.h\"  // Swish type-library\n\n#include \"swish/atl.hpp\"\n\nnamespace swish {\nnamespace shell_folder {\nnamespace com_dll {\n\nusing namespace ATL;\n\n/**\n * ATL module needed to use ATL-based objects.\n * Also provides implementation of in-proc server functions.\n */\nclass CSwishModule : public CAtlDllModuleT< CSwishModule >\n{\npublic :\n    DECLARE_LIBID(LIBID_SwishLib)\n    DECLARE_REGISTRY_APPID_RESOURCEID(\n        IDR_SWISH, \"{b816a838-5022-11dc-9153-0090f5284f85}\")\n};\n\n}}} // namespace swish::shell_folder::com_dll\n\nswish::shell_folder::com_dll::CSwishModule _Module;\n\n\n/** DLL Entry Point. */\nextern \"C\" BOOL WINAPI DllMain(\n    HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)\n{\n    static swish::shell_folder::LocaleSetup m_locale; ///< Boost.Locale manager\n    (void)hInstance;\n    return _Module.DllMain(dwReason, lpReserved); \n}\n\n/** Used to determine whether the DLL can be unloaded by OLE. */\nSTDAPI DllCanUnloadNow()\n{\n    return _Module.DllCanUnloadNow();\n}\n\n/** Return a class factory to create an object of the requested type. */\nSTDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)\n{\n    return _Module.DllGetClassObject(rclsid, riid, ppv);\n}\n\n/**\n * Add entries to the system registry.\n *\n * Registers object, typelib and all interfaces in typelib.\n */\nSTDAPI DllRegisterServer()\n{\n    HRESULT hr = _Module.DllRegisterServer();\n    return hr;\n}\n\n/** Remove entries from the system registry. */\nSTDAPI DllUnregisterServer()\n{\n    HRESULT hr = _Module.DllUnregisterServer();\n    return hr;\n}\n"
  },
  {
    "path": "swish/shell_folder/com_dll/com_dll.def",
    "content": "LIBRARY\r\n\r\nEXPORTS\r\n\tDllCanUnloadNow\t\tPRIVATE\r\n\tDllGetClassObject\tPRIVATE\r\n\tDllRegisterServer\tPRIVATE\r\n\tDllUnregisterServer\tPRIVATE\r\n"
  },
  {
    "path": "swish/shell_folder/com_dll/com_dll.rc",
    "content": "// Microsoft Visual C++ generated resource script.\r\n//\r\n#include \"resource.h\"\r\n#include \"swish/versions/metadata.h\"\r\n\r\n#define APSTUDIO_READONLY_SYMBOLS\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 2 resource.\r\n//\r\n#include \"atlres.h\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#undef APSTUDIO_READONLY_SYMBOLS\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// Neutral (Sys. Default) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)\r\n#ifdef _WIN32\r\nLANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT\r\n#pragma code_page(1252)\r\n#endif //_WIN32\r\n\r\n#ifdef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// TEXTINCLUDE\r\n//\r\n\r\n1 TEXTINCLUDE\r\nBEGIN\r\n    \"resource.h\\0\"\r\nEND\r\n\r\n2 TEXTINCLUDE\r\nBEGIN\r\n    \"#include \"\"atlres.h\"\"\\r\\0\"\r\nEND\r\n\r\n3 TEXTINCLUDE\r\nBEGIN\r\n    \"#include \"\"shell_folder.rc\"\"\\r\\n\"\r\n    \"1 TYPELIB \"\"Swish.tlb\"\"\\r\\0\"\r\nEND\r\n\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Version\r\n//\r\n\r\nVS_VERSION_INFO VERSIONINFO\r\n FILEVERSION SWISH_MAJOR_VERSION,SWISH_MINOR_VERSION,SWISH_BUGFIX_VERSION,0\r\n PRODUCTVERSION SWISH_MAJOR_VERSION,SWISH_MINOR_VERSION,SWISH_BUGFIX_VERSION,0\r\n FILEFLAGSMASK 0x3fL\r\n#ifdef _DEBUG\r\n FILEFLAGS 0x3L\r\n#else\r\n FILEFLAGS 0x2L\r\n#endif\r\n FILEOS 0x4L\r\n FILETYPE 0x2L\r\n FILESUBTYPE 0x0L\r\nBEGIN\r\n    BLOCK \"StringFileInfo\"\r\n    BEGIN\r\n        BLOCK \"080004b0\"\r\n        BEGIN\r\n            VALUE \"Comments\", SWISH_DESCRIPTION\r\n            VALUE \"FileDescription\", \"Swish Explorer plugin DLL\"\r\n            VALUE \"FileVersion\", SWISH_VERSION_STRING\r\n            VALUE \"InternalName\", \"swish-com_dll\"\r\n            VALUE \"LegalCopyright\", SWISH_COPYRIGHT\r\n            VALUE \"OriginalFilename\", \"shell_folder-com_dll.dll\"\r\n            VALUE \"ProductName\", SWISH_PROGRAM_NAME\r\n            VALUE \"ProductVersion\", SWISH_VERSION_STRING\r\n        END\r\n    END\r\n    BLOCK \"VarFileInfo\"\r\n    BEGIN\r\n        VALUE \"Translation\", 0x800, 1200\r\n    END\r\nEND\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// REGISTRY\r\n//\r\n\r\nIDR_SWISH               REGISTRY                \"Swish.rgs\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// String Table\r\n//\r\n\r\nSTRINGTABLE\r\nBEGIN\r\n    IDS_PROJNAME            \"Swish\"\r\nEND\r\n\r\n#endif    // Neutral (Sys. Default) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// English (U.K.) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)\r\n#ifdef _WIN32\r\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK\r\n#pragma code_page(1252)\r\n#endif //_WIN32\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// REGISTRY\r\n//\r\n\r\nIDR_HOSTFOLDER          REGISTRY                \"HostFolder.rgs\"\r\nIDR_REMOTEFOLDER        REGISTRY                \"RemoteFolder.rgs\"\r\n#endif    // English (U.K.) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n\r\n#ifndef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 3 resource.\r\n//\r\n#include \"swish/shell_folder/shell_folder.rc\"\r\n1 TYPELIB \"Swish.tlb\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#endif    // not APSTUDIO_INVOKED\r\n"
  },
  {
    "path": "swish/shell_folder/com_dll/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by com_dll.rc\n//\n#define IDS_PROJNAME                    100\n#define IDR_SWISH                       100\n#define IDR_HOSTFOLDER                  206\n#define IDR_REMOTEFOLDER                207\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NO_MFC                     1\n#define _APS_NEXT_RESOURCE_VALUE        208\n#define _APS_NEXT_COMMAND_VALUE         32775\n#define _APS_NEXT_CONTROL_VALUE         1023\n#define _APS_NEXT_SYMED_VALUE           106\n#endif\n#endif\n"
  },
  {
    "path": "swish/shell_folder/data_object/FileGroupDescriptor.hpp",
    "content": "/**\n    @file\n\n    FILEDESCRIPTORW clipboard format wrapper.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"GlobalLocker.hpp\"  // Manage global memory storing FGD\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/shared_ptr.hpp> // shared_ptr\n#pragma warning(push)\n#pragma warning(disable:4244) // conversion from uint64_t to uint32_t\n#include <boost/date_time/posix_time/posix_time_types.hpp> // ptime\n#include <boost/date_time/gregorian/greg_date.hpp> // date\n#include <boost/date_time/posix_time/conversion.hpp> // from_ftime\n#pragma warning(pop)\n#include <boost/numeric/conversion/cast.hpp>  // numeric_cast\n#include <boost/system/system_error.hpp> // system_error\n#include <boost/static_assert.hpp> //  BOOST_STATIC_ASSERT\n#include <boost/cstdint.hpp> // unint64_t\n\n#include <stdexcept> // out_of_range, length_error, logic_error\n#include <string>\n\n#include <shlobj.h> // FILEDESCRIPTOR, FILEGROUPDESCRIPTOR\n#include <Windows.h> // HGLOBAL, GetLastError\n\n#pragma warning(push)\n#pragma warning(disable:4996) // std::copy ... may be unsafe\n\nnamespace swish {\nnamespace shell_folder {\nnamespace data_object {\n\nnamespace {\n\n    inline boost::uint32_t lo_dword(boost::uint64_t qword)\n    {\n        return static_cast<boost::uint32_t>(qword & 0xFFFFFFFF);\n    }\n\n    inline boost::uint32_t hi_dword(boost::uint64_t qword)\n    {\n        return static_cast<boost::uint32_t>((qword >> 32) & 0xFFFFFFFF);\n    }\n\n    /**\n     * Convert a ptime to a Windows FILETIME.\n     */\n    void ptime_to_filetime(const boost::posix_time::ptime& time, FILETIME& ft)\n    {\n        boost::posix_time::ptime filetime_epoch(\n            boost::gregorian::date(1601,1,1));\n        boost::posix_time::time_duration duration = time - filetime_epoch;\n        boost::uint64_t hundred_nanosecs = duration.total_nanoseconds();\n        hundred_nanosecs /= 100UL;\n        ft.dwLowDateTime = lo_dword(hundred_nanosecs);\n        ft.dwHighDateTime = hi_dword(hundred_nanosecs);\n    }\n\n    /**\n     * Convert a Windows FILETIME to a ptime.\n     */\n    boost::posix_time::ptime filetime_to_ptime(const FILETIME& ft)\n    {\n        return boost::posix_time::from_ftime<boost::posix_time::ptime>(ft);\n    }\n\n}\n\n/**\n * Exception thrown when trying to access a field that has not been set to\n * a value.\n */\nclass field_error : public std::logic_error\n{\npublic:\n    explicit field_error(const std::string message) : std::logic_error(message)\n    {}\n};\n\n/**\n * C++ interface to the FILEDESCRIPTORW structure.\n */\nclass Descriptor : private FILEDESCRIPTORW\n{\npublic:\n\n    Descriptor() : FILEDESCRIPTORW() {}\n    Descriptor(const FILEDESCRIPTORW& d) : FILEDESCRIPTORW(d) {}\n    // default copy, assign and destruct OK\n\n    const FILEDESCRIPTORW& get() const\n    {\n        return *this;\n    }\n\n    /**\n     * Return the stored filename or relative path.\n     */\n    std::wstring path() const\n    {\n        return cFileName;\n    }\n\n    /**\n     * Save given path as the descriptor filename/path.\n     *\n     * FGD paths are relative paths using backslashes as separators.  We allow\n     * the path argument to use forward slashes, andthey will be converted\n     * accordingly.\n     */\n    void path(std::wstring path)\n    {\n        std::replace(path.begin(), path.end(), L'/', L'\\\\');\n\n        static const size_t BUFFER_SIZE =\n            sizeof(cFileName) / sizeof(cFileName[0]);\n\n        if (path.size() >= BUFFER_SIZE)\n            BOOST_THROW_EXCEPTION(\n                std::length_error(\"Path greater than MAX_PATH\"));\n\n        size_t count = path.copy(cFileName, BUFFER_SIZE - 1);\n        cFileName[count] = L'\\0';\n    }\n\n    /**\n     * Get the size of the item described by the descriptor.\n     *\n     * If the corresponding FILECONTENTS format is stored in an HGLOBAL this\n     * is also the size of the allocated memory.\n     */\n    boost::uint64_t file_size() const\n    {\n        if (!_valid_field(FD_FILESIZE))\n            BOOST_THROW_EXCEPTION(field_error(\"File size not available.\"));\n\n        boost::uint64_t s = nFileSizeHigh;\n        s = s << 32;\n        s += nFileSizeLow;\n        return s;\n    }\n\n    /**\n     * Set the size of the item described by the descriptor.\n     *\n     * If the corresponding FILECONTENTS format is stored in an HGLOBAL this\n     * is also the size of the allocated memory.\n     */\n    void file_size(boost::uint64_t size)\n    {\n        nFileSizeLow = lo_dword(size);\n        nFileSizeHigh = hi_dword(size);\n        _set_field_valid(FD_FILESIZE);\n    }\n\n    /**\n     * The date and time that the item was created.\n     */\n    boost::posix_time::ptime creation_time() const\n    {\n        if (!_valid_field(FD_CREATETIME))\n            BOOST_THROW_EXCEPTION(field_error(\"Creation time not available.\"));\n\n        return filetime_to_ptime(ftCreationTime);\n    }\n\n    /**\n     * Set the date and time that the item was created.\n     */\n    void creation_time(const boost::posix_time::ptime& time)\n    {\n        ptime_to_filetime(time, ftLastWriteTime);\n        _set_field_valid(FD_CREATETIME);\n    }\n\n    /**\n     * The date and time that the item was last accessed.\n     */\n    boost::posix_time::ptime last_access_time() const\n    {\n        if (!_valid_field(FD_ACCESSTIME))\n            BOOST_THROW_EXCEPTION(\n                field_error(\"Last access time not available.\"));\n\n        return filetime_to_ptime(ftLastAccessTime);\n    }\n\n    /**\n     * Set the date and time that the item was last accessed.\n     */\n    void last_access_time(const boost::posix_time::ptime& time)\n    {\n        ptime_to_filetime(time, ftLastAccessTime);\n        _set_field_valid(FD_ACCESSTIME);\n    }\n\n    /**\n     * The date and time that the item was last modified.\n     */\n    boost::posix_time::ptime last_write_time() const\n    {\n        if (!_valid_field(FD_WRITESTIME))\n            BOOST_THROW_EXCEPTION(\n                field_error(\"Last write time not available.\"));\n\n        return filetime_to_ptime(ftLastWriteTime);\n    }\n\n    /**\n     * Set the date and time that the item was last modified.\n     */\n    void last_write_time(const boost::posix_time::ptime& time)\n    {\n        ptime_to_filetime(time, ftLastWriteTime);\n        _set_field_valid(FD_WRITESTIME);\n    }\n\n    /**\n     * Should shell show progress UI when copying items?\n     */\n    bool want_progress() const\n    {\n        return _valid_field(FD_PROGRESSUI);\n    }\n\n    /**\n     * Set whether the shell should show progress UI when copying items.\n     */\n    void want_progress(bool show)\n    {\n        if (show)\n            _set_field_valid(FD_PROGRESSUI);\n        else\n            _unset_field_valid(FD_PROGRESSUI);\n    }\n\n    /**\n     * FILE_ATTRIBUTE* bit values of item.\n     */\n    DWORD attributes() const\n    {\n        if (!_valid_field(FD_ATTRIBUTES))\n            BOOST_THROW_EXCEPTION(field_error(\"Attributes not available.\"));\n        return dwFileAttributes;\n    }\n\n    /**\n     * Set FILE_ATTRIBUTE* bit values for item.\n     */\n    void attributes(DWORD attrs)\n    {\n        dwFileAttributes = attrs;\n        _set_field_valid(FD_ATTRIBUTES);\n    }\n\nprivate:\n\n    /**\n     * Is the field with the given field flag valid?\n     */\n    bool _valid_field(DWORD field) const\n    {\n        return !!(dwFlags & field);\n    }\n\n    /**\n     * Set the validity of the given field.\n     */\n    void _set_field_valid(DWORD field)\n    {\n        dwFlags = dwFlags | field;\n    }\n\n    /**\n     * Unset the validity of the given field.\n     */\n    void _unset_field_valid(DWORD field)\n    {\n        dwFlags = (dwFlags & (~field));\n    }\n};\n\nBOOST_STATIC_ASSERT(sizeof(Descriptor) == sizeof(FILEDESCRIPTORW));\n\n/**\n * Wrapper around the FILEGROUPDESCRIPTORW structure.\n *\n * This wrapper adds construction as well as access to the FILEDESCRIPTORS\n * contained within it.\n */\nclass FileGroupDescriptor\n{\npublic:\n\n    /**\n     * Create wrapper around an existing FILEGROUPDESCRIPTORW in global memory.\n     */\n    FileGroupDescriptor(HGLOBAL hglobal) : m_lock(hglobal) {}\n\n    // default copy, assign and destruct OK\n\n    /**\n     * Number of FILEDESCRIPTORS in the FILEGROUPDESCRIPTORW.\n     */\n    size_t size() const\n    {\n        return m_lock.get()->cItems;\n    }\n\n    /**\n     * Return a reference to the ith FILEDESCRIPTORW as a Descriptor.\n     */\n    Descriptor& operator[](size_t i) const\n    {\n        if (i >= size())\n            BOOST_THROW_EXCEPTION(\n                std::out_of_range(\n                    \"Attempt to access FILEDESCRIPTORW out of range\"));\n\n        return *static_cast<Descriptor*>(&m_lock.get()->fgd[i]);\n    }\n\nprivate:\n    GlobalLocker<FILEGROUPDESCRIPTORW> m_lock;\n};\n\n/**\n * Allocate a FILEGROUPDESCRIPTORW in global memory holding the given\n * descriptors.\n *\n * The descriptor are give as a [first, last) range who element type is\n * copyable to FILEDESCRIPTORW.\n *\n * @returns  HGLOBAL handle to the allocated global memory.  Caller must free.\n */\ntemplate<typename It>\nHGLOBAL group_descriptor_from_range(It first, It last)\n{\n    size_t count = std::distance(first, last);\n    if (count < 1)\n        BOOST_THROW_EXCEPTION(\n            std::length_error(\"Range must have at least one descriptor.\"));\n\n    size_t bytes =\n        sizeof(FILEGROUPDESCRIPTORW ) + (count * sizeof(FILEDESCRIPTORW));\n\n    HGLOBAL hglobal = ::GlobalAlloc(GMEM_MOVEABLE, bytes);\n    if (!hglobal)\n        BOOST_THROW_EXCEPTION(\n            boost::system::system_error(\n                ::GetLastError(), boost::system::get_system_category()));\n\n    try\n    {\n        GlobalLocker<FILEGROUPDESCRIPTORW> lock(hglobal);\n        lock.get()->cItems = boost::numeric_cast<UINT>(count);\n        std::copy(first, last, &lock.get()->fgd[0]);\n        // last arg above: decay array to stop false +ive by checked iterator\n    }\n    catch (...)\n    {\n        ::GlobalFree(hglobal);\n        throw;\n    }\n\n    return hglobal;\n}\n\n}}} // namespace swish::shell_folder::data_object\n\n#pragma warning(pop)\n"
  },
  {
    "path": "swish/shell_folder/data_object/GlobalLocker.hpp",
    "content": "/**\n    @file\n\n    Resource-managed HGLOBAL locking.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include <boost/system/system_error.hpp>  // system_error\n\n#include <Windows.h> // GlobalLock/Unlock, HGLOBAL, GetLastError\n\nnamespace swish {\nnamespace shell_folder {\nnamespace data_object {\n\ntemplate<typename T> class GlobalLocker;\n\n/**\n * Swap two GlobalLocker instances.\n *\n * This operation cannot fail and offers the strong guarantee.\n */\ntemplate<typename T>\ninline void swap(GlobalLocker<T>& lhs, GlobalLocker<T>& rhs) throw()\n{\n    std::swap(lhs.m_hglobal, rhs.m_hglobal);\n    std::swap(lhs.m_mem, rhs.m_mem);\n}\n\n/**\n * Resource-management (RAII) container handling locking on an HGLOBAL.\n *\n * @templateparam  Type of object the HGLOBAL points to.  The get() method\n *                 returns a pointer to an object of this type.\n */\ntemplate<typename T>\nclass GlobalLocker\n{\npublic:\n\n    /**\n     * Lock the given HGLOBAL.\n     *\n     * The HGLOBAL remains locked for the lifetime of the object.\n     *\n     * @throws system_error if locking fails.\n     */\n    explicit GlobalLocker(HGLOBAL hglobal) : \n        m_hglobal(hglobal), m_mem(::GlobalLock(hglobal))\n    {\n        if (!m_mem)\n            throw boost::system::system_error(\n                ::GetLastError(), boost::system::get_system_category());\n    }\n\n    /**\n     * Unlock the HGLOBAL.\n     *\n     * As the global lock functions maintain a lock-count for each\n     * HGLOBAL, our HGLOBAL may remain locked after this object is\n     * destroyed if it has been locked elsewhere.  For example, if the\n     * GlobalLocker is copied, that will increment the lock-count.\n     */\n    ~GlobalLocker() throw()\n    {\n        m_mem = NULL;\n        if (m_hglobal)\n        {\n            BOOL result = ::GlobalUnlock(m_hglobal);\n            (void) result;\n            assert(result || ::GetLastError() == NO_ERROR); // Too many unlocks\n        }\n        m_hglobal = NULL;\n    }\n\n    /**\n     * Copy the lock.\n     *\n     * Global locking is maintains a lock-count per HGLOBAL that holds the\n     * number of outstanding locks.  It increases every time the HGLOBAL\n     * is locked and decreases on each call the GlobalUnlock().  When it \n     * reaches zero, the global memory is actually unlocked and free to be\n     * moved.\n     *\n     * Instances of the GlobalLocker object can be copied safely as the \n     * operation increments the lock count and so destruction of one\n     * GlobalLocker instance can't accidentally unlock the memory held by \n     * another.\n     *\n     * @throws system_error if locking fails.\n     */\n    GlobalLocker(const GlobalLocker& lock) : \n        m_hglobal(lock.m_hglobal), m_mem(::GlobalLock(m_hglobal))\n    {\n        if (!m_mem)\n            throw boost::system::system_error(\n                ::GetLastError(), boost::system::get_system_category());\n    }\n\n    /**\n     * Copy-assign one lock to another.\n     *\n     * @see the copy constructor for more info on copying locks.\n     */\n    GlobalLocker& operator=(GlobalLocker lock) throw()\n    {\n        swap(*this, lock);\n        return *this;\n    }\n\n    /**\n     * Return a pointer to the item held in the HGLOBAL.\n     */\n    T* get() const\n    {\n        return static_cast<T*>(m_mem);\n    }\n\nprivate:\n    HGLOBAL m_hglobal;\n    void* m_mem;\n\n    friend void swap<>(GlobalLocker<T>&, GlobalLocker<T>&) throw();\n};\n\n}}} // namespace swish::shell_folder::data_object\n"
  },
  {
    "path": "swish/shell_folder/data_object/ShellDataObject.cpp",
    "content": "/**\n    @file\n\n    Classes to handle the typical Explorer 'Shell DataObject'.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"ShellDataObject.hpp\"\n\n#include \"GlobalLocker.hpp\" // GlobalLocker\n#include \"StorageMedium.hpp\" // StorageMedium\n\n#include <washer/clipboard.hpp> // register_format\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_cast\n\n#include <boost/shared_ptr.hpp>  // share_ptr\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <shldisp.h> // IAsyncOperation\n\nusing washer::clipboard::register_format;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl::apidl_t;\n\nusing boost::shared_ptr;\n\nusing comet::com_error;\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\n\ntemplate<> struct comet::comtype<IAsyncOperation>\n{\n    static const IID& uuid() throw() { return IID_IAsyncOperation; }\n    typedef IUnknown base;\n};\n\nnamespace swish {\nnamespace shell_folder {\nnamespace data_object {\n\nnamespace { // private\n\n    /**\n     * Lifetime-management class for a CIDA held in global memory in a\n     * STGMEDIUM.\n     *\n     * The lifetimes of a STGMEDIUM holding an HGLOBAL, a lock on that\n     * HGLOBAL and the pointer to the memory it contains.  The pointer\n     * is only valid for the duration of the lock which, in turn, can only\n     * exist while the global memory in the STGMEDIUM is allocated.\n     *\n     * Therefore, this class exists to make it easy to manage the lifetimes\n     * of these three items together.  A caller to get() is free to use the\n     * CIDA returned as long as the instance of this class remains in scope.\n     * Copying is explicity prevented as that reallocates the STGMEDIUM\n     * invalidating both the lock and the pointer to the original memory.\n     */\n    class GlobalCida\n    {\n    public:\n        GlobalCida(const StorageMedium& medium) : \n          m_medium(medium), m_lock(m_medium.get().hGlobal)\n        {\n        }\n\n        const CIDA& get() const\n        {\n            CIDA* pcida = m_lock.get();\n            if (!pcida)\n                BOOST_THROW_EXCEPTION(com_error(E_UNEXPECTED));\n            return *pcida;\n        }\n\n    private:\n        GlobalCida(const GlobalCida& cida); // Disabled\n        GlobalCida& operator=(const GlobalCida& cida); // Disabled\n        StorageMedium m_medium;\n        GlobalLocker<CIDA> m_lock;\n    };\n\n    /**\n     * Return a STGMEDIUM with a list of PIDLs in global memory.\n     */\n    StorageMedium cfstr_shellidlist_from_data_object(\n        const com_ptr<IDataObject> data_object)\n    {\n        FORMATETC fetc = {\n            register_format(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1,\n            TYMED_HGLOBAL\n        };\n\n        StorageMedium medium;\n        HRESULT hr = data_object->GetData(&fetc, medium.out());\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(com_error(hr));\n\n        assert(medium.get().hGlobal);\n        return medium;\n    }\n\n#pragma region CIDA Accessors\n\n    /**\n     * Return a pointer to the ith PIDL in the CIDA.\n     */\n    pidl_t pidl_from_cida(const CIDA& cida, int i)\n    {\n        unsigned int offset = cida.aoffset[i];\n        const BYTE* position = reinterpret_cast<const BYTE*>(&cida) + offset;\n\n        return reinterpret_cast<PCIDLIST_RELATIVE>(position);\n    }\n\n    /**\n     * Return a pointer to the PIDL corresponding to the parent folder of the \n     * other PIDLs.\n     */\n    apidl_t parent_from_cida(const CIDA& cida)\n    {\n        return washer::shell::pidl::pidl_cast<apidl_t>(pidl_from_cida(cida, 0));\n    }\n\n    /**\n     * Return a pointer to the ith child PIDL in the CIDA (i+1th PIDL).\n     */\n    pidl_t child_from_cida(const CIDA& cida, int i)\n    {\n        return pidl_from_cida(cida, i + 1);\n    }\n\n#pragma endregion\n}\n\n#pragma region ShellDataObject implementation\n\nShellDataObject::ShellDataObject(com_ptr<IDataObject> data_object) :\n    m_data_object(data_object)\n{\n}\n\nShellDataObject::~ShellDataObject()\n{\n}\n\n/**\n * Can the data_object be used asynchronously?\n */\nbool ShellDataObject::supports_async() const\n{\n\tcom_ptr<IAsyncOperation> async = (comet::com_cast)(m_data_object);\n    if (!async)\n        return false;\n\n    BOOL support;\n    HRESULT hr = async->GetAsyncMode(&support);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error_from_interface(async, hr));\n\n    // Ignoring what MSDN says: the result is *not* a VARIANT_BOOL\n    // and should *not* be compared with VARIANT_TRUE.  WTF?\n    return support != FALSE;\n}\n\ncom_ptr<IAsyncOperation> ShellDataObject::async() const\n{\n    com_ptr<IAsyncOperation> async = try_cast(m_data_object);\n    return async;\n}\n\n/**\n * Does the data object have the CFSTR_SHELLIDLIST format?\n *\n * This must not call GetData() on the data object in order to make this\n * operation cheap and to prevent premature rendering of delay-rendered data.\n * We require the format to be in an HGLOBAL for a positive result.  No other\n * storage medium is allowed.\n */\nbool ShellDataObject::has_pidl_format() const\n{\n    FORMATETC fetc = {\n        register_format(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1,\n        TYMED_HGLOBAL\n    };\n\n    return m_data_object->QueryGetData(&fetc) == S_OK;\n}\n\n/**\n * Does the data object have the CF_HDROP format?\n *\n * This must not call GetData() on the data object in order to make this\n * operation cheap and to prevent premature rendering of delay-rendered data.\n * We require the format to be in an HGLOBAL for a positive result.  No other\n * storage medium is allowed.\n */\nbool ShellDataObject::has_hdrop_format() const\n{\n    FORMATETC fetc = {\n        CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL\n    };\n\n    return m_data_object->QueryGetData(&fetc) == S_OK;\n}\n\n/**\n * Does the data object have a CFSTR_FILEDESCRIPTORA or CFSTR_FILEDESCRIPTORW \n * format?\n *\n * This must not call GetData() on the data object in order to make this\n * operation cheap and to prevent premature rendering of delay-rendered data.\n * We require the format to be in an HGLOBAL for a positive result.  No other\n * storage medium is allowed.\n */\nbool ShellDataObject::has_file_group_descriptor_format() const\n{\n    return has_unicode_file_group_descriptor_format() || \n        has_ansi_file_group_descriptor_format();\n}\n\n/**\n * Does the data object have the CFSTR_FILEDESCRIPTORW format?\n *\n * This must not call GetData() on the data object in order to make this\n * operation cheap and to prevent premature rendering of delay-rendered data.\n * We require the format to be in an HGLOBAL for a positive result.  No other\n * storage medium is allowed.\n */\nbool ShellDataObject::has_unicode_file_group_descriptor_format() const\n{\n    FORMATETC fetc = {\n        register_format(CFSTR_FILEDESCRIPTORW), NULL, DVASPECT_CONTENT, -1,\n        TYMED_HGLOBAL\n    };\n\n    return m_data_object->QueryGetData(&fetc) == S_OK;\n}\n\n/**\n * Does the data object have the CFSTR_FILEDESCRIPTORA format?\n *\n * This must not call GetData() on the data object in order to make this\n * operation cheap and to prevent premature rendering of delay-rendered data.\n * We require the format to be in an HGLOBAL for a positive result.  No other\n * storage medium is allowed.\n */\nbool ShellDataObject::has_ansi_file_group_descriptor_format() const\n{\n    FORMATETC fetc = {\n        register_format(CFSTR_FILEDESCRIPTORA), NULL, DVASPECT_CONTENT, -1,\n        TYMED_HGLOBAL\n    };\n\n    return m_data_object->QueryGetData(&fetc) == S_OK;\n}\n\n#pragma endregion\n\n#pragma region PidlFormat implementation\n\nPidlFormat::PidlFormat(const com_ptr<IDataObject>& data_object) :\n    m_data_object(data_object)\n{}\n\nPidlFormat::~PidlFormat() {}\n\n/**\n * The absolute PIDL to the common parent of the items in the SHELLIDLIST \n * format.\n */\napidl_t PidlFormat::parent_folder() const\n{\n    if (!m_data_object)\n        BOOST_THROW_EXCEPTION(std::logic_error(\"Empty (NULL) Data Object\"));\n\n    GlobalCida global_cida(cfstr_shellidlist_from_data_object(m_data_object));\n\n    apidl_t pidl = parent_from_cida(global_cida.get());\n    return pidl;\n}\n\n/**\n * The absolute PIDL of the ith item in the SHELLIDLIST format.\n */\napidl_t PidlFormat::file(UINT i) const\n{\n    if (pidl_count() == 0)\n        BOOST_THROW_EXCEPTION(std::range_error(\"Empty (NULL) Data Object\"));\n\n    return parent_folder() + relative_file(i);\n}\n\n/**\n * The ith relative PIDL in the SHELLIDLIST format.\n */\npidl_t PidlFormat::relative_file(UINT i) const\n{\n    if (!m_data_object)\n        BOOST_THROW_EXCEPTION(std::range_error(\"Empty (NULL) Data Object\"));\n\n    GlobalCida global_cida(cfstr_shellidlist_from_data_object(m_data_object));\n    if (i >= global_cida.get().cidl)\n        BOOST_THROW_EXCEPTION(std::range_error(\n            \"The index is greater than the number of PIDLs in the \"\n            \"Data Object\"));\n\n    pidl_t pidl = child_from_cida(global_cida.get(), i);\n    return pidl;\n}\n\n/**\n * Return the number of PIDLs in the CFSTR_SHELLIDLIST format of the data\n * object.\n */\nUINT PidlFormat::pidl_count() const\n{\n    if (!m_data_object)\n        return 0;\n\n    GlobalCida global_cida(cfstr_shellidlist_from_data_object(m_data_object));\n    return global_cida.get().cidl;\n}\n\n#pragma endregion\n\n}}} // namespace swish::shell_folder::data_object\n"
  },
  {
    "path": "swish/shell_folder/data_object/ShellDataObject.hpp",
    "content": "/**\n    @file\n\n    Classes to handle the typical Explorer 'Shell DataObject'.\n\n    @if license\n\n    Copyright (C) 2008, 2009, 2010, 2013\n    Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include <washer/shell/pidl.hpp> // pidl_t, PIDL wrapper types\n\n#include <comet/ptr.h>  // com_ptr\n\n#include <shldisp.h> // IAsyncOperation\n\nnamespace swish {\nnamespace shell_folder {\nnamespace data_object {\n\n/**\n * Wrapper around an IDataObject pointer providing access to the usual\n * shell formats.\n */\nclass ShellDataObject\n{\npublic:\n\n    ShellDataObject(comet::com_ptr<IDataObject> data_object);\n    ~ShellDataObject();\n\n    bool supports_async() const;\n    comet::com_ptr<IAsyncOperation> async() const;\n    bool has_pidl_format() const;\n    bool has_hdrop_format() const;\n    bool has_file_group_descriptor_format() const;\n    bool has_unicode_file_group_descriptor_format() const;\n    bool has_ansi_file_group_descriptor_format() const;\n\nprivate:\n    comet::com_ptr<IDataObject> m_data_object;\n};\n\n/**\n * Access wrapper for the items in a DataObject's SHELL_IDLIST format.\n */\nclass PidlFormat\n{\npublic:\n    PidlFormat(const comet::com_ptr<IDataObject>& data_object);\n    ~PidlFormat();\n\n    washer::shell::pidl::apidl_t parent_folder() const;\n    washer::shell::pidl::apidl_t file(UINT i) const;\n    washer::shell::pidl::pidl_t relative_file(UINT i) const;\n    UINT pidl_count() const;\n\nprivate:\n    comet::com_ptr<IDataObject> m_data_object;\n};\n\n}}} // namespace swish::shell_folder::data_object\n"
  },
  {
    "path": "swish/shell_folder/data_object/StorageMedium.hpp",
    "content": "/**\n    @file\n\n    Wrapper around STGMEDIUM structure adding lifetime management.\n\n    @if license\n\n    Copyright (C) 2008, 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/shell_folder/Pidl.h\"\n\n#include <comet/error.h> // com_error\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\nnamespace swish {\nnamespace shell_folder {\nnamespace data_object {\n\nclass StorageMedium\n{\npublic:\n    StorageMedium() throw()\n    {\n        ::ZeroMemory(&m_medium, sizeof(m_medium));\n    }\n\n    StorageMedium(const StorageMedium& medium)\n    {\n        HRESULT hr = ::CopyStgMedium(&(medium.m_medium), &m_medium);\n        if (FAILED(hr))\n            BOOST_THROW_EXCEPTION(comet::com_error(hr));\n    }\n\n    StorageMedium& operator=(StorageMedium medium) throw()\n    {\n        std::swap(m_medium, medium.m_medium);\n    }\n\n    ~StorageMedium() throw()\n    {\n        ::ReleaseStgMedium(&m_medium);\n    }\n\n    /**\n     * Return address of STGMEDIUM to use as an out-parameter.\n     *\n     * This should only be used on an empty STGMEDIUM as modifying a\n     * STGMEDIUM with allocated resources can lead to memory leaks.\n     *\n     * @todo  Instead of asserting on non-empty, release previous to make \n     *        empty.\n     */\n    STGMEDIUM* out()\n    {\n        assert(empty() || \"Taking address of non-empty STGMEDIUM\");\n        return &m_medium;\n    }\n\n    /**\n     * Read-only access to STGMEDIUM.\n     */\n    const STGMEDIUM& get() const\n    {\n        assert(!empty() || \"Accessing empty STGMEDIUM.\");\n        return m_medium;\n    }\n\n    /**\n     * Does the STGMEDIUM hold an allocated resource?\n     */\n    bool empty() const\n    {\n        return m_medium.tymed == NULL;\n    }\n\nprivate:\n    STGMEDIUM m_medium;\n};\n\n}}} // namespace swish::shell_folder::data_object\n"
  },
  {
    "path": "swish/shell_folder/locale_setup.hpp",
    "content": "/**\n    @file\n\n    Static setup for Boost.Locale.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_SHELL_FOLDER_LOCALE_SETUP_HPP\n#define SWISH_SHELL_FOLDER_LOCALE_SETUP_HPP\n#pragma once\n\n#include \"swish/atl.hpp\"\n\n#include <washer/dynamic_link.hpp> // module_path\n\n#include <boost/filesystem.hpp> // path\n#include <boost/locale.hpp> // boost::locale::generator\n\nnamespace swish {\nnamespace shell_folder {\n\nnamespace detail {\n\n    /**\n     * Initialise Boost.Locale translation mechanism.\n     */\n    inline std::locale switch_to_boost_locale()\n    {\n        using boost::filesystem::path;\n        using boost::locale::generator;\n\n        try\n        {\n            generator gen;\n\n            path module_directory = washer::module_path<char>(\n                ATL::_AtlBaseModule.GetModuleInstance()).parent_path();\n            gen.add_messages_path(module_directory.string());\n\n            gen.add_messages_domain(\"swish\");\n\n            return std::locale::global(gen(\"\")); // default locale\n        }\n        catch (std::exception)\n        {\n            // fall-back\n            return std::locale::global(std::locale::classic());\n        }\n    }\n}\n\n/**\n * Switch Boost.Locale for the duration of this module's existence.\n *\n * Resets locale to original when the module is unloaded.\n */\nclass LocaleSetup\n{\npublic:\n    LocaleSetup() : m_old_locale(detail::switch_to_boost_locale()) {}\n    ~LocaleSetup()\n    {\n        try\n        {\n            std::locale::global(m_old_locale);\n        }\n        catch (std::exception)\n        {\n            // fall-back\n            std::locale::global(std::locale::classic());\n        }\n    }\n\nprivate:\n    std::locale m_old_locale;\n};\n\n}} // namespace swish::shell_folder\n\n#endif\n"
  },
  {
    "path": "swish/shell_folder/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by shell_folder.rc\n//\n#define IDOK                            1\n#define IDCANCEL                        2\n#define IDR_SWISH                       100\n#define IDS_COLUMN_FILENAME             101\n#define IDS_COLUMN_SIZE                 102\n#define IDS_COLUMN_TYPE                 103\n#define IDS_COLUMN_PERMISSIONS          104\n#define IDS_COLUMN_MODIFIED             105\n#define IDD_KBDINTERACTIVEDIALOG        105\n#define IDS_COLUMN_OWNER                106\n#define IDS_COLUMN_GROUP                107\n#define IDS_COLUMN_OWNER_ID             108\n#define IDS_COLUMN_GROUP_ID             109\n#define IDS_COLUMN_ACCESSED             110\n#define IDS_COPYING_TITLE               117\n#define IDC_INSTRUCTION                 1022\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NO_MFC                     1\n#define _APS_NEXT_RESOURCE_VALUE        207\n#define _APS_NEXT_COMMAND_VALUE         32775\n#define _APS_NEXT_CONTROL_VALUE         1025\n#define _APS_NEXT_SYMED_VALUE           106\n#endif\n#endif\n"
  },
  {
    "path": "swish/shell_folder/shell_folder.rc",
    "content": "// Microsoft Visual C++ generated resource script.\r\n//\r\n#include \"resource.h\"\r\n\r\n#define APSTUDIO_READONLY_SYMBOLS\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 2 resource.\r\n//\r\n#include \"atlres.h\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#undef APSTUDIO_READONLY_SYMBOLS\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// English (U.S.) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n#ifdef _WIN32\r\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r\n#pragma code_page(1252)\r\n#endif //_WIN32\r\n\r\n#ifdef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// TEXTINCLUDE\r\n//\r\n\r\n1 TEXTINCLUDE \r\nBEGIN\r\n    \"resource.h\\0\"\r\nEND\r\n\r\n2 TEXTINCLUDE \r\nBEGIN\r\n    \"#include \"\"atlres.h\"\"\\r\\n\"\r\n    \"\\0\"\r\nEND\r\n\r\n3 TEXTINCLUDE \r\nBEGIN\r\n    \"\\r\\n\\0\"\r\nEND\r\n\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n#endif    // English (U.S.) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// English (U.K.) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)\r\n#ifdef _WIN32\r\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK\r\n#pragma code_page(1252)\r\n#endif //_WIN32\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Dialog\r\n//\r\n\r\nIDD_KBDINTERACTIVEDIALOG DIALOGEX 0, 0, 186, 95\r\nSTYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU\r\nCAPTION \"Dialog\"\r\nFONT 8, \"MS Shell Dlg\", 0, 0, 0x0\r\nBEGIN\r\n    DEFPUSHBUTTON   \"OK\",IDOK,73,73,50,16\r\n    PUSHBUTTON      \"Cancel\",IDCANCEL,129,73,50,16\r\n    LTEXT           \"The instruction from the server which may be multiline, include embedded newlines or even be empty\",IDC_INSTRUCTION,6,6,174,8\r\nEND\r\n\r\n#endif    // English (U.K.) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n\r\n#ifndef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 3 resource.\r\n//\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#endif    // not APSTUDIO_INVOKED\r\n\r\n"
  },
  {
    "path": "swish/shell_folder/wtl.hpp",
    "content": "/**\n    @file\n\n    Set up WTL.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n/**\n * @file\n *\n * This file must be included before any other WTL headers as they depend\n * on <atlapp.h> already being included.  Also, any macros defined\n * in this file must be allowed to affect the behaviour of other parts of \n * ATL.  This is contrary to the usual top-down include order.\n */\n#pragma once\n\n#define _WTL_NO_AUTOMATIC_NAMESPACE\n\n#include \"swish/atl.hpp\"   // Common ATL setup\n\n#include <atlapp.h>  // WTL"
  },
  {
    "path": "swish/trace.hpp",
    "content": "/**\n    @file\n\n    Debug tracing.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#pragma once\n\n#ifdef _DEBUG\n\n#include <crtdbg.h> // _CrtDebugReport\n\n#include <string>\n#include <vector>\n#include <cstdarg> // va_start, va_list, va_end\n\n#include <stdio.h> // _vsnprintf et al\n\n#include <boost/format.hpp> // format\n#pragma warning(push)\n#pragma warning(disable:4996) // unsafe function wctomb\n#include <boost/archive/iterators/mb_from_wchar.hpp> // mb_from_wchar\n#pragma warning(pop)\n\nnamespace swish {\nnamespace tracing {\n\nnamespace {\n\n    /**\n     * Debug tracer.\n     */\n    class Tracer\n    {\n    public:\n        Tracer()\n        {        \n            ::_CrtSetReportMode(\n                _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);\n            ::_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);\n        }\n\n        /**\n         * Output the trace message and break to a new line.\n         */\n        void trace(const std::string& message) const\n        {\n            std::string line = message + \"\\n\";\n            ::_CrtDbgReport(_CRT_WARN, NULL, 0, NULL, line.c_str());\n        }\n    };\n\n    /**\n     * Helper class to give same usage for boost-style formatting as printf.\n     *\n     * I.e:\n     *     trace(\"%s %d\") % \"argument\" % 42;\n     * behaves indentically to:\n     *     trace_f(\"%s %d\", \"argument\", 42);\n     *\n     * This works because the temporary TraceFormatter returned by trace() is\n     * destroyed only after the final operator% call is made.  On destruction,\n     * the formatter outputs the fed values to the tracer.\n     *\n     * @see swish::tracing::trace()\n     */\n    class TraceFormatter\n    {\n    public:\n    \n        typedef boost::archive::iterators::mb_from_wchar<\n            std::wstring::const_iterator>\n        converter;\n\n        TraceFormatter(const std::string& format) : m_format(format) {}\n\n        ~TraceFormatter() throw()\n        {\n            try\n            {\n                static Tracer tracer;\n                tracer.trace(m_format.str());\n            }\n            catch (...) {}\n        }\n\n        /**\n         * Feeding operator that narrows wstring values for output.\n         */\n        TraceFormatter& operator%(const std::wstring& value)\n        {\n            m_format % std::string(\n                converter(value.begin()), converter(value.end()));;\n            return *this;\n        }\n\n        template<typename T>\n        TraceFormatter& operator%(const T& value)\n        {\n            m_format % value;\n            return *this;\n        }\n\n    private:\n        boost::format m_format;\n    };\n\n}\n\n/**\n * Output trace message.\n *\n * Can be, optionally, fed with values in boost-format style:\n *     trace(\"%s %d\") % \"argument\" % 42;\n * or:\n *     trace(\"%1% %2%\") % \"argument\" % 42;\n */\ninline TraceFormatter trace(const std::string& format)\n{\n    return TraceFormatter(format);\n}\n\n/**\n * Output trace message.\n *\n * Can be, optionally, passed values in printf style:\n *     trace_f(\"%s %d\", \"argument\", 42);\n */\ninline void trace_f(std::string format, ...)\n{\n    //\n    // WARNING: mustn't change format to a reference - this breaks varargs\n    //\n\n    std::va_list arglist;\n    va_start(arglist, format);\n\n    try\n    {\n        int cch = ::_vscprintf(format.c_str(), arglist) + 1;\n        std::vector<char> buffer(cch);\n#pragma warning(push)\n#pragma warning(disable:4996) // unsafe function\n        ::_vsnprintf(&buffer[0], buffer.size(), format.c_str(), arglist);\n#pragma warning(pop)\n        trace(&buffer[0]);\n    }\n    catch (...)\n    {\n        va_end(arglist);\n        throw;\n    }\n    \n    va_end(arglist);\n}\n\n}} // namespace swish::trace\n\n#else\n\nnamespace swish {\nnamespace tracing {\n\nnamespace {\n\n    class DummyFormatter\n    {\n    public:\n        template<typename T>\n        DummyFormatter& operator%(const T&)\n        {\n            return *this;\n        }\n    };\n\n}\n\ninline DummyFormatter trace(const std::string&)\n{\n    return DummyFormatter();\n}\n\ninline void trace_f(std::string, ...) {}\n\n}} // namespace swish::trace\n\n#endif\n"
  },
  {
    "path": "swish/utils.hpp",
    "content": "/**\n    @file\n\n    Miscellanious Windows API utility code.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#pragma once\n\n#include <washer/shell/shell.hpp> // known_folder_path\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/filesystem.hpp> // basic_path\n#include <boost/system/system_error.hpp>\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n\n#include <WinNls.h>\n#include <Objbase.h> // GetRunningObjectTable\n\n#include <string>\n#include <vector>\n#include <cassert>\n\n#pragma region WideCharToMultiByte/MultiByteToWideChar wrappers\n\nnamespace {\n\n    template<typename _FromElem, typename _ToElem>\n    struct Converter\n    {\n        typedef _FromElem FromElem;\n        typedef _ToElem ToElem;\n        typedef std::basic_string<FromElem> FromType;\n        typedef std::basic_string<ToElem> ToType;\n    };\n\n    struct Narrow : Converter<wchar_t, char>\n    {\n        int operator()(\n            const FromElem* pszWide, int cchWide, \n            ToElem* pszNarrow, int cbNarrow)\n        {\n            return ::WideCharToMultiByte(\n                CP_UTF8, 0, pszWide, cchWide, pszNarrow, cbNarrow, NULL, NULL);\n        }\n    };\n\n    struct Widen : Converter<char, wchar_t>\n    {\n        int operator()(\n            const FromElem* pszNarrow, int cbNarrow, \n            ToElem* pszWide, int cchWide)\n        {\n            return ::MultiByteToWideChar(\n                CP_UTF8, 0, pszNarrow, cbNarrow, pszWide, cchWide);\n        }\n    };\n}\n\nnamespace swish {\nnamespace utils {\n\n/**\n * Convert a basic_string-style string from one element type to another.\n *\n * @templateparam T  Converter functor to perform the actual conversion.\n */\ntemplate<typename T>\ninline typename T::ToType ConvertString(const typename T::FromType& from)\n{\n    const int size = boost::numeric_cast<int>(from.size());\n    if (size == 0)\n        return T::ToType();\n\n    // Calculate necessary buffer size\n    int len = T()(from.data(), size, NULL, 0);\n\n    // Perform actual conversion\n    if (len > 0)\n    {\n        std::vector<T::ToElem> buffer(len);\n        len = T()(\n            from.data(), size,\n            &buffer[0], static_cast<int>(buffer.size()));\n        if (len > 0)\n        {\n            assert(len == boost::numeric_cast<int>(buffer.size()));\n            return T::ToType(&buffer[0], len);\n        }\n    }\n\n    throw boost::system::system_error(\n        ::GetLastError(), boost::system::get_system_category());\n}\n\n/**\n * Convert a Windows wide string to a UTF-8 (multi-byte) string.\n */\ninline std::string WideStringToUtf8String(const std::wstring& wide)\n{\n    return swish::utils::ConvertString<Narrow>(wide);\n}\n\n/**\n * Convert a UTF-8 (multi-byte) string to a Windows wide string.\n */\ninline std::wstring Utf8StringToWideString(const std::string& narrow)\n{\n    return swish::utils::ConvertString<Widen>(narrow);\n}\n\n}} // namespace swish::utils\n\n#pragma endregion\n\n#pragma region GetUserName wrapper\n\nnamespace {\n\n    struct NarrowUserTraits\n    {\n        typedef char element_type;\n        typedef std::basic_string<element_type> return_type;\n\n        inline static BOOL get_user_name(\n            element_type* out_buffer, DWORD* pcb_buffer)\n        {\n            return ::GetUserNameA(out_buffer, pcb_buffer);\n        }\n    };\n\n    struct WideUserTraits\n    {\n        typedef wchar_t element_type;\n        typedef std::basic_string<element_type> return_type;\n\n        inline static BOOL get_user_name(\n            element_type* out_buffer, DWORD* pcb_buffer)\n        {\n            return ::GetUserNameW(out_buffer, pcb_buffer);\n        }\n    };\n}\n\nnamespace swish {\nnamespace utils {\n\nnamespace detail {\n\n/**\n * Get the current user's username.\n */\ntemplate<typename T>\ninline typename T::return_type current_user()\n{\n    // Calculate required size of output buffer\n    DWORD len = 0;\n    if (typename T::get_user_name(NULL, &len))\n        return typename T::return_type();\n\n    DWORD err = ::GetLastError();\n    if (err != ERROR_INSUFFICIENT_BUFFER)\n    {\n        BOOST_THROW_EXCEPTION(\n            boost::system::system_error(\n                err, boost::system::get_system_category()));\n    }\n\n    // Repeat call with a buffer of required size\n    if (len > 0)\n    {\n        std::vector<T::element_type> buffer(len);\n        if (typename T::get_user_name(&buffer[0], &len))\n        {\n            return typename T::return_type(&buffer[0], len - 1);\n        }\n        else\n        {\n            BOOST_THROW_EXCEPTION(\n                boost::system::system_error(\n                    ::GetLastError(), boost::system::get_system_category()));\n        }\n    }\n\n    return typename T::return_type();\n}\n\n} // detail\n\ninline WideUserTraits::return_type current_user()\n{\n    return detail::current_user<WideUserTraits>();\n}\n\ninline NarrowUserTraits::return_type current_user_a()\n{\n    return detail::current_user<NarrowUserTraits>();\n}\n\nnamespace detail {\n\n    inline DWORD get_environment_variable(\n        const char* key, char* buffer, DWORD size)\n    {\n        return ::GetEnvironmentVariableA(key, buffer, size);\n    }\n\n    inline DWORD get_environment_variable(\n        const wchar_t* key, wchar_t* buffer, DWORD size)\n    {\n        return ::GetEnvironmentVariableW(key, buffer, size);\n    }\n}\n\n/**\n * Fetch string value from an environment variable.\n * Returns empty string if the variable isn't present in the enviroment.\n */\ntemplate<typename T>\ninline T environment_variable(const T& key)\n{\n    DWORD len = detail::get_environment_variable(key.c_str(), NULL, 0);\n    if (len == 0)\n        return T();\n\n    std::vector<T::value_type> buf(len);\n    len = detail::get_environment_variable(\n        key.c_str(), &buf[0], boost::numeric_cast<DWORD>(buf.size()));\n    if (len == 0)\n        BOOST_THROW_EXCEPTION(\n            boost::system::system_error(\n                ::GetLastError(), boost::system::get_system_category()));\n\n    return T(buf.begin(), buf.begin() + len);\n}\n\n/**\n * Find home directory path.\n *\n * @throws std::exception if path can't be found.\n * @todo  Try other means to find directory including NetUserGetInfo.\n */\ntemplate<typename T>\ninline T home_directory()\n{\n    // try SHGetKnowFolderPath\n    T home = washer::shell::special_folder_path<T::value_type>(CSIDL_PROFILE);\n    if (!home.empty())\n        return home;\n\n    // fall back to %HOME%\n    const T::value_type home_key[] = {'H', 'O', 'M', 'E', '\\0'};\n    home = environment_variable(T::string_type(home_key));\n    if (!home.empty())\n        return home;\n\n    // fall back to %USERPROFILE%\n    const T::value_type userprofile[] =\n        {'U', 'S', 'E', 'R', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\\0'};\n    home = environment_variable(T::string_type(userprofile));\n    if (!home.empty())\n        return home;\n\n    // fall back to %HOMEDRIVE%/%HOMEPATH%\n    const T::value_type home_drive_key[] =\n        {'H', 'O', 'M', 'E', 'D', 'R', 'I', 'V', 'E', '\\0'};\n    const T::value_type home_path_key[] =\n        {'H', 'O', 'M', 'E', 'P', 'A', 'T', 'H', '\\0'};\n\n    T home_drive = environment_variable(T::string_type(home_drive_key));\n    T home_path = environment_variable(T::string_type(home_path_key));\n    home = home_drive / home_path;\n    if (home.empty())\n        BOOST_THROW_EXCEPTION(\n            std::exception(\"Can't find home directory\"));\n    return home;\n}\n\nnamespace com {\n\n/**\n * Get the local Winstation Running Object Table.\n */\ninline comet::com_ptr<IRunningObjectTable> running_object_table()\n{\n    comet::com_ptr<IRunningObjectTable> rot;\n\n    HRESULT hr = ::GetRunningObjectTable(0, rot.out());\n    assert(SUCCEEDED(hr));\n    assert(rot);\n\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return rot;\n}\n\n/**\n * Look up a CLSID in the registry using the ProgId.\n */\ninline CLSID clsid_from_progid(const std::wstring& progid)\n{\n    CLSID clsid;\n    HRESULT hr = ::CLSIDFromProgID(progid.c_str(), &clsid);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return clsid;\n}\n\n/**\n * Get the class object of a component by its CLSID.\n */\ntemplate<typename T>\ninline comet::com_ptr<T> class_object(\n    const CLSID& clsid, DWORD dw_class_context=CLSCTX_ALL)\n{\n    comet::com_ptr<T> object;\n    HRESULT hr = ::CoGetClassObject(\n        clsid, dw_class_context, NULL, comet::uuidof(object.in()),\n        reinterpret_cast<void**>(object.out()));\n    \n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error(hr));\n\n    return object;\n}\n\n/**\n * Get the class object of a component by its ProgId.\n */\ntemplate<typename T>\ninline comet::com_ptr<T> class_object(\n    const std::wstring& progid, DWORD dw_class_context=CLSCTX_ALL)\n{\n    CLSID clsid = clsid_from_progid(progid);\n    return class_object<T>(clsid, dw_class_context);\n}\n\n} // namespace swish::utils::com\n\n}} // namespace swish::utils\n\n#pragma endregion\n"
  },
  {
    "path": "swish/versions/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\ninclude(GetGitRevisionDescription)\n\ngit_describe(SWISH_GIT_VERSION)\n\nset(GIT_VERSION_TEMPLATE \"${CMAKE_CURRENT_SOURCE_DIR}/git_version.h.in\")\nset(GIT_VERSION_GENERATED_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/git_version.h\")\n\nconfigure_file(\n  ${GIT_VERSION_TEMPLATE}\n  ${GIT_VERSION_GENERATED_OUTPUT}\n  @ONLY)\nset_source_files_properties(\n  ${GIT_VERSION_GENERATED_OUTPUT} PROPERTIES GENERATED TRUE)\n\nset(METADATA_TEMPLATE \"${CMAKE_CURRENT_SOURCE_DIR}/metadata.h.in\")\nset(METADATA_GENERATED_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/swish/versions/metadata.h\")\n\nconfigure_file(\n  ${METADATA_TEMPLATE}\n  ${METADATA_GENERATED_OUTPUT}\n  @ONLY)\nset_source_files_properties(\n  ${METADATA_GENERATED_OUTPUT} PROPERTIES GENERATED TRUE)\n\nset(SOURCES\n  ${GIT_VERSION_GENERATED_OUTPUT}\n  ${METADATA_GENERATED_OUTPUT}\n  version.cpp\n  version.hpp)\n\n# This target must NOT be called 'version'. That conflicts with the name of the\n# Windows library that we also need (via WinSparkle).  If the names collide,\n# CMake gets confused which one we mean and chooses the target, not the system\n# library.\nadd_library(versions ${SOURCES})\n\ntarget_include_directories(versions PUBLIC \"${CMAKE_CURRENT_BINARY_DIR}\")\n"
  },
  {
    "path": "swish/versions/git_version.h.in",
    "content": "const char git_version[] = \"@SWISH_GIT_VERSION@\";\n"
  },
  {
    "path": "swish/versions/metadata.h.in",
    "content": "/*\n    Copyright (C) 2009-2012 Vaclav Slavik\n    Copyright (C) 2013, 2014, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n    Permission is hereby granted, free of charge, to any person obtaining a\n    copy of this software and associated documentation files (the \"Software\"),\n    to deal in the Software without restriction, including without limitation\n    the rights to use, copy, modify, merge, publish, distribute, sublicense,\n    and/or sell copies of the Software, and to permit persons to whom the\n    Software is furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n    DEALINGS IN THE SOFTWARE.\n*/\n\n// Based on winsparkle-version.h.\n\n#ifndef SWISH_METADATA_H\n#define SWISH_METADATA_H\n\n// IMPORTANT: The version information in this file must be digestable by the\n// resource (RC) compiler.  No C++ allowed.\n\n#define SWISH_MAJOR_VERSION @swish_VERSION_MAJOR@\n#define SWISH_MINOR_VERSION @swish_VERSION_MINOR@\n#define SWISH_BUGFIX_VERSION @swish_VERSION_PATCH@\n#define SWISH_VERSION_STRING \"@swish_VERSION@\"\n\n#define SWISH_PROGRAM_NAME \"@SWISH_FRIENDLY_NAME@\"\n\n#define SWISH_COPYRIGHT \"@SWISH_COPYRIGHT@\"\n\n#define SWISH_DESCRIPTION \"@SWISH_DESCRIPTION@\"\n\n#endif\n"
  },
  {
    "path": "swish/versions/version.cpp",
    "content": "/**\n    @file\n\n    Swish version information.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#include \"version.hpp\"\n\n#include \"git_version.h\" // generated\n#include \"swish/versions/metadata.h\"\n\nusing std::auto_ptr;\nusing std::string;\n\nnamespace swish {\n\nstring snapshot_version()\n{\n    return git_version;\n}\n\nstd::string build_time()\n{\n    return __TIME__;\n}\n\nstd::string build_date()\n{\n    return __DATE__;\n}\n\n/*\nclass structured_version_impl\n{\npublic:\n\n    virtual ~structured_version_impl() {}\n\n    virtual int major() const = 0;\n    virtual int minor() const = 0;\n    virtual int bugfix() const = 0;\n\n    virtual std::string as_string() const = 0;\n};\n*/\n\nstructured_version::structured_version(const structured_version_impl& impl)\n    : m_pimpl(impl.clone()) {}\n\nstructured_version::structured_version(const structured_version& other)\n    : m_pimpl(other.m_pimpl->clone()) {}\n\nstructured_version& structured_version::operator=(structured_version other)\n{\n    swap(*this, other);\n    return *this;\n}\n\nint structured_version::major() const { return m_pimpl->major(); }\nint structured_version::minor() const { return m_pimpl->minor(); }\nint structured_version::bugfix() const { return m_pimpl->bugfix(); }\nstring structured_version::as_string() const { return m_pimpl->as_string(); }\n\nstructured_version release_version()\n{\n    class swish_version : public structured_version_impl\n    {\n    public:\n        virtual int major() const\n        {\n            return SWISH_MAJOR_VERSION;\n        }\n\n        virtual int minor() const\n        {\n            return SWISH_MINOR_VERSION;\n        }\n\n        virtual int bugfix() const\n        {\n            return SWISH_BUGFIX_VERSION;\n        }\n\n        virtual string as_string() const\n        {\n            return SWISH_VERSION_STRING;\n        }\n\n        virtual auto_ptr<structured_version_impl> clone() const\n        {\n            return auto_ptr<structured_version_impl>(new swish_version(*this));\n        }\n    };\n\n    return structured_version(swish_version());\n}\n\n}"
  },
  {
    "path": "swish/versions/version.hpp",
    "content": "/**\n    @file\n\n    C++ interface to Swish version information.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#ifndef SWISH_VERSION_HPP\n#define SWISH_VERSION_HPP\n\n#include <memory> // auto_ptr\n#include <string>\n\nnamespace swish {\n\n/**\n * Description of the version control snapshot from which the code was build.\n *\n * The description may be quite rough as there is no good way to describe\n * changes that occur in the working copy. \n *\n * Currently the description is the output of\n *   `git describe --abbrev=4 --dirty --always`\n * and therefore looks similar to\n *   `swish-0.7.2-1-g5227-dirty`.\n * This format should not be assumed.\n */\nstd::string snapshot_version();\n\n/**\n * The time of the last build.\n *\n * Technically, the time the compilation unit implementing this function was\n * compiled.\n */\nstd::string build_time();\n\n/**\n * The date of the last build.\n *\n * Technically, the date on which the compilation unit implementing this \n * function was compiled.\n */\nstd::string build_date();\n\n\nclass structured_version_impl\n{\npublic:\n\n    virtual ~structured_version_impl() {}\n\n    virtual int major() const = 0;\n    virtual int minor() const = 0;\n    virtual int bugfix() const = 0;\n\n    virtual std::string as_string() const = 0;\n\n    virtual std::auto_ptr<structured_version_impl> clone() const = 0;\n};\n\nclass structured_version\n{\npublic:\n\n    explicit structured_version(const structured_version_impl& impl);\n\n    structured_version(const structured_version& other);\n    structured_version& operator=(structured_version other);\n\n    int major() const;\n    int minor() const;\n    int bugfix() const;\n\n    std::string as_string() const;\n\n    friend void swap(structured_version& l, structured_version& r)\n    {\n        swap(l.m_pimpl, r.m_pimpl);\n    }\n\nprivate:\n    std::auto_ptr<structured_version_impl> m_pimpl;\n};\n\nstructured_version release_version();\n\n}\n\n#endif\n"
  },
  {
    "path": "swish/windows_api.hpp",
    "content": "/**\n    @file\n\n    Reimplementation of some Windows API functions.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#pragma once\n\n#include <ShlObj.h> // ::SHGetDesktopFolder, IL-* functions\n\nnamespace swish {\nnamespace windows_api {\n\n/**\n * Bind to the parent object of an absolute PIDL.\n *\n * This exists for compatability with Windows 9x which doesn't have this API\n * function.\n *\n * @returns  A pointer to the requested interface of the parent object along\n *           with a pointer to the item in the original PIDL relative to the\n *           object.\n *\n * Based on the implementation from the Wine project subject to the LGPL:\n * http://source.winehq.org/source/dlls/shell32/pidl.c#L1282\n * Copyright 1998 Juergen Schmied.\n */\ninline HRESULT WINAPI SHBindToParent(\n    PCIDLIST_ABSOLUTE pidl, REFIID riid, LPVOID* ppv, \n    PCUITEMID_CHILD* ppidlLast)\n{\n    IShellFolder* psfDesktop;\n    HRESULT hr = E_FAIL;\n\n    if (!ppv)\n        return E_POINTER;\n    *ppv = NULL;\n    \n    if (ppidlLast)\n        *ppidlLast = NULL;\n\n    if (!pidl)\n        return E_INVALIDARG;\n\n    hr = ::SHGetDesktopFolder(&psfDesktop);\n    if (FAILED(hr))\n        return hr;\n\n    if (::ILIsChild(pidl))\n    {\n        /* we are on desktop level */\n        hr = psfDesktop->QueryInterface(riid, ppv);\n    }\n    else\n    {\n        PIDLIST_ABSOLUTE pidlParent = ::ILCloneFull(pidl);\n        ::ILRemoveLastID(pidlParent);\n        hr = psfDesktop->BindToObject(pidlParent, NULL, riid, ppv);\n        ::ILFree(pidlParent);\n    }\n\n    psfDesktop->Release();\n\n    if (SUCCEEDED(hr) && ppidlLast)\n        *ppidlLast = ::ILFindLastID(pidl);\n\n    return hr;\n}\n\n}} // namespace swish::windows_api\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "# Copyright 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfind_package(Boost 1.40 REQUIRED COMPONENTS unit_test_framework)\n\nadd_subdirectory(common_boost)\nadd_subdirectory(fixtures)\n\noption(MEMORY_LEAKS_ARE_FAILURES\n  \"Fail the test suite if a memory leak is detected\" OFF)\n\nset(TEST_RUNNER_ARGUMENTS\n  --result_code=yes --build_info=yes --log_level=test_suite)\n\nadd_custom_target(BUILD_ALL_TESTS COMMENT \"Building all tests\")\n\nset(TEST_DATA_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/fixtures\")\n\ninclude(CMakeParseArguments)\n# swish_test_suite(SUBJECT test-target [VARIANT suite-variant] SOURCES ...\n#                  LIBRARIES ... [LABELS ...])\nfunction(SWISH_TEST_SUITE)\n  set(options)\n  set(oneValueArgs SUBJECT VARIANT)\n  set(multiValueArgs SOURCES LIBRARIES LABELS)\n  cmake_parse_arguments(SWISH_TEST_SUITE\n    \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  if(SWISH_TEST_SUITE_VARIANT)\n    set(_TEST_EXE_NAME\n      \"test-${SWISH_TEST_SUITE_SUBJECT}-${SWISH_TEST_SUITE_VARIANT}\")\n  else()\n    set(_TEST_EXE_NAME \"test-${SWISH_TEST_SUITE_SUBJECT}\")\n  endif()\n\n  configure_file(\n    ${CMAKE_CURRENT_SOURCE_DIR}/../module.cpp.in\n    ${CMAKE_CURRENT_BINARY_DIR}/module.cpp @ONLY)\n\n  add_executable(${_TEST_EXE_NAME}\n    ${SWISH_TEST_SUITE_SOURCES}\n    ${CMAKE_CURRENT_BINARY_DIR}/module.cpp)\n\n  target_link_libraries(${_TEST_EXE_NAME}\n    PRIVATE\n    ${SWISH_TEST_SUITE_SUBJECT} test-common_boost ${SWISH_TEST_SUITE_LIBRARIES})\n\n  add_dependencies(BUILD_ALL_TESTS ${_TEST_EXE_NAME})\n\n  add_test(\n    NAME ${_TEST_EXE_NAME}\n    COMMAND ${_TEST_EXE_NAME} ${TEST_RUNNER_ARGUMENTS}\n    WORKING_DIRECTORY \"${TEST_DATA_DIR}\")\n\n  if(MEMORY_LEAKS_ARE_FAILURES)\n    # Don't hide memory leak detection.  The detector can't change the error\n    # code so the test appears successful otherwise.\n    set_tests_properties(${_TEST_EXE_NAME} PROPERTIES\n      FAIL_REGULAR_EXPRESSION \"Detected memory leaks\")\n  endif()\n\n  # if(TEST_RUNNER_ENVIRONMENT)\n  #   set_tests_properties(${_TEST_EXE_NAME} PROPERTIES\n  #     ENVIRONMENT \"${TEST_RUNNER_ENVIRONMENT}\")\n  # endif()\n\n  if(SWISH_TEST_SUITE_LABELS)\n    set_tests_properties(\n      ${_TEST_EXE_NAME} PROPERTIES LABELS \"${SWISH_TEST_SUITE_LABELS}\")\n  endif()\nendfunction()\n\nset(_FIXTURE_FILES test_zip_file.zip)\nset(_FIXTURE_SSHD_ETC_FILES\n  fixture_dsakey fixture_dsakey.pub\n  fixture_hostkey fixture_hostkey.pub\n  fixture_rsakey fixture_rsakey.pub)\nset(_FIXTURE_FILES_DIR ${CMAKE_CURRENT_LIST_DIR}/common_boost)\nset(_PERMISSION_SCRIPT_DIR ${CMAKE_CURRENT_LIST_DIR})\n\nfunction(SWISH_COPY_FIXTURE_FILES TEST_TARGET)\n  foreach(FILE ${_FIXTURE_FILES})\n    add_custom_command(\n      TARGET ${TEST_TARGET} POST_BUILD\n      COMMAND ${CMAKE_COMMAND}\n      -E copy_if_different\n      ${_FIXTURE_FILES_DIR}/${FILE}\n      $<TARGET_FILE_DIR:${TEST_TARGET}>/${FILE}\n      VERBATIM)\n  endforeach()\n\n  foreach(FILE ${_FIXTURE_SSHD_ETC_FILES})\n    add_custom_command(\n      TARGET ${TEST_TARGET} POST_BUILD\n      COMMAND ${CMAKE_COMMAND}\n      -E copy_if_different\n      ${_FIXTURE_FILES_DIR}/${FILE}\n      $<TARGET_FILE_DIR:${TEST_TARGET}>/sshd-etc/${FILE}\n      VERBATIM)\n  endforeach()\n\n\n  if(CYGWIN_INSTALL_PATH)\n    add_custom_command(\n      TARGET ${TEST_TARGET} POST_BUILD\n      COMMAND\n      cd $<TARGET_FILE_DIR:${TEST_TARGET}>/sshd-etc/\n      VERBATIM)\n    add_custom_command(\n      TARGET ${TEST_TARGET} POST_BUILD\n      COMMAND\n      set PATH=${CYGWIN_INSTALL_PATH}/bin$<SEMICOLON>)\n    add_custom_command(\n      TARGET ${TEST_TARGET} POST_BUILD\n      COMMAND\n      bash ${_PERMISSION_SCRIPT_DIR}/fix_key_permissions.sh\n      VERBATIM)\n  endif()\nendfunction()\n\nadd_subdirectory(connection)\nadd_subdirectory(drop_target)\nadd_subdirectory(ezel)\nadd_subdirectory(forms)\nadd_subdirectory(host_folder)\nadd_subdirectory(nse)\nadd_subdirectory(provider-integration)\nadd_subdirectory(remote_folder)\nadd_subdirectory(shell)\nadd_subdirectory(shell_folder)\nadd_subdirectory(shell_folder-dialogue)\nadd_subdirectory(ssh)\nadd_subdirectory(versions)\n\n# swish_declare_test_target(COMMAND_NAME [OPTIONAL_FLAGS...])\nfunction(SWISH_DECLARE_TEST_TARGET COMMAND_NAME)\n  set(options ALL)\n  set(oneValueArgs)\n  set(multiValueArgs)\n  cmake_parse_arguments(SWISH_DECLARE_TEST_TARGET\n    \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  if(SWISH_DECLARE_TEST_TARGET_ALL)\n    set(SWISH_DECLARE_TEST_TARGET_ALL ALL)\n  else()\n    set(SWISH_DECLARE_TEST_TARGET_ALL)\n  endif()\n\n  # From http://stackoverflow.com/a/16163137/67013\n  if(CMAKE_CONFIGURATION_TYPES)\n    add_custom_target(${COMMAND_NAME} ${SWISH_DECLARE_TEST_TARGET_ALL}\n      COMMAND ${CMAKE_CTEST_COMMAND}\n      --force-new-ctest-process --output-on-failure\n      --build-config \"$<CONFIGURATION>\"\n      ${SWISH_DECLARE_TEST_TARGET_UNPARSED_ARGUMENTS})\n  else()\n    add_custom_target(${COMMAND_NAME} ${SWISH_DECLARE_TEST_TARGET_ALL}\n      COMMAND ${CMAKE_CTEST_COMMAND}\n      --force-new-ctest-process --output-on-failure\n      ${SWISH_DECLARE_TEST_TARGET_UNPARSED_ARGUMENTS})\n  endif()\n\n  add_dependencies(${COMMAND_NAME} BUILD_ALL_TESTS)\nendfunction()\n\nswish_declare_test_target(CHECK)\nswish_declare_test_target(CHECK_UNIT -L unit ALL)\nswish_declare_test_target(CHECK_INTEGRATION -L integration)\nswish_declare_test_target(CHECK_GUI -L gui)\n"
  },
  {
    "path": "test/common_boost/CMakeLists.txt",
    "content": "# Copyright (C) 2015, 2016 Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  data_object_utils.cpp\n  stream_utils.cpp\n  ConsumerStub.hpp\n  data_object_utils.hpp\n  fixtures.hpp\n  helpers.hpp\n  MockConsumer.hpp\n  MockProvider.hpp\n  stream_utils.hpp\n  SwishPidlFixture.hpp\n  tree.hh\n  tree.hpp)\n\nadd_library(test-common_boost ${SOURCES})\n\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(test-common_boost\n  PRIVATE shell shell_folder\n  PUBLIC Comet::comet Washer::washer ${Boost_LIBRARIES})\n"
  },
  {
    "path": "test/common_boost/ConsumerStub.hpp",
    "content": "/**\n    @file\n\n    Stub implementation of an ISftpConsumer.\n\n    @if license\n\n    Copyright (C) 2009, 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include \"test/common_boost/helpers.hpp\"\n\n#include <washer/com/catch.hpp> // WASHER_COM_CATCH_AUTO_INTERFACE\n\n#include <comet/server.h> // simple_object\n\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <string>\n\n#include <boost/filesystem.hpp>\n\nnamespace test {\n\n/**\n * Very simple consumer that just handles authentication via pub-key.\n*/\nclass CConsumerStub : public comet::simple_object<ISftpConsumer>\n{\npublic:\n\n    typedef ISftpConsumer interface_is;\n\n    CConsumerStub(\n        boost::filesystem::path privatekey, boost::filesystem::path publickey)\n        : m_privateKey(privatekey), m_publicKey(publickey) {}\n\n    // ISftpConsumer methods\n\n    virtual boost::optional<std::wstring> prompt_for_password()\n    {\n        return boost::optional<std::wstring>();\n    }\n\n    virtual boost::optional<\n        std::pair<boost::filesystem::path, boost::filesystem::path>>\n        key_files()\n    {\n        return std::make_pair(m_privateKey, m_publicKey);\n    }\n\n    virtual boost::optional<std::vector<std::string>> challenge_response(\n        const std::string& /*title*/, const std::string& /*instructions*/,\n        const std::vector<std::pair<std::string, bool>>& /*prompts*/)\n    {\n        BOOST_ERROR(\"Unexpected call to \"__FUNCTION__);\n        BOOST_THROW_EXCEPTION(std::exception(\"Not implemented\"));\n    }\n\n    HRESULT OnConfirmOverwrite(\n        BSTR /*bstrOldFile*/, BSTR /*bstrNewFile*/)\n    {\n        BOOST_ERROR(\"Unexpected call to \"__FUNCTION__);\n        return E_NOTIMPL;\n    }\n\n    HRESULT OnHostkeyMismatch(\n        BSTR /*bstrHostName*/, BSTR /*bstrHostKey*/, BSTR /*bstrHostKeyType*/)\n    {\n        return S_FALSE;\n    }\n\n    HRESULT OnHostkeyUnknown(\n        BSTR /*bstrHostName*/, BSTR /*bstrHostKey*/, BSTR /*bstrHostKeyType*/)\n    {\n        return S_FALSE;\n    }\n\nprivate:\n    boost::filesystem::path m_privateKey;\n    boost::filesystem::path m_publicKey;\n};\n\n} // namespace test\n"
  },
  {
    "path": "test/common_boost/MockConsumer.hpp",
    "content": "// Copyright 2010, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef TEST_COMMON_BOOST_MOCK_CONSUMER_HPP\n#define TEST_COMMON_BOOST_MOCK_CONSUMER_HPP\n\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <comet/bstr.h>  // bstr_t\n#include <comet/error.h> // com_error\n#include <comet/safearray.h>\n#include <comet/server.h> // simple_object\n\n#include <boost/locale.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n#include <boost/foreach.hpp>         // BOOST_FOREACH\n#include <boost/test/test_tools.hpp> // BOOST_ERROR\n\n#include <cassert> // assert\n#include <string>\n\nnamespace test\n{\n\nclass MockConsumer : public comet::simple_object<ISftpConsumer>\n{\npublic:\n    typedef ISftpConsumer interface_is;\n\n    /**\n     * Possible behaviours of file overwrite confirmation handlers\n     * OnConfirmOverwrite.\n     */\n    enum ConfirmOverwriteBehaviour\n    {\n        AllowOverwrite,   ///< Return S_OK\n        PreventOverwrite, ///< Return E_ABORT\n    };\n\n    /**\n     * Possible behaviours of mock password request handler OnPasswordRequest.\n     */\n    enum PasswordBehaviour\n    {\n        EmptyPassword,  ///< Return an empty string\n        CustomPassword, ///< Return the string set with SetPassword\n        WrongPassword,  ///< Return a very unlikely sequence of characters\n        FailPassword,   ///< Throw E_FAIL exception if password requested\n        AbortPassword,  ///< Return unitialised optional indicating abort.\n    };\n\n    /**\n     * Possible behaviours of mock keyboard-interactive request handler\n     * OnKeyboardInteractiveRequest.\n     */\n    enum KeyboardInteractiveBehaviour\n    {\n        EmptyResponse,  ///< Return an empty BSTR (not NULL, \"\")\n        CustomResponse, ///< Return the string set with SetPassword\n        WrongResponse,  ///< Return a very unlikely sequence of characters\n        FailResponse,   ///< Throw exception if kb-interaction requested\n        AbortResponse,  ///< Return E_ABORT (simulate user cancelled)\n    };\n\n    /**\n     * Possible behaviours of mock public-key file requests.\n     */\n    enum PublicKeyBehaviour\n    {\n        EmptyKeys,   ///< Return an empty BSTR (not NULL, \"\")\n        CustomKeys,  ///< Return the strings set with SetKeys\n        WrongKeys,   ///< Return the wrong, but existing, key files\n        InvalidKeys, ///< Return key files that don't exist\n        FailKeys,    ///< Throw E_FAIL exceptionn if keys requested.\n        AbortKeys,   ///< Return unitialised optional indicating abort.\n    };\n\n    MockConsumer()\n        : m_password_behaviour(FailPassword),\n          m_password_attempt_count(0),\n          m_password_attempt_count_max(1),\n          m_keyboard_interative_behaviour(FailResponse),\n          m_pubkey_behaviour(FailKeys),\n          m_ki_attempt_count(0),\n          m_ki_attempt_count_max(1),\n          m_confirm_overwrite_behaviour(PreventOverwrite),\n          m_confirmed_overwrite(false)\n    {\n    }\n\n    void set_password(const std::wstring& password)\n    {\n        m_password = password;\n    }\n\n    void set_password_behaviour(PasswordBehaviour behaviour)\n    {\n        m_password_behaviour = behaviour;\n    }\n\n    void set_password_max_attempts(int max)\n    {\n        m_password_attempt_count_max = max;\n    }\n\n    void\n    set_keyboard_interactive_behaviour(KeyboardInteractiveBehaviour behaviour)\n    {\n        m_keyboard_interative_behaviour = behaviour;\n    }\n\n    void set_keyboard_interactive_max_attempts(int max)\n    {\n        m_ki_attempt_count_max = max;\n    }\n\n    void set_key_files(const std::string& private_key,\n                       const std::string& public_key)\n    {\n        m_private_key_file = private_key;\n        m_public_key_file = public_key;\n    }\n\n    void set_pubkey_behaviour(PublicKeyBehaviour behaviour)\n    {\n        m_pubkey_behaviour = behaviour;\n    }\n\n    void set_confirm_overwrite_behaviour(ConfirmOverwriteBehaviour behaviour)\n    {\n        m_confirm_overwrite_behaviour = behaviour;\n    }\n\n    bool was_asked_to_confirm_overwrite() const\n    {\n        return m_confirmed_overwrite;\n    }\n\n    // ISftpConsumer methods\n    virtual boost::optional<std::wstring> prompt_for_password()\n    {\n        ++m_password_attempt_count;\n\n        // Perform chosen test behaviour\n        // The three password cases which should never succeed will try to send\n        // their 'reply' up to m_nMaxPassword time to simulate a user repeatedly\n        // trying the wrong password and then giving up.\n        if (m_password_attempt_count > m_password_attempt_count_max)\n            BOOST_THROW_EXCEPTION(\n                comet::com_error(\"Too many attempts\", E_FAIL));\n\n        switch (m_password_behaviour)\n        {\n        case CustomPassword:\n            return m_password;\n        case WrongPassword:\n            return L\"WrongPasswordXyayshdkhjhdk\";\n        case EmptyPassword:\n            // leave password blank\n            return std::wstring();\n        case FailPassword:\n            BOOST_THROW_EXCEPTION(\n                comet::com_error(\"Mock fail behaviour\", E_FAIL));\n        case AbortPassword:\n            return boost::optional<std::wstring>();\n        default:\n            BOOST_FAIL(\n                \"Unreachable: Unrecognised OnPasswordRequest() behaviour\");\n            return boost::optional<std::wstring>();\n        }\n    }\n\n    virtual boost::optional<\n        std::pair<boost::filesystem::path, boost::filesystem::path>>\n    key_files()\n    {\n        switch (m_pubkey_behaviour)\n        {\n        case CustomKeys:\n            return std::make_pair(m_private_key_file, m_public_key_file);\n        case WrongKeys:\n            return std::make_pair(m_public_key_file, m_private_key_file);\n        case InvalidKeys:\n            return std::make_pair(\"HumptyDumpty\", \"SatOnAWall\");\n        case EmptyKeys:\n            return std::make_pair(\"\", \"\");\n        case FailKeys:\n            BOOST_THROW_EXCEPTION(\n                comet::com_error(\"Mock fail behaviour\", E_FAIL));\n        case AbortKeys:\n            return boost::optional<\n                std::pair<boost::filesystem::path, boost::filesystem::path>>();\n        default:\n            BOOST_FAIL(\"Unreachable: Unrecognised \" __FUNCTION__ \" behaviour\");\n            return boost::optional<\n                std::pair<boost::filesystem::path, boost::filesystem::path>>();\n        }\n    }\n\n    virtual boost::optional<std::vector<std::string>>\n    challenge_response(const std::string& /*title*/,\n                       const std::string& /*instructions*/,\n                       const std::vector<std::pair<std::string, bool>>& prompts)\n    {\n        ++m_ki_attempt_count;\n\n        typedef std::pair<std::string, bool> prompt_pair;\n        BOOST_FOREACH (const prompt_pair& prompt, prompts)\n        {\n            BOOST_CHECK_GT(prompt.first.size(), 0U);\n        }\n\n        // Perform chosen test behaviour\n        // The three response cases which should never succeed will try to send\n        // their 'reply' up to m_nMaxKbdAttempts time to simulate a user\n        // repeatedly trying the wrong password and then giving up.\n        if (m_ki_attempt_count > m_ki_attempt_count_max)\n            BOOST_THROW_EXCEPTION(std::exception(\"Too many kb-int attempts\"));\n\n        std::string response;\n        switch (m_keyboard_interative_behaviour)\n        {\n        case CustomResponse:\n        {\n            response = boost::locale::conv::utf_to_utf<char>(m_password);\n            break;\n        }\n        case WrongResponse:\n            response = \"WrongPasswordXyayshdkhjhdk\";\n            break;\n        case EmptyResponse:\n            // leave response empty\n            break;\n        case FailResponse:\n            BOOST_THROW_EXCEPTION(std::exception(\"Mock fail behaviour\"));\n        case AbortResponse:\n            return boost::optional<std::vector<std::string>>();\n        default:\n            BOOST_FAIL(\"Unreachable: Unrecognised \"\n                       \"OnKeyboardInteractiveRequest() behaviour\");\n            BOOST_THROW_EXCEPTION(\n                std::exception(\"Unrecognised mock behaviour\"));\n        }\n\n        std::vector<std::string> responses;\n        // Create responses.  Return password as first response.  Any other\n        // prompts are responded to with an empty string.\n        responses.push_back(response);\n        while (responses.size() < prompts.size())\n        {\n            responses.push_back(\"\");\n        }\n\n        return responses;\n    }\n\n    HRESULT OnConfirmOverwrite(BSTR /*bstrOldFile*/, BSTR /*bstrNewFile*/)\n    {\n        m_confirmed_overwrite = true;\n\n        switch (m_confirm_overwrite_behaviour)\n        {\n        case AllowOverwrite:\n            return S_OK;\n        case PreventOverwrite:\n            return E_ABORT;\n        default:\n            assert(!\"Unreachable: Unrecognised \"\n                    \"OnConfirmOverwrite() behaviour\");\n            return E_UNEXPECTED;\n        }\n    }\n\n    HRESULT OnHostkeyMismatch(BSTR /*bstrHostName*/, BSTR /*bstrHostKey*/,\n                              BSTR /*bstrHostKeyType*/)\n    {\n        return S_FALSE;\n    }\n\n    HRESULT OnHostkeyUnknown(BSTR /*bstrHostName*/, BSTR /*bstrHostKey*/,\n                             BSTR /*bstrHostKeyType*/)\n    {\n        return S_FALSE;\n    }\n\nprivate:\n    PasswordBehaviour m_password_behaviour;\n    int m_password_attempt_count;\n    int m_password_attempt_count_max;\n    std::wstring m_password;\n\n    KeyboardInteractiveBehaviour m_keyboard_interative_behaviour;\n    int m_ki_attempt_count;\n    int m_ki_attempt_count_max;\n\n    PublicKeyBehaviour m_pubkey_behaviour;\n    std::string m_public_key_file;\n    std::string m_private_key_file;\n\n    ConfirmOverwriteBehaviour m_confirm_overwrite_behaviour;\n    bool m_confirmed_overwrite;\n};\n\n} // namespace test\n\n#endif\n"
  },
  {
    "path": "test/common_boost/MockProvider.hpp",
    "content": "/**\n    @file\n\n    Mock implementation of swish::provider::sftp_provider.\n\n    @if license\n\n    Copyright (C) 2010, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef TEST_COMMON_BOOST_MOCK_PROVIDER_HPP\n#define TEST_COMMON_BOOST_MOCK_PROVIDER_HPP\n#pragma once\n\n#include \"test/common_boost/tree.hpp\" // tree container for mocking filesystem\n\n#include \"swish/provider/sftp_provider.hpp\" // sftp_provider\n\n#include <comet/bstr.h> // bstr_t\n#include <comet/datetime.h> // datetime_t\n\n#include <boost/bind.hpp>\n#include <boost/filesystem.hpp> // path\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/format.hpp> // wformat\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <functional> // equal_to, less\n#include <string>\n#include <vector>\n\n#include <Shlwapi.h> // SHCreateMemStream\n\nnamespace test {\nnamespace detail {\n\n    typedef tree<swish::provider::sftp_filesystem_item> Filesystem;\n    typedef Filesystem::iterator FilesystemLocation;\n\n    inline bool name_match(\n        const std::wstring& name,\n        const swish::provider::sftp_filesystem_item& sftp_item)\n    {\n        return name == sftp_item.filename();\n    }\n\n    /**\n     * Return an iterator to the node in the mock filesystem indicated by the\n     * path given as a string.\n     */\n    inline FilesystemLocation find_location_from_path(\n        const Filesystem& filesystem, const ssh::filesystem::path& path)\n    {\n        // Start searching in root of 'filesystem'\n        FilesystemLocation current_dir = filesystem.begin();\n\n        // Walk down list of tokens finding each item below the previous\n        BOOST_FOREACH(ssh::filesystem::path segment, path.relative_path())\n        {\n            std::wstring name = segment.wstring();\n\n            if (name == L\".\")\n                continue;\n\n            FilesystemLocation dir = find_if(\n                filesystem.begin(current_dir), filesystem.end(current_dir),\n                boost::bind(&name_match, name, _1));\n\n            if (dir == filesystem.end(current_dir))\n            {\n                std::string message =\n                    str(boost::format(\"Mock file '%s' not found\") % name);\n                BOOST_THROW_EXCEPTION(std::exception(message.c_str()));\n            }\n\n            current_dir = dir;\n        }\n\n        if (current_dir == filesystem.end())\n            BOOST_THROW_EXCEPTION(std::exception(\"Unexpected lookup failure!\"));\n\n        return current_dir;\n    }\n\n    class mock_filesystem_file :\n        public swish::provider::sftp_filesystem_item_interface\n    {\n    public:\n        static swish::provider::sftp_filesystem_item create(\n            const std::wstring& name, ULONG permissions, ULONGLONG size,\n            comet::datetime_t date)\n        {\n            return swish::provider::sftp_filesystem_item(\n                boost::shared_ptr<swish::provider::sftp_filesystem_item_interface>(\n                    new mock_filesystem_file(name, permissions, size, date)));\n        }\n\n        BOOST_SCOPED_ENUM(type) type() const\n        {\n            return type::file;\n        }\n\n        ssh::filesystem::path filename() const\n        {\n            return m_name;\n        }\n\n        unsigned long permissions() const\n        {\n            return m_permissions;\n        }\n\n        boost::optional<std::wstring> owner() const\n        {\n            return L\"mockowner\";\n        }\n\n        unsigned long uid() const\n        {\n            return 42;\n        }\n\n        boost::optional<std::wstring> group() const\n        {\n            return L\"mockgroup\";\n        }\n\n        unsigned long gid() const\n        {\n            return 24;\n        }\n\n        boost::uint64_t size_in_bytes() const\n        {\n            return m_size;\n        }\n\n        comet::datetime_t last_accessed() const\n        {\n            return comet::datetime_t();\n        }\n\n        comet::datetime_t last_modified() const\n        {\n            return m_date;\n        }\n\n    private:\n        mock_filesystem_file(\n            const std::wstring& name, ULONG permissions,\n            ULONGLONG size, comet::datetime_t date)\n            :\n        m_name(name), m_permissions(permissions), m_size(size), m_date(date) {}\n\n        std::wstring m_name;\n        ULONG m_permissions;\n        ULONGLONG m_size;\n        comet::datetime_t m_date;\n    };\n\n    class mock_filesystem_directory :\n        public swish::provider::sftp_filesystem_item_interface\n    {\n    public:\n        static swish::provider::sftp_filesystem_item create(\n            const std::wstring& name)\n        {\n            return swish::provider::sftp_filesystem_item(\n                boost::shared_ptr<swish::provider::sftp_filesystem_item_interface>(\n                    new mock_filesystem_directory(name)));\n        }\n\n        BOOST_SCOPED_ENUM(type) type() const\n        {\n            return type::directory;\n        }\n\n        ssh::filesystem::path filename() const\n        {\n            return m_name;\n        }\n\n        unsigned long permissions() const\n        {\n            return 040777;\n        }\n\n        boost::optional<std::wstring> owner() const\n        {\n            return L\"mockowner\";\n        }\n\n        unsigned long uid() const\n        {\n            return 42;\n        }\n\n        boost::optional<std::wstring> group() const\n        {\n            return L\"mockgroup\";\n        }\n\n        unsigned long gid() const\n        {\n            return 24;\n        }\n\n        boost::uint64_t size_in_bytes() const\n        {\n            return 0U;\n        }\n\n        comet::datetime_t last_accessed() const\n        {\n            return comet::datetime_t();\n        }\n\n        comet::datetime_t last_modified() const\n        {\n            return comet::datetime_t(1601, 10, 5, 13, 54, 22);\n        }\n\n    private:\n        mock_filesystem_directory(const std::wstring& name) : m_name(name) {}\n\n        std::wstring m_name;\n    };\n\n    class mock_filesystem_link :\n        public swish::provider::sftp_filesystem_item_interface\n    {\n    public:\n        static swish::provider::sftp_filesystem_item create(\n            const std::wstring& name)\n        {\n            return swish::provider::sftp_filesystem_item(\n                boost::shared_ptr<swish::provider::sftp_filesystem_item_interface>(\n                    new mock_filesystem_link(name)));\n        }\n\n        BOOST_SCOPED_ENUM(type) type() const\n        {\n            return type::link;\n        }\n\n        ssh::filesystem::path filename() const\n        {\n            return m_name;\n        }\n\n        unsigned long permissions() const\n        {\n            return 040777;\n        }\n\n        boost::optional<std::wstring> owner() const\n        {\n            return L\"mockowner\";\n        }\n\n        unsigned long uid() const\n        {\n            return 42;\n        }\n\n        boost::optional<std::wstring> group() const\n        {\n            return L\"mockgroup\";\n        }\n\n        unsigned long gid() const\n        {\n            return 24;\n        }\n\n        boost::uint64_t size_in_bytes() const\n        {\n            return 0U;\n        }\n\n        comet::datetime_t last_accessed() const\n        {\n            return comet::datetime_t();\n        }\n\n        comet::datetime_t last_modified() const\n        {\n            return comet::datetime_t(1601, 10, 5, 13, 54, 22);\n        }\n\n    private:\n        mock_filesystem_link(const std::wstring& name) : m_name(name) {}\n\n        std::wstring m_name;\n    };\n\n    inline std::wstring tag_filename(\n        const std::wstring& filename, const ssh::filesystem::path& directory)\n    {\n        // UNOBVIOUS: converting the path to a string here, rather than passing\n        // the path directly to the formatter, because Boost.Filesystem v3,\n        // surprisingly, quotes all paths that are sent to an output stream.\n        return str(boost::wformat(filename) % directory.filename().wstring());\n    }\n\n    inline void make_item_in(\n        Filesystem& filesystem, FilesystemLocation loc,\n        const swish::provider::sftp_filesystem_item& item)\n    {\n        filesystem.append_child(loc, item);\n    }\n\n    inline void make_item_in(\n        Filesystem& filesystem, const ssh::filesystem::path& path,\n        const swish::provider::sftp_filesystem_item& item)\n    {\n        make_item_in(\n            filesystem, find_location_from_path(filesystem, path), item);\n    }\n\n    /**\n     * Generates a listing for the given directory and tags each filename with\n     * the name of the parent folder.  This allows us to detect a correct\n     * listing later.\n     */\n    inline void fill_mock_listing(\n        Filesystem& filesystem, const ssh::filesystem::path& directory)\n    {\n        std::vector<std::wstring> filenames;\n        filenames.push_back(tag_filename(L\"test%sfile\", directory));\n        filenames.push_back(tag_filename(L\"test%sFile\", directory));\n        filenames.push_back(tag_filename(L\"test%sfile.ext\", directory));\n        filenames.push_back(tag_filename(L\"test%sfile.txt\", directory));\n        filenames.push_back(tag_filename(L\"test%sfile with spaces\", directory));\n        filenames.push_back(\n            tag_filename(L\"test%sfile with \\\"quotes\\\" and spaces\", directory));\n        filenames.push_back(tag_filename(L\"test%sfile.ext.txt\", directory));\n        filenames.push_back(tag_filename(L\"test%sfile..\", directory));\n        filenames.push_back(tag_filename(L\".test%shiddenfile\", directory));\n\n        std::vector<comet::datetime_t> dates;\n        dates.push_back(comet::datetime_t());\n        dates.push_back(comet::datetime_t::now());\n        dates.push_back(comet::datetime_t(1899, 7, 13, 17, 59, 12));\n        dates.push_back(comet::datetime_t(9999, 12, 31, 23, 59, 59));\n        dates.push_back(comet::datetime_t(2000, 2, 29, 12, 47, 1));\n        dates.push_back(comet::datetime_t(1978, 3, 3, 3, 00, 00));\n        dates.push_back(comet::datetime_t(1601, 1, 1, 0, 00, 00));\n        dates.push_back(comet::datetime_t(2007, 2, 28, 0, 0, 0));\n        dates.push_back(comet::datetime_t(1752, 9, 03, 7, 27, 8));\n\n        unsigned long cycle = 0;\n        unsigned long size = 0;\n        while (!filenames.empty())\n        {\n            // Try to cycle through the permissions on each successive file\n            // TODO: I have no idea if this works\n            unsigned permissions =\n                (cycle % 1) || ((cycle % 2) << 1) || ((cycle % 3) << 2);\n\n            make_item_in(\n                filesystem, directory,\n                mock_filesystem_file::create(\n                    filenames.back(), permissions, size, dates.back()));\n\n            dates.pop_back();\n            filenames.pop_back();\n            cycle++;\n            size = (size + cycle) << 10;\n        }\n\n        // Add some dummy folders also\n        std::vector<std::wstring> folder_names;\n        folder_names.push_back(tag_filename(L\"Test%sfolder\", directory));\n        folder_names.push_back(tag_filename(L\"test%sfolder.ext\", directory));\n        folder_names.push_back(tag_filename(L\"test%sfolder.bmp\", directory));\n        folder_names.push_back(\n            tag_filename(L\"test%sfolder with spaces\", directory));\n        folder_names.push_back(tag_filename(L\".test%shiddenfolder\", directory));\n\n        while (!folder_names.empty())\n        {\n            make_item_in(\n                filesystem, directory,\n                mock_filesystem_directory::create(folder_names.back()));\n            folder_names.pop_back();\n        }\n\n        // Last but not least, links\n        std::vector<std::wstring> link_names;\n        link_names.push_back(tag_filename(L\"link%sfolder\", directory));\n        link_names.push_back(tag_filename(L\"another link%sfolder\", directory));\n        link_names.push_back(tag_filename(L\"p%s\", directory));\n        link_names.push_back(tag_filename(L\".q%s\", directory));\n        link_names.push_back(tag_filename(L\"this_link_is_broken_%s\", directory));\n\n        while (!link_names.empty())\n        {\n            make_item_in(\n                filesystem, directory,\n                mock_filesystem_link::create(link_names.back()));\n            link_names.pop_back();\n        }\n    }\n\n    struct comparator\n    {\n        bool operator()(\n            const swish::provider::sftp_filesystem_item& left,\n            const swish::provider::sftp_filesystem_item& right)\n        {\n            return left.filename() < right.filename();\n        }\n    };\n\n}\n\nclass MockProvider : public swish::provider::sftp_provider\n{\npublic:\n\n    /**\n    * Possible behaviours of listing returned by mock GetListing() method.\n    */\n    typedef enum tagListingBehaviour {\n        MockListing,     ///< Return a dummy list of files and S_OK.\n        EmptyListing,    ///< Return an empty list and S_OK.\n        SFalseNoListing, ///< Return a NULL listing and S_FALSE.\n        AbortListing,    ///< Return a NULL listing E_ABORT.\n        FailListing      ///< Return a NULL listing E_FAIL.\n    } ListingBehaviour;\n\n    /**\n    * Possible behaviours of mock Rename() method.\n    */\n    typedef enum tagRenameBehaviour {\n        RenameOK,           ///< Return S_OK - rename unconditionally succeeded.\n        ConfirmOverwrite,   ///< Call ISftpConsumer's OnConfirmOverwrite and\n        ///< return its return value.\n        AbortRename,        ///< Return E_ABORT.\n        FailRename          ///< Return E_FAIL.\n    } RenameBehaviour;\n\n    MockProvider() :\n        m_listing_behaviour(MockListing), m_rename_behaviour(RenameOK)\n    {\n        // Create filesystem root\n        detail::FilesystemLocation root = m_filesystem.insert(\n            m_filesystem.begin(),\n            detail::mock_filesystem_directory::create(L\"/\"));\n\n        // Create two subdirectories and fill them with an expected set of items\n        // whose names are 'tagged' with the directory name\n        detail::FilesystemLocation tmp =\n            m_filesystem.append_child(\n                root, detail::mock_filesystem_directory::create(L\"tmp\"));\n        detail::FilesystemLocation swish =\n            m_filesystem.append_child(\n                tmp, detail::mock_filesystem_directory::create(L\"swish\"));\n        detail::fill_mock_listing(m_filesystem, L\"/tmp\");\n        detail::fill_mock_listing(m_filesystem, L\"/tmp/swish\");\n    }\n\n    ~MockProvider()\n    {\n        m_filesystem.clear();\n    }\n\n    void set_listing_behaviour(ListingBehaviour behaviour)\n    {\n        m_listing_behaviour = behaviour;\n    }\n\n    void set_rename_behaviour(RenameBehaviour behaviour)\n    {\n        m_rename_behaviour = behaviour;\n    }\n\n    virtual swish::provider::directory_listing listing(\n        const ssh::filesystem::path& directory)\n    {\n        std::vector<swish::provider::sftp_filesystem_item> files;\n\n        switch (m_listing_behaviour)\n        {\n        case EmptyListing:\n            break;\n\n        case MockListing:\n            {\n                detail::FilesystemLocation dir =\n                    detail::find_location_from_path(m_filesystem, directory);\n\n                // Copy directory out of tree and sort alphabetically\n                files.insert(\n                    files.begin(), m_filesystem.begin(dir),\n                    m_filesystem.end(dir));\n\n                std::sort(files.begin(), files.end(), detail::comparator());\n            }\n            break;\n\n        case SFalseNoListing:\n            BOOST_THROW_EXCEPTION(comet::com_error(S_FALSE));\n\n        case AbortListing:\n            BOOST_THROW_EXCEPTION(comet::com_error(E_ABORT));\n\n        case FailListing:\n            BOOST_THROW_EXCEPTION(comet::com_error(E_FAIL));\n\n        default:\n            BOOST_THROW_EXCEPTION(comet::com_error(\n                \"Unreachable: Unrecognised mock behaviour\", E_UNEXPECTED));\n        }\n\n        return files;\n    }\n\n    virtual comet::com_ptr<IStream> get_file(\n        const ssh::filesystem::path& file_path, std::ios_base::openmode /*mode*/)\n    {\n        detail::find_location_from_path(\n            m_filesystem, file_path); // test existence\n\n        // Create IStream instance whose data is the file path\n        return ::SHCreateMemStream(\n            reinterpret_cast<const BYTE*>(file_path.wstring().c_str()),\n            static_cast<UINT>((file_path.wstring().size() + 1) * sizeof(wchar_t)));\n    }\n\n    virtual VARIANT_BOOL rename(\n        ISftpConsumer* consumer, const ssh::filesystem::path& from_path,\n        const ssh::filesystem::path& to_path)\n    {\n        detail::find_location_from_path(\n            m_filesystem, from_path); // test existence\n\n        switch (m_rename_behaviour)\n        {\n        case RenameOK:\n            return VARIANT_FALSE;\n\n        case ConfirmOverwrite:\n            {\n                HRESULT hr = consumer->OnConfirmOverwrite(\n                        comet::bstr_t(from_path).in(),\n                        comet::bstr_t(to_path).in());\n                if (SUCCEEDED(hr))\n                    return VARIANT_TRUE;\n                BOOST_THROW_EXCEPTION(\n                    comet::com_error_from_interface<ISftpConsumer>(\n                        consumer, hr));\n            }\n\n        case AbortRename:\n            BOOST_THROW_EXCEPTION(comet::com_error(E_ABORT));\n\n        case FailRename:\n            BOOST_THROW_EXCEPTION(comet::com_error(E_FAIL));\n\n        default:\n            BOOST_THROW_EXCEPTION(comet::com_error(\n                \"Unreachable: Unrecognised mock behaviour\", E_UNEXPECTED));\n        }\n    }\n\n    virtual void remove_all(const ssh::filesystem::path& /*path*/)\n    {};\n\n    virtual void create_new_directory(const ssh::filesystem::path& /*path*/)\n    {};\n\n    virtual ssh::filesystem::path resolve_link(\n        const ssh::filesystem::path& path)\n    {\n        std::wstring p(path.wstring());\n\n        // link names with 'broken' in their name we pretend to resolve to\n        // a target that doesn't exist\n        if (p.find(L\"broken\") != std::wstring::npos)\n            return ssh::filesystem::path(L\"/tmp/broken_link_target\");\n\n        // link names with 'folder' in their name we pretend target a directory\n        // (/tmp/testtmpfolder) and the others we target at a file\n        // (/tmp/testfile)\n        else if (p.find(L\"folder\") != std::wstring::npos)\n            return ssh::filesystem::path(L\"/tmp/Testtmpfolder\");\n        else\n            return ssh::filesystem::path(L\"/tmp/testtmpfile\");\n    };\n\n    virtual swish::provider::sftp_filesystem_item stat(\n        const ssh::filesystem::path& path, bool follow_links)\n    {\n        ssh::filesystem::path target;\n        if (follow_links)\n        {\n            target = resolve_link(path);\n        }\n        else\n        {\n            target = path;\n        }\n\n        detail::FilesystemLocation dir =\n            detail::find_location_from_path(m_filesystem, target);\n\n        return *dir;\n    }\n\nprivate:\n\n    detail::Filesystem m_filesystem;\n    ListingBehaviour m_listing_behaviour;\n    RenameBehaviour m_rename_behaviour;\n};\n\n} // namespace test\n\n#endif\n"
  },
  {
    "path": "test/common_boost/SwishPidlFixture.hpp",
    "content": "// Copyright 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef TEST_COMMON_SWISH_PIDL_FIXTURE_HPP\n#define TEST_COMMON_SWISH_PIDL_FIXTURE_HPP\n\n#include <swish/host_folder/host_pidl.hpp>     // create_host_itemid\n#include <swish/remote_folder/remote_pidl.hpp> // create_remote_itemid\n\n#include <comet/datetime.h> // datetime_t\n\n#include <washer/shell/pidl.hpp>  // apidl_t, cpidl_t\n#include <washer/shell/shell.hpp> // pidl_from_parsing_name\n\n#include <string>\n\nnamespace test\n{\n\nclass SwishPidlFixture\n{\npublic:\n    /**\n     * Return a PIDL pretending to be the Swish HostFolder in Explorer.\n     */\n    washer::shell::pidl::apidl_t fake_swish_pidl()\n    {\n\n#include <pshpack1.h>\n        struct fake_swish_item_id\n        {\n            USHORT cb;\n            WCHAR some_data[3];\n        };\n\n        struct fake_swish_item_template\n        {\n            fake_swish_item_id id;\n            SHITEMID terminator;\n        };\n#include <poppack.h>\n\n        fake_swish_item_template item;\n        std::memset(&item, 0, sizeof(item));\n        item.id.cb = sizeof(item.id);\n        assert(item.terminator.cb == 0);\n\n        // The full parsing name of the Swish pidl is\n        // ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\n        // ::{B816A83A-5022-11DC-9153-0090F5284F85} but we can't rely on the\n        // second part existing because Swish might not be registered on this\n        // machine.  So we make a fake one.\n        return washer::shell::pidl_from_parsing_name(\n                   L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\") +\n               washer::shell::pidl::cpidl_t(\n                   reinterpret_cast<PCITEMID_CHILD>(&item));\n    }\n\n    /**\n     * Return the PIDL of the Swish HostFolder in Explorer.\n     *\n     * Relies on Swish having been registered.\n     */\n    washer::shell::pidl::apidl_t real_swish_pidl()\n    {\n        return washer::shell::pidl_from_parsing_name(\n            L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n            L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\");\n    }\n\n    washer::shell::pidl::cpidl_t\n    create_dummy_remote_itemid(const std::wstring& filename, bool is_folder)\n    {\n        return swish::remote_folder::create_remote_itemid(\n            filename, is_folder, false, L\"bobuser\", L\"bob's group\", 1001, 65535,\n            040666, 18446744073709551615,\n            comet::datetime_t(1970, 11, 1, 9, 15, 42, 6),\n            comet::datetime_t((DATE)0));\n    }\n\n    /**\n     * Get an absolute PIDL that ends in a HOSTPIDL to root RemoteFolder on.\n     */\n    washer::shell::pidl::apidl_t create_dummy_root_host_pidl()\n    {\n        return fake_swish_pidl() +\n               swish::host_folder::create_host_itemid(\n                   L\"test.example.com\", L\"user\", L\"/tmp\", 22, L\"Test PIDL\");\n    }\n\n    /**\n     * Get an absolute PIDL that ends in a REMOTEPIDL to root RemoteFolder on.\n     */\n    washer::shell::pidl::apidl_t create_dummy_root_pidl()\n    {\n        return create_dummy_root_host_pidl() +\n               create_dummy_remote_itemid(L\"swish\", true);\n        // Some (older) tests rely on the name being \"swish\" here\n    }\n};\n}\n\n#endif\n"
  },
  {
    "path": "test/common_boost/data_object_utils.cpp",
    "content": "/**\n    @file\n\n    Helper functions for tests that involve DataObjects.\n\n    @if license\n\n    Copyright (C) 2009, 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"data_object_utils.hpp\"\n\n#include \"swish/shell/shell.hpp\"\n\n#include <washer/shell/shell.hpp> // bind_to_handler_object\n\n#include <comet/error.h> // com_error\n\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp>  // BOOST_THROW_EXCEPTION\n#include <boost/numeric/conversion/cast.hpp>  // numeric_cast\n#include <boost/system/system_error.hpp>  // system_error, get_system_category\n\n#include <string>\n#include <vector>\n\nusing swish::shell::pidl_from_path;\nusing swish::shell::ui_object_of_items;\n\nusing washer::shell::bind_to_handler_object;\n\nusing boost::filesystem::path;\nusing boost::shared_ptr;\nusing boost::numeric_cast;\nusing boost::system::get_system_category;\nusing boost::system::system_error;\n\nusing comet::com_error_from_interface;\nusing comet::com_ptr;\n\nusing std::wstring;\nusing std::vector;\n\nnamespace {\n\n    /**\n     * Return the path of the currently running executable.\n     */\n    path get_module_path(HMODULE hmodule=NULL)\n    {\n        vector<wchar_t> buffer(MAX_PATH);\n        unsigned long len = ::GetModuleFileNameW(\n            hmodule, &buffer[0], numeric_cast<unsigned long>(buffer.size()));\n\n        if (len == 0)\n            BOOST_THROW_EXCEPTION(\n                system_error(::GetLastError(), get_system_category()));\n\n        return wstring(&buffer[0], len);\n    }\n\n}\n\nnamespace test {\nnamespace data_object_utils {\n\n/**\n * Create a zip archive containing two files that we can use as a source\n * of 'virtual' namespace items.\n *\n * Virtual namespace items are not real files on disk and instead are\n * simulated by an IShellFolder implementation.  This is how Swish\n * itself presents its 'files' to Explorer.  The ZIP-file browser in\n * Windows 2000 and later does the same thing to give access to the\n * files inside a .zip.  We're going to use one of these to test our\n * shell data object wrapper with virtual items.\n */\npath create_test_zip_file(const path& in_directory)\n{\n    path source = get_module_path().parent_path()\n        / L\"test_zip_file.zip\";\n    path destination = in_directory / L\"test_zip_file.zip\";\n\n    copy_file(source, destination);\n\n    return destination;\n}\n\n/**\n * Return a DataObject with the contents of a zip file.\n */\ncom_ptr<IDataObject> data_object_for_zipfile(const path& zip_file)\n{\n    shared_ptr<ITEMIDLIST_ABSOLUTE> zip_pidl = pidl_from_path(zip_file);\n    com_ptr<IShellFolder> zip_folder =\n        bind_to_handler_object<IShellFolder>(zip_pidl.get());\n\n    com_ptr<IEnumIDList> enum_items;\n    HRESULT hr = zip_folder->EnumObjects(\n        NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, enum_items.out());\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(com_error_from_interface(zip_folder, hr));\n\n    enum_items->Reset();\n\n    vector<shared_ptr<ITEMIDLIST_ABSOLUTE> > pidls;\n    while (hr == S_OK)\n    {\n        PITEMID_CHILD pidl;\n        hr = enum_items->Next(1, &pidl, NULL);\n        if (hr == S_OK)\n        {\n            shared_ptr<ITEMID_CHILD> child_pidl(pidl, ::ILFree);\n\n            pidls.push_back(\n                shared_ptr<ITEMIDLIST_ABSOLUTE>(\n                    ::ILCombine(zip_pidl.get(), child_pidl.get()),\n                    ::ILFree));\n        }\n    }\n\n    return ui_object_of_items<IDataObject>(pidls.begin(), pidls.end());\n}\n\n}} // namespace test::data_object_utils\n"
  },
  {
    "path": "test/common_boost/data_object_utils.hpp",
    "content": "/**\n    @file\n\n    Helper functions for tests that involve DataObjects.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <boost/filesystem.hpp>  // path\n\n#include <comet/ptr.h>  // com_ptr\n\n#include <ObjIdl.h>  // IDataObject\n\nnamespace test {\nnamespace data_object_utils {\n\nboost::filesystem::path create_test_zip_file(\n    const boost::filesystem::path& in_directory);\n\ncomet::com_ptr<IDataObject> data_object_for_zipfile(\n    const boost::filesystem::path& zip_file);\n\n}} // namespace test::data_object_utils\n"
  },
  {
    "path": "test/common_boost/fixtures.hpp",
    "content": "// Copyright 2009, 2012, 2013, 2014, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include <boost/test/unit_test.hpp>\n#include <boost/system/system_error.hpp> // For system_error\n\n#include <Winsock2.h> // For WSAStartup/Cleanup\n#include <objbase.h>  // For CoInitialize/Uninitialize\n\n#include <string>\n\nnamespace test\n{\n\n/**\n * Fixture that initialises and uninitialises COM.\n */\nclass ComFixture\n{\npublic:\n    ComFixture()\n    {\n        HRESULT hr = ::CoInitialize(NULL);\n        BOOST_WARN_MESSAGE(SUCCEEDED(hr), \"::CoInitialize failed\");\n    }\n\n    virtual ~ComFixture()\n    {\n        ::CoUninitialize();\n    }\n};\n\n/**\n * Fixture that initialises and uninitialises Winsock.\n */\nclass WinsockFixture\n{\npublic:\n    WinsockFixture()\n    {\n        WSADATA wsadata;\n        int err = ::WSAStartup(MAKEWORD(2, 2), &wsadata);\n        if (err)\n            throw boost::system::system_error(\n                err, boost::system::get_system_category());\n    }\n\n    virtual ~WinsockFixture()\n    {\n        ::WSACleanup();\n    }\n};\n\n} // namespace test\n"
  },
  {
    "path": "test/common_boost/helpers.hpp",
    "content": "/**\n    @file\n\n    Helper functions for Boost.Test,\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma once\n\n#include <swish/utils.hpp>\n\n#include <comet/error.h>\n\n#include <boost/system/error_code.hpp>\n#include <boost/test/test_tools.hpp> // predicate_result\n#include <boost/filesystem.hpp>\n\n#include <string>\n#include <ostream>\n\nnamespace std {\n\n    inline std::ostream& operator<<(\n        std::ostream& out, const std::wstring& wide_in)\n    {\n        out << swish::utils::WideStringToUtf8String(wide_in);\n        return out;\n    }\n\n    inline std::ostream& operator<<(\n        std::ostream& out, const wchar_t* wide_in)\n    {\n        out << std::wstring(wide_in);\n        return out;\n    }\n\n    inline std::ostream& operator<<(\n        std::ostream& out, const boost::filesystem::path& path)\n    {\n        out << path.string();\n        return out;\n    }\n}\n\nnamespace test {\nnamespace detail {\n\ninline boost::test_tools::predicate_result s_ok(HRESULT hr)\n{\n    if (hr == S_OK)\n    {\n        boost::test_tools::predicate_result res(true);\n        res.message() << \"COM status code was S_OK\";\n        return res;\n    }\n    else\n    {\n        boost::test_tools::predicate_result res(false);\n        res.message() << \"COM status code was not S_OK: \";\n        res.message() << comet::com_error(hr).s_str();\n        return res;\n    }\n}\n\ntemplate<typename Itf>\ninline boost::test_tools::predicate_result s_ok_error_info(\n    comet::com_ptr<Itf> failure_source, HRESULT hr)\n{\n    if (hr == S_OK)\n    {\n        boost::test_tools::predicate_result res(true);\n        res.message() << \"COM status code was S_OK\";\n        return res;\n    }\n    else\n    {\n        boost::test_tools::predicate_result res(false);\n        res.message() << \"COM status code was not S_OK: \";\n        res.message() <<\n            comet::com_error_from_interface(failure_source, hr).s_str();\n        return res;\n    }\n}\n\n}\n}\n\n/**\n * COM HRESULT-specific assertions\n * @{\n */\n#define BOOST_REQUIRE_OK(hr) BOOST_REQUIRE(test::detail::s_ok( (hr) ))\n#define BOOST_CHECK_OK(hr) BOOST_CHECK(test::detail::s_ok( (hr) ))\n#define BOOST_WARN_OK(hr) BOOST_WARN(test::detail::s_ok( (hr) ))\n\n#define BOOST_REQUIRE_INTERFACE_OK(failure_source, hr) \\\n    BOOST_REQUIRE(test::detail::s_ok_error_info( (failure_source), (hr) ))\n#define BOOST_CHECK_INTERFACE_OK(failure_source, hr) \\\n    BOOST_CHECK(test::detail::s_ok_error_info( (failure_source), (hr) ))\n#define BOOST_WARN_INTERFACE_OK(failure_source, hr) \\\n    BOOST_WARN(test::detail::s_ok_error_info( (failure_source), (hr) ))\n/* @} */\n"
  },
  {
    "path": "test/common_boost/stream_utils.cpp",
    "content": "/**\n    @file\n\n    Helper functions for tests that involve IStreams.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"test/common_boost/helpers.hpp\" // BOOST_REQUIRE_OK\n\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/test/test_tools.hpp> // BOOST_CHECK, BOOST_REQUIRE\n\nusing boost::numeric_cast;\n\nusing comet::com_ptr;\n\nnamespace test {\nnamespace stream_utils {\n\nnamespace {\n\nsize_t verify_stream_read_(\n    void* data, ULONG data_size, com_ptr<IStream> stream)\n{\n    ULONG total_bytes_read = 0;\n    HRESULT hr = E_FAIL;\n    do {\n        ULONG bytes_requested = data_size - total_bytes_read;\n        ULONG bytes_read = 0;\n\n        hr = stream->Read(\n            static_cast<unsigned char*>(data) + total_bytes_read,\n            bytes_requested, &bytes_read);\n        if (hr == S_OK)\n        {\n            // S_OK indicates a complete read so make sure this read\n            // however many bytes were left from any previous (possibly\n            // none) short reads\n            BOOST_REQUIRE_EQUAL(bytes_read, bytes_requested);\n            return total_bytes_read + bytes_read;\n        }\n        else if (hr == S_FALSE)\n        {\n            // S_FALSE indicated a 'short' read so make sure it really\n            // is short\n            BOOST_CHECK_LT(bytes_read, bytes_requested);\n            total_bytes_read += bytes_read;\n            break; // end of file\n        }\n        else\n        {\n            // not really requiring S_OK; S_FALSE is fine too\n            BOOST_REQUIRE_OK(hr);\n        }\n    } while (hr == S_OK && (total_bytes_read < data_size));\n\n    // Trying to read more should succeed but return 0 bytes read\n    char buf[10];\n    ULONG should_remain_zero = 0;\n    BOOST_REQUIRE(\n        SUCCEEDED(stream->Read(buf, sizeof(buf), &should_remain_zero)));\n    BOOST_REQUIRE_EQUAL(should_remain_zero, 0U);\n\n    return total_bytes_read;\n}\n\n}\n\nsize_t verify_stream_read(\n    void* data, size_t data_size, com_ptr<IStream> stream)\n{\n    return verify_stream_read_(data, numeric_cast<ULONG>(data_size), stream);\n}\n\n}} // namespace test::stream_utils\n"
  },
  {
    "path": "test/common_boost/stream_utils.hpp",
    "content": "/**\n    @file\n\n    Helper functions for tests that involve IStreams.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef SWISH_TEST_COMMON_BOOST_STREAM_UTILS\n#define SWISH_TEST_COMMON_BOOST_STREAM_UTILS\n\n#include <comet/ptr.h>  // com_ptr\n\n#include <ObjIdl.h>  // IStream\n\nnamespace test {\nnamespace stream_utils {\n\nsize_t verify_stream_read(\n    void* data, size_t data_size, comet::com_ptr<IStream> stream);\n\n}} // namespace test::stream_utils\n\n#endif"
  },
  {
    "path": "test/common_boost/tree.hh",
    "content": "/* \n\n   $Id: tree.hh,v 1.151 2008/05/07 15:46:14 peekas Exp $\n\n   STL-like templated tree class.\n   Copyright (C) 2001-2006  Kasper Peeters <kasper.peeters@aei.mpg.de>.\n\n*/\n\n/** \\mainpage tree.hh\n    \\author   Kasper Peeters\n    \\version  2.62\n    \\date     28-Aug-2008\n    \\see      http://www.aei.mpg.de/~peekas/tree/\n    \\see      http://www.aei.mpg.de/~peekas/tree/ChangeLog\n\n   The tree.hh library for C++ provides an STL-like container class\n   for n-ary trees, templated over the data stored at the\n   nodes. Various types of iterators are provided (post-order,\n   pre-order, and others). Where possible the access methods are\n   compatible with the STL or alternative algorithms are\n   available. \n*/\n\n\n/*\n   The tree.hh code is free software; you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by\n   the Free Software Foundation; version 2 or 3.\n   \n   This program is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n   GNU General Public License for more details.\n   \n   You should have received a copy of the GNU General Public License\n   along with this program; if not, write to the Free Software\n   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n*/\n\n/** \\todo \n   - New-style move members are not completely finished yet.\n   - It would be good to have an iterator which can iterate over all\n     nodes below a given node. Something similar to the leaf iterator\n     we have right now, but not restricted to the leaves.\n   - If a range uses const iter_base& as end iterator, things will\n     inevitably go wrong, because upcast from iter_base to a non-sibling_iter\n     is incorrect. This upcast should be removed (and then all illegal uses\n     as previously in 'equal' will be flagged by the compiler). This requires\n     new copy constructors though.\n   - There's a bug in replace(sibling_iterator, ...) when the ranges\n     sit next to each other. Turned up in append_child(iter,iter)\n     but has been avoided now.\n   - \"std::operator<\" does not work correctly on our iterators, and for some\n     reason a globally defined template operator< did not get picked up. \n     Using a comparison class now, but this should be investigated.\n*/\n\n#ifndef tree_hh_\n#define tree_hh_\n\n#include <cassert>\n#include <memory>\n#include <stdexcept>\n#include <iterator>\n#include <set>\n#include <queue>\n#include <algorithm>\n\n// HP-style construct/destroy have gone from the standard,\n// so here is a copy.\n\nnamespace kp {\n\ntemplate <class T1, class T2>\nvoid constructor(T1* p, T2& val) \n   {\n   new ((void *) p) T1(val);\n   }\n\ntemplate <class T1>\nvoid constructor(T1* p) \n   {\n   new ((void *) p) T1;\n   }\n\ntemplate <class T1>\nvoid destructor(T1* p)\n   {\n   p->~T1();\n   }\n\n};\n\n/// A node in the tree, combining links to other nodes as well as the actual data.\ntemplate<class T>\nclass tree_node_ { // size: 5*4=20 bytes (on 32 bit arch), can be reduced by 8.\n   public:\n      tree_node_<T> *parent;\n      tree_node_<T> *first_child, *last_child;\n      tree_node_<T> *prev_sibling, *next_sibling;\n      T data;\n}; // __attribute__((packed));\n\ntemplate <class T, class tree_node_allocator = std::allocator<tree_node_<T> > >\nclass tree {\n   protected:\n      typedef tree_node_<T> tree_node;\n   public:\n      /// Value of the data stored at a node.\n      typedef T value_type;\n\n      class iterator_base;\n      class pre_order_iterator;\n      class post_order_iterator;\n      class sibling_iterator;\n      class leaf_iterator;\n\n      tree();\n      tree(const T&);\n      tree(const iterator_base&);\n      tree(const tree<T, tree_node_allocator>&);\n      ~tree();\n      void operator=(const tree<T, tree_node_allocator>&);\n\n      /// Base class for iterators, only pointers stored, no traversal logic.\n#ifdef __SGI_STL_PORT\n      class iterator_base : public stlport::bidirectional_iterator<T, ptrdiff_t> {\n#else\n      class iterator_base {\n#endif\n         public:\n            typedef T                               value_type;\n            typedef T*                              pointer;\n            typedef T&                              reference;\n            typedef size_t                          size_type;\n            typedef ptrdiff_t                       difference_type;\n            typedef std::bidirectional_iterator_tag iterator_category;\n\n            iterator_base();\n            iterator_base(tree_node *);\n\n            T&             operator*() const;\n            T*             operator->() const;\n\n            /// When called, the next increment/decrement skips children of this node.\n            void         skip_children();\n            /// Number of children of the node pointed to by the iterator.\n            unsigned int number_of_children() const;\n\n            sibling_iterator begin() const;\n            sibling_iterator end() const;\n\n            tree_node *node;\n         protected:\n            bool skip_current_children_;\n      };\n\n      /// Depth-first iterator, first accessing the node, then its children.\n      class pre_order_iterator : public iterator_base { \n         public:\n            pre_order_iterator();\n            pre_order_iterator(tree_node *);\n            pre_order_iterator(const iterator_base&);\n            pre_order_iterator(const sibling_iterator&);\n\n            bool    operator==(const pre_order_iterator&) const;\n            bool    operator!=(const pre_order_iterator&) const;\n            pre_order_iterator&  operator++();\n            pre_order_iterator&  operator--();\n            pre_order_iterator   operator++(int);\n            pre_order_iterator   operator--(int);\n            pre_order_iterator&  operator+=(unsigned int);\n            pre_order_iterator&  operator-=(unsigned int);\n      };\n\n      /// Depth-first iterator, first accessing the children, then the node itself.\n      class post_order_iterator : public iterator_base {\n         public:\n            post_order_iterator();\n            post_order_iterator(tree_node *);\n            post_order_iterator(const iterator_base&);\n            post_order_iterator(const sibling_iterator&);\n\n            bool    operator==(const post_order_iterator&) const;\n            bool    operator!=(const post_order_iterator&) const;\n            post_order_iterator&  operator++();\n            post_order_iterator&  operator--();\n            post_order_iterator   operator++(int);\n            post_order_iterator   operator--(int);\n            post_order_iterator&  operator+=(unsigned int);\n            post_order_iterator&  operator-=(unsigned int);\n\n            /// Set iterator to the first child as deep as possible down the tree.\n            void descend_all();\n      };\n\n      /// Breadth-first iterator, using a queue\n      class breadth_first_queued_iterator : public iterator_base {\n         public:\n            breadth_first_queued_iterator();\n            breadth_first_queued_iterator(tree_node *);\n            breadth_first_queued_iterator(const iterator_base&);\n\n            bool    operator==(const breadth_first_queued_iterator&) const;\n            bool    operator!=(const breadth_first_queued_iterator&) const;\n            breadth_first_queued_iterator&  operator++();\n            breadth_first_queued_iterator   operator++(int);\n            breadth_first_queued_iterator&  operator+=(unsigned int);\n\n         private:\n            std::queue<tree_node *> traversal_queue;\n      };\n\n      /// The default iterator types throughout the tree class.\n      typedef pre_order_iterator            iterator;\n      typedef breadth_first_queued_iterator breadth_first_iterator;\n\n      /// Iterator which traverses only the nodes at a given depth from the root.\n      class fixed_depth_iterator : public iterator_base {\n         public:\n            fixed_depth_iterator();\n            fixed_depth_iterator(tree_node *);\n            fixed_depth_iterator(const iterator_base&);\n            fixed_depth_iterator(const sibling_iterator&);\n            fixed_depth_iterator(const fixed_depth_iterator&);\n\n            bool    operator==(const fixed_depth_iterator&) const;\n            bool    operator!=(const fixed_depth_iterator&) const;\n            fixed_depth_iterator&  operator++();\n            fixed_depth_iterator&  operator--();\n            fixed_depth_iterator   operator++(int);\n            fixed_depth_iterator   operator--(int);\n            fixed_depth_iterator&  operator+=(unsigned int);\n            fixed_depth_iterator&  operator-=(unsigned int);\n\n            tree_node *top_node;\n      };\n\n      /// Iterator which traverses only the nodes which are siblings of each other.\n      class sibling_iterator : public iterator_base {\n         public:\n            sibling_iterator();\n            sibling_iterator(tree_node *);\n            sibling_iterator(const sibling_iterator&);\n            sibling_iterator(const iterator_base&);\n\n            bool    operator==(const sibling_iterator&) const;\n            bool    operator!=(const sibling_iterator&) const;\n            sibling_iterator&  operator++();\n            sibling_iterator&  operator--();\n            sibling_iterator   operator++(int);\n            sibling_iterator   operator--(int);\n            sibling_iterator&  operator+=(unsigned int);\n            sibling_iterator&  operator-=(unsigned int);\n\n            tree_node *range_first() const;\n            tree_node *range_last() const;\n            tree_node *parent_;\n         private:\n            void set_parent_();\n      };\n\n      /// Iterator which traverses only the leaves.\n      class leaf_iterator : public iterator_base {\n         public:\n            leaf_iterator();\n            leaf_iterator(tree_node *, tree_node *top=0);\n            leaf_iterator(const sibling_iterator&);\n            leaf_iterator(const iterator_base&);\n\n            bool    operator==(const leaf_iterator&) const;\n            bool    operator!=(const leaf_iterator&) const;\n            leaf_iterator&  operator++();\n            leaf_iterator&  operator--();\n            leaf_iterator   operator++(int);\n            leaf_iterator   operator--(int);\n            leaf_iterator&  operator+=(unsigned int);\n            leaf_iterator&  operator-=(unsigned int);\n         private:\n            tree_node *top_node;\n      };\n\n      /// Return iterator to the beginning of the tree.\n      inline pre_order_iterator   begin() const;\n      /// Return iterator to the end of the tree.\n      inline pre_order_iterator   end() const;\n      /// Return post-order iterator to the beginning of the tree.\n      post_order_iterator  begin_post() const;\n      /// Return post-order end iterator of the tree.\n      post_order_iterator  end_post() const;\n      /// Return fixed-depth iterator to the first node at a given depth from the given iterator.\n      fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int) const;\n      /// Return fixed-depth end iterator.\n      fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const;\n      /// Return breadth-first iterator to the first node at a given depth.\n      breadth_first_queued_iterator begin_breadth_first() const;\n      /// Return breadth-first end iterator.\n      breadth_first_queued_iterator end_breadth_first() const;\n      /// Return sibling iterator to the first child of given node.\n      sibling_iterator     begin(const iterator_base&) const;\n      /// Return sibling end iterator for children of given node.\n      sibling_iterator     end(const iterator_base&) const;\n      /// Return leaf iterator to the first leaf of the tree.\n      leaf_iterator   begin_leaf() const;\n      /// Return leaf end iterator for entire tree.\n      leaf_iterator   end_leaf() const;\n      /// Return leaf iterator to the first leaf of the subtree at the given node.\n      leaf_iterator   begin_leaf(const iterator_base& top) const;\n      /// Return leaf end iterator for the subtree at the given node.\n      leaf_iterator   end_leaf(const iterator_base& top) const;\n\n      /// Return iterator to the parent of a node.\n      template<typename iter> static iter parent(iter);\n      /// Return iterator to the previous sibling of a node.\n      template<typename iter> iter previous_sibling(iter) const;\n      /// Return iterator to the next sibling of a node.\n      template<typename iter> iter next_sibling(iter) const;\n      /// Return iterator to the next node at a given depth.\n      template<typename iter> iter next_at_same_depth(iter) const;\n\n      /// Erase all nodes of the tree.\n      void     clear();\n      /// Erase element at position pointed to by iterator, return incremented iterator.\n      template<typename iter> iter erase(iter);\n      /// Erase all children of the node pointed to by iterator.\n      void     erase_children(const iterator_base&);\n\n      /// Insert empty node as last/first child of node pointed to by position.\n      template<typename iter> iter append_child(iter position); \n      template<typename iter> iter prepend_child(iter position); \n      /// Insert node as last/first child of node pointed to by position.\n      template<typename iter> iter append_child(iter position, const T& x);\n      template<typename iter> iter prepend_child(iter position, const T& x);\n      /// Append the node (plus its children) at other_position as last/first child of position.\n      template<typename iter> iter append_child(iter position, iter other_position);\n      template<typename iter> iter prepend_child(iter position, iter other_position);\n      /// Append the nodes in the from-to range (plus their children) as last/first children of position.\n      template<typename iter> iter append_children(iter position, sibling_iterator from, sibling_iterator to);\n      template<typename iter> iter prepend_children(iter position, sibling_iterator from, sibling_iterator to);\n\n      /// Short-hand to insert topmost node in otherwise empty tree.\n      pre_order_iterator set_head(const T& x);\n      /// Insert node as previous sibling of node pointed to by position.\n      template<typename iter> iter insert(iter position, const T& x);\n      /// Specialisation of previous member.\n      sibling_iterator insert(sibling_iterator position, const T& x);\n      /// Insert node (with children) pointed to by subtree as previous sibling of node pointed to by position.\n      template<typename iter> iter insert_subtree(iter position, const iterator_base& subtree);\n      /// Insert node as next sibling of node pointed to by position.\n      template<typename iter> iter insert_after(iter position, const T& x);\n      /// Insert node (with children) pointed to by subtree as next sibling of node pointed to by position.\n      template<typename iter> iter insert_subtree_after(iter position, const iterator_base& subtree);\n\n      /// Replace node at 'position' with other node (keeping same children); 'position' becomes invalid.\n      template<typename iter> iter replace(iter position, const T& x);\n      /// Replace node at 'position' with subtree starting at 'from' (do not erase subtree at 'from'); see above.\n      template<typename iter> iter replace(iter position, const iterator_base& from);\n      /// Replace string of siblings (plus their children) with copy of a new string (with children); see above\n      sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, \n                               sibling_iterator new_begin,  sibling_iterator new_end); \n\n      /// Move all children of node at 'position' to be siblings, returns position.\n      template<typename iter> iter flatten(iter position);\n      /// Move nodes in range to be children of 'position'.\n      template<typename iter> iter reparent(iter position, sibling_iterator begin, sibling_iterator end);\n      /// Move all child nodes of 'from' to be children of 'position'.\n      template<typename iter> iter reparent(iter position, iter from);\n\n      /// Replace node with a new node, making the old node a child of the new node.\n      template<typename iter> iter wrap(iter position, const T& x);\n\n      /// Move 'source' node (plus its children) to become the next sibling of 'target'.\n      template<typename iter> iter move_after(iter target, iter source);\n      /// Move 'source' node (plus its children) to become the previous sibling of 'target'.\n      template<typename iter> iter move_before(iter target, iter source);\n      sibling_iterator move_before(sibling_iterator target, sibling_iterator source);\n      /// Move 'source' node (plus its children) to become the node at 'target' (erasing the node at 'target').\n      template<typename iter> iter move_ontop(iter target, iter source);\n\n      /// Merge with other tree, creating new branches and leaves only if they are not already present.\n      void     merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, \n                     bool duplicate_leaves=false);\n      /// Sort (std::sort only moves values of nodes, this one moves children as well).\n      void     sort(sibling_iterator from, sibling_iterator to, bool deep=false);\n      template<class StrictWeakOrdering>\n      void     sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false);\n      /// Compare two ranges of nodes (compares nodes as well as tree structure).\n      template<typename iter>\n      bool     equal(const iter& one, const iter& two, const iter& three) const;\n      template<typename iter, class BinaryPredicate>\n      bool     equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const;\n      template<typename iter>\n      bool     equal_subtree(const iter& one, const iter& two) const;\n      template<typename iter, class BinaryPredicate>\n      bool     equal_subtree(const iter& one, const iter& two, BinaryPredicate) const;\n      /// Extract a new tree formed by the range of siblings plus all their children.\n      tree     subtree(sibling_iterator from, sibling_iterator to) const;\n      void     subtree(tree&, sibling_iterator from, sibling_iterator to) const;\n      /// Exchange the node (plus subtree) with its sibling node (do nothing if no sibling present).\n      void     swap(sibling_iterator it);\n      /// Exchange two nodes (plus subtrees)\n      void     swap(iterator, iterator);\n      \n      /// Count the total number of nodes.\n      size_t   size() const;\n      /// Count the total number of nodes below the indicated node (plus one).\n      size_t   size(const iterator_base&) const;\n      /// Check if tree is empty.\n      bool     empty() const;\n      /// Compute the depth to the root or to a fixed other iterator.\n      static int depth(const iterator_base&);\n      static int depth(const iterator_base&, const iterator_base&);\n      /// Determine the maximal depth of the tree. An empty tree has max_depth=-1.\n      int      max_depth() const;\n      /// Determine the maximal depth of the tree with top node at the given position.\n      int      max_depth(const iterator_base&) const;\n      /// Count the number of children of node at position.\n      static unsigned int number_of_children(const iterator_base&);\n      /// Count the number of siblings (left and right) of node at iterator. Total nodes at this level is +1.\n      unsigned int number_of_siblings(const iterator_base&) const;\n      /// Determine whether node at position is in the subtrees with root in the range.\n      bool     is_in_subtree(const iterator_base& position, const iterator_base& begin, \n                             const iterator_base& end) const;\n      /// Determine whether the iterator is an 'end' iterator and thus not actually pointing to a node.\n      bool     is_valid(const iterator_base&) const;\n\n      /// Determine the index of a node in the range of siblings to which it belongs.\n      unsigned int index(sibling_iterator it) const;\n      /// Inverse of 'index': return the n-th child of the node at position.\n      static sibling_iterator  child(const iterator_base& position, unsigned int);\n      \n      /// Comparator class for iterators (compares pointer values; why doesn't this work automatically?)\n      class iterator_base_less {\n         public:\n            bool operator()(const typename tree<T, tree_node_allocator>::iterator_base& one,\n                            const typename tree<T, tree_node_allocator>::iterator_base& two) const\n               {\n               return one.node < two.node;\n               }\n      };\n      tree_node *head, *feet;    // head/feet are always dummy; if an iterator points to them it is invalid\n   private:\n      tree_node_allocator alloc_;\n      void head_initialise_();\n      void copy_(const tree<T, tree_node_allocator>& other);\n\n      /// Comparator class for two nodes of a tree (used for sorting and searching).\n      template<class StrictWeakOrdering>\n      class compare_nodes {\n         public:\n            compare_nodes(StrictWeakOrdering comp) : comp_(comp) {};\n            \n            bool operator()(const tree_node *a, const tree_node *b) \n               {\n               static StrictWeakOrdering comp;\n               return comp(a->data, b->data);\n               }\n         private:\n            StrictWeakOrdering comp_;\n      };\n};\n\n//template <class T, class tree_node_allocator>\n//class iterator_base_less {\n// public:\n//    bool operator()(const typename tree<T, tree_node_allocator>::iterator_base& one,\n//                  const typename tree<T, tree_node_allocator>::iterator_base& two) const\n//       {\n//       txtout << \"operatorclass<\" << one.node < two.node << std::endl;\n//       return one.node < two.node;\n//       }\n//};\n\n// template <class T, class tree_node_allocator>\n// bool operator<(const typename tree<T, tree_node_allocator>::iterator& one,\n//                const typename tree<T, tree_node_allocator>::iterator& two)\n//    {\n//    txtout << \"operator< \" << one.node < two.node << std::endl;\n//    if(one.node < two.node) return true;\n//    return false;\n//    }\n// \n// template <class T, class tree_node_allocator>\n// bool operator==(const typename tree<T, tree_node_allocator>::iterator& one,\n//                const typename tree<T, tree_node_allocator>::iterator& two)\n//    {\n//    txtout << \"operator== \" << one.node == two.node << std::endl;\n//    if(one.node == two.node) return true;\n//    return false;\n//    }\n// \n// template <class T, class tree_node_allocator>\n// bool operator>(const typename tree<T, tree_node_allocator>::iterator_base& one,\n//                const typename tree<T, tree_node_allocator>::iterator_base& two)\n//    {\n//    txtout << \"operator> \" << one.node < two.node << std::endl;\n//    if(one.node > two.node) return true;\n//    return false;\n//    }\n\n\n\n// Tree\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::tree() \n   {\n   head_initialise_();\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::tree(const T& x) \n   {\n   head_initialise_();\n   set_head(x);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::tree(const iterator_base& other)\n   {\n   head_initialise_();\n   set_head((*other));\n   replace(begin(), other);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::~tree()\n   {\n   clear();\n   alloc_.deallocate(head,1);\n   alloc_.deallocate(feet,1);\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::head_initialise_() \n   { \n   head = alloc_.allocate(1,0); // MSVC does not have default second argument \n   feet = alloc_.allocate(1,0);\n\n   head->parent=0;\n   head->first_child=0;\n   head->last_child=0;\n   head->prev_sibling=0; //head;\n   head->next_sibling=feet; //head;\n\n   feet->parent=0;\n   feet->first_child=0;\n   feet->last_child=0;\n   feet->prev_sibling=head;\n   feet->next_sibling=0;\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::operator=(const tree<T, tree_node_allocator>& other)\n   {\n   copy_(other);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::tree(const tree<T, tree_node_allocator>& other)\n   {\n   head_initialise_();\n   copy_(other);\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::copy_(const tree<T, tree_node_allocator>& other) \n   {\n   clear();\n   pre_order_iterator it=other.begin(), to=begin();\n   while(it!=other.end()) {\n      to=insert(to, (*it));\n      it.skip_children();\n      ++it;\n      }\n   to=begin();\n   it=other.begin();\n   while(it!=other.end()) {\n      to=replace(to, it);\n      to.skip_children();\n      it.skip_children();\n      ++to;\n      ++it;\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::clear()\n   {\n   if(head)\n      while(head->next_sibling!=feet)\n         erase(pre_order_iterator(head->next_sibling));\n   }\n\ntemplate<class T, class tree_node_allocator> \nvoid tree<T, tree_node_allocator>::erase_children(const iterator_base& it)\n   {\n// std::cout << \"erase_children \" << it.node << std::endl;\n   if(it.node==0) return;\n\n   tree_node *cur=it.node->first_child;\n   tree_node *prev=0;\n\n   while(cur!=0) {\n      prev=cur;\n      cur=cur->next_sibling;\n      erase_children(pre_order_iterator(prev));\n      kp::destructor(&prev->data);\n      alloc_.deallocate(prev,1);\n      }\n   it.node->first_child=0;\n   it.node->last_child=0;\n// std::cout << \"exit\" << std::endl;\n   }\n\ntemplate<class T, class tree_node_allocator> \ntemplate<class iter>\niter tree<T, tree_node_allocator>::erase(iter it)\n   {\n   tree_node *cur=it.node;\n   assert(cur!=head);\n   iter ret=it;\n   ret.skip_children();\n   ++ret;\n   erase_children(it);\n   if(cur->prev_sibling==0) {\n      cur->parent->first_child=cur->next_sibling;\n      }\n   else {\n      cur->prev_sibling->next_sibling=cur->next_sibling;\n      }\n   if(cur->next_sibling==0) {\n      cur->parent->last_child=cur->prev_sibling;\n      }\n   else {\n      cur->next_sibling->prev_sibling=cur->prev_sibling;\n      }\n\n   kp::destructor(&cur->data);\n   alloc_.deallocate(cur,1);\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator tree<T, tree_node_allocator>::begin() const\n   {\n   return pre_order_iterator(head->next_sibling);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator tree<T, tree_node_allocator>::end() const\n   {\n   return pre_order_iterator(feet);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::breadth_first_queued_iterator tree<T, tree_node_allocator>::begin_breadth_first() const\n   {\n   return breadth_first_queued_iterator(head->next_sibling);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::breadth_first_queued_iterator tree<T, tree_node_allocator>::end_breadth_first() const\n   {\n   return breadth_first_queued_iterator();\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator tree<T, tree_node_allocator>::begin_post() const\n   {\n   tree_node *tmp=head->next_sibling;\n   if(tmp!=feet) {\n      while(tmp->first_child)\n         tmp=tmp->first_child;\n      }\n   return post_order_iterator(tmp);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator tree<T, tree_node_allocator>::end_post() const\n   {\n   return post_order_iterator(feet);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator tree<T, tree_node_allocator>::begin_fixed(const iterator_base& pos, unsigned int dp) const\n   {\n   typename tree<T, tree_node_allocator>::fixed_depth_iterator ret;\n   ret.top_node=pos.node;\n\n   tree_node *tmp=pos.node;\n   unsigned int curdepth=0;\n   while(curdepth<dp) { // go down one level\n      while(tmp->first_child==0) {\n         if(tmp->next_sibling==0) {\n            // try to walk up and then right again\n            do {\n               if(tmp==ret.top_node)\n                  throw std::range_error(\"tree: begin_fixed out of range\");\n               tmp=tmp->parent;\n               if(tmp==0) \n                  throw std::range_error(\"tree: begin_fixed out of range\");\n               --curdepth;\n               } while(tmp->next_sibling==0);\n            }\n         tmp=tmp->next_sibling;\n         }\n      tmp=tmp->first_child;\n      ++curdepth;\n      }\n\n   ret.node=tmp;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator tree<T, tree_node_allocator>::end_fixed(const iterator_base& pos, unsigned int dp) const\n   {\n   assert(1==0); // FIXME: not correct yet: use is_valid() as a temporary workaround \n   tree_node *tmp=pos.node;\n   unsigned int curdepth=1;\n   while(curdepth<dp) { // go down one level\n      while(tmp->first_child==0) {\n         tmp=tmp->next_sibling;\n         if(tmp==0)\n            throw std::range_error(\"tree: end_fixed out of range\");\n         }\n      tmp=tmp->first_child;\n      ++curdepth;\n      }\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::begin(const iterator_base& pos) const\n   {\n   assert(pos.node!=0);\n   if(pos.node->first_child==0) {\n      return end(pos);\n      }\n   return pos.node->first_child;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::end(const iterator_base& pos) const\n   {\n   sibling_iterator ret(0);\n   ret.parent_=pos.node;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::begin_leaf() const\n   {\n   tree_node *tmp=head->next_sibling;\n   if(tmp!=feet) {\n      while(tmp->first_child)\n         tmp=tmp->first_child;\n      }\n   return leaf_iterator(tmp);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::end_leaf() const\n   {\n   return leaf_iterator(feet);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::begin_leaf(const iterator_base& top) const\n   {\n   tree_node *tmp=top.node;\n   while(tmp->first_child)\n       tmp=tmp->first_child;\n   return leaf_iterator(tmp, top.node);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::end_leaf(const iterator_base& top) const\n   {\n   return leaf_iterator(top.node, top.node);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::parent(iter position) \n   {\n   assert(position.node!=0);\n   return iter(position.node->parent);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::previous_sibling(iter position) const\n   {\n   assert(position.node!=0);\n   iter ret(position);\n   ret.node=position.node->prev_sibling;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::next_sibling(iter position) const\n   {\n   assert(position.node!=0);\n   iter ret(position);\n   ret.node=position.node->next_sibling;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::next_at_same_depth(iter position) const\n   {\n   // We make use of a temporary fixed_depth iterator to implement this.\n\n   typename tree<T, tree_node_allocator>::fixed_depth_iterator tmp(position.node);\n\n   ++tmp;\n   return iter(tmp);\n\n// assert(position.node!=0);\n// iter ret(position);\n//\n// if(position.node->next_sibling) {\n//    ret.node=position.node->next_sibling;\n//    }\n// else { \n//    int relative_depth=0;\n//    upper:\n//    do {\n//       ret.node=ret.node->parent;\n//       if(ret.node==0) return ret;\n//       --relative_depth;\n//       } while(ret.node->next_sibling==0);\n//    lower:\n//    ret.node=ret.node->next_sibling;\n//    while(ret.node->first_child==0) {\n//       if(ret.node->next_sibling==0)\n//          goto upper;\n//       ret.node=ret.node->next_sibling;\n//       if(ret.node==0) return ret;\n//       }\n//    while(relative_depth<0 && ret.node->first_child!=0) {\n//       ret.node=ret.node->first_child;\n//       ++relative_depth;\n//       }\n//    if(relative_depth<0) {\n//       if(ret.node->next_sibling==0) goto upper;\n//       else                          goto lower;\n//       }\n//    }\n// return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::append_child(iter position)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   tree_node *tmp=alloc_.allocate(1,0);\n   kp::constructor(&tmp->data);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node;\n   if(position.node->last_child!=0) {\n      position.node->last_child->next_sibling=tmp;\n      }\n   else {\n      position.node->first_child=tmp;\n      }\n   tmp->prev_sibling=position.node->last_child;\n   position.node->last_child=tmp;\n   tmp->next_sibling=0;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::prepend_child(iter position)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   tree_node *tmp=alloc_.allocate(1,0);\n   kp::constructor(&tmp->data);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node;\n   if(position.node->first_child!=0) {\n      position.node->first_child->prev_sibling=tmp;\n      }\n   else {\n      position.node->last_child=tmp;\n      }\n   tmp->next_sibling=position.node->first_child;\n   position.node->prev_child=tmp;\n   tmp->prev_sibling=0;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::append_child(iter position, const T& x)\n   {\n   // If your program fails here you probably used 'append_child' to add the top\n   // node to an empty tree. From version 1.45 the top element should be added\n   // using 'insert'. See the documentation for further information, and sorry about\n   // the API change.\n   assert(position.node!=head);\n   assert(position.node);\n\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, x);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node;\n   if(position.node->last_child!=0) {\n      position.node->last_child->next_sibling=tmp;\n      }\n   else {\n      position.node->first_child=tmp;\n      }\n   tmp->prev_sibling=position.node->last_child;\n   position.node->last_child=tmp;\n   tmp->next_sibling=0;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::prepend_child(iter position, const T& x)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, x);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node;\n   if(position.node->first_child!=0) {\n      position.node->first_child->prev_sibling=tmp;\n      }\n   else {\n      position.node->last_child=tmp;\n      }\n   tmp->next_sibling=position.node->first_child;\n   position.node->first_child=tmp;\n   tmp->prev_sibling=0;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::append_child(iter position, iter other)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   sibling_iterator aargh=append_child(position, value_type());\n   return replace(aargh, other);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::prepend_child(iter position, iter other)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   sibling_iterator aargh=prepend_child(position, value_type());\n   return replace(aargh, other);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::append_children(iter position, sibling_iterator from, sibling_iterator to)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   iter ret=from;\n\n   while(from!=to) {\n      insert_subtree(position.end(), from);\n      ++from;\n      }\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::prepend_children(iter position, sibling_iterator from, sibling_iterator to)\n   {\n   assert(position.node!=head);\n   assert(position.node);\n\n   iter ret=from;\n\n   while(from!=to) {\n      insert_subtree(position.begin(), from);\n      ++from;\n      }\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator tree<T, tree_node_allocator>::set_head(const T& x)\n   {\n   assert(head->next_sibling==feet);\n   return insert(iterator(feet), x);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::insert(iter position, const T& x)\n   {\n   if(position.node==0) {\n      position.node=feet; // Backward compatibility: when calling insert on a null node,\n                          // insert before the feet.\n      }\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, x);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node->parent;\n   tmp->next_sibling=position.node;\n   tmp->prev_sibling=position.node->prev_sibling;\n   position.node->prev_sibling=tmp;\n\n   if(tmp->prev_sibling==0) {\n      if(tmp->parent) // when inserting nodes at the head, there is no parent\n         tmp->parent->first_child=tmp;\n      }\n   else\n      tmp->prev_sibling->next_sibling=tmp;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::insert(sibling_iterator position, const T& x)\n   {\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, x);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->next_sibling=position.node;\n   if(position.node==0) { // iterator points to end of a subtree\n      tmp->parent=position.parent_;\n      tmp->prev_sibling=position.range_last();\n      tmp->parent->last_child=tmp;\n      }\n   else {\n      tmp->parent=position.node->parent;\n      tmp->prev_sibling=position.node->prev_sibling;\n      position.node->prev_sibling=tmp;\n      }\n\n   if(tmp->prev_sibling==0) {\n      if(tmp->parent) // when inserting nodes at the head, there is no parent\n         tmp->parent->first_child=tmp;\n      }\n   else\n      tmp->prev_sibling->next_sibling=tmp;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::insert_after(iter position, const T& x)\n   {\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, x);\n   tmp->first_child=0;\n   tmp->last_child=0;\n\n   tmp->parent=position.node->parent;\n   tmp->prev_sibling=position.node;\n   tmp->next_sibling=position.node->next_sibling;\n   position.node->next_sibling=tmp;\n\n   if(tmp->next_sibling==0) {\n      if(tmp->parent) // when inserting nodes at the head, there is no parent\n         tmp->parent->last_child=tmp;\n      }\n   else {\n      tmp->next_sibling->prev_sibling=tmp;\n      }\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::insert_subtree(iter position, const iterator_base& subtree)\n   {\n   // insert dummy\n   iter it=insert(position, value_type());\n   // replace dummy with subtree\n   return replace(it, subtree);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::insert_subtree_after(iter position, const iterator_base& subtree)\n   {\n   // insert dummy\n   iter it=insert_after(position, value_type());\n   // replace dummy with subtree\n   return replace(it, subtree);\n   }\n\n// template <class T, class tree_node_allocator>\n// template <class iter>\n// iter tree<T, tree_node_allocator>::insert_subtree(sibling_iterator position, iter subtree)\n//    {\n//    // insert dummy\n//    iter it(insert(position, value_type()));\n//    // replace dummy with subtree\n//    return replace(it, subtree);\n//    }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::replace(iter position, const T& x)\n   {\n   kp::destructor(&position.node->data);\n   kp::constructor(&position.node->data, x);\n   return position;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class iter>\niter tree<T, tree_node_allocator>::replace(iter position, const iterator_base& from)\n   {\n   assert(position.node!=head);\n   tree_node *current_from=from.node;\n   tree_node *start_from=from.node;\n   tree_node *current_to  =position.node;\n\n   // replace the node at position with head of the replacement tree at from\n// std::cout << \"warning!\" << position.node << std::endl;\n   erase_children(position);  \n// std::cout << \"no warning!\" << std::endl;\n   tree_node* tmp = alloc_.allocate(1,0);\n   kp::constructor(&tmp->data, (*from));\n   tmp->first_child=0;\n   tmp->last_child=0;\n   if(current_to->prev_sibling==0) {\n      if(current_to->parent!=0)\n         current_to->parent->first_child=tmp;\n      }\n   else {\n      current_to->prev_sibling->next_sibling=tmp;\n      }\n   tmp->prev_sibling=current_to->prev_sibling;\n   if(current_to->next_sibling==0) {\n      if(current_to->parent!=0)\n         current_to->parent->last_child=tmp;\n      }\n   else {\n      current_to->next_sibling->prev_sibling=tmp;\n      }\n   tmp->next_sibling=current_to->next_sibling;\n   tmp->parent=current_to->parent;\n   kp::destructor(&current_to->data);\n   alloc_.deallocate(current_to,1);\n   current_to=tmp;\n   \n   // only at this stage can we fix 'last'\n   tree_node *last=from.node->next_sibling;\n\n   pre_order_iterator toit=tmp;\n   // copy all children\n   do {\n      assert(current_from!=0);\n      if(current_from->first_child != 0) {\n         current_from=current_from->first_child;\n         toit=append_child(toit, current_from->data);\n         }\n      else {\n         while(current_from->next_sibling==0 && current_from!=start_from) {\n            current_from=current_from->parent;\n            toit=parent(toit);\n            assert(current_from!=0);\n            }\n         current_from=current_from->next_sibling;\n         if(current_from!=last) {\n            toit=append_child(parent(toit), current_from->data);\n            }\n         }\n      } while(current_from!=last);\n\n   return current_to;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::replace(\n   sibling_iterator orig_begin, \n   sibling_iterator orig_end, \n   sibling_iterator new_begin, \n   sibling_iterator new_end)\n   {\n   tree_node *orig_first=orig_begin.node;\n   tree_node *new_first=new_begin.node;\n   tree_node *orig_last=orig_first;\n   while((++orig_begin)!=orig_end)\n      orig_last=orig_last->next_sibling;\n   tree_node *new_last=new_first;\n   while((++new_begin)!=new_end)\n      new_last=new_last->next_sibling;\n\n   // insert all siblings in new_first..new_last before orig_first\n   bool first=true;\n   pre_order_iterator ret;\n   while(1==1) {\n      pre_order_iterator tt=insert_subtree(pre_order_iterator(orig_first), pre_order_iterator(new_first));\n      if(first) {\n         ret=tt;\n         first=false;\n         }\n      if(new_first==new_last)\n         break;\n      new_first=new_first->next_sibling;\n      }\n\n   // erase old range of siblings\n   bool last=false;\n   tree_node *next=orig_first;\n   while(1==1) {\n      if(next==orig_last) \n         last=true;\n      next=next->next_sibling;\n      erase((pre_order_iterator)orig_first);\n      if(last) \n         break;\n      orig_first=next;\n      }\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::flatten(iter position)\n   {\n   if(position.node->first_child==0)\n      return position;\n\n   tree_node *tmp=position.node->first_child;\n   while(tmp) {\n      tmp->parent=position.node->parent;\n      tmp=tmp->next_sibling;\n      } \n   if(position.node->next_sibling) {\n      position.node->last_child->next_sibling=position.node->next_sibling;\n      position.node->next_sibling->prev_sibling=position.node->last_child;\n      }\n   else {\n      position.node->parent->last_child=position.node->last_child;\n      }\n   position.node->next_sibling=position.node->first_child;\n   position.node->next_sibling->prev_sibling=position.node;\n   position.node->first_child=0;\n   position.node->last_child=0;\n\n   return position;\n   }\n\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\niter tree<T, tree_node_allocator>::reparent(iter position, sibling_iterator begin, sibling_iterator end)\n   {\n   tree_node *first=begin.node;\n   tree_node *last=first;\n\n   assert(first!=position.node);\n   \n   if(begin==end) return begin;\n   // determine last node\n   while((++begin)!=end) {\n      last=last->next_sibling;\n      }\n   // move subtree\n   if(first->prev_sibling==0) {\n      first->parent->first_child=last->next_sibling;\n      }\n   else {\n      first->prev_sibling->next_sibling=last->next_sibling;\n      }\n   if(last->next_sibling==0) {\n      last->parent->last_child=first->prev_sibling;\n      }\n   else {\n      last->next_sibling->prev_sibling=first->prev_sibling;\n      }\n   if(position.node->first_child==0) {\n      position.node->first_child=first;\n      position.node->last_child=last;\n      first->prev_sibling=0;\n      }\n   else {\n      position.node->last_child->next_sibling=first;\n      first->prev_sibling=position.node->last_child;\n      position.node->last_child=last;\n      }\n   last->next_sibling=0;\n\n   tree_node *pos=first;\n   while(1==1) {\n      pos->parent=position.node;\n      if(pos==last) break;\n      pos=pos->next_sibling;\n      }\n\n   return first;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter> iter tree<T, tree_node_allocator>::reparent(iter position, iter from)\n   {\n   if(from.node->first_child==0) return position;\n   return reparent(position, from.node->first_child, end(from));\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter> iter tree<T, tree_node_allocator>::wrap(iter position, const T& x)\n   {\n   assert(position.node!=0);\n   sibling_iterator fr=position, to=position;\n   ++to;\n   iter ret = insert(position, x);\n   reparent(ret, fr, to);\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter> iter tree<T, tree_node_allocator>::move_after(iter target, iter source)\n   {\n   tree_node *dst=target.node;\n   tree_node *src=source.node;\n   assert(dst);\n   assert(src);\n\n   if(dst==src) return source;\n   if(dst->next_sibling)\n      if(dst->next_sibling==src) // already in the right spot\n         return source;\n\n   // take src out of the tree\n   if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling;\n   else                     src->parent->first_child=src->next_sibling;\n   if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling;\n   else                     src->parent->last_child=src->prev_sibling;\n\n   // connect it to the new point\n   if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src;\n   else                     dst->parent->last_child=src;\n   src->next_sibling=dst->next_sibling;\n   dst->next_sibling=src;\n   src->prev_sibling=dst;\n   src->parent=dst->parent;\n   return src;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter> iter tree<T, tree_node_allocator>::move_before(iter target, iter source)\n   {\n   tree_node *dst=target.node;\n   tree_node *src=source.node;\n   assert(dst);\n   assert(src);\n\n   if(dst==src) return source;\n   if(dst->prev_sibling)\n      if(dst->prev_sibling==src) // already in the right spot\n         return source;\n\n   // take src out of the tree\n   if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling;\n   else                     src->parent->first_child=src->next_sibling;\n   if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling;\n   else                     src->parent->last_child=src->prev_sibling;\n\n   // connect it to the new point\n   if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src;\n   else                     dst->parent->first_child=src;\n   src->prev_sibling=dst->prev_sibling;\n   dst->prev_sibling=src;\n   src->next_sibling=dst;\n   src->parent=dst->parent;\n   return src;\n   }\n\n// specialisation for sibling_iterators\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::move_before(sibling_iterator target, \n                                                                                         sibling_iterator source)\n   {\n   tree_node *dst=target.node;\n   tree_node *src=source.node;\n   tree_node *dst_prev_sibling;\n   if(dst==0) { // must then be an end iterator\n      dst_prev_sibling=target.parent_->last_child;\n      assert(dst_prev_sibling);\n      }\n   else dst_prev_sibling=dst->prev_sibling;\n   assert(src);\n\n   if(dst==src) return source;\n   if(dst_prev_sibling)\n      if(dst_prev_sibling==src) // already in the right spot\n         return source;\n\n   // take src out of the tree\n   if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling;\n   else                     src->parent->first_child=src->next_sibling;\n   if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling;\n   else                     src->parent->last_child=src->prev_sibling;\n\n   // connect it to the new point\n   if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src;\n   else                    target.parent_->first_child=src;\n   src->prev_sibling=dst_prev_sibling;\n   if(dst) {\n      dst->prev_sibling=src;\n      src->parent=dst->parent;\n      }\n   src->next_sibling=dst;\n   return src;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter> iter tree<T, tree_node_allocator>::move_ontop(iter target, iter source)\n   {\n   tree_node *dst=target.node;\n   tree_node *src=source.node;\n   assert(dst);\n   assert(src);\n\n   if(dst==src) return source;\n\n   // remember connection points\n   tree_node *b_prev_sibling=dst->prev_sibling;\n   tree_node *b_next_sibling=dst->next_sibling;\n   tree_node *b_parent=dst->parent;\n\n   // remove target\n   erase(target);\n\n   // take src out of the tree\n   if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling;\n   else                     src->parent->first_child=src->next_sibling;\n   if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling;\n   else                     src->parent->last_child=src->prev_sibling;\n\n   // connect it to the new point\n   if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src;\n   else                  b_parent->first_child=src;\n   if(b_next_sibling!=0) b_next_sibling->prev_sibling=src;\n   else                  b_parent->last_child=src;\n   src->prev_sibling=b_prev_sibling;\n   src->next_sibling=b_next_sibling;\n   src->parent=b_parent;\n   return src;\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::merge(sibling_iterator to1,   sibling_iterator to2,\n                                          sibling_iterator from1, sibling_iterator from2,\n                                          bool duplicate_leaves)\n   {\n   sibling_iterator fnd;\n   while(from1!=from2) {\n      if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found\n         if(from1.begin()==from1.end()) { // full depth reached\n            if(duplicate_leaves)\n               append_child(parent(to1), (*from1));\n            }\n         else { // descend further\n            merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves);\n            }\n         }\n      else { // element missing\n         insert_subtree(to2, from1);\n         }\n      ++from1;\n      }\n   }\n\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::sort(sibling_iterator from, sibling_iterator to, bool deep)\n   {\n   std::less<T> comp;\n   sort(from, to, comp, deep);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <class StrictWeakOrdering>\nvoid tree<T, tree_node_allocator>::sort(sibling_iterator from, sibling_iterator to, \n                                        StrictWeakOrdering comp, bool deep)\n   {\n   if(from==to) return;\n   // make list of sorted nodes\n   // CHECK: if multiset stores equivalent nodes in the order in which they\n   // are inserted, then this routine should be called 'stable_sort'.\n   std::multiset<tree_node *, compare_nodes<StrictWeakOrdering> > nodes(comp);\n   sibling_iterator it=from, it2=to;\n   while(it != to) {\n      nodes.insert(it.node);\n      ++it;\n      }\n   // reassemble\n   --it2;\n\n   // prev and next are the nodes before and after the sorted range\n   tree_node *prev=from.node->prev_sibling;\n   tree_node *next=it2.node->next_sibling;\n   typename std::multiset<tree_node *, compare_nodes<StrictWeakOrdering> >::iterator nit=nodes.begin(), eit=nodes.end();\n   if(prev==0) {\n      if((*nit)->parent!=0) // to catch \"sorting the head\" situations, when there is no parent\n         (*nit)->parent->first_child=(*nit);\n      }\n   else prev->next_sibling=(*nit);\n\n   --eit;\n   while(nit!=eit) {\n      (*nit)->prev_sibling=prev;\n      if(prev)\n         prev->next_sibling=(*nit);\n      prev=(*nit);\n      ++nit;\n      }\n   // prev now points to the last-but-one node in the sorted range\n   if(prev)\n      prev->next_sibling=(*eit);\n\n   // eit points to the last node in the sorted range.\n   (*eit)->next_sibling=next;\n   (*eit)->prev_sibling=prev; // missed in the loop above\n   if(next==0) {\n      if((*eit)->parent!=0) // to catch \"sorting the head\" situations, when there is no parent\n         (*eit)->parent->last_child=(*eit);\n      }\n   else next->prev_sibling=(*eit);\n\n   if(deep) {  // sort the children of each node too\n      sibling_iterator bcs(*nodes.begin());\n      sibling_iterator ecs(*eit);\n      ++ecs;\n      while(bcs!=ecs) {\n         sort(begin(bcs), end(bcs), comp, deep);\n         ++bcs;\n         }\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\nbool tree<T, tree_node_allocator>::equal(const iter& one_, const iter& two, const iter& three_) const\n   {\n   std::equal_to<T> comp;\n   return equal(one_, two, three_, comp);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter>\nbool tree<T, tree_node_allocator>::equal_subtree(const iter& one_, const iter& two_) const\n   {\n   std::equal_to<T> comp;\n   return equal_subtree(one_, two_, comp);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter, class BinaryPredicate>\nbool tree<T, tree_node_allocator>::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const\n   {\n   pre_order_iterator one(one_), three(three_);\n\n// if(one==two && is_valid(three) && three.number_of_children()!=0)\n//    return false;\n   while(one!=two && is_valid(three)) {\n      if(!fun(*one,*three))\n         return false;\n      if(one.number_of_children()!=three.number_of_children()) \n         return false;\n      ++one;\n      ++three;\n      }\n   return true;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntemplate <typename iter, class BinaryPredicate>\nbool tree<T, tree_node_allocator>::equal_subtree(const iter& one_, const iter& two_, BinaryPredicate fun) const\n   {\n   pre_order_iterator one(one_), two(two_);\n\n   if(!fun(*one,*two)) return false;\n   if(number_of_children(one)!=number_of_children(two)) return false;\n   return equal(begin(one),end(one),begin(two),fun);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator> tree<T, tree_node_allocator>::subtree(sibling_iterator from, sibling_iterator to) const\n   {\n   tree tmp;\n   tmp.set_head(value_type());\n   tmp.replace(tmp.begin(), tmp.end(), from, to);\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::subtree(tree& tmp, sibling_iterator from, sibling_iterator to) const\n   {\n   tmp.set_head(value_type());\n   tmp.replace(tmp.begin(), tmp.end(), from, to);\n   }\n\ntemplate <class T, class tree_node_allocator>\nsize_t tree<T, tree_node_allocator>::size() const\n   {\n   size_t i=0;\n   pre_order_iterator it=begin(), eit=end();\n   while(it!=eit) {\n      ++i;\n      ++it;\n      }\n   return i;\n   }\n\ntemplate <class T, class tree_node_allocator>\nsize_t tree<T, tree_node_allocator>::size(const iterator_base& top) const\n   {\n   size_t i=0;\n   pre_order_iterator it=top, eit=top;\n   eit.skip_children();\n   ++eit;\n   while(it!=eit) {\n      ++i;\n      ++it;\n      }\n   return i;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::empty() const\n   {\n   pre_order_iterator it=begin(), eit=end();\n   return (it==eit);\n   }\n\ntemplate <class T, class tree_node_allocator>\nint tree<T, tree_node_allocator>::depth(const iterator_base& it) \n   {\n   tree_node* pos=it.node;\n   assert(pos!=0);\n   int ret=0;\n   while(pos->parent!=0) {\n      pos=pos->parent;\n      ++ret;\n      }\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\nint tree<T, tree_node_allocator>::depth(const iterator_base& it, const iterator_base& root) \n   {\n   tree_node* pos=it.node;\n   assert(pos!=0);\n   int ret=0;\n   while(pos->parent!=0 && pos!=root.node) {\n      pos=pos->parent;\n      ++ret;\n      }\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\nint tree<T, tree_node_allocator>::max_depth() const\n   {\n   int maxd=-1;\n   for(tree_node *it = head->next_sibling; it!=feet; it=it->next_sibling)\n      maxd=std::max(maxd, max_depth(it));\n\n   return maxd;\n   }\n\n\ntemplate <class T, class tree_node_allocator>\nint tree<T, tree_node_allocator>::max_depth(const iterator_base& pos) const\n   {\n   tree_node *tmp=pos.node;\n\n   if(tmp==0 || tmp==head || tmp==feet) return -1;\n\n   int curdepth=0, maxdepth=0;\n   while(true) { // try to walk the bottom of the tree\n      while(tmp->first_child==0) {\n         if(tmp==pos.node) return maxdepth;\n         if(tmp->next_sibling==0) {\n            // try to walk up and then right again\n            do {\n               tmp=tmp->parent;\n               if(tmp==0) return maxdepth;\n               --curdepth;\n               } while(tmp->next_sibling==0);\n            }\n         if(tmp==pos.node) return maxdepth;\n         tmp=tmp->next_sibling;\n         }\n      tmp=tmp->first_child;\n      ++curdepth;\n      maxdepth=std::max(curdepth, maxdepth);\n      } \n   }\n\ntemplate <class T, class tree_node_allocator>\nunsigned int tree<T, tree_node_allocator>::number_of_children(const iterator_base& it) \n   {\n   tree_node *pos=it.node->first_child;\n   if(pos==0) return 0;\n   \n   unsigned int ret=1;\n//   while(pos!=it.node->last_child) {\n//      ++ret;\n//      pos=pos->next_sibling;\n//      }\n   while((pos=pos->next_sibling))\n      ++ret;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\nunsigned int tree<T, tree_node_allocator>::number_of_siblings(const iterator_base& it) const\n   {\n   tree_node *pos=it.node;\n   unsigned int ret=0;\n   // count forward\n   while(pos->next_sibling && \n         pos->next_sibling!=head &&\n         pos->next_sibling!=feet) {\n      ++ret;\n      pos=pos->next_sibling;\n      }\n   // count backward\n   pos=it.node;\n   while(pos->prev_sibling && \n         pos->prev_sibling!=head &&\n         pos->prev_sibling!=feet) {\n      ++ret;\n      pos=pos->prev_sibling;\n      }\n   \n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::swap(sibling_iterator it)\n   {\n   tree_node *nxt=it.node->next_sibling;\n   if(nxt) {\n      if(it.node->prev_sibling)\n         it.node->prev_sibling->next_sibling=nxt;\n      else\n         it.node->parent->first_child=nxt;\n      nxt->prev_sibling=it.node->prev_sibling;\n      tree_node *nxtnxt=nxt->next_sibling;\n      if(nxtnxt)\n         nxtnxt->prev_sibling=it.node;\n      else\n         it.node->parent->last_child=it.node;\n      nxt->next_sibling=it.node;\n      it.node->prev_sibling=nxt;\n      it.node->next_sibling=nxtnxt;\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::swap(iterator one, iterator two)\n   {\n   // if one and two are adjacent siblings, use the sibling swap\n   if(one.node->next_sibling==two.node) swap(one);\n   else if(two.node->next_sibling==one.node) swap(two);\n   else {\n      tree_node *nxt1=one.node->next_sibling;\n      tree_node *nxt2=two.node->next_sibling;\n      tree_node *pre1=one.node->prev_sibling;\n      tree_node *pre2=two.node->prev_sibling;\n      tree_node *par1=one.node->parent;\n      tree_node *par2=two.node->parent;\n\n      // reconnect\n      one.node->parent=par2;\n      one.node->next_sibling=nxt2;\n      if(nxt2) nxt2->prev_sibling=one.node;\n      else     par2->last_child=one.node;\n      one.node->prev_sibling=pre2;\n      if(pre2) pre2->next_sibling=one.node;\n      else     par2->first_child=one.node;    \n\n      two.node->parent=par1;\n      two.node->next_sibling=nxt1;\n      if(nxt1) nxt1->prev_sibling=two.node;\n      else     par1->last_child=two.node;\n      two.node->prev_sibling=pre1;\n      if(pre1) pre1->next_sibling=two.node;\n      else     par1->first_child=two.node;\n      }\n   }\n\n// template <class BinaryPredicate>\n// tree<T, tree_node_allocator>::iterator tree<T, tree_node_allocator>::find_subtree(\n//    sibling_iterator subfrom, sibling_iterator subto, iterator from, iterator to, \n//    BinaryPredicate fun) const\n//    {\n//    assert(1==0); // this routine is not finished yet.\n//    while(from!=to) {\n//       if(fun(*subfrom, *from)) {\n//          \n//          }\n//       }\n//    return to;\n//    }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::is_in_subtree(const iterator_base& it, const iterator_base& begin, \n                                                 const iterator_base& end) const\n   {\n   // FIXME: this should be optimised.\n   pre_order_iterator tmp=begin;\n   while(tmp!=end) {\n      if(tmp==it) return true;\n      ++tmp;\n      }\n   return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::is_valid(const iterator_base& it) const\n   {\n   if(it.node==0 || it.node==feet || it.node==head) return false;\n   else return true;\n   }\n\ntemplate <class T, class tree_node_allocator>\nunsigned int tree<T, tree_node_allocator>::index(sibling_iterator it) const\n   {\n   unsigned int ind=0;\n   if(it.node->parent==0) {\n      while(it.node->prev_sibling!=head) {\n         it.node=it.node->prev_sibling;\n         ++ind;\n         }\n      }\n   else {\n      while(it.node->prev_sibling!=0) {\n         it.node=it.node->prev_sibling;\n         ++ind;\n         }\n      }\n   return ind;\n   }\n\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::child(const iterator_base& it, unsigned int num) \n   {\n   tree_node *tmp=it.node->first_child;\n   while(num--) {\n      assert(tmp!=0);\n      tmp=tmp->next_sibling;\n      }\n   return tmp;\n   }\n\n\n\n\n// Iterator base\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::iterator_base::iterator_base()\n   : node(0), skip_current_children_(false)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::iterator_base::iterator_base(tree_node *tn)\n   : node(tn), skip_current_children_(false)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\nT& tree<T, tree_node_allocator>::iterator_base::operator*() const\n   {\n   return node->data;\n   }\n\ntemplate <class T, class tree_node_allocator>\nT* tree<T, tree_node_allocator>::iterator_base::operator->() const\n   {\n   return &(node->data);\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::post_order_iterator::operator!=(const post_order_iterator& other) const\n   {\n   if(other.node!=this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::post_order_iterator::operator==(const post_order_iterator& other) const\n   {\n   if(other.node==this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::pre_order_iterator::operator!=(const pre_order_iterator& other) const\n   {\n   if(other.node!=this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::pre_order_iterator::operator==(const pre_order_iterator& other) const\n   {\n   if(other.node==this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::sibling_iterator::operator!=(const sibling_iterator& other) const\n   {\n   if(other.node!=this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::sibling_iterator::operator==(const sibling_iterator& other) const\n   {\n   if(other.node==this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::leaf_iterator::operator!=(const leaf_iterator& other) const\n   {\n   if(other.node!=this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::leaf_iterator::operator==(const leaf_iterator& other) const\n   {\n   if(other.node==this->node && other.top_node==this->top_node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::iterator_base::begin() const\n   {\n   if(node->first_child==0) \n      return end();\n\n   sibling_iterator ret(node->first_child);\n   ret.parent_=this->node;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::iterator_base::end() const\n   {\n   sibling_iterator ret(0);\n   ret.parent_=node;\n   return ret;\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::iterator_base::skip_children()\n   {\n   skip_current_children_=true;\n   }\n\ntemplate <class T, class tree_node_allocator>\nunsigned int tree<T, tree_node_allocator>::iterator_base::number_of_children() const\n   {\n   tree_node *pos=node->first_child;\n   if(pos==0) return 0;\n   \n   unsigned int ret=1;\n   while(pos!=node->last_child) {\n      ++ret;\n      pos=pos->next_sibling;\n      }\n   return ret;\n   }\n\n\n\n// Pre-order iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::pre_order_iterator::pre_order_iterator() \n   : iterator_base(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::pre_order_iterator::pre_order_iterator(tree_node *tn)\n   : iterator_base(tn)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::pre_order_iterator::pre_order_iterator(const iterator_base &other)\n   : iterator_base(other.node)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::pre_order_iterator::pre_order_iterator(const sibling_iterator& other)\n   : iterator_base(other.node)\n   {\n   if(this->node==0) {\n      if(other.range_last()!=0)\n         this->node=other.range_last();\n      else \n         this->node=other.parent_;\n      this->skip_children();\n      ++(*this);\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator& tree<T, tree_node_allocator>::pre_order_iterator::operator++()\n   {\n   assert(this->node!=0);\n   if(!this->skip_current_children_ && this->node->first_child != 0) {\n      this->node=this->node->first_child;\n      }\n   else {\n      this->skip_current_children_=false;\n      while(this->node->next_sibling==0) {\n         this->node=this->node->parent;\n         if(this->node==0)\n            return *this;\n         }\n      this->node=this->node->next_sibling;\n      }\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator& tree<T, tree_node_allocator>::pre_order_iterator::operator--()\n   {\n   assert(this->node!=0);\n   if(this->node->prev_sibling) {\n      this->node=this->node->prev_sibling;\n      while(this->node->last_child)\n         this->node=this->node->last_child;\n      }\n   else {\n      this->node=this->node->parent;\n      if(this->node==0)\n         return *this;\n      }\n   return *this;\n}\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator tree<T, tree_node_allocator>::pre_order_iterator::operator++(int n)\n   {\n   pre_order_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator tree<T, tree_node_allocator>::pre_order_iterator::operator--(int n)\n{\n  pre_order_iterator copy = *this;\n  --(*this);\n  return copy;\n}\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator& tree<T, tree_node_allocator>::pre_order_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::pre_order_iterator& tree<T, tree_node_allocator>::pre_order_iterator::operator-=(unsigned int num)\n   {\n   while(num>0) {\n      --(*this);\n      --num;\n      }\n   return (*this);\n   }\n\n\n\n// Post-order iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::post_order_iterator::post_order_iterator() \n   : iterator_base(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::post_order_iterator::post_order_iterator(tree_node *tn)\n   : iterator_base(tn)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::post_order_iterator::post_order_iterator(const iterator_base &other)\n   : iterator_base(other.node)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::post_order_iterator::post_order_iterator(const sibling_iterator& other)\n   : iterator_base(other.node)\n   {\n   if(this->node==0) {\n      if(other.range_last()!=0)\n         this->node=other.range_last();\n      else \n         this->node=other.parent_;\n      this->skip_children();\n      ++(*this);\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator& tree<T, tree_node_allocator>::post_order_iterator::operator++()\n   {\n   assert(this->node!=0);\n   if(this->node->next_sibling==0) {\n      this->node=this->node->parent;\n      this->skip_current_children_=false;\n      }\n   else {\n      this->node=this->node->next_sibling;\n      if(this->skip_current_children_) {\n         this->skip_current_children_=false;\n         }\n      else {\n         while(this->node->first_child)\n            this->node=this->node->first_child;\n         }\n      }\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator& tree<T, tree_node_allocator>::post_order_iterator::operator--()\n   {\n   assert(this->node!=0);\n   if(this->skip_current_children_ || this->node->last_child==0) {\n      this->skip_current_children_=false;\n      while(this->node->prev_sibling==0)\n         this->node=this->node->parent;\n      this->node=this->node->prev_sibling;\n      }\n   else {\n      this->node=this->node->last_child;\n      }\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator tree<T, tree_node_allocator>::post_order_iterator::operator++(int)\n   {\n   post_order_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator tree<T, tree_node_allocator>::post_order_iterator::operator--(int)\n   {\n   post_order_iterator copy = *this;\n   --(*this);\n   return copy;\n   }\n\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator& tree<T, tree_node_allocator>::post_order_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::post_order_iterator& tree<T, tree_node_allocator>::post_order_iterator::operator-=(unsigned int num)\n   {\n   while(num>0) {\n      --(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::post_order_iterator::descend_all()\n   {\n   assert(this->node!=0);\n   while(this->node->first_child)\n      this->node=this->node->first_child;\n   }\n\n\n// Breadth-first iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::breadth_first_queued_iterator::breadth_first_queued_iterator()\n   : iterator_base()\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::breadth_first_queued_iterator::breadth_first_queued_iterator(tree_node *tn)\n   : iterator_base(tn)\n   {\n   traversal_queue.push(tn);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other)\n   : iterator_base(other.node)\n   {\n   traversal_queue.push(other.node);\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const\n   {\n   if(other.node!=this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const\n   {\n   if(other.node==this->node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::breadth_first_queued_iterator& tree<T, tree_node_allocator>::breadth_first_queued_iterator::operator++()\n   {\n   assert(this->node!=0);\n\n   // Add child nodes and pop current node\n   sibling_iterator sib=this->begin();\n   while(sib!=this->end()) {\n      traversal_queue.push(sib.node);\n      ++sib;\n      }\n   traversal_queue.pop();\n   if(traversal_queue.size()>0)\n      this->node=traversal_queue.front();\n   else \n      this->node=0;\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::breadth_first_queued_iterator tree<T, tree_node_allocator>::breadth_first_queued_iterator::operator++(int n)\n   {\n   breadth_first_queued_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::breadth_first_queued_iterator& tree<T, tree_node_allocator>::breadth_first_queued_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --num;\n      }\n   return (*this);\n   }\n\n\n\n// Fixed depth iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::fixed_depth_iterator::fixed_depth_iterator()\n   : iterator_base()\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::fixed_depth_iterator::fixed_depth_iterator(tree_node *tn)\n   : iterator_base(tn), top_node(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other)\n   : iterator_base(other.node), top_node(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other)\n   : iterator_base(other.node), top_node(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other)\n   : iterator_base(other.node), top_node(other.top_node)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const\n   {\n   if(other.node==this->node && other.top_node==top_node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\nbool tree<T, tree_node_allocator>::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const\n   {\n   if(other.node!=this->node || other.top_node!=top_node) return true;\n   else return false;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator& tree<T, tree_node_allocator>::fixed_depth_iterator::operator++()\n   {\n   assert(this->node!=0);\n\n   if(this->node->next_sibling) {\n      this->node=this->node->next_sibling;\n      }\n   else { \n      int relative_depth=0;\n      upper:\n      do {\n         if(this->node==this->top_node) {\n            this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented\n            return *this;\n            }\n         this->node=this->node->parent;\n         if(this->node==0) return *this;\n         --relative_depth;\n         } while(this->node->next_sibling==0);\n      lower:\n      this->node=this->node->next_sibling;\n      while(this->node->first_child==0) {\n         if(this->node->next_sibling==0)\n            goto upper;\n         this->node=this->node->next_sibling;\n         if(this->node==0) return *this;\n         }\n      while(relative_depth<0 && this->node->first_child!=0) {\n         this->node=this->node->first_child;\n         ++relative_depth;\n         }\n      if(relative_depth<0) {\n         if(this->node->next_sibling==0) goto upper;\n         else                          goto lower;\n         }\n      }\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator& tree<T, tree_node_allocator>::fixed_depth_iterator::operator--()\n   {\n   assert(this->node!=0);\n\n   if(this->node->prev_sibling) {\n      this->node=this->node->prev_sibling;\n      }\n   else { \n      int relative_depth=0;\n      upper:\n      do {\n         if(this->node==this->top_node) {\n            this->node=0;\n            return *this;\n            }\n         this->node=this->node->parent;\n         if(this->node==0) return *this;\n         --relative_depth;\n         } while(this->node->prev_sibling==0);\n      lower:\n      this->node=this->node->prev_sibling;\n      while(this->node->last_child==0) {\n         if(this->node->prev_sibling==0)\n            goto upper;\n         this->node=this->node->prev_sibling;\n         if(this->node==0) return *this;\n         }\n      while(relative_depth<0 && this->node->last_child!=0) {\n         this->node=this->node->last_child;\n         ++relative_depth;\n         }\n      if(relative_depth<0) {\n         if(this->node->prev_sibling==0) goto upper;\n         else                            goto lower;\n         }\n      }\n   return *this;\n\n//\n//\n// assert(this->node!=0);\n// if(this->node->prev_sibling!=0) {\n//    this->node=this->node->prev_sibling;\n//    assert(this->node!=0);\n//    if(this->node->parent==0 && this->node->prev_sibling==0) // head element\n//       this->node=0;\n//    }\n// else {\n//    tree_node *par=this->node->parent;\n//    do {\n//       par=par->prev_sibling;\n//       if(par==0) { // FIXME: need to keep track of this!\n//          this->node=0;\n//          return *this;\n//          }\n//       } while(par->last_child==0);\n//    this->node=par->last_child;\n//    }\n// return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator tree<T, tree_node_allocator>::fixed_depth_iterator::operator++(int)\n   {\n   fixed_depth_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator tree<T, tree_node_allocator>::fixed_depth_iterator::operator--(int)\n   {\n   fixed_depth_iterator copy = *this;\n   --(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator& tree<T, tree_node_allocator>::fixed_depth_iterator::operator-=(unsigned int num)\n   {\n   while(num>0) {\n      --(*this);\n      --(num);\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::fixed_depth_iterator& tree<T, tree_node_allocator>::fixed_depth_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --(num);\n      }\n   return *this;\n   }\n\n\n// Sibling iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::sibling_iterator::sibling_iterator() \n   : iterator_base()\n   {\n   set_parent_();\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::sibling_iterator::sibling_iterator(tree_node *tn)\n   : iterator_base(tn)\n   {\n   set_parent_();\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::sibling_iterator::sibling_iterator(const iterator_base& other)\n   : iterator_base(other.node)\n   {\n   set_parent_();\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::sibling_iterator::sibling_iterator(const sibling_iterator& other)\n   : iterator_base(other), parent_(other.parent_)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\nvoid tree<T, tree_node_allocator>::sibling_iterator::set_parent_()\n   {\n   parent_=0;\n   if(this->node==0) return;\n   if(this->node->parent!=0)\n      parent_=this->node->parent;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator& tree<T, tree_node_allocator>::sibling_iterator::operator++()\n   {\n   if(this->node)\n      this->node=this->node->next_sibling;\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator& tree<T, tree_node_allocator>::sibling_iterator::operator--()\n   {\n   if(this->node) this->node=this->node->prev_sibling;\n   else {\n      assert(parent_);\n      this->node=parent_->last_child;\n      }\n   return *this;\n}\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::sibling_iterator::operator++(int)\n   {\n   sibling_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator tree<T, tree_node_allocator>::sibling_iterator::operator--(int)\n   {\n   sibling_iterator copy = *this;\n   --(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator& tree<T, tree_node_allocator>::sibling_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::sibling_iterator& tree<T, tree_node_allocator>::sibling_iterator::operator-=(unsigned int num)\n   {\n   while(num>0) {\n      --(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::tree_node *tree<T, tree_node_allocator>::sibling_iterator::range_first() const\n   {\n   tree_node *tmp=parent_->first_child;\n   return tmp;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::tree_node *tree<T, tree_node_allocator>::sibling_iterator::range_last() const\n   {\n   return parent_->last_child;\n   }\n\n// Leaf iterator\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::leaf_iterator::leaf_iterator() \n   : iterator_base(0), top_node(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::leaf_iterator::leaf_iterator(tree_node *tn, tree_node *top)\n   : iterator_base(tn), top_node(top)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::leaf_iterator::leaf_iterator(const iterator_base &other)\n   : iterator_base(other.node), top_node(0)\n   {\n   }\n\ntemplate <class T, class tree_node_allocator>\ntree<T, tree_node_allocator>::leaf_iterator::leaf_iterator(const sibling_iterator& other)\n   : iterator_base(other.node), top_node(0)\n   {\n   if(this->node==0) {\n      if(other.range_last()!=0)\n         this->node=other.range_last();\n      else \n         this->node=other.parent_;\n      ++(*this);\n      }\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator& tree<T, tree_node_allocator>::leaf_iterator::operator++()\n   {\n   assert(this->node!=0);\n   if(this->node->first_child!=0) { // current node is no longer leaf (children got added)\n       while(this->node->first_child) \n           this->node=this->node->first_child;\n       }\n   else {\n       while(this->node->next_sibling==0) { \n           if (this->node->parent==0) return *this;\n           this->node=this->node->parent;\n           if (top_node != 0 && this->node==top_node) return *this;\n           }\n       this->node=this->node->next_sibling;\n       while(this->node->first_child)\n           this->node=this->node->first_child;\n       }\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator& tree<T, tree_node_allocator>::leaf_iterator::operator--()\n   {\n   assert(this->node!=0);\n   while (this->node->prev_sibling==0) {\n      if (this->node->parent==0) return *this;\n      this->node=this->node->parent;\n      if (top_node !=0 && this->node==top_node) return *this; \n      }\n   this->node=this->node->prev_sibling;\n   while(this->node->last_child)\n      this->node=this->node->last_child;\n   return *this;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::leaf_iterator::operator++(int)\n   {\n   leaf_iterator copy = *this;\n   ++(*this);\n   return copy;\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator tree<T, tree_node_allocator>::leaf_iterator::operator--(int)\n   {\n   leaf_iterator copy = *this;\n   --(*this);\n   return copy;\n   }\n\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator& tree<T, tree_node_allocator>::leaf_iterator::operator+=(unsigned int num)\n   {\n   while(num>0) {\n      ++(*this);\n      --num;\n      }\n   return (*this);\n   }\n\ntemplate <class T, class tree_node_allocator>\ntypename tree<T, tree_node_allocator>::leaf_iterator& tree<T, tree_node_allocator>::leaf_iterator::operator-=(unsigned int num)\n   {\n   while(num>0) {\n      --(*this);\n      --num;\n      }\n   return (*this);\n   }\n\n#endif\n\n// Local variables:\n// default-tab-width: 3\n// End:\n"
  },
  {
    "path": "test/common_boost/tree.hpp",
    "content": "/**\n    @file\n\n    Warnings wrapper for tree.hh n-ary tree container by Kasper Peeters.\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef TEST_COMMON_BOOST_TREE_HPP\n#define TEST_COMMON_BOOST_TREE_HPP\n#pragma once\n\n#pragma warning (push)\n#pragma warning (disable: 4100)\n#include \"tree.hh\"\n#pragma warning (pop)\n\n#endif\n"
  },
  {
    "path": "test/connection/CMakeLists.txt",
    "content": "# Copyright (C) 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(UNIT_TESTS\n  connection_spec_test.cpp)\n\nset(INTEGRATION_TESTS\n  authenticated_session_test.cpp\n  connection_spec_create_session_test.cpp\n  running_session_test.cpp\n  session_manager_test.cpp\n  session_pool_test.cpp)\n\nswish_test_suite(\n  SUBJECT connection VARIANT unit\n  SOURCES ${UNIT_TESTS}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n\nswish_test_suite(\n  SUBJECT connection VARIANT integration\n  SOURCES ${INTEGRATION_TESTS}\n  LIBRARIES ${Boost_LIBRARIES} openssh_fixture\n  LABELS integration)\n"
  },
  {
    "path": "test/connection/authenticated_session_test.cpp",
    "content": "// Copyright 2013, 2014, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/authenticated_session.hpp\" // Test subject\n\n#include \"test/common_boost/ConsumerStub.hpp\"\n#include \"test/fixtures/openssh_fixture.hpp\"\n\n#include <boost/date_time/posix_time/posix_time_duration.hpp>\n#include <boost/make_shared.hpp>\n#include <boost/move/move.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/thread.hpp> // this_thread\n\n#include <string>\n#include <vector>\n\nusing test::CConsumerStub;\nusing test::fixtures::openssh_fixture;\n\nusing swish::connection::authenticated_session;\n\nusing boost::make_shared;\nusing boost::move;\nusing boost::shared_ptr;\nusing boost::test_tools::predicate_result;\n\nusing std::vector;\nusing std::wstring;\n\nBOOST_FIXTURE_TEST_SUITE(authenticated_session_tests, openssh_fixture)\n\nnamespace\n{\n\npredicate_result sftp_is_alive(authenticated_session& session)\n{\n    try\n    {\n        session.get_sftp_filesystem().directory_iterator(\"/\");\n        return true;\n    }\n    catch (...)\n    {\n        predicate_result res(false);\n        res.message() << \"SFTP not working; unable to access root directory\";\n        return res;\n    }\n}\n}\n\n/**\n * Test that connecting succeeds.\n */\nBOOST_AUTO_TEST_CASE(connect)\n{\n    authenticated_session session(\n        whost(), port(), wuser(),\n        new CConsumerStub(private_key_path(), public_key_path()));\n    BOOST_CHECK(!session.is_dead());\n    BOOST_CHECK(sftp_is_alive(session));\n}\n\nBOOST_AUTO_TEST_CASE(multiple_connections)\n{\n    vector<shared_ptr<authenticated_session>> sessions;\n    for (int i = 0; i < 5; i++)\n    {\n        sessions.push_back(make_shared<authenticated_session>(\n            whost(), port(), wuser(),\n            new CConsumerStub(private_key_path(), public_key_path())));\n    }\n\n    for (int i = 0; i < 5; i++)\n    {\n        BOOST_CHECK(!sessions.at(i)->is_dead());\n        BOOST_CHECK(sftp_is_alive(*sessions.at(i)));\n    }\n}\n\n/**\n * Test that session reports its death.\n */\nBOOST_AUTO_TEST_CASE(server_death)\n{\n    authenticated_session session(\n        whost(), port(), wuser(),\n        new CConsumerStub(private_key_path(), public_key_path()));\n\n    BOOST_CHECK(sftp_is_alive(session));\n\n    stop_server();\n\n    boost::this_thread::sleep(boost::posix_time::milliseconds(2000));\n\n    BOOST_CHECK(session.is_dead());\n    BOOST_CHECK(!sftp_is_alive(session));\n}\n\n/**\n * Test that session reports its death if server is restarted.\n */\nBOOST_AUTO_TEST_CASE(server_restart)\n{\n    authenticated_session session(\n        whost(), port(), wuser(),\n        new CConsumerStub(private_key_path(), public_key_path()));\n\n    BOOST_CHECK(sftp_is_alive(session));\n\n    restart_server();\n\n    boost::this_thread::sleep(boost::posix_time::milliseconds(2000));\n\n    BOOST_CHECK(session.is_dead());\n    BOOST_CHECK(!sftp_is_alive(session));\n}\n\nnamespace\n{\n\nauthenticated_session move_create(const wstring& host, unsigned int port,\n                                  const wstring& user, ISftpConsumer* consumer)\n{\n    authenticated_session session(host, port, user, consumer);\n    return move(session);\n}\n}\n\nBOOST_AUTO_TEST_CASE(move_contruct)\n{\n    authenticated_session session =\n        move_create(whost(), port(), wuser(),\n                    new CConsumerStub(private_key_path(), public_key_path()));\n    BOOST_CHECK(!session.is_dead());\n    BOOST_CHECK(sftp_is_alive(session));\n}\n\nBOOST_AUTO_TEST_CASE(move_assign)\n{\n    authenticated_session session1(\n        whost(), port(), wuser(),\n        new CConsumerStub(private_key_path(), public_key_path()));\n    authenticated_session session2(\n        whost(), port(), wuser(),\n        new CConsumerStub(private_key_path(), public_key_path()));\n\n    session1 = move(session2);\n\n    BOOST_CHECK(!session1.is_dead());\n    BOOST_CHECK(sftp_is_alive(session1));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/connection/connection_spec_create_session_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/connection_spec.hpp\" // Test subject\n#include \"swish/connection/authenticated_session.hpp\"\n\n#include \"test/common_boost/ConsumerStub.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/fixtures/openssh_fixture.hpp\"\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/test/unit_test.hpp>\n\n#include <exception>\n#include <map>\n\nusing swish::connection::authenticated_session;\nusing swish::connection::connection_spec;\n\nusing test::fixtures::openssh_fixture;\nusing test::CConsumerStub;\n\nusing comet::com_ptr;\n\nusing boost::test_tools::predicate_result;\n\nusing std::exception;\nusing std::map;\n\nnamespace\n{\n\nclass fixture : private openssh_fixture\n{\npublic:\n    connection_spec get_connection()\n    {\n        return connection_spec(whost(), wuser(), port());\n    }\n\n    com_ptr<ISftpConsumer> consumer()\n    {\n        com_ptr<CConsumerStub> consumer =\n            new CConsumerStub(private_key_path(), public_key_path());\n        return consumer;\n    }\n\n    /**\n     * Check that the given session responds sensibly to a request.\n     */\n    predicate_result alive(authenticated_session& session)\n    {\n        try\n        {\n            session.get_sftp_filesystem().directory_iterator(\"/\");\n\n            predicate_result res(true);\n            res.message() << \"Provider seems to be alive\";\n            return res;\n        }\n        catch (const exception& e)\n        {\n            predicate_result res(false);\n            res.message() << \"Provider seems to be dead: \" << e.what();\n            return res;\n        }\n    }\n};\n}\n\nBOOST_FIXTURE_TEST_SUITE(connection_spec_session_create, fixture)\n\nBOOST_AUTO_TEST_CASE(create)\n{\n    authenticated_session session(get_connection().create_session(consumer()));\n    BOOST_CHECK(alive(session));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/connection/connection_spec_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/connection_spec.hpp\" // Test subject\n\n#include \"test/common_boost/helpers.hpp\"\n\n#include <boost/test/unit_test.hpp>\n\n#include <map>\n\nusing swish::connection::connection_spec;\n\nusing std::map;\n\nBOOST_AUTO_TEST_SUITE(connection_spec_comparison)\n\nBOOST_AUTO_TEST_CASE(self)\n{\n    connection_spec s(L\"A\", L\"b\", 12);\n    BOOST_CHECK(!(s < s));\n}\n\nBOOST_AUTO_TEST_CASE(equal)\n{\n    connection_spec s1(L\"A\", L\"b\", 12);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    BOOST_CHECK(!(s1 < s2));\n    BOOST_CHECK(!(s2 < s1));\n}\n\nBOOST_AUTO_TEST_CASE(less_host)\n{\n    connection_spec s1(L\"A\", L\"b\", 12);\n    connection_spec s2(L\"B\", L\"b\", 12);\n    BOOST_CHECK(s1 < s2);\n    BOOST_CHECK(!(s2 < s1));\n}\n\nBOOST_AUTO_TEST_CASE(equal_host_less_user)\n{\n    connection_spec s1(L\"A\", L\"a\", 12);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    BOOST_CHECK(s1 < s2);\n    BOOST_CHECK(!(s2 < s1));\n}\n\nBOOST_AUTO_TEST_CASE(greater_host_less_user)\n{\n    connection_spec s1(L\"B\", L\"a\", 12);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    BOOST_CHECK(!(s1 < s2));\n    BOOST_CHECK(s2 < s1);\n}\n\nBOOST_AUTO_TEST_CASE(equal_host_equal_user_less_port)\n{\n    connection_spec s1(L\"A\", L\"b\", 11);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    BOOST_CHECK(s1 < s2);\n    BOOST_CHECK(!(s2 < s1));\n}\n\nBOOST_AUTO_TEST_CASE(equal_host_greater_user_less_port)\n{\n    connection_spec s1(L\"A\", L\"c\", 11);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    BOOST_CHECK(!(s1 < s2));\n    BOOST_CHECK(s2 < s1);\n}\n\nBOOST_AUTO_TEST_CASE(use_as_map_key_same)\n{\n    connection_spec s1(L\"A\", L\"b\", 12);\n    connection_spec s2(L\"A\", L\"b\", 12);\n    map<connection_spec, int> m;\n    m[s1] = 3;\n    m[s2] = 7;\n    BOOST_CHECK_EQUAL(m.size(), 1);\n    BOOST_CHECK_EQUAL(m[s1], 7);\n    BOOST_CHECK_EQUAL(m[s2], 7);\n}\n\nBOOST_AUTO_TEST_CASE(use_as_map_key_different_user)\n{\n    connection_spec s1(L\"A\", L\"b\", 12);\n    connection_spec s2(L\"A\", L\"a\", 12);\n    map<connection_spec, int> m;\n    m[s1] = 3;\n    m[s2] = 7;\n    BOOST_CHECK_EQUAL(m.size(), 2);\n    BOOST_CHECK_EQUAL(m[s1], 3);\n    BOOST_CHECK_EQUAL(m[s2], 7);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/connection/running_session_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/running_session.hpp\" // Test subject\n\n#include \"test/fixtures/openssh_fixture.hpp\"\n\n#include <boost/make_shared.hpp>\n#include <boost/move/move.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <stdexcept> // runtime_error\n#include <string>\n#include <vector>\n\nusing test::fixtures::openssh_fixture;\n\nusing swish::connection::running_session;\n\nusing boost::make_shared;\nusing boost::move;\nusing boost::shared_ptr;\n\nusing std::runtime_error;\nusing std::vector;\nusing std::wstring;\n\nBOOST_FIXTURE_TEST_SUITE(running_session_tests, openssh_fixture)\n\nBOOST_AUTO_TEST_CASE(connecting_with_correct_host_and_port_succeeds)\n{\n    running_session session(whost(), port());\n    BOOST_CHECK(!session.is_dead());\n}\n\nBOOST_AUTO_TEST_CASE(connection_failure_throws_error)\n{\n    BOOST_CHECK_THROW(running_session(L\"nonsense.invalid\", 65535),\n                      runtime_error);\n}\n\nBOOST_AUTO_TEST_CASE(multiple_connections_do_not_interfere)\n{\n    vector<shared_ptr<running_session>> sessions;\n    for (int i = 0; i < 5; i++)\n    {\n        sessions.push_back(make_shared<running_session>(whost(), port()));\n    }\n\n    for (int i = 0; i < 5; i++)\n    {\n        BOOST_CHECK(!sessions.at(i)->is_dead());\n    }\n}\n\nnamespace\n{\n\nrunning_session move_create(const wstring& host, unsigned int port)\n{\n    running_session session(host, port);\n    return move(session);\n}\n}\n\nBOOST_AUTO_TEST_CASE(session_survives_move_construction)\n{\n    running_session session = move_create(whost(), port());\n    BOOST_CHECK(!session.is_dead());\n}\n\nBOOST_AUTO_TEST_CASE(session_survives_move_assignment)\n{\n    running_session session1(whost(), port());\n    running_session session2(whost(), port());\n\n    session1 = move(session2);\n\n    BOOST_CHECK(!session1.is_dead());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/connection/session_manager_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/session_manager.hpp\" // Test subject\n\n#include \"swish/connection/authenticated_session.hpp\"\n#include \"swish/connection/connection_spec.hpp\"\n\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/fixtures/openssh_fixture.hpp\"\n#include \"test/common_boost/ConsumerStub.hpp\"\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/container/vector.hpp> // move-aware vector\n#include <boost/move/move.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/mutex.hpp>\n#include <boost/thread/thread.hpp>\n\n#include <exception>\n#include <string>\n#include <vector>\n\nusing swish::connection::authenticated_session;\nusing swish::connection::connection_spec;\nusing swish::connection::session_manager;\nusing swish::connection::session_reservation;\n\nusing test::CConsumerStub;\nusing test::fixtures::openssh_fixture;\n\nusing comet::com_ptr;\n\nusing boost::container::vector;\nusing boost::move;\nusing boost::mutex;\nusing boost::shared_ptr;\nusing boost::ref;\nusing boost::test_tools::predicate_result;\nusing boost::thread;\n\nusing std::exception;\nusing std::string;\n\nnamespace\n{ // private\n\n/**\n * Fixture that returns backend connections from the connection pool.\n */\nclass fixture : private openssh_fixture\n{\npublic:\n    connection_spec get_connection()\n    {\n        return connection_spec(whost(), wuser(), port());\n    }\n\n    com_ptr<ISftpConsumer> consumer()\n    {\n        com_ptr<CConsumerStub> consumer =\n            new CConsumerStub(private_key_path(), public_key_path());\n        return consumer;\n    }\n};\n\n/**\n * Check that the given provider responds sensibly to a request.\n */\npredicate_result alive(authenticated_session& session)\n{\n    try\n    {\n        session.get_sftp_filesystem().directory_iterator(\"/\");\n\n        predicate_result res(true);\n        res.message() << \"Session seems to be alive\";\n        return res;\n    }\n    catch (const exception& e)\n    {\n        predicate_result res(false);\n        res.message() << \"Session seems to be dead: \" << e.what();\n        return res;\n    }\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(session_manager_tests, fixture)\n\nBOOST_AUTO_TEST_CASE(new_reservation_are_registered_with_session_manager)\n{\n    connection_spec spec(get_connection());\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    session_reservation ticket =\n        session_manager().reserve_session(spec, consumer(), \"Testing\");\n\n    BOOST_CHECK(session_manager().has_session(spec));\n\n    authenticated_session& session = ticket.session();\n\n    BOOST_CHECK(session_manager().has_session(spec));\n\n    BOOST_CHECK(alive(session));\n}\n\nBOOST_AUTO_TEST_CASE(session_outlives_reservation)\n{\n    connection_spec spec(get_connection());\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    session_manager().reserve_session(spec, consumer(), \"Testing\");\n\n    BOOST_CHECK(session_manager().has_session(spec));\n}\n\nBOOST_AUTO_TEST_CASE(factory_reuses_existing_sessions)\n{\n    connection_spec spec(get_connection());\n\n    session_reservation ticket1 =\n        session_manager().reserve_session(spec, consumer(), \"Testing1\");\n\n    session_reservation ticket2 =\n        session_manager().reserve_session(spec, consumer(), \"Testing2\");\n\n    BOOST_CHECK(&(ticket1.session()) == &(ticket2.session()));\n}\n\nnamespace\n{\nclass progress_callback : boost::noncopyable\n{\npublic:\n    progress_callback(\n        vector<session_reservation> tickets = vector<session_reservation>())\n        : m_releasing_started(false), m_tickets(move(tickets))\n    {\n    }\n\n    template <typename Range>\n    bool operator()(const Range& pending_tasks)\n    {\n        mutex::scoped_lock lock(m_mutex);\n\n        m_notified_task_ranges.push_back(std::vector<string>(\n            boost::begin(pending_tasks), boost::end(pending_tasks)));\n\n        if (!m_releasing_started)\n        {\n            thread(&progress_callback::release_tickets, this);\n\n            m_releasing_started = true;\n        }\n\n        return true;\n    }\n\n    std::vector<std::vector<string>> notifications()\n    {\n        mutex::scoped_lock lock(m_mutex);\n        return m_notified_task_ranges;\n    }\n\nprivate:\n    void release_tickets()\n    {\n        while (!m_tickets.empty())\n        {\n            m_tickets.erase(m_tickets.end() - 1);\n        }\n    }\n\n    mutex m_mutex;\n    bool m_releasing_started;\n\n    // Stores the tickets we need to simulate other task gradually\n    // releasing their reservations on this\n    vector<session_reservation> m_tickets;\n\n    // Stores each range of tasks we are notified of, in the\n    // order we are notified of them\n    std::vector<std::vector<string>> m_notified_task_ranges;\n};\n}\n\nBOOST_AUTO_TEST_CASE(removing_session_really_removes_it)\n{\n    connection_spec spec(get_connection());\n\n    session_manager().reserve_session(spec, consumer(), \"Testing\");\n\n    BOOST_CHECK(session_manager().has_session(spec));\n\n    progress_callback progress;\n    session_manager().disconnect_session(spec, ref(progress));\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    // There should be no pending_task notifications because there were\n    // no tasks with a reservation when we disconnected the session.\n    // The only notification should be the empty task range indicating 'done'\n    BOOST_REQUIRE_EQUAL(progress.notifications().size(), 1U);\n    BOOST_CHECK_EQUAL(progress.notifications()[0].size(), 0U);\n}\n\nBOOST_AUTO_TEST_CASE(removing_session_with_pending_task)\n{\n    connection_spec spec(get_connection());\n\n    vector<session_reservation> tasks;\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing\"));\n\n    progress_callback progress(move(tasks));\n    session_manager().disconnect_session(spec, ref(progress));\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    // The progress should have been notified twice ...\n    BOOST_REQUIRE_EQUAL(progress.notifications().size(), 2U);\n    // ... first with one pending task\n    BOOST_REQUIRE_EQUAL(progress.notifications()[0].size(), 1U);\n    BOOST_CHECK_EQUAL(progress.notifications()[0][0], \"Testing\");\n    // ... then to say it's done\n    BOOST_CHECK_EQUAL(progress.notifications()[1].size(), 0U);\n}\n\nBOOST_AUTO_TEST_CASE(removing_session_with_multiple_pending_tasks)\n{\n    connection_spec spec(get_connection());\n\n    vector<session_reservation> tasks;\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing\"));\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing2\"));\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing3\"));\n\n    progress_callback progress(move(tasks));\n    session_manager().disconnect_session(spec, ref(progress));\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    // The progress should have been notified four times ...\n    BOOST_REQUIRE_EQUAL(progress.notifications().size(), 4U);\n    // ... each time with one less task\n    BOOST_REQUIRE_EQUAL(progress.notifications()[0].size(), 3U);\n    BOOST_CHECK_EQUAL(progress.notifications()[0][0], \"Testing\");\n    BOOST_CHECK_EQUAL(progress.notifications()[0][1], \"Testing2\");\n    BOOST_CHECK_EQUAL(progress.notifications()[0][2], \"Testing3\");\n    BOOST_REQUIRE_EQUAL(progress.notifications()[1].size(), 2U);\n    BOOST_CHECK_EQUAL(progress.notifications()[1][0], \"Testing\");\n    BOOST_CHECK_EQUAL(progress.notifications()[1][1], \"Testing2\");\n    BOOST_REQUIRE_EQUAL(progress.notifications()[2].size(), 1U);\n    BOOST_CHECK_EQUAL(progress.notifications()[2][0], \"Testing\");\n    // ... until it's done\n    BOOST_CHECK_EQUAL(progress.notifications()[3].size(), 0U);\n}\n\nBOOST_AUTO_TEST_CASE(removing_session_with_colliding_task_names)\n{\n    connection_spec spec(get_connection());\n\n    vector<session_reservation> tasks;\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing\"));\n    tasks.push_back(\n        session_manager().reserve_session(spec, consumer(), \"Testing\"));\n\n    progress_callback progress(move(tasks));\n    session_manager().disconnect_session(spec, ref(progress));\n\n    BOOST_CHECK(!session_manager().has_session(spec));\n\n    // The progress should have been notified thrice ...\n    BOOST_REQUIRE_EQUAL(progress.notifications().size(), 3U);\n    // ... each time with one less task\n    BOOST_REQUIRE_EQUAL(progress.notifications()[0].size(), 2U);\n    BOOST_CHECK_EQUAL(progress.notifications()[0][0], \"Testing\");\n    BOOST_CHECK_EQUAL(progress.notifications()[0][1], \"Testing\");\n    BOOST_REQUIRE_EQUAL(progress.notifications()[1].size(), 1U);\n    BOOST_CHECK_EQUAL(progress.notifications()[1][0], \"Testing\");\n    // ... until it's done\n    BOOST_CHECK_EQUAL(progress.notifications()[2].size(), 0U);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/connection/session_pool_test.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2013, 2014, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/connection/session_pool.hpp\" // Test subject\n#include \"swish/connection/connection_spec.hpp\"\n\n#include \"test/common_boost/ConsumerStub.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/fixtures/openssh_fixture.hpp\"\n\n#include <comet/ptr.h>  // com_ptr\n#include <comet/util.h> // thread\n\n#include <boost/test/unit_test.hpp>\n#include <boost/shared_ptr.hpp>                       // shared_ptr\n#include <boost/foreach.hpp>                          // BOOST_FOREACH\n#include <boost/exception/diagnostic_information.hpp> // diagnostic_information\n#include <boost/exception_ptr.hpp>\n\n#include <algorithm>\n#include <exception>\n#include <vector>\n\nusing swish::connection::authenticated_session;\nusing swish::connection::connection_spec;\nusing swish::connection::session_pool;\n\nusing test::CConsumerStub;\nusing test::fixtures::openssh_fixture;\n\nusing comet::com_ptr;\nusing comet::thread;\n\nusing boost::exception_ptr;\nusing boost::shared_ptr;\nusing boost::test_tools::predicate_result;\n\nusing std::exception;\nusing std::vector;\n\nnamespace\n{ // private\n\nclass fixture : public openssh_fixture\n{\npublic:\n    connection_spec get_connection()\n    {\n        return connection_spec(whost(), wuser(), port());\n    }\n\n    com_ptr<ISftpConsumer> consumer()\n    {\n        com_ptr<CConsumerStub> consumer =\n            new CConsumerStub(private_key_path(), public_key_path());\n        return consumer;\n    }\n\n    /**\n     * Check that the given session responds sensibly to a request.\n     */\n    predicate_result alive(authenticated_session& session)\n    {\n        try\n        {\n            session.get_sftp_filesystem().directory_iterator(\"/\");\n\n            predicate_result res(true);\n            res.message() << \"Provider seems to be alive\";\n            return res;\n        }\n        catch (const exception& e)\n        {\n            predicate_result res(false);\n            res.message() << \"Provider seems to be dead: \" << e.what();\n            return res;\n        }\n    }\n};\n}\n\nBOOST_FIXTURE_TEST_SUITE(pool_tests, fixture)\n\n/**\n * Test the situation where the specified connection is not already in the\n * pool.\n *\n * Ensures a connection specification can create a session and that the\n * pool reports session status correctly.\n */\nBOOST_AUTO_TEST_CASE(new_session)\n{\n    connection_spec spec(get_connection());\n\n    BOOST_CHECK(!session_pool().has_session(spec));\n\n    authenticated_session& session =\n        session_pool().pooled_session(spec, consumer());\n\n    BOOST_CHECK(session_pool().has_session(spec));\n\n    BOOST_CHECK(alive(session));\n}\n\n/**\n * Test that creating a session does not affect the status of unrelated\n * connections.\n */\nBOOST_AUTO_TEST_CASE(unrelated_unaffected_by_creation)\n{\n    connection_spec unrelated_spec(L\"Unrelated\", L\"Spec\", 123);\n\n    BOOST_CHECK(!session_pool().has_session(unrelated_spec));\n\n    authenticated_session& session =\n        session_pool().pooled_session(get_connection(), consumer());\n\n    BOOST_CHECK(!session_pool().has_session(unrelated_spec));\n}\n\n/**\n * Test that the pool reuses existing sessions.\n */\nBOOST_AUTO_TEST_CASE(existing_session)\n{\n    connection_spec spec(get_connection());\n\n    authenticated_session& first_session =\n        session_pool().pooled_session(spec, consumer());\n\n    authenticated_session& second_session =\n        session_pool().pooled_session(spec, consumer());\n\n    BOOST_CHECK(&second_session == &first_session);\n\n    BOOST_CHECK(alive(second_session));\n\n    BOOST_CHECK(session_pool().has_session(spec));\n}\n\nconst int THREAD_COUNT = 30;\n\ntemplate <typename T>\nclass use_session_thread : public thread\n{\npublic:\n    use_session_thread(T* fixture, exception_ptr& error)\n        : thread(), m_fixture(fixture), m_error(error)\n    {\n    }\n\nprivate:\n    DWORD thread_main()\n    {\n        try\n        {\n            {\n                connection_spec spec(m_fixture->get_connection());\n\n                // This first call may or may not return running depending on\n                // whether it is on the first thread scheduled, so we don't test\n                // its value, just that it succeeds.\n                session_pool().has_session(spec);\n\n                authenticated_session& first_session =\n                    session_pool().pooled_session(spec, m_fixture->consumer());\n\n                // However, by this point it *must* be running\n                if (!session_pool().has_session(spec))\n                    BOOST_THROW_EXCEPTION(\n                        std::exception(\"Test failed: no session\"));\n\n                if (!m_fixture->alive(first_session))\n                    BOOST_THROW_EXCEPTION(\n                        std::exception(\"Test failed: first session is dead\"));\n\n                authenticated_session& second_session =\n                    session_pool().pooled_session(spec, m_fixture->consumer());\n\n                if (!session_pool().has_session(spec))\n                    BOOST_THROW_EXCEPTION(\n                        std::exception(\"Test failed: no session\"));\n\n                if (!m_fixture->alive(second_session))\n                    BOOST_THROW_EXCEPTION(\n                        std::exception(\"Test failed: second session is dead\"));\n\n                if (&second_session != &first_session)\n                    BOOST_THROW_EXCEPTION(\n                        std::exception(\"Test failed: session was not reused\"));\n            }\n        }\n        catch (...)\n        {\n            // Boost.Test is not threadsafe so we can't report the error\n            // directly when it happens.  Instead pass it out via exception_ptr\n            // to let the test find it.\n            m_error = boost::current_exception();\n        }\n\n        return 1;\n    }\n\n    exception_ptr& m_error;\n    T* m_fixture;\n};\n\ntypedef use_session_thread<fixture> test_thread;\n\n/**\n * Retrieve and prod a session from many threads.\n */\nBOOST_AUTO_TEST_CASE(threaded)\n{\n    vector<shared_ptr<test_thread>> threads(THREAD_COUNT);\n    vector<exception_ptr> errors(THREAD_COUNT);\n\n    for (size_t i = 0; i < threads.size(); ++i)\n    {\n        threads[i] = shared_ptr<test_thread>(new test_thread(this, errors[i]));\n        threads[i]->start();\n    }\n\n    for (size_t i = 0; i < threads.size(); ++i)\n    {\n        threads[i]->wait();\n    }\n\n    // Must process errors after finished waiting for all threads, otherwise\n    // remaining threads will try to write to errors vector after it has been\n    // destroyed\n    for (size_t i = 0; i < errors.size(); ++i)\n    {\n        if (errors[i])\n        {\n            boost::rethrow_exception(errors[i]);\n        }\n    }\n}\n\nBOOST_AUTO_TEST_CASE(remove_session)\n{\n    connection_spec spec(get_connection());\n\n    authenticated_session& session =\n        session_pool().pooled_session(spec, consumer());\n\n    session_pool().remove_session(spec);\n\n    BOOST_CHECK(!session_pool().has_session(spec));\n}\n\n/**\n * Test that sessions in the pool survive server restarts\n * (modulo re-authentication).\n *\n * By 'survive', we mean the pool is able to serve a usable session\n * with the same specification, not that the actual session instance has\n * to be the same (value-semantics and all that jazz).\n */\nBOOST_AUTO_TEST_CASE(sessions_across_server_restart)\n{\n    connection_spec spec(get_connection());\n\n    session_pool().pooled_session(spec, consumer());\n\n    BOOST_CHECK(session_pool().has_session(spec));\n\n    restart_server();\n\n    BOOST_CHECK(alive(session_pool().pooled_session(spec, consumer())));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/drop_target/CMakeLists.txt",
    "content": "# Copyright (C) 2015, 2016  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(UNIT_TESTS\n  rooted_source_test.cpp)\n\nset(INTEGRATION_TESTS\n  drop_target_test.cpp)\n\nswish_test_suite(\n  SUBJECT drop_target VARIANT unit\n  SOURCES ${UNIT_TESTS}\n  LIBRARIES shell ${Boost_LIBRARIES} local_sandbox_fixture\n  LABELS unit)\n\nswish_test_suite(\n  SUBJECT drop_target VARIANT integration\n  SOURCES ${INTEGRATION_TESTS}\n  LIBRARIES shell ${Boost_LIBRARIES} provider_fixture local_sandbox_fixture\n  LABELS integration)\n"
  },
  {
    "path": "test/drop_target/drop_target_test.cpp",
    "content": "// Copyright 2009, 2010, 2012, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/drop_target/DropTarget.hpp\" // Test subject\n#include \"swish/shell/shell.hpp\"            // data_object_for_files\n\n#include \"test/common_boost/data_object_utils.hpp\" // DataObjects on zip\n#include \"test/common_boost/helpers.hpp\"           // BOOST_REQUIRE_OK\n#include \"test/common_boost/fixtures.hpp\"          // ComFixture\n#include \"test/fixtures/local_sandbox_fixture.hpp\"\n#include \"test/fixtures/provider_fixture.hpp\"\n\n#include <ssh/stream.hpp>\n#include <ssh/filesystem.hpp>\n\n#include <boost/make_shared.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n\n#include <shlobj.h>\n\n#include <string>\n#include <vector>\n#include <iterator>\n#include <algorithm>\n\nusing swish::drop_target::CDropTarget;\nusing swish::drop_target::DropActionCallback;\nusing swish::drop_target::copy_data_to_provider;\nusing swish::drop_target::Progress;\nusing swish::shell::data_object_for_files;\n\nusing test::ComFixture;\nusing test::fixtures::provider_fixture;\nusing test::fixtures::local_sandbox_fixture;\nusing test::data_object_utils::create_test_zip_file;\nusing test::data_object_utils::data_object_for_zipfile;\n\nusing comet::com_ptr;\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::filesystem::ofstream;\nusing boost::filesystem::path;\nusing boost::make_shared;\nusing boost::shared_ptr;\nusing boost::test_tools::predicate_result;\n\nusing std::string;\nusing std::wstring;\nusing std::vector;\nusing std::istreambuf_iterator;\n\nusing washer::shell::pidl::apidl_t;\n\nnamespace\n{ // private\n\nconst string TEST_DATA = \"Lorem ipsum dolor sit amet.\\nbob\\r\\nsally\";\nconst string LARGER_TEST_DATA =\n    \";sdkfna;sldjnksj fjnweneofiun weof woenf woeunr2938y4192n34kj1458c\"\n    \"d;ofn3498tv 3405jnv 3498thv-948rc 34f 9485hv94htc rwr98thv3948h534h4\";\n\n/**\n * The test data which will be written and read from files to\n * check correct transmission.\n */\nstring test_data()\n{\n    return TEST_DATA;\n}\n\n/**\n * Write some data to a collection of local files and return them in\n * a DataObject created by the shell.\n *\n * The files must all be in the same filesystem folder.\n */\ntemplate <typename It>\ncom_ptr<IDataObject> create_multifile_data_object(It begin, It end)\n{\n    std::for_each(begin, end, fill_file);\n    return data_object_for_files(begin, end);\n}\n\n/**\n * Write some data to a local file and return it as a DataObject.\n */\ncom_ptr<IDataObject> create_data_object(const path& local)\n{\n    return create_multifile_data_object(&local, &local + 1);\n}\n\n/**\n * Fill a file with the test data.\n */\nvoid fill_file(const path& file)\n{\n    ofstream stream(file, std::ios_base::binary);\n    stream.write(test_data().c_str(), test_data().size());\n}\n\n/**\n * Create a new empty file at the given path.\n */\nvoid create_empty_file(path name)\n{\n    ofstream file(name, std::ios_base::out | std::ios_base::trunc);\n}\n\nclass ProgressStub : public Progress\n{\npublic:\n    bool user_cancelled()\n    {\n        return false;\n    }\n    void line(DWORD, const wstring&)\n    {\n    }\n    void line_path(DWORD, const wstring&)\n    {\n    }\n    void update(ULONGLONG, ULONGLONG)\n    {\n    }\n    void hide()\n    {\n    }\n    void show()\n    {\n    }\n};\n\nclass CopyCallbackStub : public DropActionCallback\n{\npublic:\n    void site(com_ptr<IUnknown>)\n    {\n    }\n\n    std::auto_ptr<Progress> progress()\n    {\n        return std::auto_ptr<Progress>(new ProgressStub());\n    }\n\n    bool can_overwrite(const ssh::filesystem::path&)\n    {\n        throw std::exception(\"unexpected request to confirm overwrite\");\n    }\n\n    void handle_last_exception()\n    {\n    }\n};\n\nclass ForbidOverwrite : public CopyCallbackStub\n{\npublic:\n    bool can_overwrite(const ssh::filesystem::path&)\n    {\n        return false;\n    }\n};\n\nclass AllowOverwrite : public CopyCallbackStub\n{\npublic:\n    bool can_overwrite(const ssh::filesystem::path&)\n    {\n        return true;\n    }\n};\n\nclass DropTargetFixture : public provider_fixture,\n                          public ComFixture,\n                          public local_sandbox_fixture\n{\npublic:\n    comet::com_ptr<IDropTarget> create_drop_target()\n    {\n        ssh::filesystem::path target_directory = sandbox() / L\"drop-target\";\n        create_directory(filesystem(), target_directory);\n\n        return new CDropTarget(Provider(), directory_pidl(target_directory),\n                               make_shared<CopyCallbackStub>());\n    }\n\n    /**\n     * Check if a file's contents is our test data.\n     */\n    predicate_result file_contents_correct(const ssh::filesystem::path& file)\n    {\n        ifstream stream(filesystem(), file);\n        string contents = string(istreambuf_iterator<char>(stream),\n                                 istreambuf_iterator<char>());\n\n        if (contents.size() != test_data().size())\n        {\n            predicate_result res(false);\n            res.message() << \"File contents differs in length from expected [\"\n                          << contents.size() << \" != \" << test_data().size()\n                          << \"] [\" << contents << \" != \" << test_data() << \"]\";\n            return res;\n        }\n        else if (contents != test_data())\n        {\n            for (size_t i = 0; i < contents.size(); ++i)\n            {\n                if (contents[i] != test_data()[i])\n                {\n                    predicate_result res(false);\n                    res.message() << \"File contents differs at index \" << i\n                                  << \" [\" << contents[i]\n                                  << \" != \" << test_data()[i] << \"] [\"\n                                  << contents << \" != \" << test_data() << \"]\";\n                    return res;\n                }\n\n                return true;\n            }\n        };\n        return true;\n    }\n};\n}\n\n#pragma region SFTP folder Drop Target tests\nBOOST_FIXTURE_TEST_SUITE(drop_target_tests, DropTargetFixture)\n\n/**\n * Create an instance.\n */\nBOOST_AUTO_TEST_CASE(create)\n{\n    com_ptr<IDropTarget> sp = create_drop_target();\n    BOOST_REQUIRE(sp);\n}\n\n#pragma region DataObject copy tests\nBOOST_FIXTURE_TEST_SUITE(drop_target_copy_tests, DropTargetFixture)\n\n/**\n * Copy single regular file.\n *\n * Test our ability to handle a DataObject produced by the shell for a\n * single, regular file (real file in the filesystem).\n */\nBOOST_AUTO_TEST_CASE(copy_single)\n{\n    path file = new_file_in_local_sandbox();\n\n    com_ptr<IDataObject> spdo = create_data_object(file);\n\n    ssh::filesystem::path destination = new_directory_in_sandbox();\n\n    shared_ptr<CopyCallbackStub> cb(new CopyCallbackStub);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    ssh::filesystem::path expected = destination / file.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_CHECK_EQUAL(file_size(filesystem(), expected), test_data().size());\n    BOOST_REQUIRE(file_contents_correct(expected));\n}\n\n/**\n * Copy several regular files.\n *\n * Test our ability to handle a DataObject produced by the shell for\n * more than one regular file (real files in the filesystem).\n */\nBOOST_AUTO_TEST_CASE(copy_many)\n{\n    vector<path> locals;\n    locals.push_back(new_file_in_local_sandbox());\n    locals.push_back(new_file_in_local_sandbox());\n    locals.push_back(new_file_in_local_sandbox());\n\n    com_ptr<IDataObject> spdo =\n        create_multifile_data_object(locals.begin(), locals.end());\n\n    ssh::filesystem::path destination = new_directory_in_sandbox();\n\n    shared_ptr<CopyCallbackStub> cb(new CopyCallbackStub);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    vector<path>::const_iterator it;\n    for (it = locals.begin(); it != locals.end(); ++it)\n    {\n        ssh::filesystem::path expected =\n            destination / (*it).filename().wstring();\n        BOOST_REQUIRE(exists(filesystem(), expected));\n        BOOST_REQUIRE(file_contents_correct(expected));\n    }\n}\n\n/**\n * Recursively copy a folder hierarchy.\n *\n * Our test hierarchy look like this:\n * Sandbox - file0\n *         \\ file1\n *         \\ empty_folder\n *         \\ non_empty_folder - second_level_file\n *                            \\ second_level_folder - third_level_file\n *\n * We could just make a DataObject by passing the sandbox dir to the shell\n * function but instead we pass the four items directly within it to\n * test how we handle a mix of recursive dirs and simple files.\n */\nBOOST_AUTO_TEST_CASE(copy_recursively)\n{\n    vector<path> top_level;\n\n    // Build top-level - these are the only items stored in the vector\n    top_level.push_back(new_file_in_local_sandbox());\n    top_level.push_back(new_file_in_local_sandbox());\n\n    path empty_folder = local_sandbox() / L\"empty\";\n    path non_empty_folder = local_sandbox() / L\"non-empty\";\n    create_directory(empty_folder);\n    create_directory(non_empty_folder);\n    top_level.push_back(empty_folder);\n    top_level.push_back(non_empty_folder);\n\n    // Build lower levels\n\n    path second_level_folder = non_empty_folder / L\"second-level-folder\";\n    create_directory(second_level_folder);\n\n    path second_level_file = non_empty_folder / L\"second-level-file\";\n    create_empty_file(second_level_file);\n    fill_file(second_level_file);\n\n    path second_level_zip_file = create_test_zip_file(non_empty_folder);\n\n    path third_level_file = second_level_folder / L\"third-level-file\";\n    create_empty_file(third_level_file);\n    fill_file(third_level_file);\n\n    com_ptr<IDataObject> spdo =\n        create_multifile_data_object(top_level.begin(), top_level.end());\n\n    ssh::filesystem::path destination = sandbox() / L\"copy-destination\";\n    create_directory(filesystem(), destination);\n\n    shared_ptr<CopyCallbackStub> cb(new CopyCallbackStub);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    ssh::filesystem::path expected;\n\n    expected = destination / top_level[0].filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(file_contents_correct(expected));\n\n    expected = destination / top_level[0].filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(file_contents_correct(expected));\n\n    expected = destination / empty_folder.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_empty(filesystem(), expected));\n\n    expected = destination / non_empty_folder.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n\n    expected = destination / non_empty_folder.filename().wstring() /\n               second_level_file.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(file_contents_correct(expected));\n\n    expected = destination / non_empty_folder.filename().wstring() /\n               second_level_folder.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(!is_empty(filesystem(), expected));\n\n    // The zip file must be copied as-is, not expanded\n    expected = destination / non_empty_folder.filename().wstring() /\n               second_level_zip_file.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_regular_file(filesystem(), expected));\n    BOOST_REQUIRE_GT(file_size(filesystem(), expected), 800);\n\n    expected = destination / non_empty_folder.filename().wstring() /\n               second_level_folder.filename().wstring() /\n               third_level_file.filename().wstring();\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(file_contents_correct(expected));\n}\n\n/**\n * Recursively copy a virtual hierarchy from a ZIP file.\n *\n * Our test hierarchy look like this:\n * Sandbox - file1.txt\n *         \\ file2.txt\n *         \\ empty_folder\n *         \\ non_empty_folder - second_level_file\n *                            \\ second_level_folder - third_level_file\n *\n * We could just make a DataObject by passing the sandbox dir to the shell\n * function but instead we pass the four items directly within it to\n * test how we handle a mix of recursive dirs and simple files.\n */\nBOOST_AUTO_TEST_CASE(copy_virtual_hierarchy_recursively)\n{\n    com_ptr<IDataObject> spdo =\n        data_object_for_zipfile(create_test_zip_file(local_sandbox()));\n\n    ssh::filesystem::path destination = sandbox() / L\"copy-destination\";\n    create_directory(filesystem(), destination);\n\n    shared_ptr<CopyCallbackStub> cb(new CopyCallbackStub);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    ssh::filesystem::path expected;\n\n    expected = destination / L\"file1.txt\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n\n    expected = destination / L\"file2.txt\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n\n    expected = destination / L\"empty\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_empty(filesystem(), expected));\n\n    expected = destination / L\"non-empty\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n\n    expected = destination / L\"non-empty\" / L\"second-level-file\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n\n    expected = destination / L\"non-empty\" / L\"second-level-folder\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(!is_empty(filesystem(), expected));\n\n    expected = destination / L\"non-empty\" / L\"second-level-folder\" /\n               L\"third-level-file\";\n    BOOST_REQUIRE(exists(filesystem(), expected));\n}\n\n/**\n * Overwrite an existing file.\n *\n * Must ask the user to confirm.  This test and the test after together\n * ensure that the user's response makes a difference to the outcome and\n * thereby proves that the user was asked.\n */\nBOOST_AUTO_TEST_CASE(copy_overwrite_yes)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n\n    ssh::filesystem::path destination = sandbox() / L\"copy-destination\";\n    ssh::filesystem::path obstruction = destination / file.filename().wstring();\n\n    create_directory(filesystem(), destination);\n    ssh::filesystem::ofstream(filesystem(), obstruction).close();\n\n    BOOST_CHECK(exists(filesystem(), obstruction));\n    BOOST_CHECK(!file_contents_correct(obstruction));\n\n    shared_ptr<AllowOverwrite> cb(new AllowOverwrite);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    BOOST_CHECK(exists(filesystem(), obstruction));\n    BOOST_CHECK(file_contents_correct(obstruction));\n}\n\n/**\n * Deny permission to overwrite an existing file.\n */\nBOOST_AUTO_TEST_CASE(copy_overwrite_no)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n\n    ssh::filesystem::path destination = sandbox() / L\"copy-destination\";\n    ssh::filesystem::path obstruction = destination / file.filename().wstring();\n\n    create_directory(filesystem(), destination);\n    ssh::filesystem::ofstream(filesystem(), obstruction).close(); // empty\n\n    BOOST_CHECK(exists(filesystem(), obstruction));\n    BOOST_CHECK(!file_contents_correct(obstruction));\n\n    shared_ptr<ForbidOverwrite> cb(new ForbidOverwrite);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    BOOST_CHECK(exists(filesystem(), obstruction));\n    BOOST_CHECK_EQUAL(file_size(filesystem(), obstruction), 0); // still empty\n}\n\n/**\n * Overwrite a larger file.\n *\n * Tests that we truncate the large file before writing.  Otherwise the\n * final\n * file would be corrupt.\n */\nBOOST_AUTO_TEST_CASE(copy_overwrite_larger)\n{\n    path target = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(target);\n\n    ssh::filesystem::path destination = sandbox() / L\"copy-destination\";\n    ssh::filesystem::path obstruction =\n        destination / target.filename().wstring();\n\n    // make sure that the destination file already exists and is larger\n    // that what we're about to copy to it\n    create_directory(filesystem(), destination);\n    ssh::filesystem::ofstream stream(filesystem(), obstruction);\n    stream << LARGER_TEST_DATA;\n    stream.close();\n\n    BOOST_REQUIRE(exists(filesystem(), obstruction));\n    BOOST_REQUIRE(!file_contents_correct(obstruction));\n\n    shared_ptr<AllowOverwrite> cb(new AllowOverwrite);\n    copy_data_to_provider(spdo, Provider(), directory_pidl(destination), cb);\n\n    BOOST_REQUIRE(exists(filesystem(), obstruction));\n    BOOST_REQUIRE(file_contents_correct(obstruction));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n\n#pragma region Drag - n - Drop behaviour tests\nBOOST_FIXTURE_TEST_SUITE(drop_target_dnd_tests, DropTargetFixture)\n\n/**\n * Drag enter.\n * Simulate the user dragging a file onto our folder with the left\n * mouse button.  The 'shell' is requesting either a copy or a link at our\n * discretion.  The folder drop target should respond S_OK and specify\n * that the effect of the operation is a copy.\n */\nBOOST_AUTO_TEST_CASE(drag_enter)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n    com_ptr<IDropTarget> spdt = create_drop_target();\n\n    POINTL pt = {0, 0};\n    DWORD dwEffect = DROPEFFECT_COPY | DROPEFFECT_LINK;\n    BOOST_REQUIRE_OK(spdt->DragEnter(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n}\n\n/**\n * Drag enter.\n * Simulate the user dragging a file onto our folder but requesting an\n * effect, link, that we don't support.  The folder drop target should\n * respond S_OK but set the effect to DROPEFFECT_NONE to indicate the drop\n * wasn't possible.\n */\nBOOST_AUTO_TEST_CASE(drag_enter_bad_effect)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n    com_ptr<IDropTarget> spdt = create_drop_target();\n\n    POINTL pt = {0, 0};\n    DWORD dwEffect = DROPEFFECT_LINK;\n    BOOST_REQUIRE_OK(spdt->DragEnter(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_NONE));\n}\n\n/**\n * Drag over.\n * Simulate the situation where a user drags a file over our folder and\n * changes\n * which operation they want as they do so.  In other words, on DragEnter\n * they\n * chose a link which we cannot perform but as they continue the drag\n * (DragOver)\n * they chang their request to a copy which we can do.\n *\n * The folder drop target should respond S_OK and specify that the effect of\n * the operation is a copy.\n */\nBOOST_AUTO_TEST_CASE(drag_over)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n    com_ptr<IDropTarget> spdt = create_drop_target();\n\n    POINTL pt = {0, 0};\n\n    // Do enter with link which should be declined (DROPEFFECT_NONE)\n    DWORD dwEffect = DROPEFFECT_LINK;\n    BOOST_REQUIRE_OK(spdt->DragEnter(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_NONE));\n\n    // Change request to copy which should be accepted\n    dwEffect = DROPEFFECT_COPY;\n    BOOST_REQUIRE_OK(spdt->DragOver(MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n}\n\n/**\n * Drag leave.\n * Simulate an aborted drag-drop loop where the user drags a file onto our\n * folder, moves it around, and then leaves without dropping.\n *\n * The folder drop target should respond S_OK and any subsequent\n * DragOver calls should be declined.\n */\nBOOST_AUTO_TEST_CASE(drag_leave)\n{\n    path file = new_file_in_local_sandbox();\n    com_ptr<IDataObject> spdo = create_data_object(file);\n    com_ptr<IDropTarget> spdt = create_drop_target();\n\n    POINTL pt = {0, 0};\n\n    // Do enter with copy which sould be accepted\n    DWORD dwEffect = DROPEFFECT_COPY;\n    BOOST_REQUIRE_OK(spdt->DragEnter(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n\n    // Continue drag\n    BOOST_REQUIRE_OK(spdt->DragOver(MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n\n    // Finish drag without dropping\n    BOOST_REQUIRE_OK(spdt->DragLeave());\n\n    // Decline any further queries until next DragEnter()\n    BOOST_REQUIRE_OK(spdt->DragOver(MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_NONE));\n}\n\n/**\n * Drag and drop.\n * Simulate a complete drag-drop loop where the user drags a file onto our\n * folder, moves it around, and then drops it.\n *\n * The folder drop target should copy the contents of the DataObject to the\n * remote end, respond S_OK and any subsequent DragOver calls should be\n * declined until a new drag-and-drop loop is started with DragEnter().\n */\nBOOST_AUTO_TEST_CASE(drop)\n{\n    path file = new_file_in_local_sandbox();\n\n    com_ptr<IDataObject> spdo = create_data_object(file);\n    com_ptr<IDropTarget> spdt = create_drop_target();\n\n    POINTL pt = {0, 0};\n\n    // Do enter with copy which should be accepted\n    DWORD dwEffect = DROPEFFECT_COPY;\n    BOOST_REQUIRE_OK(spdt->DragEnter(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n\n    // Continue drag\n    BOOST_REQUIRE_OK(spdt->DragOver(MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n\n    // Drop onto DropTarget\n    BOOST_REQUIRE_OK(spdt->Drop(spdo.in(), MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_COPY));\n\n    // Decline any further queries until next DragEnter()\n    BOOST_REQUIRE_OK(spdt->DragOver(MK_LBUTTON, pt, &dwEffect));\n    BOOST_REQUIRE_EQUAL(dwEffect, static_cast<DWORD>(DROPEFFECT_NONE));\n\n    ssh::filesystem::path expected =\n        sandbox() / L\"drop-target\" / file.filename().wstring();\n    BOOST_CHECK(exists(filesystem(), expected));\n    BOOST_CHECK(file_contents_correct(expected));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/drop_target/rooted_source_test.cpp",
    "content": "// Copyright 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/drop_target/RootedSource.hpp\" // Test subject\n\n#include <test/common_boost/helpers.hpp> // wchar_t ostream\n#include <test/fixtures/local_sandbox_fixture.hpp>\n\n#include <washer/shell/shell.hpp>      // pidl_from_parsing_name\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n#include <washer/shell/pidl.hpp>       // apidl_t, cpidl_t\n\n#include <boost/filesystem/path.hpp>    // path\n#include <boost/filesystem/fstream.hpp> // ofstream\n#include <boost/test/unit_test.hpp>\n\nusing swish::drop_target::RootedSource;\n\nusing test::fixtures::local_sandbox_fixture;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl_from_parsing_name;\nusing washer::shell::pidl_shell_item;\n\nusing boost::filesystem::wofstream;\nusing boost::filesystem::path;\n\nnamespace\n{\n\nclass RootedSourceFixture : public local_sandbox_fixture\n{\npublic:\n    path test_root()\n    {\n        return local_sandbox();\n    }\n\n    path child_file()\n    {\n        return new_file_in_local_sandbox();\n    }\n\n    path child_directory()\n    {\n        path directory = local_sandbox() / L\"testdir\";\n        create_directory(directory);\n        return directory;\n    }\n\n    path grandchild_file()\n    {\n        path directory = local_sandbox() / L\"testdir\";\n        create_directory(directory);\n\n        path file = directory / L\"testfile.txt\";\n        wofstream s(file);\n\n        return file;\n    }\n\n    path greatgrandchild_file()\n    {\n        path directory1 = local_sandbox() / L\"testdir1\";\n        create_directory(directory1);\n\n        path directory2 = directory1 / L\"testdir2\";\n        create_directory(directory2);\n\n        path file = directory2 / L\"testfile.txt\";\n        wofstream s(file);\n\n        return file;\n    }\n};\n\ninline bool operator==(const apidl_t& lhs, const apidl_t& rhs)\n{\n    return pidl_shell_item(lhs).parsing_name() ==\n           pidl_shell_item(rhs).parsing_name();\n}\n\napidl_t to_pidl(const path& path)\n{\n    return pidl_from_parsing_name(path.wstring());\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(rooted_source_tests, RootedSourceFixture)\n\n/**\n * Test the source where the root is the source itself (no branch).\n */\nBOOST_AUTO_TEST_CASE(root)\n{\n    apidl_t root_pidl = to_pidl(test_root());\n    RootedSource source(root_pidl, cpidl_t());\n\n    BOOST_CHECK(source.pidl() == root_pidl);\n    BOOST_CHECK(source.common_root() == root_pidl);\n    BOOST_CHECK_EQUAL(source.relative_name(), L\"\");\n}\n\n/**\n * Test the source where the source is a file directly under the root.\n */\nBOOST_AUTO_TEST_CASE(child)\n{\n    path file = child_file();\n    apidl_t pidl = to_pidl(file);\n\n    RootedSource source(pidl.parent(), pidl.last_item());\n\n    BOOST_CHECK(source.pidl() == pidl);\n    BOOST_CHECK(source.common_root() == pidl.parent());\n    BOOST_CHECK_EQUAL(source.relative_name(), file.filename());\n}\n\n/**\n * Test the source where the source is a directory directly under the root.\n */\nBOOST_AUTO_TEST_CASE(child_dir)\n{\n    path directory = child_directory();\n    apidl_t pidl = to_pidl(directory);\n\n    RootedSource source(pidl.parent(), pidl.last_item());\n\n    BOOST_CHECK(source.pidl() == pidl);\n    BOOST_CHECK(source.common_root() == pidl.parent());\n    BOOST_CHECK_EQUAL(source.relative_name(), directory.filename());\n}\n\n/**\n * Test the source where the source is grandchild of the root.\n */\nBOOST_AUTO_TEST_CASE(grandchild)\n{\n    path file = grandchild_file();\n    apidl_t pidl = to_pidl(file);\n    apidl_t root_pidl = pidl.parent().parent();\n    pidl_t branch = pidl.parent().last_item() + pidl.last_item();\n\n    RootedSource source(root_pidl, branch);\n\n    BOOST_CHECK(source.pidl() == pidl);\n    BOOST_CHECK(source.common_root() == root_pidl);\n\n    path expected_relative_name = file.parent_path().filename();\n    expected_relative_name /= file.filename();\n    BOOST_CHECK_EQUAL(source.relative_name(), expected_relative_name.wstring());\n}\n\n/**\n * Test the source where the source is grandchild of the root.\n */\nBOOST_AUTO_TEST_CASE(greatgrandchild)\n{\n    path file = greatgrandchild_file();\n    apidl_t pidl = to_pidl(file);\n    apidl_t root_pidl = pidl.parent().parent().parent();\n    pidl_t branch = pidl.parent().parent().last_item() +\n                    pidl.parent().last_item() + pidl.last_item();\n\n    RootedSource source(root_pidl, branch);\n\n    BOOST_CHECK(source.pidl() == pidl);\n    BOOST_CHECK(source.common_root() == root_pidl);\n\n    path expected_relative_name = file.parent_path().parent_path().filename();\n    expected_relative_name /= file.parent_path().filename();\n    expected_relative_name /= file.filename();\n    BOOST_CHECK_EQUAL(source.relative_name(), expected_relative_name.wstring());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/ezel/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  form_test.cpp)\n\nswish_test_suite(\n  SUBJECT ezel\n  SOURCES ${SOURCES}\n  LIBRARIES shell_folder ${Boost_LIBRARIES}\n  LABELS gui)\n"
  },
  {
    "path": "test/ezel/form_test.cpp",
    "content": "/**\n    @file\n\n    Tests for forms.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <test/common_boost/helpers.hpp> // wstring output\n\n#include <ezel/form.hpp> // test subject\n\n#include <ezel/controls/button.hpp> // button\n#include <ezel/controls/checkbox.hpp> // checkbox\n#include <ezel/controls/edit.hpp> // edit\n#include <ezel/controls/label.hpp> // label\n\n#include <boost/bind.hpp> // bind\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/test/unit_test.hpp>\n\nusing ezel::form;\nusing ezel::controls::button;\nusing ezel::controls::checkbox;\nusing ezel::controls::edit;\nusing ezel::controls::label;\n\nusing boost::bind;\n\nBOOST_AUTO_TEST_SUITE(form_tests)\n\nnamespace {\n\n    class form1\n    {\n    public:\n        form1() : m_form(L\"my title\", 30, 40, 30, 30)\n        {\n            m_form.on_activate().connect(\n                boost::bind(&form1::test_creation_and_die, this));\n            m_form.show();\n        }\n\n\n        void test_creation_and_die(/*bool by_mouse*/)\n        {\n//            BOOST_CHECK(!by_mouse);\n            BOOST_CHECK_EQUAL(m_form.text(), L\"my title\");\n            m_form.end();\n        }\n\n        form& get_form() { return m_form; }\n\n    private:\n        form m_form;\n    };\n\n    class form2\n    {\n    public:\n        form2() : m_form(L\"\", 30, 40, 30, 30)\n        {\n            m_form.on_create().connect(\n                boost::bind(&form2::test_creation_and_die, this));\n            m_form.show();\n        }\n\n\n        bool test_creation_and_die()\n        {\n            BOOST_CHECK_EQUAL(m_form.text(), L\"\");\n            m_form.end();\n            return true;\n        }\n\n        form& get_form() { return m_form; }\n\n    private:\n        form m_form;\n    };\n\n    /**\n     * Monitor text change event.\n     */\n    class form3\n    {\n    public:\n        form3() :\n            m_form(L\"initial text\", 30, 40, 30, 30),\n            m_change_detected(false)\n        {\n            m_form.on_create().connect(\n                boost::bind(&form3::test_and_die, this));\n            m_form.on_text_changed().connect(\n                boost::bind(&form3::text_changed, this));\n            m_form.show();\n        }\n\n        void text_changed()\n        {\n            m_change_detected = true;\n        }\n\n        bool test_and_die()\n        {\n            BOOST_CHECK_EQUAL(m_form.text(), L\"initial text\");\n            m_form.text(L\"changed text\");\n            BOOST_CHECK(m_change_detected);\n            BOOST_CHECK_EQUAL(m_form.text(), L\"changed text\");\n            m_form.end();\n            return true;\n        }\n\n    private:\n        form m_form;\n        bool m_change_detected;\n    };\n}\n\n/**\n * Create a form and test some basic properties.  Then destroy it and test\n * them again.\n */\nBOOST_AUTO_TEST_CASE( create_form )\n{\n    form1 frm;\n    BOOST_CHECK_EQUAL(frm.get_form().text(), L\"my title\");\n}\n\n/**\n * Create a form with an empty title.\n */\nBOOST_AUTO_TEST_CASE( create_form_no_title )\n{\n    form2 frm;\n    BOOST_CHECK_EQUAL(frm.get_form().text(), L\"\");\n}\n\n/**\n * Test that we can react to changes in form properties.\n * In other words, test that events work for forms.\n */\nBOOST_AUTO_TEST_CASE( create_form_change_title )\n{\n    form3 frm;\n}\n\n/**\n * Put a button on a form.\n */\nBOOST_AUTO_TEST_CASE( form_with_button )\n{\n    form frm(L\"my title\", 30, 40, 100, 50);\n    \n    button hello(L\"Hello\", 0, 0, 30, 20);\n    hello.on_click().connect(frm.killer());\n    frm.add_control(hello);\n\n    frm.show();\n    BOOST_CHECK_EQUAL(frm.text(), L\"my title\");\n    BOOST_CHECK_EQUAL(hello.text(), L\"Hello\");\n}\n\nnamespace {\n\n    void beep() { ::MessageBeep(0); }\n\n}\n\n/**\n * Put two buttons on a form.\n */\nBOOST_AUTO_TEST_CASE( form_with_two_controls )\n{\n    form frm(L\"Pick one\", 30, 40, 200, 50);\n    \n    button hello(L\"Oh noes!\", 10, 10, 50, 20, true);\n    hello.on_click().connect(frm.killer());\n\n    button parp(L\"Parp\", 70, 10, 50, 20);\n    parp.on_click().connect(beep);\n\n    frm.add_control(hello);\n    frm.add_control(parp);\n    frm.show();\n}\n\n/**\n * Put two different controls on a form.\n */\nBOOST_AUTO_TEST_CASE( form_with_different_controls )\n{\n    form frm(L\"A button and a box went to tea\", 30, 40, 200, 50);\n    \n    button hello(L\"Hello\", 10, 10, 30, 20, true);\n    hello.on_click().connect(frm.killer());\n    frm.add_control(hello);\n\n    edit text_box(L\"Some text\", 70, 10, 70, 14, edit::style::default);\n    text_box.on_update().connect(beep);\n    frm.add_control(text_box);\n\n    frm.show();\n}\n\n/**\n * Test that control template alignment is being done correctly.\n *\n * Change the control alignment by varying the title text by one character at\n * a time to cycle though the four alignment possibilities:\n * aligned, off-by-one, off-by-two, off-by-three (not necessarily in that\n * order).\n */\nBOOST_AUTO_TEST_CASE( four_different_alignments )\n{\n    wchar_t* titles[] = { L\"Hello\", L\"Helloo\", L\"Hellooo\", L\"Helloooo\" };\n\n    BOOST_FOREACH(wchar_t* title, titles)\n    {\n        form frm(L\"You'll see me four times\", 30, 40, 200, 50);\n        \n        button hello(title, 10, 10, 60, 20);\n        hello.on_click().connect(frm.killer());\n\n        label lab(L\"press the button to exit\", 70, 10, 50, 20);\n        frm.add_control(hello);\n        frm.add_control(lab);\n        frm.show();\n    }\n}\n\n/**\n * Put a button on a form using inline temporary construction.\n *\n * The add_control method should copy the new button in such a way that\n * it works once the temporary is destroyed.\n */\nBOOST_AUTO_TEST_CASE( form_with_button_inline_contructor )\n{\n    form frm(L\"my title\", 30, 40, 100, 50);\n    \n    button close(L\"Close\", 40, 25, 60, 20, true);\n    close.on_click().connect(frm.killer());\n    frm.add_control(close);\n    \n    frm.add_control(button(L\"I do nothing\", 0, 0, 75, 20));\n\n    frm.show();\n}\n\n/**\n * Link two controls.\n */\nBOOST_AUTO_TEST_CASE( one_control_updates_another )\n{\n    form frm(L\"Multipass\", 30, 40, 220, 50);\n    \n    button close(L\"Close\", 10, 10, 30, 20);\n    close.on_click().connect(frm.killer());\n\n    label lab(L\"My old text\", 160, 15, 50, 20);\n    button change(L\"Click me to change him\", 50, 10, 100, 20, true);\n    change.on_click().connect(\n        bind(&label::text, boost::ref(lab), L\"I got new!\"));\n\n    frm.add_control(change);\n    frm.add_control(close);\n    frm.add_control(lab);\n\n    frm.show();\n\n    BOOST_CHECK_EQUAL(lab.text(), L\"I got new!\");\n}\n\n/**\n * Chain two events (beep end end).\n */\nBOOST_AUTO_TEST_CASE( chain_events )\n{\n    form frm(L\"I should beep then die\", 30, 40, 100, 50);\n    \n    button ping(L\"Ping!\", 0, 0, 100, 50);\n\n    ping.on_click().connect(beep);\n    ping.on_click().connect(frm.killer());\n\n    frm.add_control(ping);\n\n    frm.show();\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/fix_key_permissions.sh",
    "content": "set -eu\nif [ \"${BASH_VERSINFO[0]}\" -ge 3 ]; then\n    set -o pipefail\nfi\n\nIFS=$'\\n\\t'\n\nKEYFILE=fixture_hostkey\nchgrp -v 545 $KEYFILE\nchmod -v 600 $KEYFILE\n"
  },
  {
    "path": "test/fixtures/CMakeLists.txt",
    "content": "# Copyright (C) 2016  Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nadd_library(local_sandbox_fixture\n  local_sandbox_fixture.cpp\n  local_sandbox_fixture.hpp)\ntarget_link_libraries(local_sandbox_fixture ${Boost_LIBRARIES})\n\nadd_library(openssh_fixture\n  openssh_fixture.cpp\n  openssh_fixture.hpp)\n\nhunter_add_package(Boost.Process)\nhunter_add_package(Comet)\nhunter_add_package(Washer)\n\nfind_package(Comet REQUIRED CONFIG)\nfind_package(Washer REQUIRED CONFIG)\n\ntarget_link_libraries(openssh_fixture\n  PUBLIC ${Boost_LIBRARIES}\n  PRIVATE Boost::Process)\n\nadd_library(session_fixture\n  session_fixture.cpp\n  session_fixture.hpp)\n\ntarget_link_libraries(session_fixture\n  PUBLIC openssh_fixture ssh)\n\nadd_library(sftp_fixture\n  sftp_fixture.cpp\n  sftp_fixture.hpp)\n\ntarget_link_libraries(sftp_fixture\n  PUBLIC session_fixture)\n\nadd_library(com_stream_fixture\n  com_stream_fixture.cpp\n  com_stream_fixture.hpp)\n\ntarget_link_libraries(com_stream_fixture\n  PUBLIC sftp_fixture)\n\nadd_library(provider_fixture\n  provider_fixture.cpp\n  provider_fixture.hpp)\n\ntarget_link_libraries(provider_fixture\n  PUBLIC sftp_fixture Comet::comet Washer::washer)\n"
  },
  {
    "path": "test/fixtures/com_stream_fixture.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"com_stream_fixture.hpp\"\n\n#include \"swish/connection/authenticated_session.hpp\"\n\n#include <ssh/filesystem.hpp>\n#include <ssh/stream.hpp> // fstream\n\n#include <comet/ptr.h>    // com_ptr\n#include <comet/stream.h> // adapt_stream_pointer\n\n#include <boost/make_shared.hpp>\n#include <boost/shared_ptr.hpp> // shared_ptr\n\nusing swish::connection::authenticated_session;\n\nusing ssh::filesystem::fstream;\nusing ssh::filesystem::path;\n\nusing comet::adapt_stream_pointer;\nusing comet::com_ptr;\n\nusing boost::make_shared;\nusing boost::shared_ptr;\n\nusing std::ios_base;\n\nnamespace test\n{\nnamespace fixtures\n{\n\ncom_stream_fixture::com_stream_fixture() : m_path(new_file_in_sandbox())\n{\n}\n\ncom_ptr<IStream> com_stream_fixture::get_stream(ios_base::openmode flags)\n{\n    // TODO: This should not create the stream directly but should use\n    // SftpDirectory.  This can happen when SftpDirectory is merged with\n    // the provider project\n    return adapt_stream_pointer(make_shared<fstream>(boost::ref(filesystem()),\n                                                     test_file().wstring(),\n                                                     flags),\n                                test_file().wstring());\n}\n\npath com_stream_fixture::test_file()\n{\n    return m_path;\n}\n}\n} // namespace test::fixtures\n"
  },
  {
    "path": "test/fixtures/com_stream_fixture.hpp",
    "content": "// Copyright 2009, 2010, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_COM_STREAM_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_COM_STREAM_FIXTURE_HPP\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\nnamespace test\n{\nnamespace fixtures\n{\n\n/**\n * Extends the Sandbox fixture by allowing the creation of swish::provider\n * IStreams that pass through the OpenSSH server pointing to files in the\n * sandbox.\n */\nclass com_stream_fixture : virtual public sftp_fixture\n{\npublic:\n    /**\n     * Initialise the test fixture with the path of a new, empty file\n     * in the sandbox.\n     */\n    com_stream_fixture();\n\n    /**\n     * Create an IStream instance open on a temporary file in our sandbox.\n     * By default the stream is open for reading and writing but different\n     * flags can be passed to change this.\n     */\n    comet::com_ptr<IStream> get_stream(\n        std::ios_base::openmode flags = std::ios_base::in | std::ios_base::out);\n\n    ssh::filesystem::path test_file();\n\nprivate:\n    ssh::filesystem::path m_path;\n};\n}\n} // namespace test::fixtures\n\n#endif\n"
  },
  {
    "path": "test/fixtures/fixture_dsakey",
    "content": "-----BEGIN DSA PRIVATE KEY-----\nMIIBvAIBAAKBgQCtiYdgpPvFtfi7Ba44DiB+1x8kojjT0nRvn2hU2aa4p4fXI8kd\n6Hc57VQO/lLhR9eFpxjP7m+jGwF468Q6NU8xiC71ucep0OoXS7u8RcoIoWfLDtZi\nDDlahnZTW04mB5fFxo2y7dYl31vE4TPdSxhqpkvnIBIstMFh2M7Dl0w8/QIVAP95\nu6dg1OW6gGsRgiircsy1A9tzAoGBAIzwc5FCnJnzAJm9Hjv0AFV5l/i/DQulZ9pu\nEILkNiHCfDR+lTJ8VxAR7J3pgjmvYzeeRvi519ez1YriktDt66kIknQOcHB8ghyg\nU+dff79SkDcpg8LnX5xb3cVMgABujA0sSpaW1wwm64RXdvmoQvWu6ympUT0l0dEd\noYVkb4ytAoGAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6\nFzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbU\nfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7GooCFQCyaA6T\ntkJunHP+F0Xg/WAUV6tcqA==\n-----END DSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/fixture_dsakey.pub",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty\n"
  },
  {
    "path": "test/fixtures/fixture_hostkey",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6\ncvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99\nfnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB\neNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMA\nwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQK\nypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbwIBIwKCAQAd9Cu9heWrs+UAinvv\nIwmq/EhnDGQijJoOt1zEMrpXSekyq7mQDgN0SZdJLPeSlSRQ5nVq5/dZroYB3A5i\nE7N3F7nibcJskWq5rcMyGjQHwod8wqfMiGcL6mjeZu2jLXprm0NDpJ3DyicbCA2G\nEhnpoHmktIBE5FsslI/nHer2o6OA/kVWSEjak+pvI1pm22T8QOBBfY0yAX7B0ebk\n8o4lB4cdLf3In7Q0ahpHNOwIPdRvQ2c4Tm/DcfUBkTW2ZYGUd45cFsyHqXZscNNy\nGX2Wcy/FLEvQ6zBFJsNLpxCYsUyBxfSDygn9dx9RQfiWFXjdRaRPpyRAr+BTXkLU\nyvabAoGBANt7sxfjvu/SLkRc7TnBoJ0h/AL7Mcuu9PJmOnis4boyF9ZxqbiRiS3J\nyK+EKxfC0S+xf5WJ5uf7dVGnOXHXKaRl4xH90iRtryNlvtILZwHw1DTqRFxv9jtz\ntTRrYMEHAnMKzadgDfV/lv4iJ6nwFzK76GQ7RQNZYiGTMEh3pUNjAoGBAMvNLGpz\nFxhpIh+fVvRjawKgGVP87T482WOUdsF18EEPFMe6D7DO5xpLuJi+C7QkvMI8WjvD\n/3RGvaSh9Wt7ikLZpeogiSJy121HsEqheTR5hTx2t72ClrjZvIhLbQMRu6PqGPu/\nHOC2urEGGYm7O2vnftwpuG3zIVVLM2KstPCFAoGBAM7w+VEJ7opYdMQdGi8kRvqN\nwbmrAxCAY0ryrCijALbexgS0T5DDu9q28Gr49W4stpquq35dc0/BNBnJje7+EVHc\naGFrqOCErHHU9b66Sy23LnsIxBykFAwrRHNAq66u1mx35nk9Tv1pq58nhHun21u4\nfAa7ijZblwm2qd3tJsqBAoGAEXf8ficfPJtMEVbM8GBLADmbxV7Sga1xuBQKLdbo\ntR6MwKmMUPvKqnuE2eRnZzZZUnoznrkHRHsXkcS9Q7ohyzc6G2Hf3mGdb8RQ8HQ9\nlsiWZESwqdf+SlvOVNND27EQFV01V2gnC/JnxgfWTaJVjOf07ky4CWycdQZyHmaT\nKo8CgYB58jOyXMdo2ggOCG/HX2H92KPPpFUBFCX27fCue8BZLD5quIltpXupx5oj\nEyltgvPcmNDgvdSadkHvP5s6nykS+n5we+d9yIIJF/BfETWsXjR3ooip+trqiirw\n0aHqUDFcYn9unm2wtrMYYViiDLRijNwLZ2sG0JIU4JHyseh+NA==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/fixture_hostkey.pub",
    "content": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrBeNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMAwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbw== awl03@bounty\n"
  },
  {
    "path": "test/fixtures/fixture_rsakey",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJ\nevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBp\nvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5\nRqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNX\nb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYIL\njzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3QIBIwKCAQB1Hpyhoi2LXCIVfXPM\nAU6AtWvRY12PtdSl8uqr+nX2JATNBZlUCTaUE6qQJNxEZyDeMNvzZdxV5gkzQ2Fi\nTpQIyRddbH01fJKoTxzlHzbLfAeCj9mFqicahOkHAMN8K6Ddqxe89zhD60w0SgjW\n91tLzZQ2sxE70RxBdPQOpbaZLxmUZSVxRgf5djotyZqB4CcGblKCEZYJ9ZemgCnF\ngEcSsqcn0Jxfu+aEJ4WinN2orWs+okfgsUu9G9Ozwcy9Ptq1LkIzcwwTIpL7TTDd\nLMvhql39a07SysepjFRHxjvXh8Gv+SsLvKQPJHheVv8XoG0dZd+9/Eden9rHKoVm\nvGPLAoGBANGDQtv5K/md/3sRGeJ6Ir3/Ao+WMe8C5onck+hW4y/2yQqm3ZLzyZon\nKdWRj2q4dnxFZyoyDgX0UHLpM4aSsMRjn4C6vcPLcYaZ9CGB5FWPGZrq+q6vuMGK\nV9/fo4ZNFkNK3wo4WCSgxC1Y8XUJc3klOvPVjsmVxZaeZnkukkAFAoGBAMCkqe/S\nhrKITzjZuyGN90a2Nq+3xMNGuc400Qvoi27D1OcSn7SJ/K3tVWbENOH3CAlkmlZT\n46IM2SRRmM0bxF3aThEwnsD5yPqgz+tcweX+gK3nXnP5JZfYF1kArXk80/eYhNE9\nPwnJNXDQMoxaM0/X6BVgQyt03/Q12lH9u0j5AoGAR9U7fp6Su/uoDO/rnhs/HJHy\nP9u5WULSsuyKe4uBF8JTjp+cbOXeuIJ0vkCI8WPQ2iZsg37gPI5Hd9rtGDJLPATm\nOsOuxslowG9MY0J6K/aMb6EFfbiXHckIL3/gS02hO6SkPgSwgZY0odVaGX+VThtk\nq18ppDNZr/vLXL+CmZsCgYEAlJxIlG80tZxaXw5dKIN1nPL2/JUUIZz1vFShQ7Nk\nP4EglP+9B52lqr5mc9kwHAe1vhpobns6kvP393IlawbKrsz6ZQg/8/PkLw5XQIli\nYPeH1pyKsTyKtvcn9DO5BcE1zaGLB9ApULEpOcUuTwPBLvcDfjRREuUhywT44Coi\nw0MCgYAX5yc7/Z3R6M30rGsrgb1Y2siHYsi2LCygUj7TDGQYpaZN4afPJOT5H/Nr\n7x7dgZkbOR6PQFm00VgML0XxKih59t0dcQ+2qk1LX5JDKRF/1kER3np6dpceteDu\ncC+MEHB/KvijnviAtBZGvD0O7oZgvbkKHESu2igXpAnfXPZFvw==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/fixture_rsakey.pub",
    "content": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty\n"
  },
  {
    "path": "test/fixtures/fixture_wrong_dsakey",
    "content": "-----BEGIN DSA PRIVATE KEY-----\nMIIBuwIBAAKBgQCE1v/lL1VvjlJMyG7q0wAgl2tqVMzy5h1RVOtDS8bTlXLJg7ks\nT63wTmXlp2HedgKkfHCu7AKsjPyg1CTrvRBa8BFEvMoUDARonMwql34aiKVMy/t0\n/ehnmCQV+ZMFpsVFnphJpZuXLTW1F3pnEbSNud5sACjbWb51uly5AUynuwIVAOhj\nrbNOaAtC1oYki8CVwpkQ8rHhAoGAYSepXRF3GJSjseYgJ2bCgcJS0L9agcvKAf+F\ndc+ZDJOchhnZC/hGHsjAfg62KowwKuOYsbcR3S4LJxiERcmRabww+kUIL1E8bLaQ\nRbOygNsHU8LyBdSx3WqC2WEOpVkTAjYDWTkbN+qkb53IBoI0GwFt5P9GHvQcAGkj\nGJQAWWYCgYAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMi\na0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/\nTq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pAIVAKV45RLf\nhg2GNXvO68Q4tt3F6kSP\n-----END DSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/fixture_wrong_dsakey.pub",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAITW/+UvVW+OUkzIburTACCXa2pUzPLmHVFU60NLxtOVcsmDuSxPrfBOZeWnYd52AqR8cK7sAqyM/KDUJOu9EFrwEUS8yhQMBGiczCqXfhqIpUzL+3T96GeYJBX5kwWmxUWemEmlm5ctNbUXemcRtI253mwAKNtZvnW6XLkBTKe7AAAAFQDoY62zTmgLQtaGJIvAlcKZEPKx4QAAAIBhJ6ldEXcYlKOx5iAnZsKBwlLQv1qBy8oB/4V1z5kMk5yGGdkL+EYeyMB+DrYqjDAq45ixtxHdLgsnGIRFyZFpvDD6RQgvUTxstpBFs7KA2wdTwvIF1LHdaoLZYQ6lWRMCNgNZORs36qRvncgGgjQbAW3k/0Ye9BwAaSMYlABZZgAAAIAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMia0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/Tq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pA== awl03@bounty\n"
  },
  {
    "path": "test/fixtures/local_sandbox_fixture.cpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"local_sandbox_fixture.hpp\"\n\n#include <boost/filesystem.hpp>\n#include <boost/filesystem/fstream.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\nusing boost::filesystem::path;\nusing boost::filesystem::ofstream;\nusing boost::filesystem::temp_directory_path;\nusing boost::uuids::random_generator;\n\nnamespace test\n{\nnamespace fixtures\n{\n\nlocal_sandbox_fixture::local_sandbox_fixture()\n    : m_sandbox(temp_directory_path() / to_string(random_generator()()))\n{\n    create_directory(m_sandbox);\n}\n\nlocal_sandbox_fixture::~local_sandbox_fixture()\n{\n    try\n    {\n        remove_all(m_sandbox);\n    }\n    catch (...)\n    {\n    }\n}\n\npath local_sandbox_fixture::local_sandbox()\n{\n    return m_sandbox;\n}\n\npath local_sandbox_fixture::new_file_in_local_sandbox(const path& name)\n{\n    path p = local_sandbox() / name;\n    ofstream file(p);\n    return p;\n}\n\npath local_sandbox_fixture::new_file_in_local_sandbox()\n{\n    random_generator generator;\n\n    path filename = to_string(generator());\n    return new_file_in_local_sandbox(filename);\n}\n\npath local_sandbox_fixture::new_directory_in_local_sandbox()\n{\n    random_generator generator;\n\n    path directory_name = to_string(generator());\n    path directory = local_sandbox() / directory_name;\n    create_directory(directory);\n    return directory;\n}\n\npath local_sandbox_fixture::new_directory_in_local_sandbox(const path& name)\n{\n    path p = local_sandbox() / name;\n    create_directory(p);\n    return p;\n}\n}\n}\n"
  },
  {
    "path": "test/fixtures/local_sandbox_fixture.hpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_LOCAL_SANDBOX_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_LOCAL_SANDBOX_FIXTURE_HPP\n\n#include <boost/filesystem.hpp>\n\nnamespace test\n{\nnamespace fixtures\n{\n/**\n * Fixture that creates and destroys a sandbox directory.\n */\nclass local_sandbox_fixture\n{\npublic:\n    local_sandbox_fixture();\n    virtual ~local_sandbox_fixture();\n\n    boost::filesystem::path local_sandbox();\n    boost::filesystem::path new_file_in_local_sandbox();\n    boost::filesystem::path\n    new_file_in_local_sandbox(const boost::filesystem::path& name);\n    boost::filesystem::path new_directory_in_local_sandbox();\n    boost::filesystem::path\n    new_directory_in_local_sandbox(const boost::filesystem::path& name);\n\nprivate:\n    boost::filesystem::path m_sandbox;\n};\n}\n}\n#endif\n"
  },
  {
    "path": "test/fixtures/openssh_fixture.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"openssh_fixture.hpp\"\n\n#include \"swish/connection/session_pool.hpp\"\n\n#include <boost/assign/list_of.hpp>\n#include <boost/date_time/posix_time/posix_time_duration.hpp>\n#include <boost/foreach.hpp>\n#include <boost/io/detail/quoted_manip.hpp>\n#include <boost/optional.hpp>\n#include <boost/lexical_cast.hpp>\n#include <boost/locale.hpp>\n#include <boost/process/context.hpp>\n#include <boost/process/environment.hpp>\n#include <boost/process/operations.hpp> // find_executable_in_path\n#include <boost/process/pistream.hpp>\n#include <boost/process/self.hpp>\n#include <boost/process/stream_behavior.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/thread.hpp> // this_thread\n\n#include <iterator>\n#include <map>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\nusing boost::assign::list_of;\nusing boost::filesystem::path;\nusing boost::io::quoted;\nusing boost::lexical_cast;\nusing boost::locale::conv::to_utf;\nusing boost::locale::generator;\nusing boost::locale::util::get_system_locale;\nusing boost::optional;\nusing boost::process::behavior::pipe;\nusing boost::process::child;\nusing boost::process::context;\nusing boost::process::environment;\nusing boost::process::find_executable_in_path;\nusing boost::process::pistream;\nusing boost::process::self;\nusing boost::process::stderr_id;\nusing boost::process::stdout_id;\n\nusing std::locale;\nusing std::map;\nusing std::ostream_iterator;\nusing std::ostringstream;\nusing std::runtime_error;\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\nnamespace\n{ // private\n\nconst string SSHD_PRIVATE_KEY_FILE = \"fixture_dsakey\";\nconst string SSHD_PUBLIC_KEY_FILE = \"fixture_dsakey.pub\";\nconst string SSHD_WRONG_PRIVATE_KEY_FILE = \"fixture_wrong_dsakey\";\nconst string SSHD_WRONG_PUBLIC_KEY_FILE = \"fixture_wrong_dsakey.pub\";\n\ntemplate <typename ArgSequence>\nstring error_message_from_stderr(const string& command,\n                                 const ArgSequence& arguments, child& process)\n{\n    pistream command_stderr(process.get_handle(stderr_id));\n\n    ostringstream message;\n    message << quoted(command) << \" \";\n    copy(arguments.begin(), arguments.end(),\n         ostream_iterator<string>(message, \" \"));\n    message << \" failed: \";\n    message << command_stderr.rdbuf() << std::flush;\n\n    return message.str();\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_executable(const path& executable,\n                                 const ArgSequence& arguments)\n{\n    context ctx;\n    ctx.env = self::get_environment();\n    ctx.streams[stdout_id] = pipe();\n    ctx.streams[stderr_id] = pipe();\n\n    child process = create_child(executable.string(), arguments, ctx);\n\n    pistream command_stdout(process.get_handle(stdout_id));\n    Out out;\n    command_stdout >> out;\n\n    int status = process.wait();\n    // TODO: Check if process exited, with WIFEXITED - may need to upgrade\n    // Boost.Process to the 2012 version to get mitigate.hpp, which handles this\n    // portably.\n    if (status == 0)\n    {\n        return out;\n    }\n    else\n    {\n        BOOST_THROW_EXCEPTION(runtime_error(error_message_from_stderr(\n            executable.string(), arguments, process)));\n    }\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_command(const string& command,\n                              const ArgSequence& arguments)\n{\n    path command_executable = find_executable_in_path(command);\n\n    return single_value_from_executable<Out>(command_executable, arguments);\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_docker_command(const ArgSequence& arguments)\n{\n    return single_value_from_command<Out>(\"docker\", arguments);\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_docker_machine_command(const ArgSequence& arguments)\n{\n    return single_value_from_command<Out>(\"docker-machine\", arguments);\n}\n\ntemplate <typename ArgSequence>\nvoid run_docker_command(const ArgSequence& arguments)\n{\n    single_value_from_docker_command<string>(arguments);\n}\n\noptional<string> docker_machine_name()\n{\n    const string docker_machine_name_variable = \"DOCKER_MACHINE_NAME\";\n\n    boost::process::environment environment = self::get_environment();\n    if (environment.count(docker_machine_name_variable) == 1)\n    {\n        return environment[docker_machine_name_variable];\n    }\n    else\n    {\n        return optional<string>();\n    }\n}\n}\n\nextern \"C\" {\nextern void ERR_free_strings();\nextern void ERR_remove_state(unsigned long);\nextern void EVP_cleanup();\nextern void CRYPTO_cleanup_all_ex_data();\nextern void ENGINE_cleanup();\nextern void CONF_modules_unload(int);\nextern void CONF_modules_free();\nextern void RAND_cleanup();\n}\n\nnamespace\n{\n\nstruct global_fixture\n{\n    global_fixture()\n    {\n        // Ensure the docker image has been built\n        vector<string> build_command = (list_of(string(\"build\")), \"-t\",\n                                        \"swish/openssh_server\", \"ssh_server\");\n        run_docker_command(build_command);\n    }\n\n    ~global_fixture()\n    {\n        // We call this here as a bit of a hack to stop memory-leak\n        // detection incorrectly detecting OpenSSL global data as a\n        // leak\n        swish::connection::session_pool().destroy();\n        ::RAND_cleanup();\n        ::ENGINE_cleanup();\n        ::CONF_modules_unload(1);\n        ::CONF_modules_free();\n        ::EVP_cleanup();\n        ::ERR_free_strings();\n        ::ERR_remove_state(0);\n        ::CRYPTO_cleanup_all_ex_data();\n    }\n};\n}\n\nBOOST_GLOBAL_FIXTURE(global_fixture);\n\nnamespace test\n{\nnamespace fixtures\n{\nopenssh_fixture::openssh_fixture()\n{\n    vector<string> docker_command =\n        (list_of(string(\"run\")), \"--detach\", \"-P\", \"swish/openssh_server\");\n    m_container_id = single_value_from_docker_command<string>(docker_command);\n    m_host = ask_docker_for_host();\n    m_port = ask_docker_for_port();\n}\n\nopenssh_fixture::~openssh_fixture()\n{\n    try\n    {\n        stop_server();\n    }\n    catch (...)\n    {\n    }\n}\n\nvoid openssh_fixture::stop_server()\n{\n    vector<string> stop_command = (list_of(string(\"stop\")), m_container_id);\n    run_docker_command(stop_command);\n}\n\nvoid openssh_fixture::restart_server()\n{\n    stop_server();\n\n    vector<string> docker_command =\n        (list_of(string(\"run\")), \"--detach\", \"-p\",\n         lexical_cast<string>(m_port) + \":22\", \"swish/openssh_server\");\n    m_container_id = single_value_from_docker_command<string>(docker_command);\n    // We make sure we bind to the same port in the new docker container.  Do we\n    // have to do anything to ensure we get the same IP?  Presumably not unless\n    // docker-machine changed machine in the middle of restarting.\n    assert(ask_docker_for_host() == m_host);\n    assert(ask_docker_for_port() == m_port);\n}\n\nstring openssh_fixture::host() const\n{\n    return m_host;\n}\n\nwstring openssh_fixture::whost() const\n{\n    generator gen;\n    locale loc = gen(get_system_locale());\n    return to_utf<wchar_t>(m_host, loc);\n}\n\nstring openssh_fixture::ask_docker_for_host() const\n{\n    optional<string> active_docker_machine = docker_machine_name();\n    if (active_docker_machine)\n    {\n        // This can be flaky when tests run in parallel (see\n        // https://github.com/docker/machine/issues/2612), so we retry a few\n        // times with exponential backoff if it fails\n        int attempt_no = 0;\n        boost::posix_time::milliseconds wait_time(100);\n        while (true)\n        {\n            ++attempt_no;\n            try\n            {\n                vector<string> machine_ip_command =\n                    (list_of(string(\"ip\")), \"default\");\n                return single_value_from_docker_machine_command<string>(\n                    machine_ip_command);\n            }\n            catch (const runtime_error&)\n            {\n                if (attempt_no > 5)\n                {\n                    throw;\n                }\n                else\n                {\n                    wait_time *= 2;\n                }\n            }\n            boost::this_thread::sleep(wait_time);\n        }\n    }\n    else\n    {\n        vector<string> inspect_host_command =\n            (list_of(string(\"inspect\")), \"--format\",\n             \"{{ .NetworkSettings.IPAddress }}\", m_container_id);\n        return single_value_from_docker_command<string>(inspect_host_command);\n    }\n}\n\nstring openssh_fixture::user() const\n{\n    return \"swish\";\n}\n\nwstring openssh_fixture::wuser() const\n{\n    return L\"swish\";\n}\n\nint openssh_fixture::port() const\n{\n    return m_port;\n}\n\nstring openssh_fixture::password() const\n{\n    return \"my test password\";\n}\n\nwstring openssh_fixture::wpassword() const\n{\n    return L\"my test password\";\n}\n\nint openssh_fixture::ask_docker_for_port() const\n{\n    vector<string> inspect_host_command =\n        (list_of(string(\"inspect\")), \"--format\",\n         \"{{ index (index (index .NetworkSettings.Ports \\\"22/tcp\\\") \"\n         \"0) \\\"HostPort\\\" }}\",\n         m_container_id);\n    return single_value_from_docker_command<int>(inspect_host_command);\n}\n\n/**\n * The private half of a key-pair that is expected to authenticate successfully\n * with the fixture server.\n */\npath openssh_fixture::private_key_path() const\n{\n    return SSHD_PRIVATE_KEY_FILE;\n}\n\n/**\n * The public half of a key-pair that is expected to authenticate successfully\n * with the fixture server.\n */\npath openssh_fixture::public_key_path() const\n{\n    return SSHD_PUBLIC_KEY_FILE;\n}\n\n/**\n * The private half of a key-pair that is expected to fail to authenticate\n * with the fixture server.\n *\n * This must be in the same format as the successful key-pair so that the key\n * mismatches rather than format mismatches are the cause of authentication\n * failure regardless of which combination of keys is passed.\n */\npath openssh_fixture::wrong_private_key_path() const\n{\n    return SSHD_WRONG_PRIVATE_KEY_FILE;\n}\n\n/**\n * The public half of a key-pair that is expected to fail to authenticate\n * with the fixture server.\n *\n * This must be in the same format as the successful key-pair so that the\n * key mismatches rather than format mismatches are the cause of\n * authentication\n * failure regardless of which combination of keys is passed.\n */\npath openssh_fixture::wrong_public_key_path() const\n{\n    return SSHD_WRONG_PUBLIC_KEY_FILE;\n}\n}\n} // namespace test::fixtures\n"
  },
  {
    "path": "test/fixtures/openssh_fixture.hpp",
    "content": "// Copyright 2009, 2010, 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_OPENSSH_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_OPENSSH_FIXTURE_HPP\n\n#include <boost/filesystem.hpp> // path\n\n#include <string>\n\nnamespace test\n{\nnamespace fixtures\n{\n\n/**\n * Fixture that starts and stops an OpenSSH server.\n */\nclass openssh_fixture\n{\npublic:\n    openssh_fixture();\n    virtual ~openssh_fixture();\n    void stop_server();\n    void restart_server();\n\n    std::string host() const;\n    std::string user() const;\n    std::wstring whost() const;\n    std::wstring wuser() const;\n    int port() const;\n    std::string password() const;\n    std::wstring wpassword() const;\n    boost::filesystem::path private_key_path() const;\n    boost::filesystem::path public_key_path() const;\n    boost::filesystem::path wrong_private_key_path() const;\n    boost::filesystem::path wrong_public_key_path() const;\n\nprivate:\n    std::string m_container_id;\n    std::string m_host;\n    int m_port;\n\n    std::string ask_docker_for_host() const;\n    int ask_docker_for_port() const;\n};\n}\n} // namespace test::fixtures\n\n#endif\n"
  },
  {
    "path": "test/fixtures/provider_fixture.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"provider_fixture.hpp\"\n\n#include \"test/common_boost/MockConsumer.hpp\"\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/connection/session_manager.hpp\"\n#include \"swish/provider/Provider.hpp\"         // CProvider\n#include \"swish/host_folder/host_pidl.hpp\"     // create_host_itemid\n#include \"swish/shell_folder/SftpDataObject.h\" // CSftpDataObject\n#include \"swish/shell_folder/SftpDirectory.h\"  // CSftpDirectory\n\n#include <comet/ptr.h> // com_ptr\n\n#include <washer/shell/pidl_array.hpp> // PIDL array wrapper\n#include <washer/shell/shell.hpp>      // desktop_folder\n\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\nusing swish::connection::connection_spec;\nusing swish::connection::session_manager;\nusing swish::connection::session_reservation;\nusing swish::host_folder::create_host_itemid;\nusing swish::provider::CProvider;\nusing swish::provider::sftp_provider;\n\nusing comet::com_ptr;\n\nusing namespace washer::shell::pidl;\nusing washer::shell::desktop_folder;\n\nusing boost::numeric_cast;\nusing boost::shared_ptr;\n\nusing std::vector;\n\nusing ssh::filesystem::path;\n\nnamespace comet\n{\n\ntemplate <>\nstruct comtype<IDataObject>\n{\n    static const IID& uuid()\n    {\n        return IID_IDataObject;\n    }\n    typedef ::IUnknown base;\n};\n}\n\nnamespace test\n{\nnamespace fixtures\n{\nshared_ptr<sftp_provider> provider_fixture::Provider()\n{\n    session_reservation ticket(session_manager().reserve_session(\n        connection_spec(whost(), wuser(), port()), Consumer(),\n        \"Running tests\"));\n\n    return boost::shared_ptr<CProvider>(new CProvider(ticket));\n}\n\ncom_ptr<test::MockConsumer> provider_fixture::Consumer()\n{\n    com_ptr<test::MockConsumer> consumer = new test::MockConsumer();\n    consumer->set_pubkey_behaviour(test::MockConsumer::CustomKeys);\n    consumer->set_key_files(private_key_path().string(),\n                            public_key_path().string());\n    return consumer;\n}\n\napidl_t provider_fixture::directory_pidl(const path& directory)\n{\n    return real_swish_pidl() +\n           create_host_itemid(whost(), wuser(), directory, port());\n}\n\napidl_t provider_fixture::sandbox_pidl()\n{\n    return directory_pidl(sandbox());\n}\n\nvector<cpidl_t> provider_fixture::pidls_in_sandbox()\n{\n    CSftpDirectory dir(sandbox_pidl(), Provider());\n    com_ptr<IEnumIDList> pidl_enum = dir.GetEnum(\n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN);\n\n    vector<cpidl_t> pidls;\n    cpidl_t pidl;\n    while (pidl_enum->Next(1, pidl.out(), NULL) == S_OK)\n    {\n        pidls.push_back(pidl);\n    }\n\n    return pidls;\n}\n\ncom_ptr<IDataObject> provider_fixture::data_object_from_sandbox()\n{\n    vector<cpidl_t> pidls = pidls_in_sandbox();\n    pidl_array<cpidl_t> array(pidls.begin(), pidls.end());\n    BOOST_REQUIRE_EQUAL(array.size(), 2U);\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(numeric_cast<UINT>(array.size()), array.as_array(),\n                            sandbox_pidl().get(), Provider());\n    BOOST_REQUIRE(data_object);\n    return data_object;\n}\n}\n} // namespace test::fixture\n"
  },
  {
    "path": "test/fixtures/provider_fixture.hpp",
    "content": "// Copyright 2009, 2010, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_PROVIDER_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_PROVIDER_FIXTURE_HPP\n\n#include \"test/common_boost/MockConsumer.hpp\"\n#include \"test/common_boost/SwishPidlFixture.hpp\"\n#include \"test/fixtures/sftp_fixture.hpp\"\n\n#include \"swish/provider/sftp_provider.hpp\"\n\n#include <ssh/filesystem/path.hpp>\n\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/filesystem/path.hpp> // path\n#include <boost/shared_ptr.hpp>\n\n#include <string>\n\n#include <vector>\n\n#include <ObjIdl.h> // IDataObject\n\nnamespace test\n{\nnamespace fixtures\n{\n\n/** Fixture for tests that need a backend data provider. */\nclass provider_fixture : virtual public sftp_fixture,\n                         public test::SwishPidlFixture // for fake_swish_pidl\n{\npublic:\n    /**\n     * Get an sftp_provider connected to the fixture SSH server.\n     */\n    boost::shared_ptr<swish::provider::sftp_provider> Provider();\n\n    /**\n     * Get a dummy consumer to use in calls to provider.\n     */\n    comet::com_ptr<test::MockConsumer> Consumer();\n\n    /**\n     * Return an absolute PIDL to a remote directory.\n     *\n     * We cheat by returning a PIDL to a HostFolder item with the\n     * shortcut path set to the remote directory.\n     */\n    washer::shell::pidl::apidl_t\n    directory_pidl(const ssh::filesystem::path& directory);\n\n    /**\n     * Return an absolute PIDL to the sandbox on the remote end.\n     *\n     * This is, of course, the local sandbox but the PIDL points to\n     * it via Swish rather than via the local filesystem.\n     */\n    washer::shell::pidl::apidl_t sandbox_pidl();\n\n    /**\n     * Return pidls for all the items in the sandbox directory.\n     */\n    std::vector<washer::shell::pidl::cpidl_t> pidls_in_sandbox();\n\n    /**\n     * Make a DataObject to all the items in the sandbox, via the SFTP\n     * connection.\n     */\n    comet::com_ptr<IDataObject> data_object_from_sandbox();\n};\n}\n} // namespace test::fixture\n\n#endif\n"
  },
  {
    "path": "test/fixtures/session_fixture.cpp",
    "content": "// Copyright 2010, 2011, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"session_fixture.hpp\"\n\n#include <boost/system/system_error.hpp> // system_error\n#include <boost/throw_exception.hpp>     // BOOST_THROW_EXCEPTION\n\n#include <locale>\n#include <sstream>   // basic_ostringstream\n#include <stdexcept> // logic_error\n#include <string>\n\nusing ssh::session;\n\nusing boost::system::system_error;\n\nusing std::auto_ptr;\nusing std::locale;\nusing std::string;\nusing std::ostringstream;\nusing std::logic_error;\n\nnamespace test\n{\nnamespace fixtures\n{\n\nnamespace\n{\n\n/**\n * Locale-independent port number to port string conversion.\n */\nstring port_to_string(long port)\n{\n    ostringstream stream;\n    stream.imbue(locale::classic()); // force locale-independence\n    stream << port;\n    if (!stream)\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Unable to convert port number to string\"));\n\n    return stream.str();\n}\n\nvoid open_socket_to_host(boost::asio::io_service& io,\n                         boost::asio::ip::tcp::socket& socket,\n                         const string& host_name, int port)\n{\n    using boost::asio::ip::tcp;\n\n    tcp::resolver resolver(io);\n    typedef tcp::resolver::query Lookup;\n    Lookup query(host_name, port_to_string(port));\n\n    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);\n    tcp::resolver::iterator end;\n\n    boost::system::error_code error = boost::asio::error::host_not_found;\n    while (error && endpoint_iterator != end)\n    {\n        socket.close();\n        socket.connect(*endpoint_iterator++, error);\n    }\n    if (error)\n        BOOST_THROW_EXCEPTION(system_error(error));\n}\n}\n\nsession_fixture::session_fixture()\n    : m_io(0),\n      m_socket(m_io),\n      m_session(session(open_socket(host(), port()).native()))\n{\n}\n\nsession& session_fixture::test_session()\n{\n    return m_session;\n}\n\nauto_ptr<boost::asio::ip::tcp::socket>\nsession_fixture::connect_additional_socket()\n{\n    auto_ptr<boost::asio::ip::tcp::socket> socket(\n        new boost::asio::ip::tcp::socket(m_io));\n\n    open_socket_to_host(m_io, *socket, host(), port());\n\n    return socket;\n}\n\nboost::asio::ip::tcp::socket&\nsession_fixture::open_socket(const string& host_name, int port)\n{\n    open_socket_to_host(m_io, m_socket, host_name, port);\n\n    return m_socket;\n}\n}\n} // namespace test::fixtures\n"
  },
  {
    "path": "test/fixtures/session_fixture.hpp",
    "content": "// Copyright 2010, 2011, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_SESSION_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_SESSION_FIXTURE_HPP\n\n#include \"openssh_fixture.hpp\"\n\n#include <ssh/session.hpp>\n\n#include <boost/asio/ip/tcp.hpp> // Boost sockets\n\n#include <string>\n\nnamespace test\n{\nnamespace fixtures\n{\n\n/**\n * Fixture serving ssh::session objects connected to a running server.\n */\nclass session_fixture : virtual public openssh_fixture\n{\npublic:\n    session_fixture();\n\n    ssh::session& test_session();\n\n    std::auto_ptr<boost::asio::ip::tcp::socket> connect_additional_socket();\n\nprivate:\n    boost::asio::ip::tcp::socket& open_socket(const std::string& host_name,\n                                              int port);\n\n    boost::asio::io_service m_io; ///< Boost IO system\n    boost::asio::ip::tcp::socket m_socket;\n    ssh::session m_session;\n};\n}\n} // namespace test::fixtures\n\n#endif\n"
  },
  {
    "path": "test/fixtures/sftp_fixture.cpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n#include <ssh/stream.hpp>\n\n#include <boost/bind.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\n#include <string>\n\nusing ssh::filesystem::directory_iterator;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::path;\nusing ssh::filesystem::sftp_file;\nusing ssh::filesystem::sftp_filesystem;\nusing ssh::session;\n\nusing boost::bind;\nusing boost::uuids::random_generator;\n\nusing test::fixtures::session_fixture;\n\nusing std::string;\n\nnamespace\n{\n\nbool filename_matches(const string& filename, const sftp_file& remote_file)\n{\n    return filename == remote_file.path().filename();\n}\n}\n\nnamespace test\n{\nnamespace fixtures\n{\n\nsftp_fixture::sftp_fixture() : m_filesystem(authenticate_and_create_sftp())\n{\n}\n\nsftp_filesystem& sftp_fixture::filesystem()\n{\n    return m_filesystem;\n}\n\npath sftp_fixture::sandbox() const\n{\n    return \"sandbox\";\n}\n\npath sftp_fixture::absolute_sandbox() const\n{\n    return \"/home/swish/sandbox\";\n}\n\nsftp_file sftp_fixture::find_file_in_sandbox(const string& filename)\n{\n    directory_iterator it = filesystem().directory_iterator(sandbox());\n    directory_iterator pos = find_if(it, filesystem().directory_iterator(),\n                                     bind(filename_matches, filename, _1));\n    BOOST_REQUIRE(pos != filesystem().directory_iterator());\n\n    return *pos;\n}\n\npath sftp_fixture::new_file_in_sandbox()\n{\n    random_generator generator;\n\n    path filename = to_string(generator());\n    return new_file_in_sandbox(filename);\n}\n\npath sftp_fixture::new_file_in_sandbox(const path& filename)\n{\n    path file = sandbox() / filename;\n    ofstream(filesystem(), file).close();\n    return file;\n}\n\npath sftp_fixture::new_file_in_sandbox_containing_data(const string& data)\n{\n    path p = new_file_in_sandbox();\n    ofstream s(filesystem(), p);\n\n    s.write(data.data(), data.size());\n\n    return p;\n}\n\npath sftp_fixture::new_file_in_sandbox_containing_data(const path& name,\n                                                       const string& data)\n{\n    path p = new_file_in_sandbox(name);\n    ofstream s(filesystem(), p);\n\n    s.write(data.data(), data.size());\n\n    return p;\n}\n\npath sftp_fixture::new_directory_in_sandbox()\n{\n    random_generator generator;\n\n    path directory_name = to_string(generator());\n    path directory = sandbox() / directory_name;\n    create_directory(filesystem(), directory);\n    return directory;\n}\n\npath sftp_fixture::new_directory_in_sandbox(const path& name)\n{\n    path directory = sandbox() / name;\n    create_directory(filesystem(), directory);\n    return directory;\n}\n\nvoid sftp_fixture::create_symlink(const path& link, const path& target)\n{\n    // Passing arguments in the wrong order to work around OpenSSH bug\n    ::ssh::filesystem::create_symlink(filesystem(), target, link);\n}\n\nsftp_filesystem sftp_fixture::authenticate_and_create_sftp()\n{\n    session& s = test_session();\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n    return s.connect_to_filesystem();\n}\n}\n}\n"
  },
  {
    "path": "test/fixtures/sftp_fixture.hpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SWISH_TEST_FIXTURES_SFTP_FIXTURE_HPP\n#define SWISH_TEST_FIXTURES_SFTP_FIXTURE_HPP\n\n#include \"session_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n\n#include <string>\n\nnamespace test\n{\nnamespace fixtures\n{\nclass sftp_fixture : virtual public session_fixture\n{\npublic:\n    sftp_fixture();\n\n    ssh::filesystem::path sandbox() const;\n\n    ssh::filesystem::path absolute_sandbox() const;\n\n    ssh::filesystem::sftp_filesystem& filesystem();\n\n    ssh::filesystem::sftp_file\n    find_file_in_sandbox(const std::string& filename);\n\n    ssh::filesystem::path new_file_in_sandbox();\n\n    ssh::filesystem::path\n    new_file_in_sandbox(const ssh::filesystem::path& filename);\n\n    ssh::filesystem::path\n    new_file_in_sandbox_containing_data(const std::string& data);\n\n    ssh::filesystem::path\n    new_file_in_sandbox_containing_data(const ssh::filesystem::path& name,\n                                        const std::string& data);\n\n    ssh::filesystem::path new_directory_in_sandbox();\n\n    ssh::filesystem::path\n    new_directory_in_sandbox(const ssh::filesystem::path& name);\n\n    void create_symlink(const ssh::filesystem::path& link,\n                        const ssh::filesystem::path& target);\n\nprivate:\n    ssh::filesystem::sftp_filesystem authenticate_and_create_sftp();\n\n    ssh::filesystem::sftp_filesystem m_filesystem;\n};\n}\n}\n\n#endif\n"
  },
  {
    "path": "test/fixtures/ssh_server/Dockerfile",
    "content": "# Copyright 2016 Alexander Lamaison\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU 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 General Public License for more details.\n\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http:#www.gnu.org/licenses/>.\n\nFROM debian:jessie\n\nRUN apt-get update \\\n && apt-get install -y openssh-server \\\n && apt-get clean \\\n && rm -rf /var/lib/apt/lists/*\nRUN mkdir /var/run/sshd\n\n# Chmodding because, when building on Windows, files are copied in with\n# -rwxr-xr-x permissions.\n#\n# Copying to a temp location, then moving because chmodding the copied file has\n# no effect (Docker AUFS-related bug maybe?)\nCOPY ssh_host_rsa_key /tmp/etc/ssh/ssh_host_rsa_key\nRUN mv /tmp/etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key\nRUN chmod 600 /etc/ssh/ssh_host_rsa_key\n\nRUN adduser --disabled-password --gecos 'Test user for Swish integration tests' swish\nRUN echo 'swish:my test password' | chpasswd\n\nRUN sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config\n\n# SSH login fix. Otherwise user is kicked off after login\nRUN sed 's@session\\s*required\\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\nUSER swish\n\nRUN mkdir -p /home/swish/.ssh\nRUN mkdir -p /home/swish/sandbox\n\nCOPY authorized_keys /tmp/swish/.ssh/authorized_keys\nRUN cp /tmp/swish/.ssh/authorized_keys /home/swish/.ssh/authorized_keys\nRUN chmod 600 /home/swish/.ssh/authorized_keys\n\nUSER root\n\nEXPOSE 22\n# # -e gives logs via 'docker logs'\nCMD [\"/usr/sbin/sshd\", \"-D\", \"-e\"]\n"
  },
  {
    "path": "test/fixtures/ssh_server/authorized_keys",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty\nssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty\n"
  },
  {
    "path": "test/fixtures/ssh_server/ssh_host_rsa_key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6\ncvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99\nfnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB\neNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMA\nwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQK\nypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbwIBIwKCAQAd9Cu9heWrs+UAinvv\nIwmq/EhnDGQijJoOt1zEMrpXSekyq7mQDgN0SZdJLPeSlSRQ5nVq5/dZroYB3A5i\nE7N3F7nibcJskWq5rcMyGjQHwod8wqfMiGcL6mjeZu2jLXprm0NDpJ3DyicbCA2G\nEhnpoHmktIBE5FsslI/nHer2o6OA/kVWSEjak+pvI1pm22T8QOBBfY0yAX7B0ebk\n8o4lB4cdLf3In7Q0ahpHNOwIPdRvQ2c4Tm/DcfUBkTW2ZYGUd45cFsyHqXZscNNy\nGX2Wcy/FLEvQ6zBFJsNLpxCYsUyBxfSDygn9dx9RQfiWFXjdRaRPpyRAr+BTXkLU\nyvabAoGBANt7sxfjvu/SLkRc7TnBoJ0h/AL7Mcuu9PJmOnis4boyF9ZxqbiRiS3J\nyK+EKxfC0S+xf5WJ5uf7dVGnOXHXKaRl4xH90iRtryNlvtILZwHw1DTqRFxv9jtz\ntTRrYMEHAnMKzadgDfV/lv4iJ6nwFzK76GQ7RQNZYiGTMEh3pUNjAoGBAMvNLGpz\nFxhpIh+fVvRjawKgGVP87T482WOUdsF18EEPFMe6D7DO5xpLuJi+C7QkvMI8WjvD\n/3RGvaSh9Wt7ikLZpeogiSJy121HsEqheTR5hTx2t72ClrjZvIhLbQMRu6PqGPu/\nHOC2urEGGYm7O2vnftwpuG3zIVVLM2KstPCFAoGBAM7w+VEJ7opYdMQdGi8kRvqN\nwbmrAxCAY0ryrCijALbexgS0T5DDu9q28Gr49W4stpquq35dc0/BNBnJje7+EVHc\naGFrqOCErHHU9b66Sy23LnsIxBykFAwrRHNAq66u1mx35nk9Tv1pq58nhHun21u4\nfAa7ijZblwm2qd3tJsqBAoGAEXf8ficfPJtMEVbM8GBLADmbxV7Sga1xuBQKLdbo\ntR6MwKmMUPvKqnuE2eRnZzZZUnoznrkHRHsXkcS9Q7ohyzc6G2Hf3mGdb8RQ8HQ9\nlsiWZESwqdf+SlvOVNND27EQFV01V2gnC/JnxgfWTaJVjOf07ky4CWycdQZyHmaT\nKo8CgYB58jOyXMdo2ggOCG/HX2H92KPPpFUBFCX27fCue8BZLD5quIltpXupx5oj\nEyltgvPcmNDgvdSadkHvP5s6nykS+n5we+d9yIIJF/BfETWsXjR3ooip+trqiirw\n0aHqUDFcYn9unm2wtrMYYViiDLRijNwLZ2sG0JIU4JHyseh+NA==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/fixtures/test_known_hosts",
    "content": "host1.example.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\nunrecognisedkey.example.com,192.168.2.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\nhost2.example.com,10.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\nhost3.example.com,192.168.1.1 ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\n"
  },
  {
    "path": "test/fixtures/test_known_hosts_hashed",
    "content": "# There are two entries for each host, first the hashed IP address, second\n# the hashed host.  If you regenerate this file from test_known_hosts\n# using ssh-keygen -H and the erasure tests start failing, ssh-keygen probably\n# reordered them so that the host has is first.  Just swap each pair of lines\n# to fix this.\n|1|zgCaq7/YEx5K8JidOuvMUgLen5M=|2E8Pgkzd8Izo5yUCTkdUHJYxmOE= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ==\n|1|t3DdA7tkFZOFbi/s0eZ7J5+EFoo=|pZVcJk4rGYIZTdmM56xF75BUBIA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ==\n|1|omREKWLfSo4emtLIvwB/TQrLwAk=|banp/Ra/d10UwM5WwLtzMUeKd7M= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n|1|TdmIYPmnBFnDMkYhDtKjnoIyW8M=|tsCyyCykyxVRnt2NejdekCgEqFo= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n|1|QoctyHBi5b2zysdvpnPn/nh7jXI=|UhxbMpvA8OkmFMJBm5ym3Enfurc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n|1|sf6lAuYow3JBSdiZQ99BP4Qnbqc=|LeWkRRPlTMoztEws6an/K6vHwNY= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n|1|MNSfXoAF6FmLI6GUb1hOh+rzd40=|eM/07xIcp1edKt3h4kUsnzyjKjY= ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w==\n|1|oS2oUoU87z0tnrn6xoZlietQCyo=|k1m5Wo5oSXL5ddO0LgnMtXwg5FY= ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w==\n"
  },
  {
    "path": "test/fixtures/test_known_hosts_out",
    "content": "192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\nhost1.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\n192.168.2.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\nunrecognisedkey.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n10.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\nhost2.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n192.168.1.1 ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\nhost3.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\n"
  },
  {
    "path": "test/forms/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  add_host_test.cpp\n  password_test.cpp)\n\nswish_test_suite(\n  SUBJECT forms\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS gui)\n"
  },
  {
    "path": "test/forms/add_host_test.cpp",
    "content": "/**\n    @file\n\n    Exercise new host dialogue box.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#pragma comment(linker, \\\n    \"\\\"/manifestdependency:type='Win32' \"\\\n    \"name='Microsoft.Windows.Common-Controls' \"\\\n    \"version='6.0.0.0' \"\\\n    \"processorArchitecture='*' \"\\\n    \"publicKeyToken='6595b64144ccf1df' \"\\\n    \"language='*'\\\"\")\n\n#include \"swish/forms/add_host.hpp\" // test subject\n\n#include <boost/test/unit_test.hpp>\n\nBOOST_AUTO_TEST_SUITE(add_host_tests)\n\nnamespace {\n\n/**\n * Sends a button click to the Cancel button of the dialog programmatically.\n *\n * @todo  This relies on internal knowledge that the cancel is the 17th control\n *        and the dialog template applies an offset of 100.  Not cool!\n */\nDWORD WINAPI click_cancel_thread(LPVOID /*thread_param*/)\n{\n    ::Sleep(2000);\n    HWND hwnd = GetForegroundWindow();\n\n    // Send Cancel button click message to dialog box\n    //WPARAM wParam = MAKEWPARAM(IDCANCEL, BN_CLICKED);\n    //pThis->m_dlg.SendMessage(WM_COMMAND, wParam);\n\n    // Alternatively post left mouse button up/down directly to Cancel button\n    ::SendMessage(\n        ::GetDlgItem(hwnd, 117), WM_LBUTTONDOWN, MK_LBUTTON, NULL);\n    ::SendMessage(::GetDlgItem(hwnd, 117), WM_LBUTTONUP, NULL, NULL);\n    return 0;\n}\n\n}\n\nBOOST_AUTO_TEST_CASE( show )\n{\n    DWORD thread_id;\n    HANDLE thread = ::CreateThread(\n        NULL, 0, click_cancel_thread, NULL, 0, &thread_id);\n\n    try\n    {\n        swish::forms::add_host(NULL);\n    }\n    catch (const std::exception& e)\n    {\n        BOOST_REQUIRE_EQUAL(e.what(), \"user cancelled form\");\n    }\n\n    ::CloseHandle(thread);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/forms/password_test.cpp",
    "content": "/**\n    @file\n\n    Exercise password dialogue box.\n\n    @if license\n\n    Copyright (C) 2010, 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/forms/password.hpp\" // test subject\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nBOOST_AUTO_TEST_SUITE(password_tests)\n\n/**\n * Sends a button click to the Cancel button of the dialog programmatically.\n *\n * @todo  This relies on internal knowledge that the cancel is the 4th control\n *        and the dialog template applies an offset of 100.  Not cool!\n */\nDWORD WINAPI click_cancel_thread(LPVOID /*thread_param*/)\n{\n    ::Sleep(1700);\n    HWND hwnd = GetForegroundWindow();\n\n    // Post left mouse button up/down directly to Cancel button\n    ::SendMessage(\n        ::GetDlgItem(hwnd, 103), WM_LBUTTONDOWN, MK_LBUTTON, NULL);\n    ::SendMessage(::GetDlgItem(hwnd, 103), WM_LBUTTONUP, NULL, NULL);\n    return 0;\n}\n\nBOOST_AUTO_TEST_CASE( show )\n{\n    DWORD thread_id;\n    HANDLE thread = ::CreateThread(\n        NULL, 0, click_cancel_thread, NULL, 0, &thread_id);\n\n    std::wstring password;\n    BOOST_CHECK(\n        !swish::forms::password_prompt(\n            NULL, L\"Oi! Gimme a password\", password));\n    BOOST_CHECK(password.empty());\n\n    ::CloseHandle(thread);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/host_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  columns_test.cpp\n  host_management_test.cpp\n  host_pidl_test.cpp\n  properties_test.cpp\n  view_callback_test.cpp)\n\nswish_test_suite(\n  SUBJECT host_folder\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n"
  },
  {
    "path": "test/host_folder/columns_test.cpp",
    "content": "/**\n    @file\n\n    Exercise host-folder columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/host_folder/columns.hpp> // test subject, Column\n#include <swish/host_folder/host_pidl.hpp> // create_host_itemid\n\n#include <test/common_boost/helpers.hpp> // wide-string output\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nusing swish::host_folder::Column;\nusing swish::host_folder::create_host_itemid;\n\nusing washer::shell::pidl::cpidl_t;\n\nusing std::wstring;\n\nBOOST_AUTO_TEST_SUITE(column_tests)\n\nnamespace {\n\n    cpidl_t gimme_pidl()\n    {\n        return create_host_itemid(\n            L\"myhost\", L\"bobuser\", L\"/home/bobuser\", 25, L\"My Label\");\n    }\n\n    wstring header(size_t index)\n    {\n        Column col(index);\n        return col.header();\n    }\n\n    wstring detail(size_t index)\n    {\n        Column col(index);\n        return col.detail(gimme_pidl());\n    }\n}\n\nBOOST_AUTO_TEST_CASE( label )\n{\n    BOOST_CHECK_EQUAL(header(0), L\"Name\");\n    BOOST_CHECK_EQUAL(detail(0), L\"My Label\");\n}\n\nBOOST_AUTO_TEST_CASE( host )\n{\n    BOOST_CHECK_EQUAL(header(1), L\"Host\");\n    BOOST_CHECK_EQUAL(detail(1), L\"myhost\");\n}\n\nBOOST_AUTO_TEST_CASE( user )\n{\n    BOOST_CHECK_EQUAL(header(2), L\"Username\");\n    BOOST_CHECK_EQUAL(detail(2), L\"bobuser\");\n}\n\nBOOST_AUTO_TEST_CASE( port )\n{\n    BOOST_CHECK_EQUAL(header(3), L\"Port\");\n    BOOST_CHECK_EQUAL(detail(3), L\"25\");\n}\n\nBOOST_AUTO_TEST_CASE( path )\n{\n    BOOST_CHECK_EQUAL(header(4), L\"Remote path\");\n    BOOST_CHECK_EQUAL(detail(4), L\"/home/bobuser\");\n}\n\nBOOST_AUTO_TEST_CASE( type )\n{\n    BOOST_CHECK_EQUAL(header(5), L\"Type\");\n    BOOST_CHECK_EQUAL(detail(5), L\"Network Drive\");\n}\n\n/**\n * Get one header too far.\n */\nBOOST_AUTO_TEST_CASE( out_of_bounds )\n{\n    BOOST_CHECK_THROW(header(6), std::exception);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/host_folder/host_management_test.cpp",
    "content": "/**\n    @file\n\n    Exercise host management registry manipulation.\n\n    @if license\n\n    Copyright (C) 2012, 2015  Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/host_folder/host_management.hpp> // test subject\n\n#include <swish/host_folder/host_pidl.hpp> // host_itemid_view,\n                                           // find_host_itemid\n\n#include <test/common_boost/helpers.hpp> // wide-string output\n\n#include <washer/shell/pidl.hpp> // cpidl_t\n\n#include <boost/test/unit_test.hpp>\n\n#include <comet/regkey.h>\n\n#include <string>\n\nusing swish::host_folder::host_management::AddConnectionToRegistry;\nusing swish::host_folder::host_management::FindConnectionInRegistry;\nusing swish::host_folder::host_management::LoadConnectionsFromRegistry;\nusing swish::host_folder::host_management::RemoveConnectionFromRegistry;\nusing swish::host_folder::host_management::RenameConnectionInRegistry;\nusing swish::host_folder::host_itemid_view;\nusing swish::host_folder::find_host_itemid;\n\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::regkey;\n\nusing std::exception;\nusing std::wstring;\n\nnamespace {\n\n    const wstring TEST_CONNECTION_NAME = L\"T\";\n    // It doesn't matter what this name is, just as long as it's different\n    const wstring OTHER_TEST_CONNECTION_NAME = L\"T2\";\n\n    struct cleanup_fixture\n    {\n        ~cleanup_fixture()\n        {\n            regkey connections =\n                regkey(HKEY_CURRENT_USER).open(L\"Software\\\\Swish\\\\Connections\");\n            connections.delete_subkey_nothrow(TEST_CONNECTION_NAME);\n            connections.delete_subkey_nothrow(OTHER_TEST_CONNECTION_NAME);\n        }\n    };\n\n    regkey test_connection_key()\n    {\n        return regkey(HKEY_CURRENT_USER).open(\n            L\"Software\\\\Swish\\\\Connections\\\\\" + TEST_CONNECTION_NAME);\n    }\n\n    regkey other_test_connection_key()\n    {\n        return regkey(HKEY_CURRENT_USER).open(\n            L\"Software\\\\Swish\\\\Connections\\\\\" + OTHER_TEST_CONNECTION_NAME);\n    }\n}\n\nBOOST_FIXTURE_TEST_SUITE(host_management_tests, cleanup_fixture)\n\nBOOST_AUTO_TEST_CASE( add_minimal )\n{\n    AddConnectionToRegistry(TEST_CONNECTION_NAME, L\"h\", 1U, L\"u\", L\"/\");\n\n    regkey new_connection = test_connection_key();\n\n    BOOST_CHECK_EQUAL(new_connection[L\"Host\"].str(), L\"h\");\n    BOOST_CHECK_EQUAL(new_connection[L\"User\"].str(), L\"u\");\n    BOOST_CHECK_EQUAL(new_connection[L\"Port\"].dword(), 1U);\n    BOOST_CHECK_EQUAL(new_connection[L\"Path\"].str(), L\"/\");\n}\n\nBOOST_AUTO_TEST_CASE( add )\n{\n    wstring hostname =\n        L\"a.nice.really.beautiful.long.loooooooooooooooooooooooooooooo\"\n        L\"ooooooong.host.name.example\";\n    wstring username =\n        L\"dsflkm dfsdoifmo opim[i\\\"moimoimoimoim[ipom]0k3\\\"9k42p3m4l23 4k 23;\"\n        L\"krjn1;oi[9j[c09j38j4kj2 3k4 ;2o3iun4[029j3[9mre4;cj ;l3i45r c\";\n    wstring path =\n        L\"/krjn1;oi[9j[c09j38j4kj2 3k4 ;2o3iun4[029j3[9mre4;cj ;l3i45r c\"\n        L\"dsflkm dfsdoifmo opim[i\\\"moimoimoimoim[ipom]0k3\\\"9k42p3m4l23 4k 23;\";\n\n    AddConnectionToRegistry(\n        TEST_CONNECTION_NAME, hostname, 65535U, username, path);\n\n    regkey new_connection = test_connection_key();\n\n    BOOST_CHECK_EQUAL(new_connection[L\"Host\"].str(), hostname);\n    BOOST_CHECK_EQUAL(new_connection[L\"User\"].str(), username);\n    BOOST_CHECK_EQUAL(new_connection[L\"Port\"].dword(), 65535U);\n    BOOST_CHECK_EQUAL(new_connection[L\"Path\"].str(), path);\n}\n\nBOOST_AUTO_TEST_CASE( load )\n{\n    AddConnectionToRegistry(TEST_CONNECTION_NAME, L\"h\", 1U, L\"u\", L\"/\");\n    BOOST_CHECK_GE(LoadConnectionsFromRegistry().size(), 1);\n}\n\nBOOST_AUTO_TEST_CASE( remove )\n{\n    AddConnectionToRegistry(TEST_CONNECTION_NAME, L\"h\", 1U, L\"u\", L\"/\");\n    RemoveConnectionFromRegistry(TEST_CONNECTION_NAME);\n\n    BOOST_CHECK_THROW(test_connection_key(), exception);\n}\n\nBOOST_AUTO_TEST_CASE( rename )\n{\n    AddConnectionToRegistry(TEST_CONNECTION_NAME, L\"h\", 1U, L\"u\", L\"/\");\n    RenameConnectionInRegistry(TEST_CONNECTION_NAME, OTHER_TEST_CONNECTION_NAME);\n\n    regkey renamed_connection = other_test_connection_key();\n\n    BOOST_CHECK_EQUAL(renamed_connection[L\"Host\"].str(), L\"h\");\n    BOOST_CHECK_EQUAL(renamed_connection[L\"User\"].str(), L\"u\");\n    BOOST_CHECK_EQUAL(renamed_connection[L\"Port\"].dword(), 1U);\n    BOOST_CHECK_EQUAL(renamed_connection[L\"Path\"].str(), L\"/\");\n\n    BOOST_CHECK_THROW(test_connection_key(), exception);\n}\n\nBOOST_AUTO_TEST_CASE( find )\n{\n    AddConnectionToRegistry(TEST_CONNECTION_NAME, L\"h\", 1U, L\"u\", L\"/\");\n    cpidl_t connection = *FindConnectionInRegistry(TEST_CONNECTION_NAME);\n    host_itemid_view connection_view = host_itemid_view(connection);\n\n    BOOST_CHECK_EQUAL(connection_view.host(), L\"h\");\n    BOOST_CHECK_EQUAL(connection_view.user(), L\"u\");\n    BOOST_CHECK_EQUAL(connection_view.port(), 1U);\n    BOOST_CHECK_EQUAL(connection_view.path(), L\"/\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/host_folder/host_pidl_test.cpp",
    "content": "/**\n    @file\n\n    Exercise host PIDL.\n\n    @if license\n\n    Copyright (C) 2011, 2016 Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/host_folder/host_pidl.hpp> // test subject\n\n#include \"test/common_boost/helpers.hpp\"          // wide-char comparison\n#include <test/common_boost/SwishPidlFixture.hpp> // fake_swish_pidl\n\n#include <washer/shell/pidl.hpp>  // apidl_t, cpidl_t\n#include <washer/shell/shell.hpp> // pidl_from_parsing_name\n\n#include <boost/test/unit_test.hpp>\n\n#include <exception>\n#include <stdexcept> // runtime_error\n#include <string>\n\nusing swish::host_folder::create_host_itemid;\nusing swish::host_folder::host_itemid_view;\nusing swish::host_folder::find_host_itemid;\nusing swish::host_folder::url_from_host_itemid;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl_from_parsing_name;\n\nusing comet::com_ptr;\n\nusing std::exception;\nusing std::runtime_error;\n\nBOOST_FIXTURE_TEST_SUITE(host_pidl_tests, test::SwishPidlFixture)\n\nBOOST_AUTO_TEST_CASE(create)\n{\n    cpidl_t item = create_host_itemid(L\"host.example.com\", L\"bobuser\",\n                                      L\"/home/directory\", 65535, L\"My Label\");\n\n    BOOST_REQUIRE(host_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).user(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).path(), L\"/home/directory\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).label(), L\"My Label\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).port(), 65535);\n    // repeat\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n}\n\nBOOST_AUTO_TEST_CASE(create_from_raw)\n{\n    cpidl_t managed_pidl =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/home/directory\",\n                           65535, L\"My Label\");\n    PCITEMID_CHILD item = managed_pidl.get();\n\n    BOOST_REQUIRE(host_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).user(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).path(), L\"/home/directory\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).label(), L\"My Label\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).port(), 65535);\n    // repeat\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n}\n\nBOOST_AUTO_TEST_CASE(create_default_arg)\n{\n    cpidl_t item = create_host_itemid(L\"host.example.com\", L\"bobuser\",\n                                      L\"/home/directory\", 65535);\n\n    BOOST_REQUIRE(host_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).user(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).path(), L\"/home/directory\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).label(), L\"\");\n    BOOST_CHECK_EQUAL(host_itemid_view(item).port(), 65535);\n    // repeat\n    BOOST_CHECK_EQUAL(host_itemid_view(item).host(), L\"host.example.com\");\n}\n\nBOOST_AUTO_TEST_CASE(invalid_host_item)\n{\n    apidl_t pidl = washer::shell::special_folder_pidl(CSIDL_DRIVES);\n\n    BOOST_CHECK(!host_itemid_view(pidl).valid());\n    BOOST_CHECK_THROW(host_itemid_view(pidl).host(), exception);\n    BOOST_CHECK_THROW(host_itemid_view(pidl).user(), exception);\n    BOOST_CHECK_THROW(host_itemid_view(pidl).path(), exception);\n    BOOST_CHECK_THROW(host_itemid_view(pidl).label(), exception);\n    BOOST_CHECK_THROW(host_itemid_view(pidl).port(), exception);\n    // repeat\n    BOOST_CHECK_THROW(host_itemid_view(pidl).host(), exception);\n}\n\nBOOST_AUTO_TEST_CASE(find_host_item_in_pidl)\n{\n    apidl_t pidl = fake_swish_pidl();\n    cpidl_t item =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/\", 65535);\n    pidl += item;\n\n    pidl_t result = *find_host_itemid(pidl);\n\n    BOOST_REQUIRE(host_itemid_view(result).valid());\n    BOOST_CHECK_EQUAL(host_itemid_view(result).host(), L\"host.example.com\");\n    BOOST_CHECK_EQUAL(host_itemid_view(result).user(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(host_itemid_view(result).path(), L\"/\");\n    BOOST_CHECK_EQUAL(host_itemid_view(result).label(), L\"\");\n    BOOST_CHECK_EQUAL(host_itemid_view(result).port(), 65535);\n}\n\nBOOST_AUTO_TEST_CASE(fail_to_find_host_item_in_pidl)\n{\n    apidl_t pidl = fake_swish_pidl();\n\n    BOOST_CHECK_THROW(find_host_itemid(pidl), runtime_error);\n}\n\nBOOST_AUTO_TEST_CASE(hostitem_to_url)\n{\n    cpidl_t item =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p\", 65535);\n\n    BOOST_CHECK_EQUAL(url_from_host_itemid(item, false),\n                      L\"sftp://bobuser@host.example.com:65535//p\");\n}\n\nBOOST_AUTO_TEST_CASE(hostitem_to_url_default_port)\n{\n    cpidl_t item =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p\", 22);\n\n    BOOST_CHECK_EQUAL(url_from_host_itemid(item, false),\n                      L\"sftp://bobuser@host.example.com//p\");\n}\n\nBOOST_AUTO_TEST_CASE(hostitem_to_url_canonical)\n{\n    cpidl_t item =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p\", 65535);\n\n    BOOST_CHECK_EQUAL(url_from_host_itemid(item, true),\n                      L\"sftp://bobuser@host.example.com:65535//p\");\n}\n\nBOOST_AUTO_TEST_CASE(hostitem_to_url_default_port_canonical)\n{\n    cpidl_t item =\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p\", 22);\n\n    BOOST_CHECK_EQUAL(url_from_host_itemid(item, true),\n                      L\"sftp://bobuser@host.example.com:22//p\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/host_folder/properties_test.cpp",
    "content": "/**\n    @file\n\n    Exercise host-folder properties.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/host_folder/properties.hpp> // test subject\n#include <swish/host_folder/host_pidl.hpp> // create_host_itemid\n\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\n#include <Propkey.h> // PKEY_ *\n\nusing swish::host_folder::compare_pidls_by_property;\nusing swish::host_folder::create_host_itemid;\nusing swish::host_folder::property_from_pidl;\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::property_key;\n\nusing comet::variant_t;\nusing std::string;\n\nBOOST_AUTO_TEST_SUITE(properties_tests)\n\nnamespace {\n\n    cpidl_t gimme_pidl()\n    {\n        return create_host_itemid(\n            L\"myhost\", L\"bobuser\", L\"/home/bobuser\", 25, L\"My Label\");\n    }\n}\n\nBOOST_AUTO_TEST_CASE( prop_label )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ItemNameDisplay);\n    BOOST_CHECK_EQUAL(prop, \"My Label\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_host )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ComputerName);\n    BOOST_CHECK_EQUAL(prop, \"myhost\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_user )\n{\n    string prop = property_from_pidl(\n        gimme_pidl(), swish::host_folder::PKEY_SwishHostUser);\n    BOOST_CHECK_EQUAL(prop, \"bobuser\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_port )\n{\n    string prop = property_from_pidl(\n        gimme_pidl(), swish::host_folder::PKEY_SwishHostPort);\n    BOOST_CHECK_EQUAL(prop, \"25\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_path )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ItemPathDisplay);\n    BOOST_CHECK_EQUAL(prop, \"/home/bobuser\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_type )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ItemType);\n    BOOST_CHECK_EQUAL(prop, \"Network Drive\");\n}\n\nnamespace {\n    \n    cpidl_t comp_pidl()\n    {\n        return create_host_itemid(\n            L\"myhost\", L\"boxuser\", L\"/home/aobuser\", 24, L\"Your Label\");\n            //   ==        >               <         <     >\n    }\n\n    int compare(const property_key& key)\n    {\n        return compare_pidls_by_property(gimme_pidl(), comp_pidl(), key);\n    }\n}\n\nBOOST_AUTO_TEST_CASE( comp_label )\n{\n    int res = compare(PKEY_ItemNameDisplay);\n    BOOST_CHECK_LT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_host )\n{\n    int res = compare(PKEY_ComputerName);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_user )\n{\n    int res = compare(swish::host_folder::PKEY_SwishHostUser);\n    BOOST_CHECK_LT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_port )\n{\n    int res = compare(swish::host_folder::PKEY_SwishHostPort);\n    BOOST_CHECK_GT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_path )\n{\n    int res = compare(PKEY_ItemPathDisplay);\n    BOOST_CHECK_GT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_type )\n{\n    int res = compare(PKEY_ItemType);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/host_folder/view_callback_test.cpp",
    "content": "/**\n    @file\n\n    Tests for object that manages relationship with Explorer window.\n\n    @if license\n\n    Copyright (C) 2013, 2016  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#include <swish/host_folder/ViewCallback.hpp> // test subject\n\n#include <test/common_boost/helpers.hpp>          // BOOST_CHECK_OK\n#include <test/common_boost/SwishPidlFixture.hpp> // fake_swish_pidl\n\n#include <washer/gui/menu/button/string_button_description.hpp>\n#include <washer/gui/menu/item/command_item.hpp>\n#include <washer/gui/menu/item/separator_item.hpp>\n#include <washer/gui/menu/item/sub_menu_item.hpp>\n#include <washer/gui/menu/item/sub_menu_item_description.hpp>\n#include <washer/gui/menu/menu.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nusing test::SwishPidlFixture;\n\nusing swish::host_folder::CViewCallback;\n\nusing namespace washer::gui::menu;\n\nusing comet::com_ptr;\n\nusing std::wstring;\n\nBOOST_FIXTURE_TEST_SUITE(view_callback_tests, SwishPidlFixture)\n\nBOOST_AUTO_TEST_CASE(basic_init)\n{\n    com_ptr<IShellFolderViewCB> cb = new CViewCallback(fake_swish_pidl());\n\n    // TODO: Use real message-only window\n    BOOST_CHECK_INTERFACE_OK(cb,\n                             cb->MessageSFVCB(SFVM_WINDOWCREATED, NULL, NULL));\n}\n\nnamespace\n{\n\nsub_menu_item_description dummy_menu(wstring title, UINT id)\n{\n    menu empty_menu;\n\n    sub_menu_item_description menu(string_button_description(title),\n                                   empty_menu);\n    menu.id(id);\n\n    return menu;\n}\n\nsub_menu_item_description dummy_tools_menu()\n{\n    // Purposely not called \"Tools\" to test that code doesn't rely on it\n    return dummy_menu(L\"Bert\", FCIDM_MENU_TOOLS);\n}\n\nsub_menu_item_description dummy_help_menu()\n{\n    // Purposely not called \"Help\" to test that code doesn't rely on it\n    return dummy_menu(L\"Sally\", FCIDM_MENU_HELP);\n}\n\nsub_menu_item_description dummy_file_menu()\n{\n    // Purposely not called \"File\" to test that code doesn't rely on it\n    return dummy_menu(L\"Bob\", FCIDM_MENU_FILE);\n}\n\nclass counting_visitor\n{\npublic:\n    typedef size_t result_type;\n\n    size_t operator()(const sub_menu_item& item)\n    {\n        return item.menu().size();\n    }\n\n    template <typename ItemType>\n    size_t operator()(const ItemType&)\n    {\n        return 0;\n    }\n};\n}\n\nBOOST_AUTO_TEST_CASE(merge_menu)\n{\n    com_ptr<IShellFolderViewCB> cb = new CViewCallback(fake_swish_pidl());\n\n    HMENU raw_menu = ::CreateMenu();\n    // Bitten here by the Most Vexing Parse\n    menu_bar bar((menu_handle::adopt_handle(raw_menu)));\n\n    bar.insert(dummy_tools_menu());\n    bar.insert(dummy_file_menu());\n    bar.insert(dummy_help_menu());\n\n    size_t size_before = bar.size();\n\n    UINT first_id_before = 42;\n    QCMINFO q = {raw_menu, 7, first_id_before, 999, NULL};\n    BOOST_CHECK_INTERFACE_OK(\n        cb,\n        cb->MessageSFVCB(SFVM_MERGEMENU, NULL, reinterpret_cast<LPARAM>(&q)));\n\n    // Merge should not have inserted anything into the menu bar\n    BOOST_CHECK_EQUAL(bar.size(), size_before);\n\n    // But should definitely have inserted something in one of its submenus\n    size_t count = 0;\n    BOOST_FOREACH (const menu_bar::iterator::value_type& item, bar)\n    {\n        count += item.accept(counting_visitor());\n    }\n    BOOST_CHECK_GT(count, 0U);\n\n    // The QCMINFO should have been updated to reflect the added items\n    BOOST_CHECK_GT(q.idCmdFirst, first_id_before);\n}\n\nBOOST_AUTO_TEST_CASE(merge_menu_no_tools)\n{\n    com_ptr<IShellFolderViewCB> cb = new CViewCallback(fake_swish_pidl());\n\n    HMENU raw_menu = ::CreateMenu();\n    // Bitten here by the Most Vexing Parse\n    menu_bar bar((menu_handle::adopt_handle(raw_menu)));\n\n    bar.insert(dummy_file_menu());\n    bar.insert(dummy_help_menu());\n\n    QCMINFO q = {raw_menu, 7, 42, 999, NULL};\n    BOOST_CHECK_INTERFACE_OK(\n        cb,\n        cb->MessageSFVCB(SFVM_MERGEMENU, NULL, reinterpret_cast<LPARAM>(&q)));\n}\n\nBOOST_AUTO_TEST_CASE(merge_menu_no_help)\n{\n    com_ptr<IShellFolderViewCB> cb = new CViewCallback(fake_swish_pidl());\n\n    HMENU raw_menu = ::CreateMenu();\n    // Bitten here by the Most Vexing Parse\n    menu_bar bar((menu_handle::adopt_handle(raw_menu)));\n\n    bar.insert(dummy_file_menu());\n    bar.insert(dummy_tools_menu());\n\n    QCMINFO q = {raw_menu, 7, 42, 999, NULL};\n    BOOST_CHECK_INTERFACE_OK(\n        cb,\n        cb->MessageSFVCB(SFVM_MERGEMENU, NULL, reinterpret_cast<LPARAM>(&q)));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/module.cpp.in",
    "content": "/**\n    @file\n\n    Test suite runner.\n\n    @if license\n\n    Copyright (C) 2013, 2014  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#define BOOST_TEST_MODULE Swish @SWISH_TEST_SUITE_SUBJECT@ tests\n#include <boost/test/unit_test.hpp>\n"
  },
  {
    "path": "test/nse/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  default_context_menu_callback_tests.cpp\n  explorer_command_tests.cpp)\n\nswish_test_suite(\n  SUBJECT nse\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n"
  },
  {
    "path": "test/nse/default_context_menu_callback_tests.cpp",
    "content": "/**\n    @file\n\n    Unit tests for defcm callback implementation.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/nse/default_context_menu_callback.hpp\" // test subject\n\n#include \"test/common_boost/helpers.hpp\"\n#include <boost/test/unit_test.hpp>\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n\n#include <string>\n#include <vector>\n\nusing swish::nse::default_context_menu_callback;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\nBOOST_AUTO_TEST_SUITE(default_context_menu_callback_tests)\n\nBOOST_AUTO_TEST_CASE( create )\n{\n    default_context_menu_callback();\n}\n\nBOOST_AUTO_TEST_CASE( unhandled_message )\n{\n    default_context_menu_callback callback;\n    HRESULT hr = callback(NULL, NULL, (UINT)-1, 6, 7);\n    BOOST_CHECK_EQUAL(hr, E_NOTIMPL);\n}\n\nnamespace {\n\n    class verb_callback : public default_context_menu_callback\n    {\n        virtual void verb(HWND, com_ptr<IDataObject>, UINT, wstring& verb_out)\n        {\n            verb_out = L\"test\";\n        }\n\n        virtual void verb(HWND, com_ptr<IDataObject>, UINT, string& verb_out)\n        {\n            verb_out = \"another test\";\n        }\n    };\n\n}\n\nBOOST_AUTO_TEST_CASE( verbw )\n{\n    verb_callback callback;\n    vector<wchar_t> buffer(5, L'Z');\n    HRESULT hr = callback(\n        NULL, NULL, DFM_GETVERBW, MAKELONG(6, buffer.size()),\n        (LPARAM)(&buffer[0]));\n    BOOST_REQUIRE_OK(hr);\n\n    wchar_t* expected = L\"test\";\n    BOOST_CHECK_EQUAL_COLLECTIONS(\n        buffer.begin(), buffer.end(), expected, expected + 5);\n}\n\nBOOST_AUTO_TEST_CASE( verbw_buffer_too_small )\n{\n    verb_callback callback;\n    vector<wchar_t> buffer(4, L'Z');\n    HRESULT hr = callback(\n        NULL, NULL, DFM_GETVERBW, MAKELONG(6, buffer.size()),\n        (LPARAM)(&buffer[0]));\n    BOOST_CHECK(FAILED(hr));\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/nse/explorer_command_tests.cpp",
    "content": "/* Copyright (C) 2010, 2011, 2013, 2015\n   Alexander Lamaison <swish@lammy.co.uk>\n\n   This program is free software: you can redistribute it and/or modify\n   it under the terms of the GNU General Public License as published by the\n   Free Software Foundation, either version 3 of the License, or (at your\n   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 General Public License for more details.\n\n   You should have received a copy of the GNU General Public License\n   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"swish/nse/explorer_command.hpp\" // test subject\n\n#include \"swish/nse/Command.hpp\" // Command\n\n#include \"test/common_boost/helpers.hpp\"\n#include <boost/test/unit_test.hpp>\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h> // com_ptr\n#include <comet/uuid_fwd.h> // uuid_t\n\n#include <boost/shared_ptr.hpp> // shared_ptr\n\n#include <string>\n\nusing swish::nse::CExplorerCommandProvider;\nusing swish::nse::CExplorerCommand;\nusing swish::nse::Command;\nusing swish::nse::command_site;\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::uuidof;\nusing comet::uuid_t;\n\nusing boost::shared_ptr;\n\nusing std::wstring;\n\nnamespace {\n\n    struct TestCommand : public Command\n    {\n        TestCommand(\n            const wstring& title, const uuid_t& guid,\n            const wstring& tool_tip=wstring(),\n            const wstring& icon_descriptor=wstring())\n        : Command(title, guid, tool_tip, icon_descriptor) {}\n\n        BOOST_SCOPED_ENUM(state) state(com_ptr<IShellItemArray>, bool) const\n        {\n            return state::enabled;\n        }\n\n        void operator()(\n            com_ptr<IShellItemArray>, const command_site&,\n            com_ptr<IBindCtx>)\n        const\n        {} // noop\n    };\n\n    const uuid_t DUMMY_GUID_1(\"002F9D5D-DB85-4224-9097-B1D06E681252\");\n    const uuid_t DUMMY_GUID_2(\"3BDC0E76-2D94-43c3-AC33-ED629C24AA70\");\n\n    struct DummyCommand1 : public TestCommand\n    {\n        DummyCommand1() : TestCommand(\n            L\"command_1\", DUMMY_GUID_1, L\"tool-tip-1\") {}\n    };\n\n    struct DummyCommand2 : public TestCommand\n    {\n        DummyCommand2() : TestCommand(\n            L\"command_2\", DUMMY_GUID_2, L\"tool-tip-2\") {}\n    };\n\n    CExplorerCommandProvider::ordered_commands dummy_commands()\n    {\n        CExplorerCommandProvider::ordered_commands commands;\n        commands.push_back(new CExplorerCommand<DummyCommand1>());\n        commands.push_back(new CExplorerCommand<DummyCommand2>());\n        return commands;\n    }\n}\n\nBOOST_AUTO_TEST_SUITE(explorer_command_tests)\n\nBOOST_AUTO_TEST_CASE( create_empty_provider )\n{\n    com_ptr<IExplorerCommandProvider> commands = new CExplorerCommandProvider(\n        CExplorerCommandProvider::ordered_commands());\n    BOOST_REQUIRE(commands);\n\n    // Test GetCommands\n    com_ptr<IEnumExplorerCommand> enum_commands;\n    BOOST_REQUIRE_OK(\n        commands->GetCommands(\n            NULL, uuidof(enum_commands.in()),\n            reinterpret_cast<void**>(enum_commands.out())));\n\n    com_ptr<IExplorerCommand> command;\n    BOOST_REQUIRE_EQUAL(enum_commands->Next(1, command.out(), NULL), S_FALSE);\n\n    // Test GetCommand\n    BOOST_REQUIRE_EQUAL(\n        commands->GetCommand(\n            GUID_NULL, uuidof(command.in()),\n            reinterpret_cast<void**>(command.out())),\n        E_FAIL);\n}\n\nBOOST_AUTO_TEST_CASE( commands )\n{\n    com_ptr<IExplorerCommandProvider> commands = new CExplorerCommandProvider(\n        dummy_commands());\n    BOOST_REQUIRE(commands);\n\n    // Test GetCommands\n    com_ptr<IEnumExplorerCommand> enum_commands;\n    BOOST_REQUIRE_OK(\n        commands->GetCommands(\n            NULL, uuidof(enum_commands.in()),\n            reinterpret_cast<void**>(enum_commands.out())));\n\n    com_ptr<IExplorerCommand> command;\n    uuid_t guid;\n\n    BOOST_REQUIRE_OK(enum_commands->Next(1, command.out(), NULL));\n    BOOST_REQUIRE_OK(command->GetCanonicalName(guid.out()));\n    BOOST_REQUIRE_EQUAL(guid, DUMMY_GUID_1);\n\n    BOOST_REQUIRE_OK(enum_commands->Next(1, command.out(), NULL));\n    BOOST_REQUIRE_OK(command->GetCanonicalName(guid.out()));\n    BOOST_REQUIRE_EQUAL(guid, DUMMY_GUID_2);\n\n    BOOST_REQUIRE_EQUAL(enum_commands->Next(1, command.out(), NULL), S_FALSE);\n\n    // Test GetCommand\n    BOOST_REQUIRE_OK(\n        commands->GetCommand(\n            DUMMY_GUID_2, uuidof(command.in()),\n            reinterpret_cast<void**>(command.out())));\n    BOOST_REQUIRE_OK(command->GetCanonicalName(guid.out()));\n    BOOST_REQUIRE_EQUAL(guid, DUMMY_GUID_2);\n\n    BOOST_REQUIRE_OK(\n        commands->GetCommand(\n            DUMMY_GUID_1, uuidof(command.in()),\n            reinterpret_cast<void**>(command.out())));\n    BOOST_REQUIRE_OK(command->GetCanonicalName(guid.out()));\n    BOOST_REQUIRE_EQUAL(guid, DUMMY_GUID_1);\n\n    BOOST_REQUIRE_EQUAL(\n        commands->GetCommand(\n            GUID_NULL, uuidof(command.in()),\n            reinterpret_cast<void**>(command.out())),\n        E_FAIL);\n}\n\nnamespace {\n\n    const GUID TEST_GUID =\n        { 0x1621a875, 0x1252, 0x4bde,\n        { 0xb7, 0x69, 0x70, 0xa9, 0x5f, 0x49, 0x7c, 0x5f } };\n\n    struct HostCommand : public Command\n    {\n        HostCommand() : Command(L\"title\", TEST_GUID, L\"tool-tip\") {}\n\n        BOOST_SCOPED_ENUM(state) state(com_ptr<IShellItemArray>, bool) const\n        { return state::enabled; }\n\n        void operator()(\n            com_ptr<IShellItemArray>, const command_site&, com_ptr<IBindCtx>)\n        const\n        {\n            throw com_error(E_ABORT);\n        }\n    };\n\n    com_ptr<IExplorerCommand> host_command()\n    {\n        com_ptr<IExplorerCommand> command =\n            new CExplorerCommand<HostCommand>();\n\n        BOOST_REQUIRE(command);\n        return command;\n    }\n}\n\n\n/**\n * GetTitle returns the string given in the constructor.\n */\nBOOST_AUTO_TEST_CASE( title )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    wchar_t* ret_val;\n    BOOST_REQUIRE_OK(command->GetTitle(NULL, &ret_val));\n\n    shared_ptr<wchar_t> title(ret_val, ::CoTaskMemFree);\n    BOOST_REQUIRE_EQUAL(title.get(), L\"title\");\n}\n\n/**\n * GetIcon returns the expected empty string as it wasn't set in the\n * constructor.\n */\nBOOST_AUTO_TEST_CASE( icon )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    wchar_t* ret_val;\n    BOOST_REQUIRE_OK(command->GetIcon(NULL, &ret_val));\n\n    shared_ptr<wchar_t> icon(ret_val, ::CoTaskMemFree);\n    BOOST_REQUIRE_EQUAL(icon.get(), L\"\");\n}\n\n/**\n * GetToolTip returns the string given in the constructor.\n */\nBOOST_AUTO_TEST_CASE( tool_tip )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    wchar_t* ret_val;\n    BOOST_REQUIRE_OK(command->GetToolTip(NULL, &ret_val));\n\n    shared_ptr<wchar_t> tip(ret_val, ::CoTaskMemFree);\n    BOOST_REQUIRE_EQUAL(tip.get(), L\"tool-tip\");\n}\n\n/**\n * GetCanonicalName returns the test GUID given in the constructor.\n */\nBOOST_AUTO_TEST_CASE( guid )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    GUID guid;\n    BOOST_REQUIRE_OK(command->GetCanonicalName(&guid));\n    BOOST_REQUIRE_EQUAL(uuid_t(guid), uuid_t(TEST_GUID));\n}\n\n/**\n * GetFlags returns ECF_DEFAULT (0).\n */\nBOOST_AUTO_TEST_CASE( flags )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    EXPCMDFLAGS flags;\n    BOOST_REQUIRE_OK(command->GetFlags(&flags));\n    BOOST_REQUIRE_EQUAL(flags, 0U);\n}\n\n/**\n * GetState returns ECS_ENABLED (0).\n */\nBOOST_AUTO_TEST_CASE( state )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    EXPCMDSTATE flags;\n    BOOST_REQUIRE_OK(command->GetState(NULL, false, &flags));\n    BOOST_REQUIRE_EQUAL(flags, 0U);\n}\n\n/**\n * Invoke returns error that matches exception thrown by throwing_function\n * passed to constructor.\n */\nBOOST_AUTO_TEST_CASE( invoke )\n{\n    com_ptr<IExplorerCommand> command = host_command();\n\n    BOOST_REQUIRE_EQUAL(command->Invoke(NULL, NULL), E_ABORT);\n}\n\n\nnamespace {\n\n    const GUID TEST_GUID2 =\n        { 0xae4792b2, 0x3b35, 0x4c07,\n        { 0x9a, 0x96, 0x2f, 0x33, 0xc5, 0x56, 0xdb, 0x4a } };\n\n    struct CommandNeedingSite : public Command\n    {\n        CommandNeedingSite() : Command(L\"title\", TEST_GUID2, L\"tool-tip\") {}\n\n        BOOST_SCOPED_ENUM(state) state(com_ptr<IShellItemArray>, bool) const\n        { return state::enabled; }\n\n        void operator()(\n            com_ptr<IShellItemArray>, const command_site&, com_ptr<IBindCtx>)\n        const\n        {\n            throw com_error(E_ABORT);\n        }\n\n        void set_site(com_ptr<IUnknown> ole_site) {}\n    };\n\n}\n\n/**\n * A CExplorerCommand must support IObjectWithSite.\n */\nBOOST_AUTO_TEST_CASE( support_ole_site )\n{\n    com_ptr<IExplorerCommand> command =\n        new CExplorerCommand<CommandNeedingSite>();\n\n    com_ptr<IObjectWithSite> object_with_site = try_cast(command);\n    BOOST_REQUIRE(object_with_site);\n\n    BOOST_REQUIRE_OK(object_with_site->SetSite(NULL));\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/provider-integration/CMakeLists.txt",
    "content": "# Copyright 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  auth_test.cpp\n  provider_test.cpp\n  stream_test.cpp\n  stream_create_test.cpp\n  stream_read_test.cpp\n  stream_write_test.cpp)\n\nswish_test_suite(\n  SUBJECT provider VARIANT integration\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES} openssh_fixture provider_fixture sftp_fixture\n  com_stream_fixture\n  LABELS integration)\n"
  },
  {
    "path": "test/provider-integration/auth_test.cpp",
    "content": "// Copyright 2008, 2009, 2010, 2012, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"test/fixtures/openssh_fixture.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/MockConsumer.hpp\"\n\n#include \"swish/connection/authenticated_session.hpp\"\n#include \"swish/connection/connection_spec.hpp\"\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h>   // com_ptr\n\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <exception>\n\nusing test::fixtures::openssh_fixture;\nusing test::MockConsumer;\n\nusing swish::connection::authenticated_session;\nusing swish::connection::connection_spec;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing boost::system::system_error;\nusing boost::test_tools::predicate_result;\n\nusing std::exception;\n\nnamespace\n{\n\n/**\n * Check that the given session responds sensibly to a request.\n */\npredicate_result alive(authenticated_session& session)\n{\n    try\n    {\n        session.get_sftp_filesystem().directory_iterator(\"/\");\n\n        predicate_result res(true);\n        res.message() << \"Session seems to be alive\";\n        return res;\n    }\n    catch (const exception& e)\n    {\n        predicate_result res(false);\n        res.message() << \"Session seems to be dead: \" << e.what();\n        return res;\n    }\n}\n\nbool is_e_abort(com_error e)\n{\n    return e.hr() == E_ABORT;\n}\n\nclass fixture : public openssh_fixture\n{\npublic:\n    swish::connection::connection_spec as_connection_spec() const\n    {\n        return swish::connection::connection_spec(whost(), wuser(), port());\n    }\n};\n}\n\nBOOST_FIXTURE_TEST_SUITE(network_auth_tests, fixture)\n\n// This test needs kb-int to be disabled on the server, otherwise it will be\n// requested first and either succeed, which means password-auth doesn't get\n// tested, or fail, which aborts the whole process.\n/*\nBOOST_AUTO_TEST_CASE(SimplePasswordAuthentication)\n{\n    // Choose mock behaviours to force only simple password authentication\n    com_ptr<MockConsumer> consumer = new MockConsumer();\n    consumer->set_password_behaviour(MockConsumer::CustomPassword);\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::AbortResponse);\n    consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n    consumer->set_password(wpassword());\n\n    // Fails if keyboard-int supported on the server as that gets preference\n    // and replies with user-aborted\n    authenticated_session session =\n        as_connection_spec().create_session(consumer);\n\n    BOOST_CHECK(alive(session));\n}\n*/\n\nBOOST_AUTO_TEST_CASE(KeyboardInteractiveAuthentication)\n{\n    // Choose mock behaviours to force only kbd-interactive authentication\n    com_ptr<MockConsumer> consumer = new MockConsumer();\n    consumer->set_password_behaviour(MockConsumer::FailPassword);\n    consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::CustomResponse);\n    consumer->set_password(wpassword());\n\n    // This may fail if the server (which we can't control) doesn't allow\n    // ki-auth\n    authenticated_session session =\n        as_connection_spec().create_session(consumer);\n\n    BOOST_CHECK(alive(session));\n}\n\nBOOST_AUTO_TEST_CASE(WrongPasswordOrResponse)\n{\n    com_ptr<MockConsumer> consumer = new MockConsumer();\n\n    consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n    // We don't know which of password or kb-int (or both) is set up on the\n    // server so we have to prime both to return the wrong password else\n    // we may get E_ABORT for the kb-interactive response\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::WrongResponse);\n    consumer->set_password_behaviour(MockConsumer::WrongPassword);\n\n    // FIXME: Any exception will do.  We don't have fine enough control over the\n    // mock to test this properly.\n    BOOST_CHECK_THROW(as_connection_spec().create_session(consumer), exception);\n}\n\nBOOST_AUTO_TEST_CASE(UserAborted)\n{\n    com_ptr<MockConsumer> consumer = new MockConsumer();\n\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::AbortResponse);\n    consumer->set_password_behaviour(MockConsumer::AbortPassword);\n    consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n\n    BOOST_CHECK_EXCEPTION(as_connection_spec().create_session(consumer),\n                          com_error, is_e_abort);\n}\n\n/**\n * Test to see that we can connect successfully after an aborted attempt.\n */\nBOOST_AUTO_TEST_CASE(ReconnectAfterAbort)\n{\n    // Choose mock behaviours to simulate a user cancelling authentication\n    com_ptr<MockConsumer> consumer = new MockConsumer();\n    consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n    consumer->set_password_behaviour(MockConsumer::AbortPassword);\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::AbortResponse);\n\n    BOOST_CHECK_EXCEPTION(as_connection_spec().create_session(consumer),\n                          com_error, is_e_abort);\n\n    // Change mock behaviours so that authentication succeeds\n    consumer->set_password_max_attempts(2);\n    consumer->set_keyboard_interactive_max_attempts(2);\n    consumer->set_password_behaviour(MockConsumer::CustomPassword);\n    consumer->set_keyboard_interactive_behaviour(MockConsumer::CustomResponse);\n    consumer->set_password(wpassword());\n\n    authenticated_session session =\n        as_connection_spec().create_session(consumer);\n\n    BOOST_CHECK(alive(session));\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/provider-integration/stream_create_test.cpp",
    "content": "// Copyright 2009, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"test/fixtures/com_stream_fixture.hpp\"\n\n#include <comet/error.h> // com_error\n\n#include <boost/test/unit_test.hpp>\n\nusing test::fixtures::com_stream_fixture;\n\nusing comet::com_error;\n\nBOOST_FIXTURE_TEST_SUITE(stream_create, com_stream_fixture)\n\n/**\n * Open a stream to a file that doesn't already exist.\n * The file should be created as only the `std::ios_base::out` flag is set.\n */\nBOOST_AUTO_TEST_CASE(new_file)\n{\n    // Delete sandbox file before creating stream\n    remove(filesystem(), test_file());\n\n    BOOST_REQUIRE(!exists(filesystem(), test_file()));\n\n    get_stream(std::ios_base::out);\n\n    BOOST_REQUIRE(exists(filesystem(), test_file()));\n}\n\n/**\n * Open a stream for reading to a file that doesn't already exist.\n * This should fail and the file should not be created as the\n * `std::ios_base::out` flag isn't set which would cause the file to\n * be created.\n */\nBOOST_AUTO_TEST_CASE(non_existent_file_fail)\n{\n    // Delete sandbox file before creating stream\n    remove(filesystem(), test_file());\n\n    BOOST_REQUIRE(!exists(filesystem(), test_file()));\n\n    BOOST_REQUIRE_THROW(get_stream(std::ios_base::in), std::exception);\n\n    BOOST_REQUIRE(!exists(filesystem(), test_file()));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/provider-integration/stream_read_test.cpp",
    "content": "// Copyright 2009, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"test/fixtures/com_stream_fixture.hpp\"\n\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/stream_utils.hpp\" // verify_stream_read\n\n#include <ssh/filesystem.hpp>\n#include <ssh/stream.hpp>\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/test/unit_test.hpp>\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <exception>\n#include <string>\n#include <vector>\n\nusing test::fixtures::com_stream_fixture;\nusing test::stream_utils::verify_stream_read;\n\nusing comet::com_ptr;\n\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::perms;\n\nusing boost::numeric_cast;\nusing boost::shared_ptr;\n\nusing std::exception;\nusing std::string;\nusing std::vector;\n\nnamespace\n{ // private\n\nconst string TEST_DATA = \"Humpty dumpty\\nsat on the wall.\\n\\rHumpty ...\";\n\n/**\n * Fixture for tests that need to read data from an existing file.\n */\nclass stream_read_fixture : public com_stream_fixture\n{\npublic:\n    /**\n     * Put test data into a file in our sandbox.\n     */\n    stream_read_fixture() : com_stream_fixture()\n    {\n        ofstream file(filesystem(), test_file(), std::ios::binary);\n        file << expected_data() << std::flush;\n    }\n\n    /**\n     * Create an IStream instance open for reading on a temporary file\n     * in our sandbox.  The file contained the same data that\n     * ExpectedData() returns.\n     */\n    com_ptr<IStream> get_read_stream()\n    {\n        return get_stream(std::ios_base::in);\n    }\n\n    /**\n     * Return the data we expect to be able to read using the IStream.\n     */\n    string expected_data()\n    {\n        return TEST_DATA;\n    }\n};\n}\n\nBOOST_FIXTURE_TEST_SUITE(stream_read, stream_read_fixture)\n\n/**\n * Simply get a stream.\n */\nBOOST_AUTO_TEST_CASE(get)\n{\n    com_ptr<IStream> stream = get_read_stream();\n    BOOST_REQUIRE(stream);\n}\n\n/**\n * Get a read stream to a read-only file.\n * This tests that we aren't inadvertently asking for more permissions than\n * we need.\n */\nBOOST_AUTO_TEST_CASE(get_readonly)\n{\n    permissions(filesystem(), test_file(), perms::owner_read);\n\n    com_ptr<IStream> stream = get_read_stream();\n    BOOST_REQUIRE(stream);\n}\n\n/**\n * Try to get a stream to a non-readable file.\n * Test how we deal with opening failures.\n */\nBOOST_AUTO_TEST_CASE(throws_exception_opening_unreadable_file)\n{\n    permissions(filesystem(), test_file(), perms::none);\n\n    BOOST_REQUIRE_THROW(get_read_stream(), exception);\n}\n\n/**\n * Read a sequence of characters.\n */\nBOOST_AUTO_TEST_CASE(read_a_string)\n{\n    com_ptr<IStream> stream = get_read_stream();\n\n    string expected = expected_data();\n    vector<char> buf(expected.size());\n\n    size_t bytes_read =\n        verify_stream_read(&buf[0], numeric_cast<ULONG>(buf.size()), stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, expected.size());\n\n    // Test that the bytes we read match\n    BOOST_REQUIRE_EQUAL_COLLECTIONS(buf.begin(), buf.end(), expected.begin(),\n                                    expected.end());\n}\n\n/**\n * Read a sequence of characters from a read-only file.\n */\nBOOST_AUTO_TEST_CASE(read_a_string_readonly)\n{\n    permissions(filesystem(), test_file(), perms::owner_read);\n\n    com_ptr<IStream> stream = get_read_stream();\n\n    string expected = expected_data();\n    vector<char> buf(expected.size());\n\n    size_t bytes_read =\n        verify_stream_read(&buf[0], numeric_cast<ULONG>(buf.size()), stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, expected.size());\n\n    // Test that the bytes we read match\n    BOOST_REQUIRE_EQUAL_COLLECTIONS(buf.begin(), buf.end(), expected.begin(),\n                                    expected.end());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/provider-integration/stream_test.cpp",
    "content": "// Copyright 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"test/common_boost/MockConsumer.hpp\" // MockConsumer\n#include \"test/common_boost/fixtures.hpp\"     // WinsockFixture\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/stream_utils.hpp\" // verify_stream_read\n#include \"test/fixtures/openssh_fixture.hpp\"\n\n#include \"swish/connection/connection_spec.hpp\"\n#include \"swish/connection/session_manager.hpp\" // session_reservation\n#include \"swish/provider/sftp_provider.hpp\"     // sftp_provider, ISftpConsumer\n#include \"swish/provider/Provider.hpp\"          // CProvider\n\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/filesystem/path.hpp>         // wpath\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/shared_ptr.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <algorithm> // generate\n#include <cstdlib>   // rand\n#include <memory>    // auto_ptr\n#include <string>\n#include <vector>\n\nusing test::MockConsumer;\nusing test::WinsockFixture;\nusing test::fixtures::openssh_fixture;\nusing test::stream_utils::verify_stream_read;\n\nusing swish::connection::session_manager;\nusing swish::connection::session_reservation;\nusing swish::connection::connection_spec;\nusing swish::provider::sftp_provider;\nusing swish::provider::CProvider;\n\nusing comet::com_ptr;\n\nusing boost::filesystem::wpath;\nusing boost::numeric_cast;\nusing boost::shared_ptr;\n\nusing std::auto_ptr;\nusing std::string;\nusing std::generate;\nusing std::rand;\nusing std::vector;\nusing std::wstring;\n\nnamespace\n{\n\nclass fixture : public WinsockFixture, public openssh_fixture\n{\npublic:\n    fixture() : m_provider(new CProvider(reserve_session(new MockConsumer())))\n    {\n    }\n\n    swish::connection::connection_spec as_connection_spec() const\n    {\n        return swish::connection::connection_spec(whost(), wuser(), port());\n    }\n\n    shared_ptr<sftp_provider> provider() const\n    {\n        return m_provider;\n    }\n\n    com_ptr<IStream> get_stream(const wstring& path,\n                                std::ios_base::openmode open_mode)\n    {\n        return provider()->get_file(path, open_mode);\n    }\n\n    session_reservation reserve_session(com_ptr<MockConsumer> consumer)\n    {\n        consumer->set_pubkey_behaviour(MockConsumer::AbortKeys);\n        consumer->set_keyboard_interactive_behaviour(\n            MockConsumer::CustomResponse);\n        consumer->set_password_behaviour(MockConsumer::CustomPassword);\n        consumer->set_password(wpassword());\n\n        return session_manager().reserve_session(as_connection_spec(), consumer,\n                                                 \"Running tests\");\n    }\n\nprivate:\n    shared_ptr<sftp_provider> m_provider;\n};\n}\n\nBOOST_FIXTURE_TEST_SUITE(remote_stream_tests, fixture)\n\n/**\n * Simply get a stream.\n */\nBOOST_AUTO_TEST_CASE(get)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n    BOOST_REQUIRE(stream);\n}\n\nBOOST_AUTO_TEST_CASE(stat)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    STATSTG stat = STATSTG();\n    HRESULT hr = stream->Stat(&stat, STATFLAG_DEFAULT);\n    BOOST_REQUIRE_OK(hr);\n\n    BOOST_CHECK(stat.pwcsName);\n    BOOST_CHECK_EQUAL(L\"lastlog\", stat.pwcsName);\n    BOOST_CHECK_EQUAL(STGTY_STREAM, (STGTY)stat.type);\n    BOOST_CHECK_GT(stat.cbSize.QuadPart, 0U);\n    FILETIME ft;\n    BOOST_REQUIRE_OK(::CoFileTimeNow(&ft));\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.mtime)), 1);\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.atime)), 1);\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.ctime)), 1);\n    BOOST_CHECK_EQUAL(stat.grfMode, 0U);\n    BOOST_CHECK_EQUAL(stat.grfLocksSupported, 0U);\n    BOOST_CHECK(stat.clsid == GUID_NULL);\n    BOOST_CHECK_EQUAL(stat.grfStateBits, 0U);\n    BOOST_CHECK_EQUAL(stat.reserved, 0U);\n}\n\nBOOST_AUTO_TEST_CASE(stat_exclude_name)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    STATSTG stat = STATSTG();\n    HRESULT hr = stream->Stat(&stat, STATFLAG_NONAME);\n    BOOST_REQUIRE_OK(hr);\n\n    BOOST_CHECK(!stat.pwcsName);\n    BOOST_CHECK_EQUAL(STGTY_STREAM, (STGTY)stat.type);\n    BOOST_CHECK_GT(stat.cbSize.QuadPart, 0U);\n    FILETIME ft;\n    BOOST_REQUIRE_OK(::CoFileTimeNow(&ft));\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.mtime)), 1);\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.atime)), 1);\n    BOOST_CHECK_EQUAL(::CompareFileTime(&ft, &(stat.ctime)), 1);\n    BOOST_CHECK_EQUAL(stat.grfMode, 0U);\n    BOOST_CHECK_EQUAL(stat.grfLocksSupported, 0U);\n    BOOST_CHECK(stat.clsid == GUID_NULL);\n    BOOST_CHECK_EQUAL(stat.grfStateBits, 0U);\n    BOOST_CHECK_EQUAL(stat.reserved, 0U);\n}\n\nBOOST_AUTO_TEST_CASE(read_file_small_buffer)\n{\n    com_ptr<IStream> stream = get_stream(L\"/proc/cpuinfo\", std::ios_base::in);\n\n    string file_contents_read;\n    ULONG bytes_read = 0;\n    char buf[1];\n    HRESULT hr;\n    do\n    {\n        hr = stream->Read(buf, ARRAYSIZE(buf), &bytes_read);\n        file_contents_read.append(buf, bytes_read);\n    } while (hr == S_OK && bytes_read == ARRAYSIZE(buf));\n\n    BOOST_CHECK_GT(file_contents_read.size(), 100U);\n    BOOST_CHECK_EQUAL(\"processor\", file_contents_read.substr(0, 9));\n}\n\nBOOST_AUTO_TEST_CASE(read_file_medium_buffer)\n{\n    com_ptr<IStream> stream = get_stream(L\"/proc/cpuinfo\", std::ios_base::in);\n\n    string file_contents_read;\n    ULONG bytes_read = 0;\n    char buf[4096];\n    HRESULT hr;\n    do\n    {\n        hr = stream->Read(buf, ARRAYSIZE(buf), &bytes_read);\n        file_contents_read.append(buf, bytes_read);\n    } while (hr == S_OK && bytes_read == ARRAYSIZE(buf));\n\n    BOOST_CHECK_GT(file_contents_read.size(), 100U);\n    BOOST_CHECK_EQUAL(\"processor\", file_contents_read.substr(0, 9));\n}\n\n/**\n * This highlights problems caused by short reads.\n * /dev/random produces data very slowly so the stream should block while\n * waiting for more data to become available.\n * libssh2 seems to get this wrong between 1.2.8 and 1.3.0\n */\n// FIXME: This probably works but, since we changed to using a buffered stream,\n// takes much too long to find out.  The reason is that the buffered stream\n// tries to fill its buffer before returning the small number of characters\n// we requested.  The buffer is much bigger than that number so the test\n// runs and runs\n/*\nBOOST_AUTO_TEST_CASE( read_small_buffer_from_slow_blocking_device )\n{\n    com_ptr<IStream> stream = get_stream(L\"/dev/random\", std::ios_base::in);\n\n    vector<char> buffer(15, 'x');\n    size_t bytes_read = verify_stream_read(&buffer[0], buffer.size(), stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, buffer.size());\n}\n*/\n\n/**\n * This tests a scenario that should *never* block.\n * /dev/zero immediately produces an endless stream of zeroes so the stream\n * should just keep reading until the buffer is full.  If it blocks, something\n * has gone wrong somewhere.\n */\nBOOST_AUTO_TEST_CASE(read_large_buffer)\n{\n    com_ptr<IStream> stream = get_stream(L\"/dev/zero\", std::ios_base::in);\n\n    // using int to get legible output when collection comparison fails\n    vector<int> buffer(20000, 74);\n    size_t size = buffer.size() * sizeof(buffer[0]);\n    size_t bytes_read = verify_stream_read(&buffer[0], size, stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, size);\n\n    vector<int> expected(20000, 0);\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected.begin(), expected.end());\n}\n\nnamespace\n{\n\nvector<int> random_buffer(size_t buffer_size)\n{\n    vector<int> buffer(buffer_size);\n    generate(buffer.begin(), buffer.end(), rand);\n    return buffer;\n}\n}\n\nBOOST_AUTO_TEST_CASE(roundtrip)\n{\n    com_ptr<IStream> stream = get_stream(\n        L\"test_file\", // trunc causes file creation (which in suppressed)\n        std::ios_base::in | std::ios_base::out | std::ios_base::trunc);\n\n    // using int to get legible output when collection comparison fails\n    vector<int> source_data = random_buffer(6543210);\n    ULONG size_in_bytes =\n        numeric_cast<ULONG>(source_data.size() * sizeof(source_data[0]));\n\n    ULONG bytes_written = 0;\n    HRESULT hr = stream->Write(&source_data[0], size_in_bytes, &bytes_written);\n    BOOST_REQUIRE_OK(hr);\n    BOOST_CHECK_EQUAL(bytes_written, size_in_bytes);\n\n    LARGE_INTEGER dlibMove = {0};\n    ULARGE_INTEGER dlibNewPos = {0};\n    hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n    BOOST_REQUIRE_OK(hr);\n    BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 0UL);\n\n    vector<int> buffer(source_data.size(), 33);\n    size_t bytes_read = verify_stream_read(&buffer[0], size_in_bytes, stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, size_in_bytes);\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  source_data.begin(), source_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(read_empty_file)\n{\n    com_ptr<IStream> stream = get_stream(L\"/dev/null\", std::ios_base::in);\n\n    vector<char> buffer(6543210, 'x');\n    size_t bytes_read = verify_stream_read(&buffer[0], buffer.size(), stream);\n\n    BOOST_CHECK_EQUAL(bytes_read, 0U);\n\n    char expected[] = {'x', 'x', 'x', 'x'};\n    BOOST_CHECK_EQUAL_COLLECTIONS(&buffer[0], &buffer[0] + 4, expected,\n                                  expected + 4);\n}\n\nBOOST_AUTO_TEST_CASE(seek_noop)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move by 0 relative to current position\n    {\n        LARGE_INTEGER dlibMove = {0};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 0UL);\n    }\n\n    // Move by 0 but don't provide return slot\n    {\n        LARGE_INTEGER dlibMove = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, NULL);\n        BOOST_REQUIRE_OK(hr);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_relative)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move by 7 relative to current position: absolute pos 7\n    {\n        LARGE_INTEGER dlibMove = {7};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 7UL);\n    }\n\n    // Move by 7 relative to current position: absolute pos 14\n    {\n        LARGE_INTEGER dlibMove = {7};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 14UL);\n    }\n\n    // Move by -5 relative to current position: absolute pos 9\n    {\n        LARGE_INTEGER dlibMove;\n        dlibMove.QuadPart = -5;\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 9UL);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_relative_fail)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move by 7 relative to current position: absolute pos 7\n    {\n        LARGE_INTEGER dlibMove = {7};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 7UL);\n    }\n\n    // Move by -9 relative to current position: absolute pos -2\n    {\n        LARGE_INTEGER dlibMove;\n        dlibMove.QuadPart = -9;\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_CHECK_EQUAL(hr, STG_E_INVALIDFUNCTION);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_absolute)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move to absolute position 7\n    {\n        LARGE_INTEGER dlibMove = {7};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 7UL);\n    }\n\n    // Move to absolute position 14\n    {\n        LARGE_INTEGER dlibMove = {14};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 14UL);\n    }\n\n    // Move to beginning of file: absolute position 0\n    {\n        LARGE_INTEGER dlibMove = {0};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 0UL);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_absolute_fail)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move to absolute position -3\n    {\n        LARGE_INTEGER dlibMove;\n        dlibMove.QuadPart = -3;\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n        BOOST_CHECK_EQUAL(hr, STG_E_INVALIDFUNCTION);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_get_current_position)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    // Move to absolute position 7\n    {\n        LARGE_INTEGER dlibMove = {7};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_SET, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 7UL);\n    }\n\n    // Move by 0 relative to current pos which should return current pos\n    {\n        LARGE_INTEGER dlibMove = {0};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_CUR, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, 7UL);\n    }\n}\n\nBOOST_AUTO_TEST_CASE(seek_relative_to_end)\n{\n    com_ptr<IStream> stream =\n        get_stream(L\"/var/log/lastlog\", std::ios_base::in);\n\n    HRESULT hr;\n\n    ULONGLONG uSize;\n    // Move to end of file: absolute position 0 from end\n    {\n        LARGE_INTEGER dlibMove = {0};\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_END, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        // Should be a fairly large number\n        BOOST_CHECK_GT(dlibNewPos.QuadPart, 100UL);\n        uSize = dlibNewPos.QuadPart;\n    }\n\n    // Move to absolute position 7 from end of file\n    {\n        LARGE_INTEGER dlibMove;\n        dlibMove.QuadPart = -7;\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_END, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        // Should be a fairly large number\n        BOOST_CHECK_GT(dlibNewPos.QuadPart, 100UL);\n        // Should be size of file minus 7\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, uSize - 7);\n    }\n\n    // Move 50 past end of the file: this should still succeed\n    {\n        LARGE_INTEGER dlibMove;\n        dlibMove.QuadPart = 50;\n        ULARGE_INTEGER dlibNewPos = {0};\n        hr = stream->Seek(dlibMove, STREAM_SEEK_END, &dlibNewPos);\n        BOOST_REQUIRE_OK(hr);\n        // Should be a fairly large number\n        BOOST_CHECK_GT(dlibNewPos.QuadPart, 100UL);\n        // Should be size of file plus 50\n        BOOST_CHECK_EQUAL(dlibNewPos.QuadPart, uSize + 50);\n    }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n\n/*\n    void testStatExact()\n    {\n        CComPtr<IStream> pStream = _CreateConnectInit(\"/boot/grub/default\");\n\n        STATSTG stat;\n        ::ZeroMemory(&stat, sizeof stat);\n        HRESULT hr = stream->Stat(&stat, STATFLAG_DEFAULT);\n        BOOST_REQUIRE_OK(hr);\n\n        BOOST_CHECK(stat.pwcsName);\n        BOOST_CHECK_EQUAL(CString(L\"default\"), CString(stat.pwcsName));\n        BOOST_CHECK_EQUAL(STGTY_STREAM, (STGTY)stat.type);\n        BOOST_CHECK_EQUAL((ULONGLONG)197, stat.cbSize.QuadPart);\n        BOOST_CHECK_EQUAL((DWORD)0, stat.grfMode);\n        BOOST_CHECK_EQUAL((DWORD)0, stat.grfLocksSupported);\n        //BOOST_CHECK_EQUAL(GUID_NULL, stat.clsid);\n        BOOST_CHECK_EQUAL((DWORD)0, stat.grfStateBits);\n        BOOST_CHECK_EQUAL((DWORD)0, stat.reserved);\n    }\n\n    void testReadFileExact()\n    {\n        CComPtr<IStream> pStream = _CreateConnectInit(\"/boot/grub/default\");\n        HRESULT hr;\n\n        CStringA strFile;\n        ULONG cbRead = 0;\n        char buf[4096];\n        do {\n            hr = stream->Read(buf, ARRAYSIZE(buf), &cbRead);\n            strFile.Append(buf, cbRead);\n        } while (hr == S_OK && cbRead == ARRAYSIZE(buf));\n\n        BOOST_CHECK_EQUAL(197, strFile.GetLength());\n        BOOST_CHECK_EQUAL(CStringA(szTestFile), strFile);\n    }\n\n*/\n"
  },
  {
    "path": "test/provider-integration/stream_write_test.cpp",
    "content": "// Copyright 2009, 2011, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"test/fixtures/com_stream_fixture.hpp\"\n\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/stream_utils.hpp\" // verify_stream_read\n\n#include <ssh/filesystem.hpp>\n\n#include <comet/error.h> // com_error\n#include <comet/ptr.h>   // com_ptr\n\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/test/unit_test.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <algorithm> // generate\n#include <cstdlib>   // rand\n#include <string>\n#include <vector>\n\nusing test::fixtures::com_stream_fixture;\nusing test::stream_utils::verify_stream_read;\n\nusing ssh::filesystem::perms;\n\nusing comet::com_error;\nusing comet::com_ptr;\n\nusing boost::numeric_cast;\nusing boost::shared_ptr;\n\nusing std::exception;\nusing std::generate;\nusing std::rand;\nusing std::string;\nusing std::vector;\n\nBOOST_FIXTURE_TEST_SUITE(stream_write, com_stream_fixture)\n\n/**\n * Simply get a stream.\n */\nBOOST_AUTO_TEST_CASE(get)\n{\n    com_ptr<IStream> stream = get_stream();\n    BOOST_REQUIRE(stream);\n}\n\n/**\n * Try to get a writable stream to a read-only file.\n * Test how we deal with opening failures.\n */\nBOOST_AUTO_TEST_CASE(get_readonly)\n{\n    permissions(filesystem(), test_file(), perms::owner_read);\n\n    BOOST_REQUIRE_THROW(get_stream(), exception);\n}\n\n/**\n * Write one byte to stream, read it back and check that it is the same.\n */\nBOOST_AUTO_TEST_CASE(write_one_byte)\n{\n    com_ptr<IStream> stream = get_stream();\n\n    // Write the character 'M' to the file\n    char in[1] = {'M'};\n    ULONG cbWritten = 0;\n    BOOST_REQUIRE_OK(stream->Write(in, sizeof(in), &cbWritten));\n    BOOST_REQUIRE_EQUAL(cbWritten, sizeof(in));\n\n    // Reset seek pointer to beginning and read back\n    LARGE_INTEGER move = {0};\n    BOOST_REQUIRE_OK(stream->Seek(move, STREAM_SEEK_SET, NULL));\n\n    char out[1];\n    verify_stream_read(out, sizeof(out), stream);\n    BOOST_REQUIRE_EQUAL('M', out[0]);\n}\n\n/**\n * Write a sequence of characters.\n */\nBOOST_AUTO_TEST_CASE(write_a_string)\n{\n    com_ptr<IStream> stream = get_stream();\n\n    string in = \"Lorem ipsum dolor sit amet. \";\n    ULONG cbWritten = 0;\n    BOOST_REQUIRE_OK(\n        stream->Write(&in[0], numeric_cast<ULONG>(in.size()), &cbWritten));\n    BOOST_REQUIRE_EQUAL(cbWritten, in.size());\n\n    // Reset seek pointer to beginning and read back\n    LARGE_INTEGER move = {0};\n    BOOST_REQUIRE_OK(stream->Seek(move, STREAM_SEEK_SET, NULL));\n\n    vector<char> out(in.size());\n    verify_stream_read(&out[0], numeric_cast<ULONG>(out.size()), stream);\n    BOOST_REQUIRE_EQUAL_COLLECTIONS(out.begin(), out.end(), in.begin(),\n                                    in.end());\n}\n\nnamespace\n{\n\nvector<int> random_buffer(size_t buffer_size)\n{\n    vector<int> buffer(buffer_size);\n    generate(buffer.begin(), buffer.end(), rand);\n    return buffer;\n}\n}\n\n/**\n * Write a large buffer.\n */\nBOOST_AUTO_TEST_CASE(write_large)\n{\n    com_ptr<IStream> stream = get_stream();\n\n    vector<int> in = random_buffer(1000000);\n\n    ULONG cbWritten = 0;\n    ULONG size_in_bytes = numeric_cast<ULONG>(in.size() * sizeof(int));\n    BOOST_REQUIRE_OK(stream->Write(&in[0], size_in_bytes, &cbWritten));\n    BOOST_REQUIRE_EQUAL(cbWritten, size_in_bytes);\n\n    // Reset seek pointer to beginning and read back\n    LARGE_INTEGER move = {0};\n    BOOST_REQUIRE_OK(stream->Seek(move, STREAM_SEEK_SET, NULL));\n\n    vector<int> out(in.size());\n    verify_stream_read(&out[0], size_in_bytes, stream);\n    BOOST_REQUIRE_EQUAL_COLLECTIONS(out.begin(), out.end(), in.begin(),\n                                    in.end());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/remote_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(UNIT_TESTS\n  columns_test.cpp\n  properties_test.cpp\n  remote_pidl_test.cpp\n  swish_pidl_test.cpp)\n\nset(INTEGRATION_TESTS\n  remote_commands_test.cpp)\n\nswish_test_suite(\n  SUBJECT remote_folder VARIANT unit\n  SOURCES ${UNIT_TESTS}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n\nswish_test_suite(\n  SUBJECT remote_folder VARIANT integration\n  SOURCES ${INTEGRATION_TESTS}\n  LIBRARIES ${Boost_LIBRARIES} provider_fixture\n  LABELS integration)\n"
  },
  {
    "path": "test/remote_folder/columns_test.cpp",
    "content": "/**\n    @file\n\n    Exercise remote-folder columns.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/remote_folder/columns.hpp> // test subject, Column\n#include <swish/remote_folder/remote_pidl.hpp> // create_remote_itemid\n\n#include <test/common_boost/helpers.hpp> // wide-string output\n\n#include <washer/shell/format.hpp> // format_date_time\n\n#include <comet/datetime.h> // datetime_t\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nusing swish::remote_folder::Column;\nusing swish::remote_folder::create_remote_itemid;\n\nusing washer::shell::format_date_time;\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::datetime_t;\n\nusing std::wstring;\n\nBOOST_AUTO_TEST_SUITE(column_tests)\n\nnamespace {\n\n    cpidl_t gimme_pidl()\n    {\n        return create_remote_itemid(\n            L\"some filename.txt\", false, false, L\"bobowner\", L\"mygroup\",\n            578, 1001, 0100666, 1024, datetime_t(2010, 1, 1, 12, 30, 17, 42),\n            datetime_t(2010, 1, 1, 0, 0, 5, 7));\n    }\n\n    wstring header(size_t index)\n    {\n        Column col(index);\n        return col.header();\n    }\n\n    wstring detail(size_t index)\n    {\n        Column col(index);\n        return col.detail(gimme_pidl());\n    }\n}\n\n\nBOOST_AUTO_TEST_CASE( label )\n{\n    BOOST_CHECK_EQUAL(header(0), L\"Name\");\n    BOOST_CHECK_EQUAL(detail(0), L\"some filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( size )\n{\n    BOOST_CHECK_EQUAL(header(1), L\"Size\");\n    BOOST_CHECK_EQUAL(detail(1), L\"1 KB\");\n}\n\nBOOST_AUTO_TEST_CASE( type )\n{\n    BOOST_CHECK_EQUAL(header(2), L\"Type\");\n    BOOST_CHECK_EQUAL(detail(2), L\"Text Document\");\n}\n\nBOOST_AUTO_TEST_CASE( modified )\n{\n    BOOST_CHECK_EQUAL(header(3), L\"Date Modified\");\n    BOOST_CHECK_EQUAL(\n        detail(3),\n        format_date_time<wchar_t>(datetime_t(2010, 1, 1, 12, 30, 17, 42)));\n}\n\nBOOST_AUTO_TEST_CASE( accessed )\n{\n    BOOST_CHECK_EQUAL(header(4), L\"Date Accessed\");\n    BOOST_CHECK_EQUAL(\n        detail(4),\n        format_date_time<wchar_t>(datetime_t(2010, 1, 1, 0, 0, 5, 7)));\n}\n\nBOOST_AUTO_TEST_CASE( perms )\n{\n    BOOST_CHECK_EQUAL(header(5), L\"Permissions\");\n    BOOST_CHECK_EQUAL(detail(5), L\"-rw-rw-rw-\");\n}\n\nBOOST_AUTO_TEST_CASE( owner )\n{\n    BOOST_CHECK_EQUAL(header(6), L\"Owner\");\n    BOOST_CHECK_EQUAL(detail(6), L\"bobowner\");\n}\n\nBOOST_AUTO_TEST_CASE( group )\n{\n    BOOST_CHECK_EQUAL(header(7), L\"Group\");\n    BOOST_CHECK_EQUAL(detail(7), L\"mygroup\");\n}\n\nBOOST_AUTO_TEST_CASE( ownerid )\n{\n    BOOST_CHECK_EQUAL(header(8), L\"Owner ID\");\n    BOOST_CHECK_EQUAL(detail(8), L\"578\");\n}\n\nBOOST_AUTO_TEST_CASE( groupid )\n{\n    BOOST_CHECK_EQUAL(header(9), L\"Group ID\");\n    BOOST_CHECK_EQUAL(detail(9), L\"1001\");\n}\n\n/**\n * Get one header too far.\n */\nBOOST_AUTO_TEST_CASE( out_of_bounds )\n{\n    BOOST_CHECK_THROW(header(10), std::exception);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/remote_folder/properties_test.cpp",
    "content": "/**\n    @file\n\n    Exercise remote-folder properties.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/remote_folder/properties.hpp> // test subject\n#include <swish/remote_folder/remote_pidl.hpp> // create_remote_itemid\n\n#include <washer/shell/property_key.hpp> // property_key\n\n#include <comet/datetime.h> // datetime_t\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\n#include <Propkey.h> // PKEY_ *\n\nusing swish::remote_folder::compare_pidls_by_property;\nusing swish::remote_folder::create_remote_itemid;\nusing swish::remote_folder::property_from_pidl;\n\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::property_key;\n\nusing comet::variant_t;\nusing comet::datetime_t;\n\nusing std::string;\n\nBOOST_AUTO_TEST_SUITE(properties_tests)\n\nnamespace {\n\n    cpidl_t gimme_pidl()\n    {\n        return create_remote_itemid(\n            L\"some filename.txt\", false, false, L\"bobowner\", L\"mygroup\",\n            578, 1001, 0100666, 1024, datetime_t(2010, 1, 1, 12, 30, 17, 42),\n            datetime_t(2010, 1, 1, 0, 0, 5, 7));\n    }\n}\n\nBOOST_AUTO_TEST_CASE( prop_label )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ItemNameDisplay);\n    BOOST_CHECK_EQUAL(prop, \"some filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_owner )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_FileOwner);\n    BOOST_CHECK_EQUAL(prop, \"bobowner\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_group )\n{\n    string prop = property_from_pidl(\n        gimme_pidl(), swish::remote_folder::PKEY_Group);\n    BOOST_CHECK_EQUAL(prop, \"mygroup\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_ownerid )\n{\n    int prop = property_from_pidl(\n        gimme_pidl(), swish::remote_folder::PKEY_OwnerId);\n    BOOST_CHECK_EQUAL(prop, 578);\n}\n\nBOOST_AUTO_TEST_CASE( prop_groupid )\n{\n    int prop = property_from_pidl(\n        gimme_pidl(), swish::remote_folder::PKEY_GroupId);\n    BOOST_CHECK_EQUAL(prop, 1001);\n}\n\nBOOST_AUTO_TEST_CASE( prop_perms )\n{\n    string prop = property_from_pidl(\n        gimme_pidl(), swish::remote_folder::PKEY_Permissions);\n    BOOST_CHECK_EQUAL(prop, \"-rw-rw-rw-\");\n}\n\nBOOST_AUTO_TEST_CASE( prop_size )\n{\n    ULONGLONG prop =\n        property_from_pidl(gimme_pidl(), PKEY_Size).as_ulonglong();\n    BOOST_CHECK_EQUAL(prop, 1024U);\n}\n\nBOOST_AUTO_TEST_CASE( prop_modified )\n{\n    datetime_t prop = property_from_pidl(gimme_pidl(), PKEY_DateModified);\n    BOOST_CHECK_EQUAL(prop, datetime_t(2010, 1, 1, 12, 30, 17, 42));\n}\n\nBOOST_AUTO_TEST_CASE( prop_accessed )\n{\n    datetime_t prop = property_from_pidl(gimme_pidl(), PKEY_DateAccessed);\n    BOOST_CHECK_EQUAL(prop, datetime_t(2010, 1, 1, 0, 0, 5, 7));\n}\n\nBOOST_AUTO_TEST_CASE( prop_type )\n{\n    string prop = property_from_pidl(gimme_pidl(), PKEY_ItemTypeText);\n    BOOST_CHECK_EQUAL(prop, \"Text Document\");\n}\n\nnamespace {\n    \n    cpidl_t comp_pidl()\n    {\n        return create_remote_itemid(\n            L\"sane filename.txt\", false, false, L\"booowner\", L\"mygroup\",0, 1001,\n            // <                                   >          ==        <  ==\n            0100666, 1023,\n            //  ==   <\n            datetime_t(2010, 1, 1, 12, 30, 17, 43), // >\n            datetime_t(2010, 1, 1, 0, 0, 5, 7)); // ==\n    }\n\n    int compare(const property_key& key)\n    {\n        return compare_pidls_by_property(gimme_pidl(), comp_pidl(), key);\n    }\n}\n\nBOOST_AUTO_TEST_CASE( comp_label )\n{\n    int res = compare(PKEY_ItemNameDisplay);\n    BOOST_CHECK_GT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_owner )\n{\n    int res = compare(PKEY_FileOwner);\n    BOOST_CHECK_LT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_group )\n{\n    int res = compare(swish::remote_folder::PKEY_Group);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_ownerid )\n{\n    int res = compare(swish::remote_folder::PKEY_OwnerId);\n    BOOST_CHECK_GT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_groupid )\n{\n    int res = compare(swish::remote_folder::PKEY_GroupId);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_perms )\n{\n    int res = compare(swish::remote_folder::PKEY_Permissions);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_size )\n{\n    int res = compare(PKEY_Size);\n    BOOST_CHECK_GT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_modified )\n{\n    int res = compare(PKEY_DateModified);\n    BOOST_CHECK_LT(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_accessed )\n{\n    int res = compare(PKEY_DateAccessed);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_CASE( comp_type )\n{\n    int res = compare(PKEY_ItemTypeText);\n    BOOST_CHECK_EQUAL(res, 0);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/remote_folder/remote_commands_test.cpp",
    "content": "// Copyright 2011, 2012, 2013, 2015, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/remote_folder/commands/commands.hpp\"  // test subject\n#include \"swish/remote_folder/commands/NewFolder.hpp\" // test subject\n\n#include \"test/common_boost/helpers.hpp\" // BOOST_REQUIRE_OK\n#include \"test/fixtures/provider_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/bind.hpp> // bind;\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nusing swish::nse::IEnumUICommand;\nusing swish::nse::IUICommand;\nusing swish::nse::command_site;\nusing swish::remote_folder::commands::NewFolder;\nusing swish::remote_folder::commands::remote_folder_task_pane_tasks;\n\nusing test::fixtures::provider_fixture;\n\nusing comet::com_ptr;\n\nusing ssh::filesystem::directory_iterator;\nusing ssh::filesystem::path;\n\nusing boost::bind;\n\nusing std::distance;\nusing std::wstring;\n\nnamespace\n{ // private\n\nconst wstring NEW_FOLDER = L\"New folder\";\n\nclass NewFolderCommandFixture : public provider_fixture\n{\npublic:\n    NewFolder new_folder_command()\n    {\n        return NewFolder(sandbox_pidl(),\n                         bind(&NewFolderCommandFixture::Provider, this),\n                         bind(&NewFolderCommandFixture::Consumer, this));\n    }\n};\n}\n\ntemplate <>\nstruct comet::comtype<IObjectWithSite>\n{\n    static const IID& uuid() throw()\n    {\n        return IID_IObjectWithSite;\n    }\n    typedef ::IUnknown base;\n};\n\n#pragma region NewFolder tests\nBOOST_FIXTURE_TEST_SUITE(new_folder_tests, NewFolderCommandFixture)\n\n/**\n * Test NewFolder command has correct properties that don't involve executing\n * the command.\n */\nBOOST_AUTO_TEST_CASE(non_execution_properties)\n{\n    NewFolder command = new_folder_command();\n    BOOST_CHECK(!command.guid().is_null());\n    BOOST_CHECK(!command.title(NULL).empty());\n    BOOST_CHECK(!command.tool_tip(NULL).empty());\n    BOOST_CHECK(!command.icon_descriptor(NULL).empty());\n    BOOST_CHECK_EQUAL(command.state(NULL, true), NewFolder::state::enabled);\n}\n\n/**\n * Test in empty directory that (inevitably) has no collisions.\n */\nBOOST_AUTO_TEST_CASE(no_collision_empty)\n{\n    path expected = sandbox() / NEW_FOLDER;\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      1);\n}\n\n/**\n * Test in a directory that isn't empty but which doesn't have any collisions.\n */\nBOOST_AUTO_TEST_CASE(no_collision)\n{\n    new_file_in_sandbox();\n    path expected = sandbox() / NEW_FOLDER;\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      2);\n}\n\n/**\n * Test in a directory that has existing \"New folder\".  Should create\n  * \"New folder (2)\" instead.\n */\nBOOST_AUTO_TEST_CASE(basic_collision)\n{\n    path collision = sandbox() / NEW_FOLDER;\n    path expected = sandbox() / (NEW_FOLDER + L\" (2)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      2);\n}\n\n/**\n * Test in a directory that has existing \"New folder (2)\" but not \"New folder\".\n * We want to make sure that this doesn't prevent \"New folder\" being created.\n */\nBOOST_AUTO_TEST_CASE(non_interfering_collision)\n{\n    path collision = sandbox() / (NEW_FOLDER + L\" (2)\");\n    path expected = sandbox() / NEW_FOLDER;\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      2);\n}\n\n/**\n * Test in a directory that has existing \"New folder\" and \"New folder (2)\".\n * Should create \"New folder (3)\" instead.\n */\nBOOST_AUTO_TEST_CASE(multiple_collision)\n{\n    path collision1 = sandbox() / NEW_FOLDER;\n    path collision2 = sandbox() / (NEW_FOLDER + L\" (2)\");\n    path expected = sandbox() / (NEW_FOLDER + L\" (3)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision1));\n    BOOST_REQUIRE(create_directory(filesystem(), collision2));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision1));\n    BOOST_REQUIRE(is_directory(filesystem(), collision2));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      3);\n}\n\n/**\n * Test in a directory that has existing \"New folder\" and \"New folder (3)\"\n * but not \"New folder (2). Should create \"New folder (2)\" in the gap.\n */\nBOOST_AUTO_TEST_CASE(non_contiguous_collision1)\n{\n    path collision1 = sandbox() / NEW_FOLDER;\n    path collision2 = sandbox() / (NEW_FOLDER + L\" (3)\");\n    path expected = sandbox() / (NEW_FOLDER + L\" (2)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision1));\n    BOOST_REQUIRE(create_directory(filesystem(), collision2));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision1));\n    BOOST_REQUIRE(is_directory(filesystem(), collision2));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      3);\n}\n\n/**\n * Test in a directory that has existing \"New folder\", \"New folder (2)\" and\n * \"New Folder (4)\" but not \"New folder (3)\". Should create \"New folder (3)\" in\n * the gap.\n */\nBOOST_AUTO_TEST_CASE(non_contiguous_collision2)\n{\n    path collision1 = sandbox() / NEW_FOLDER;\n    path collision2 = sandbox() / (NEW_FOLDER + L\" (2)\");\n    path collision3 = sandbox() / (NEW_FOLDER + L\" (4)\");\n    path expected = sandbox() / (NEW_FOLDER + L\" (3)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision1));\n    BOOST_REQUIRE(create_directory(filesystem(), collision2));\n    BOOST_REQUIRE(create_directory(filesystem(), collision3));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision1));\n    BOOST_REQUIRE(is_directory(filesystem(), collision2));\n    BOOST_REQUIRE(is_directory(filesystem(), collision3));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      4);\n}\n\n/**\n * Test in a directory that has existing \"New folder\", \"New folder (2)\" and\n * \"New folder (3) \" (note the trailing space). Should create \"New folder (3)\"\n * as it doesn't collide.\n */\nBOOST_AUTO_TEST_CASE(collision_suffix_mismatch)\n{\n    path collision1 = sandbox() / NEW_FOLDER;\n    path collision2 = sandbox() / (NEW_FOLDER + L\" (2)\");\n    path collision3 = sandbox() / (NEW_FOLDER + L\" (3) \");\n    path expected = sandbox() / (NEW_FOLDER + L\" (3)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision1));\n    BOOST_REQUIRE(create_directory(filesystem(), collision2));\n    BOOST_REQUIRE(create_directory(filesystem(), collision3));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision1));\n    BOOST_REQUIRE(is_directory(filesystem(), collision2));\n    BOOST_REQUIRE(is_directory(filesystem(), collision3));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      4);\n}\n\n/**\n * Test in a directory that has existing \"New folder\", \"New folder (2)\" and\n * \" New folder (3)\" (note the leading space). Should create \"New folder (3)\"\n * as it doesn't collide.\n */\nBOOST_AUTO_TEST_CASE(collision_prefix_mismatch)\n{\n    path collision1 = sandbox() / NEW_FOLDER;\n    path collision2 = sandbox() / (NEW_FOLDER + L\" (2)\");\n    path collision3 = sandbox() / (L\" \" + NEW_FOLDER + L\" (3) \");\n    path expected = sandbox() / (NEW_FOLDER + L\" (3)\");\n\n    BOOST_REQUIRE(create_directory(filesystem(), collision1));\n    BOOST_REQUIRE(create_directory(filesystem(), collision2));\n    BOOST_REQUIRE(create_directory(filesystem(), collision3));\n\n    NewFolder command = new_folder_command();\n    command(NULL, command_site(), NULL);\n\n    BOOST_REQUIRE(is_directory(filesystem(), expected));\n    BOOST_REQUIRE(is_directory(filesystem(), collision1));\n    BOOST_REQUIRE(is_directory(filesystem(), collision2));\n    BOOST_REQUIRE(is_directory(filesystem(), collision3));\n\n    BOOST_CHECK_EQUAL(distance(filesystem().directory_iterator(sandbox()),\n                               filesystem().directory_iterator()),\n                      4);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n\n#pragma region Task pane tests\nBOOST_FIXTURE_TEST_SUITE(new_folder_task_pane_tests, provider_fixture)\n\n/**\n * Test that task pane items can have their OLE site set.\n */\nBOOST_AUTO_TEST_CASE(task_pane_old_site)\n{\n    std::pair<com_ptr<IEnumUICommand>, com_ptr<IEnumUICommand>> panes =\n        remote_folder_task_pane_tasks(\n            sandbox_pidl(), NULL,\n            bind(&NewFolderCommandFixture::Provider, this),\n            bind(&NewFolderCommandFixture::Consumer, this));\n\n    BOOST_REQUIRE(panes.first);\n\n    com_ptr<IUICommand> new_folder;\n    ULONG fetched = 0;\n    HRESULT hr = panes.first->Next(1, new_folder.out(), &fetched);\n    BOOST_REQUIRE_OK(hr);\n\n    com_ptr<IObjectWithSite> object = try_cast(new_folder);\n    hr = object->SetSite(NULL);\n    BOOST_REQUIRE_OK(hr);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/remote_folder/remote_pidl_test.cpp",
    "content": "/**\n    @file\n\n    Exercise remote PIDL.\n\n    @if license\n\n    Copyright (C) 2011  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/remote_folder/remote_pidl.hpp> // test subject\n\n#include \"test/common_boost/helpers.hpp\" // wide-char comparison\n\n#include <comet/datetime.h> // datetime_t\n\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t\n#include <washer/shell/shell.hpp> // pidl_from_parsing_name\n\n#include <boost/test/unit_test.hpp>\n\n#include <exception>\n#include <stdexcept> // runtime_error\n#include <string>\n\nusing swish::remote_folder::create_remote_itemid;\nusing swish::remote_folder::path_from_remote_pidl;\nusing swish::remote_folder::remote_itemid_view;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::pidl::pidl_t;\nusing washer::shell::pidl_from_parsing_name;\n\nusing comet::com_ptr;\nusing comet::datetime_t;\n\nusing std::exception;\nusing std::runtime_error;\nusing std::wstring;\n\nnamespace {\n\n    /**\n     * Return the PIDL to the Swish HostFolder in Explorer.\n     */\n    apidl_t swish_pidl()\n    {\n        return pidl_from_parsing_name(\n            L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n            L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\");\n    }\n\n    cpidl_t test_remote_itemid(const wstring& filename, bool is_folder)\n    {\n        return create_remote_itemid(\n            filename, is_folder, false, L\"bobuser\", L\"bob's group\", 1001, 65535,\n            040666, 18446744073709551615, datetime_t(1970, 11, 1, 9, 15, 42, 6),\n            datetime_t((DATE)0));\n    }\n}\n\nBOOST_AUTO_TEST_SUITE( remote_pidl_tests )\n\nBOOST_AUTO_TEST_CASE( create_for_file )\n{\n    cpidl_t item = test_remote_itemid(L\"testfile.txt\", false);\n\n    BOOST_REQUIRE(remote_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfile.txt\");\n    BOOST_CHECK(!remote_itemid_view(item).is_folder());\n    BOOST_CHECK(!remote_itemid_view(item).is_link());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group(), L\"bob's group\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner_id(), 1001U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group_id(), 65535U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).permissions(), 040666U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).size(), 18446744073709551615U);\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_modified(),\n        datetime_t(1970, 11, 1, 9, 15, 42, 6));\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_accessed(), datetime_t());\n    // repeat\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfile.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( create_for_file_from_raw )\n{\n    cpidl_t managed_pidl = test_remote_itemid(L\"testfile.txt\", false);\n    PCITEMID_CHILD item = managed_pidl.get();\n\n    BOOST_REQUIRE(remote_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfile.txt\");\n    BOOST_CHECK(!remote_itemid_view(item).is_folder());\n    BOOST_CHECK(!remote_itemid_view(item).is_link());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group(), L\"bob's group\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner_id(), 1001U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group_id(), 65535U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).permissions(), 040666U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).size(), 18446744073709551615U);\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_modified(),\n        datetime_t(1970, 11, 1, 9, 15, 42, 6));\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_accessed(), datetime_t());\n    // repeat\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfile.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( create_for_folder )\n{\n    cpidl_t item = test_remote_itemid(L\"testfolder.txt\", true);\n\n    BOOST_REQUIRE(remote_itemid_view(item).valid());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfolder.txt\");\n    BOOST_CHECK(remote_itemid_view(item).is_folder());\n    BOOST_CHECK(!remote_itemid_view(item).is_link());\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner(), L\"bobuser\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group(), L\"bob's group\");\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).owner_id(), 1001U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).group_id(), 65535U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).permissions(), 040666U);\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).size(), 18446744073709551615U);\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_modified(),\n        datetime_t(1970, 11, 1, 9, 15, 42, 6));\n    BOOST_CHECK_EQUAL(\n        remote_itemid_view(item).date_accessed(), datetime_t());\n    // repeat\n    BOOST_CHECK_EQUAL(remote_itemid_view(item).filename(), L\"testfolder.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( invalid_remote_item )\n{\n    apidl_t pidl = washer::shell::special_folder_pidl(CSIDL_DRIVES);\n\n    BOOST_CHECK(!remote_itemid_view(pidl).valid());\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).filename(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).is_folder(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).is_link(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).owner(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).group(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).owner_id(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).group_id(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).permissions(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).size(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).date_modified(), exception);\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).date_accessed(), exception);\n    // repeat\n    BOOST_CHECK_THROW(remote_itemid_view(pidl).filename(), exception);\n}\n\nBOOST_AUTO_TEST_CASE( build_path_from_remote_pidl )\n{\n    pidl_t pidl =\n        test_remote_itemid(L\"foo\", true) + test_remote_itemid(L\"bar\", false) +\n        test_remote_itemid(L\"biscuit.txt\", false);\n\n    BOOST_CHECK_EQUAL(\n        path_from_remote_pidl(pidl), \"foo/bar/biscuit.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( build_path_from_remote_pidl_renders_expected_string )\n{\n    // The path may compare equal to the expected string without rendering\n    // itself as the same string.  For example, the slashes might be backslashes\n    // instead of forward slashes.  This causes problems down the line.\n    pidl_t pidl =\n        test_remote_itemid(L\"foo\", true) + test_remote_itemid(L\"bar\", false) +\n        test_remote_itemid(L\"biscuit.txt\", false);\n\n    BOOST_CHECK_EQUAL(\n        path_from_remote_pidl(pidl).string(), \"foo/bar/biscuit.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( build_path_from_remote_pidl_single )\n{\n    cpidl_t pidl = test_remote_itemid(L\"foo\", true);\n\n    BOOST_CHECK_EQUAL(path_from_remote_pidl(pidl), L\"foo\");\n}\n\nBOOST_AUTO_TEST_CASE( build_path_from_remote_pidl_root )\n{\n    cpidl_t pidl = test_remote_itemid(L\"/\", true);\n\n    BOOST_CHECK_EQUAL(path_from_remote_pidl(pidl), L\"/\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/remote_folder/swish_pidl_test.cpp",
    "content": "/**\n    @file\n\n    Exercise code that operates over complete Swish PIDLs.\n\n    @if license\n\n    Copyright (C) 2011, 2016 Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <swish/remote_folder/swish_pidl.hpp> // test subject\n\n#include <swish/host_folder/host_pidl.hpp> // create_host_itemid\n\n#include \"test/common_boost/helpers.hpp\" // wide-char comparison\n#include \"test/common_boost/SwishPidlFixture.hpp\"\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <boost/test/unit_test.hpp>\n\nusing swish::host_folder::create_host_itemid;\nusing swish::remote_folder::absolute_path_from_swish_pidl;\n\nusing washer::shell::pidl::apidl_t;\n\nBOOST_FIXTURE_TEST_SUITE(swish_pidl_tests, test::SwishPidlFixture)\n\n/**\n * Test that a Swish PIDL ending in just a host itemid results in the\n * correct path.\n *\n * @TODO: test with remote itemids as well.\n */\nBOOST_AUTO_TEST_CASE(pidl_to_absolute_path_host_item_only)\n{\n    apidl_t pidl =\n        fake_swish_pidl() +\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p/q\", 22);\n\n    BOOST_CHECK_EQUAL(absolute_path_from_swish_pidl(pidl), L\"/p/q\");\n}\n\n/**\n * Test path extraction for a Swish PIDL containing a remote item id.\n */\nBOOST_AUTO_TEST_CASE(pidl_to_absolute_path)\n{\n    apidl_t pidl =\n        fake_swish_pidl() +\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p/q\", 22);\n    pidl += create_dummy_remote_itemid(L\"foo\", false);\n\n    BOOST_CHECK_EQUAL(absolute_path_from_swish_pidl(pidl), L\"/p/q/foo\");\n}\n/**\n * Test path extraction for a Swish PIDL containing a remote item id.\n */\nBOOST_AUTO_TEST_CASE(pidl_to_absolute_path_renders_expected_string)\n{\n    // The path may compare equal to the expected string without rendering\n    // itself as the same string.  For example, the slashes might be backslashes\n    // instead of forward slashes.  This causes problems down the line.\n    apidl_t pidl =\n        fake_swish_pidl() +\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p/q\", 22);\n    pidl += create_dummy_remote_itemid(L\"foo\", false);\n\n    BOOST_CHECK_EQUAL(absolute_path_from_swish_pidl(pidl).string(), \"/p/q/foo\");\n}\n\n/**\n * Test path extraction for a Swish PIDL containing two remote item ids.\n */\nBOOST_AUTO_TEST_CASE(pidl_to_absolute_path_multiple_remote_items)\n{\n    apidl_t pidl =\n        fake_swish_pidl() +\n        create_host_itemid(L\"host.example.com\", L\"bobuser\", L\"/p/q\", 22);\n    pidl += create_dummy_remote_itemid(L\"foo\", true);\n    pidl += create_dummy_remote_itemid(L\".bob\", false);\n\n    BOOST_CHECK_EQUAL(absolute_path_from_swish_pidl(pidl), L\"/p/q/foo/.bob\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/shell/CMakeLists.txt",
    "content": "# Copyright 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  shell_test.cpp)\n\nswish_test_suite(\n  SUBJECT shell\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES} local_sandbox_fixture\n  LABELS unit)\n\nswish_copy_fixture_files(test-shell)\n"
  },
  {
    "path": "test/shell/shell_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for the shell utility functions.\n\n    @if license\n\n    Copyright (C) 2009, 2011, 2012, 2015\n    Alexander Lamaison <swish@lammy.co.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/shell/shell.hpp\" // Test subject\n\n// use PidlFormat to inspect DataObjects produces by the Windows Shell\n#include \"swish/shell_folder/data_object/ShellDataObject.hpp\"\n\n#include \"test/common_boost/fixtures.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/fixtures/local_sandbox_fixture.hpp\"\n\n#include <washer/shell/pidl.hpp> // apidl_t\n\n#include <boost/test/unit_test.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <vector>\n#include <string>\n#include <algorithm> // transform\n\nusing swish::shell::ui_object_of_item;\nusing swish::shell::ui_object_of_items;\nusing swish::shell::path_from_pidl;\nusing swish::shell::pidl_from_path;\nusing swish::shell::data_object_for_files;\nusing swish::shell::data_object_for_file;\nusing swish::shell::data_object_for_directory;\nusing swish::shell_folder::data_object::PidlFormat;\n\nusing washer::shell::pidl::apidl_t;\n\nusing test::ComFixture;\nusing test::fixtures::local_sandbox_fixture;\n\nusing boost::filesystem::path;\nusing boost::test_tools::predicate_result;\nusing boost::shared_ptr;\n\nusing std::vector;\nusing std::wstring;\n\nnamespace\n{ // private\n\n/**\n * Check that a PIDL and a filesystem path refer to the same item.\n */\npredicate_result pidl_path_equivalence(apidl_t pidl, path path)\n{\n    vector<wchar_t> name(MAX_PATH);\n    ::SHGetPathFromIDListW(pidl.get(), &name[0]);\n\n    if (!equivalent(path, &name[0]))\n    {\n        predicate_result res(false);\n        res.message() << \"Different items [\" << wstring(&name[0])\n                      << \" != \" << path.string() << \"]\";\n        return res;\n    }\n\n    return true;\n}\n\nclass ShellFunctionFixture : public ComFixture, public local_sandbox_fixture\n{\npublic:\n};\n}\n\n//\n// There are three types of shell function being tested here: those\n// that require real filesystem (non-virtual paths), those to do with\n// DataObjects specifically and those that are generic with respect to\n// both the above (they work on generic objects and take PIDLs instead\n// of paths).\n// Perhaps these three types should be separated out.\n//\n\n#pragma region Shell utility function tests\nBOOST_FIXTURE_TEST_SUITE(shell_utility_tests, ShellFunctionFixture)\n\n/**\n * Convert a PIDL to a path.  The path should match the source from which the\n * PIDL was created.\n *\n * Tests path_from_pidl().\n */\nBOOST_AUTO_TEST_CASE(convert_pidl_to_path)\n{\n    path source = new_file_in_local_sandbox();\n    shared_ptr<ITEMIDLIST_ABSOLUTE> pidl(\n        ::ILCreateFromPathW(source.wstring().c_str()), ::ILFree);\n\n    path path_from_conversion = path_from_pidl(pidl.get());\n\n    BOOST_REQUIRE(equivalent(path_from_conversion, source));\n}\n\n/**\n * Make a PIDL from a path.  We should be able to convert the PIDL back to\n * a path that refers to the same item as the original path.\n *\n * Tests pidl_from_path().\n */\nBOOST_AUTO_TEST_CASE(convert_path_to_pidl)\n{\n    path source = new_file_in_local_sandbox();\n\n    shared_ptr<ITEMIDLIST_ABSOLUTE> pidl = pidl_from_path(source);\n\n    vector<wchar_t> buffer(MAX_PATH);\n    BOOST_REQUIRE(::SHGetPathFromIDListW(pidl.get(), &buffer[0]));\n    BOOST_REQUIRE(equivalent(wstring(&buffer[0]), source));\n}\n\n/**\n * Ask the shell for a DataObject 'on' a given file.  This means that the\n * shell should create a DataObject holding a PIDL list format\n * (CFSTR_SHELLIDLIST) with two items in it:\n * - an absolute PIDL to the given file's parent folder\n * - the file's single-item (child) PIDL relative to the parent folder\n *\n * Tests data_object_for_file().\n */\nBOOST_AUTO_TEST_CASE(single_item_dataobject)\n{\n    path source = new_file_in_local_sandbox();\n\n    PidlFormat format(data_object_for_file(source));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 1U);\n\n    BOOST_REQUIRE(\n        pidl_path_equivalence(format.parent_folder(), local_sandbox()));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(0), source));\n}\n\n/**\n * Ask the shell for a DataObject 'on' two items in the same folder.\n * This means that the shell should create a DataObject holding a PIDL list\n * format (CFSTR_SHELLIDLIST) with three items in it:\n * - an absolute PIDL to the given files' parent folder\n * - the first file's single-item (child) PIDL relative to the parent folder\n * - the second file's single-item (child) PIDL relative to the parent folder\n *\n * Tests data_object_for_files().\n */\nBOOST_AUTO_TEST_CASE(multi_item_dataobject)\n{\n    vector<path> sources;\n    sources.push_back(new_file_in_local_sandbox());\n    sources.push_back(new_file_in_local_sandbox());\n\n    PidlFormat format(data_object_for_files(sources.begin(), sources.end()));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 2U);\n\n    BOOST_REQUIRE(\n        pidl_path_equivalence(format.parent_folder(), local_sandbox()));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(0), sources[0]));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(1), sources[1]));\n}\n\n/**\n * Ask for an associated object of a given file.  In this case we ask for a\n * DataObject because then we can subject it to the same tests as the\n * data_object_for_file test above.\n *\n * Tests ui_object_of_item().\n */\nBOOST_AUTO_TEST_CASE(single_item_ui_object)\n{\n    path source = new_file_in_local_sandbox();\n\n    PidlFormat format(\n        ui_object_of_item<IDataObject>(pidl_from_path(source).get()));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 1U);\n\n    BOOST_REQUIRE(\n        pidl_path_equivalence(format.parent_folder(), local_sandbox()));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(0), source));\n}\n\n/**\n * Ask for an associated object of two files in the same folder.  In this case\n * we ask for a DataObject because then we can subject it to the same tests\n * as the data_object_for_files test above.\n *\n * Tests ui_object_of_items().\n */\nBOOST_AUTO_TEST_CASE(multi_item_ui_object)\n{\n    vector<path> sources;\n    sources.push_back(new_file_in_local_sandbox());\n    sources.push_back(new_file_in_local_sandbox());\n\n    vector<shared_ptr<ITEMIDLIST_ABSOLUTE>> pidls;\n    transform(sources.begin(), sources.end(), back_inserter(pidls),\n              pidl_from_path);\n\n    PidlFormat format(\n        ui_object_of_items<IDataObject>(pidls.begin(), pidls.end()));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 2U);\n\n    BOOST_REQUIRE(\n        pidl_path_equivalence(format.parent_folder(), local_sandbox()));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(0), sources[0]));\n    BOOST_REQUIRE(pidl_path_equivalence(format.file(1), sources[1]));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/shell_folder/CMakeLists.txt",
    "content": "# Copyright (C) 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(UNIT_TESTS\n  data_object_test.cpp\n  exercise_data_object.h\n  file_group_descriptor_test.cpp\n  global_lock_test.cpp\n  sftp_data_object_nasty_old_test.cpp\n  sftp_directory_test.cpp\n  shell_data_object_test.cpp\n  utils_test.cpp)\n\nset(INTEGRATION_TESTS\n  atl.cpp\n  exercise_data_object.h\n  remote_folder_test.cpp\n  sftp_data_object_test.cpp)\n\nswish_test_suite(\n  SUBJECT shell_folder VARIANT unit\n  SOURCES ${UNIT_TESTS}\n  LIBRARIES ${Boost_LIBRARIES} local_sandbox_fixture\n  LABELS unit)\n\nswish_test_suite(\n  SUBJECT shell_folder VARIANT integration\n  SOURCES ${INTEGRATION_TESTS}\n  LIBRARIES ${Boost_LIBRARIES} provider_fixture\n  LABELS integration)\n\nswish_copy_fixture_files(test-shell_folder-integration)\n"
  },
  {
    "path": "test/shell_folder/atl.cpp",
    "content": "/**\n    @file\n\n    ATL Module required for ATL support.\n\n    @if licence\n\n    Copyright (C) 2009, 2015  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/atl.hpp\"\n\nnamespace test {\nnamespace swish {\nnamespace shell_folder {\n\nusing ATL::CAtlModule;\n\n/**\n * ATL module needed to use ATL-based objects.\n */\nclass CModule : public CAtlModule\n{\npublic :\n\n    virtual HRESULT AddCommonRGSReplacements(IRegistrarBase*) throw()\n    {\n        return S_OK;\n    }\n};\n\n}}} // namespace test::swish::com_dll\n\ntest::swish::shell_folder::CModule _AtlModule; ///< Global module instance\n"
  },
  {
    "path": "test/shell_folder/data_object_test.cpp",
    "content": "/**\n    @file\n\n    Testing DataObject implementation.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"test/common_boost/fixtures.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/SwishPidlFixture.hpp\"\n#include \"exercise_data_object.h\"\n\n#include \"swish/host_folder/host_pidl.hpp\" // create_host_itemid\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n#include \"swish/shell_folder/DataObject.h\"\n\n#include <washer/shell/pidl.hpp>\n\n#include <comet/bstr.h> // bstr_t\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/test/unit_test.hpp>\n\n#include <vector>\n\nusing swish::host_folder::create_host_itemid;\nusing swish::remote_folder::remote_itemid_view;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::bstr_t;\nusing comet::com_ptr;\n\nnamespace comet {\n\ntemplate<> struct comtype<IDataObject>\n{\n\tstatic const IID& uuid() { return IID_IDataObject; }\n\ttypedef ::IUnknown base;\n};\n\n}\n\nnamespace test {\n\nnamespace {\n\n/**\n * Test enumerator for the presence of CFSTR_SHELLIDLIST but the absence of\n * CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS.\n *\n * Format-limited version of _testEnumerator() in DataObjectTests.h.\n */\nvoid _testCDataObjectEnumerator(com_ptr<IEnumFORMATETC> pEnum)\n{\n    CLIPFORMAT cfShellIdList = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));\n    CLIPFORMAT cfDescriptor = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));\n    CLIPFORMAT cfContents = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILECONTENTS));\n\n    bool fFoundShellIdList = false;\n    bool fFoundDescriptor = false;\n    bool fFoundContents = false;\n\n    HRESULT hr;\n    do {\n        FORMATETC fetc;\n        hr = pEnum->Next(1, &fetc, NULL);\n        if (hr == S_OK)\n        {\n            if (fetc.cfFormat == cfShellIdList)\n                fFoundShellIdList = true;\n            else if (fetc.cfFormat == cfDescriptor)\n                fFoundDescriptor = true;\n            else if (fetc.cfFormat == cfContents)\n                fFoundContents = true;\n        }\n    } while (hr == S_OK);\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format present\n    BOOST_CHECK(fFoundShellIdList);\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format absent\n    BOOST_CHECK(!fFoundDescriptor);\n\n    // Test CFSTR_FILECONTENTS (IStream) format absent\n    BOOST_CHECK(!fFoundContents);\n}\n\n/**\n * Test the GetData() enumerator for the presence of CFSTR_SHELLIDLIST\n * but the absence of CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS.\n *\n * Test the SetData() enumerator for the presence of all three formats.\n *\n * Format-limited version of _testBothEnumerators() in DataObjectTests.h.\n */\nvoid _testBothCDataObjectEnumerators(com_ptr<IDataObject> data_object)\n{\n    HRESULT hr;\n\n    // Test enumerator of GetData() formats\n    com_ptr<IEnumFORMATETC> spEnumGet;\n    hr = data_object->EnumFormatEtc(DATADIR_GET, spEnumGet.out());\n    BOOST_REQUIRE_OK(hr);\n    _testCDataObjectEnumerator(spEnumGet);\n\n    // Test enumerator of SetData() formats\n    com_ptr<IEnumFORMATETC> spEnumSet;\n    hr = data_object->EnumFormatEtc(DATADIR_SET, spEnumSet.out());\n    BOOST_REQUIRE_OK(hr);\n    _testCDataObjectEnumerator(spEnumSet);\n}\n\n/**\n * Test QueryGetData() enumerator for the presence of CFSTR_SHELLIDLIST\n * but the absence of CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS.\n *\n * Format-limited version of _testQueryFormats() in DataObjectTests.h.\n */\nvoid _testCDataObjectQueryFormats(com_ptr<IDataObject> data_object)\n{\n    // Test CFSTR_SHELLIDLIST (PIDL array) format succeeds\n    CFormatEtc fetcShellIdList(CFSTR_SHELLIDLIST);\n    BOOST_REQUIRE_OK(data_object->QueryGetData(&fetcShellIdList));\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format fails\n    CFormatEtc fetcDescriptor(CFSTR_FILEDESCRIPTOR);\n    BOOST_CHECK(data_object->QueryGetData(&fetcDescriptor) == S_FALSE);\n\n    // Test CFSTR_FILECONTENTS (IStream) format fails\n    CFormatEtc fetcContents(CFSTR_FILECONTENTS);\n    BOOST_CHECK(data_object->QueryGetData(&fetcContents) == S_FALSE);\n}\n\nclass TestFixture : public SwishPidlFixture, public ComFixture\n{\n};\n\n}\n\n/**\n * Tests for our generic shell DataObject wrapper.  This class only creates\n * CFSTR_SHELLIDLIST formats (and some misc private shell ones) on its own.\n * However, it will store other format when they are set using SetData() and\n * will return them in GetData() and well as acknowleging their presence\n * in QueryGetData() and in the IEnumFORMATETC.\n *\n * Creation of other formats is left to the CSftpDataObject subclass.\n *\n * These tests verify this behaviour.\n */\nBOOST_FIXTURE_TEST_SUITE(data_object_tests, TestFixture)\n\nBOOST_AUTO_TEST_CASE( Create )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCITEMID_CHILD pidl_array[] = { pidl.get() };\n\n    com_ptr<IDataObject> data_object =\n        new CDataObject(1, pidl_array, root.get());\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    cpidl_t root_child = root.last_item();\n    remote_itemid_view folder(root_child);\n    _testShellPIDLFolder(data_object, folder.filename());\n    _testShellPIDL(data_object, remote_itemid_view(pidl).filename(), 0);\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format\n    // CDataObject should not produce a CFSTR_FILEDESCRIPTOR format\n    BOOST_CHECK_THROW(\n        _testFileDescriptor(data_object, L\"testswishfile.ext\", 0),\n        std::exception);\n\n    // Test CFSTR_FILECONTENTS (IStream) format\n    // CDataObject should not produce a CFSTR_FILECONTENTS format\n    BOOST_CHECK_THROW(\n        _testStreamContents(data_object, L\"/tmp/swish/testswishfile.ext\", 0),\n        std::exception);\n}\n\nBOOST_AUTO_TEST_CASE( CreateMulti )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object =\n        new CDataObject(3, aPidl, root.get());\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    cpidl_t root_child = root.last_item();\n    remote_itemid_view folder(root_child);\n    _testShellPIDLFolder(data_object, folder.filename());\n    _testShellPIDL(data_object, remote_itemid_view(pidl1).filename(), 0);\n    _testShellPIDL(data_object, remote_itemid_view(pidl2).filename(), 1);\n    _testShellPIDL(data_object, remote_itemid_view(pidl3).filename(), 2);\n}\n\n/**\n * Test that QueryGetData fails for all our formats when created with\n * empty PIDL list.\n */\nBOOST_AUTO_TEST_CASE( QueryFormatsEmpty )\n{\n    com_ptr<IDataObject> data_object = new CDataObject(0, NULL, NULL);\n\n    // Test that QueryGetData() responds negatively for all our formats\n    _testQueryFormats(data_object, true);\n}\n\n/**\n * Test that none of our expected formats are in the enumerator when \n * created with empty PIDL list.\n */\nBOOST_AUTO_TEST_CASE( EnumFormatsEmpty )\n{\n    com_ptr<IDataObject> data_object = new CDataObject(0, NULL, NULL);\n\n    // Test that enumerators of both GetData() and SetData()\n    // formats fail to enumerate any of our formats\n    _testBothEnumerators(data_object, true);\n}\n\n/**\n * Test that QueryGetData responds successfully for all our formats.\n */\nBOOST_AUTO_TEST_CASE( QueryFormats )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCITEMID_CHILD pidl_array[] = { pidl.get() };\n\n    com_ptr<IDataObject> data_object = \n        new CDataObject(1, pidl_array, root.get());\n\n    // Perform query tests\n    _testCDataObjectQueryFormats(data_object);\n}\n\n/**\n * Test that all our expected formats are in the enumeration.\n */\nBOOST_AUTO_TEST_CASE( EnumFormats )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCITEMID_CHILD pidl_array[] = { pidl.get() };\n\n    com_ptr<IDataObject> data_object = \n        new CDataObject(1, pidl_array, root.get());\n\n    // Test enumerators of both GetData() and SetData() formats\n    _testBothCDataObjectEnumerators(data_object);\n}\n\n/**\n * Test that QueryGetData responds successfully for all our formats when\n * initialised with multiple PIDLs.\n */\nBOOST_AUTO_TEST_CASE( QueryFormatsMulti )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object = new CDataObject(3, aPidl, root.get());\n\n    // Perform query tests\n    _testCDataObjectQueryFormats(data_object);\n}\n\n/**\n * Test that all our expected formats are in the enumeration when\n * initialised with multiple PIDLs.\n */\nBOOST_AUTO_TEST_CASE( EnumFormatsMulti )\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object = new CDataObject(3, aPidl, root.get());\n\n    // Test enumerators of both GetData() and SetData() formats\n    _testBothCDataObjectEnumerators(data_object);\n}\n\n} // namespace test\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/shell_folder/exercise_data_object.h",
    "content": "/**\n    @file\n\n    Miscellaneous tests for the Swish DataObject.\n\n    @if license\n\n    Copyright (C) 2012  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#ifndef TEST_SHELL_FOLDER_EXERCISE_DATA_OBJECT_HPP\n#define TEST_SHELL_FOLDER_EXERCISE_DATA_OBJECT_HPP\n\n#include \"swish/host_folder/host_pidl.hpp\" // host_itemid_view\n#include \"swish/remote_folder/remote_pidl.hpp\" // path_from_remote_pidl\n                                               // remote_itemid_view\n#include \"swish/shell_folder/DataObject.h\"\n\n#include <comet/error.h> // com_error_from_interface\n#include <comet/ptr.h> // com_ptr\n\n#include <boost/numeric/conversion/cast.hpp> // numeric_cast\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <algorithm> // replace\n#include <string>\n#include <vector>\n\n/**\n * Test that Shell PIDL from DataObject holds the expected number of PIDLs.\n */\nstatic void _testShellPIDLCount(\n    comet::com_ptr<IDataObject> data_object, UINT nExpected)\n{\n    FORMATETC fetc = {\n        (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_SHELLIDLIST),\n        NULL,\n        DVASPECT_CONTENT,\n        -1,\n        TYMED_HGLOBAL\n    };\n\n    STGMEDIUM stg;\n    HRESULT hr = data_object->GetData(&fetc, &stg);\n    BOOST_REQUIRE_OK(hr);\n\n    BOOST_CHECK(stg.hGlobal);\n    CIDA *pida = (CIDA *)::GlobalLock(stg.hGlobal);\n    BOOST_CHECK(pida);\n\n    UINT nActual = pida->cidl;\n    BOOST_CHECK_EQUAL(nExpected, nActual);\n\n    ::GlobalUnlock(stg.hGlobal);\n    pida = NULL;\n    ::ReleaseStgMedium(&stg);\n}\n\n#define GetPIDLFolder(pida) \\\n    (PCIDLIST_ABSOLUTE)(((LPBYTE)pida)+(pida)->aoffset[0])\n#define GetPIDLItem(pida, i) \\\n    (PCIDLIST_RELATIVE)(((LPBYTE)pida)+(pida)->aoffset[i+1])\n\n/**\n * Test that Shell PIDL from DataObject represent the expected file.\n */\nstatic void _testShellPIDL(\n    comet::com_ptr<IDataObject> data_object, std::wstring expected, UINT iFile)\n{\n    FORMATETC fetc = {\n        (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_SHELLIDLIST),\n        NULL,\n        DVASPECT_CONTENT,\n        -1,\n        TYMED_HGLOBAL\n    };\n\n    STGMEDIUM stg;\n    HRESULT hr = data_object->GetData(&fetc, &stg);\n    BOOST_REQUIRE_OK(hr);\n\n    BOOST_CHECK(stg.hGlobal);\n    CIDA *pida = (CIDA *)::GlobalLock(stg.hGlobal);\n    BOOST_CHECK(pida);\n\n    BOOST_CHECK_EQUAL(\n        expected,\n        swish::remote_folder::path_from_remote_pidl(\n            GetPIDLItem(pida, iFile)).wstring().c_str());\n\n    ::GlobalUnlock(stg.hGlobal);\n    pida = NULL;\n    ::ReleaseStgMedium(&stg);\n}\n\n/**\n * Test that Shell PIDL from DataObject represents the common root folder.\n *\n * The PIDL may be a RemoteItemId, in which case @p expected should be the\n * name of the directory (e.g \"tmp\"), but it may also be an HostItemId in which\n * case the path (e.g. \"/tmp\") that is expected to be found in that item\n * should be passed.\n */\nstatic void _testShellPIDLFolder(\n    comet::com_ptr<IDataObject> data_object, std::wstring expected)\n{\n    FORMATETC fetc = {\n        (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_SHELLIDLIST),\n        NULL,\n        DVASPECT_CONTENT,\n        -1,\n        TYMED_HGLOBAL\n    };\n\n    STGMEDIUM stg;\n    HRESULT hr = data_object->GetData(&fetc, &stg);\n    BOOST_REQUIRE_OK(hr);\n\n    BOOST_CHECK(stg.hGlobal);\n    CIDA *pida = (CIDA *)::GlobalLock(stg.hGlobal);\n    BOOST_CHECK(pida);\n\n    // Test folder PIDL which may be a RemoteItemId or a HostItemId\n    washer::shell::pidl::cpidl_t actual = ::ILFindLastID(GetPIDLFolder(pida));\n    if (swish::remote_folder::remote_itemid_view(actual).valid())\n    {\n        BOOST_CHECK_EQUAL(\n            expected,\n            swish::remote_folder::remote_itemid_view(actual).filename());\n    }\n    else if (swish::host_folder::host_itemid_view(actual).valid())\n    {\n        swish::host_folder::host_itemid_view itemid(actual);\n        std::wstring actual_path = itemid.path().wstring();\n        BOOST_CHECK_EQUAL(expected, actual_path);\n    }\n    else\n    {\n        BOOST_FAIL(\"Invalid PIDL\");\n    }\n    ::GlobalUnlock(stg.hGlobal);\n    pida = NULL;\n    ::ReleaseStgMedium(&stg);\n}\n\n/**\n * Test that the the FILEGROUPDESCRIPTOR and ith FILEDESCRIPTOR\n * match expected values.\n * File descriptors should use Windows path separators so we replace\n * forward slashes with back slashes in expected string.\n */\nstatic void _testFileDescriptor(\n    comet::com_ptr<IDataObject> data_object, std::wstring expected, UINT iFile)\n{\n    std::replace(expected.begin(), expected.end(), L'/', L'\\\\');\n\n    FORMATETC fetc = {\n        (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR),\n        NULL,\n        DVASPECT_CONTENT,\n        -1,\n        TYMED_HGLOBAL\n    };\n\n    STGMEDIUM stg = STGMEDIUM();\n    stg.tymed = fetc.tymed;\n    HRESULT hr = data_object->GetData(&fetc, &stg);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error_from_interface(data_object, hr));\n\n    BOOST_CHECK(stg.hGlobal);\n    FILEGROUPDESCRIPTOR *fgd =\n        (FILEGROUPDESCRIPTOR *)::GlobalLock(stg.hGlobal);\n    BOOST_CHECK(fgd);\n\n    BOOST_CHECK(iFile < fgd->cItems);\n    BOOST_CHECK(fgd->fgd);\n    std::wstring actual(fgd->fgd[iFile].cFileName);\n    BOOST_CHECK_EQUAL(expected, actual);\n\n    ::GlobalUnlock(stg.hGlobal);\n    fgd = NULL;\n    ::ReleaseStgMedium(&stg);\n}\n\n/**\n * Test that the contents of the DummyStream matches what is expected.\n */\nstatic void _testStreamContents(\n    comet::com_ptr<IDataObject> data_object, std::wstring expected, UINT iFile)\n{\n    FORMATETC fetc = {\n        (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_FILECONTENTS),\n        NULL,\n        DVASPECT_CONTENT,\n        iFile,\n        TYMED_ISTREAM\n    };\n\n    STGMEDIUM stg;\n    HRESULT hr = data_object->GetData(&fetc, &stg);\n    if (FAILED(hr))\n        BOOST_THROW_EXCEPTION(comet::com_error_from_interface(data_object, hr));\n\n    BOOST_CHECK(stg.pstm);\n\n    std::vector<wchar_t> buffer(MAX_PATH);\n    ULONG cbRead = 0;\n    hr = stg.pstm->Read(\n        &buffer[0], boost::numeric_cast<ULONG>(buffer.size()), &cbRead);\n    BOOST_REQUIRE_OK(hr);\n\n    std::wstring actual(&buffer[0]);\n    BOOST_CHECK_EQUAL(expected, actual);\n\n    ::ReleaseStgMedium(&stg);\n}\n\n/**\n * Test for success (or failure) when querying the presence of\n * our expected formats.\n */\nstatic void _testQueryFormats(\n    comet::com_ptr<IDataObject> data_object, bool fFailTest=false)\n{\n    HRESULT hr;\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    if (!fFailTest) // Vista includes this format even for empty PIDL array\n    {\n        CFormatEtc fetcShellIdList(CFSTR_SHELLIDLIST);\n        hr = data_object->QueryGetData(&fetcShellIdList);\n        BOOST_REQUIRE_OK(hr);\n    }\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format\n    CFormatEtc fetcDescriptor(CFSTR_FILEDESCRIPTOR);\n    hr = data_object->QueryGetData(&fetcDescriptor);\n    BOOST_CHECK(hr == ((fFailTest) ? S_FALSE : S_OK));\n\n    // Test CFSTR_FILECONTENTS (IStream)\n\n    // Since Windows 7 (or maybe Vista) we must get TYMED_ISTREAM right here.\n    // Previously if you prodded with a TYMED_ISTREAM but checked with\n    // TYMED_GLOBAL it still worked.  Not any more\n    CFormatEtc fetcContents(CFSTR_FILECONTENTS, TYMED_ISTREAM);\n    hr = data_object->QueryGetData(&fetcContents);\n    BOOST_CHECK(hr == ((fFailTest) ? S_FALSE : S_OK));\n}\n\n/**\n * Test enumerator for the presence (or absence) of our expected formats.\n */\nstatic void _testEnumerator(\n    comet::com_ptr<IEnumFORMATETC> pEnum, bool fFailTest=false)\n{\n    CLIPFORMAT cfShellIdList = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_SHELLIDLIST));\n    CLIPFORMAT cfDescriptor = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));\n    CLIPFORMAT cfContents = static_cast<CLIPFORMAT>(\n        ::RegisterClipboardFormat(CFSTR_FILECONTENTS));\n\n    bool fFoundShellIdList = false;\n    bool fFoundDescriptor = false;\n    bool fFoundContents = false;\n\n    HRESULT hr;\n    do {\n        FORMATETC fetc;\n        hr = pEnum->Next(1, &fetc, NULL);\n        if (hr == S_OK)\n        {\n            if (fetc.cfFormat == cfShellIdList)\n                fFoundShellIdList = true;\n            else if (fetc.cfFormat == cfDescriptor)\n                fFoundDescriptor = true;\n            else if (fetc.cfFormat == cfContents)\n                fFoundContents = true;\n        }\n    } while (hr == S_OK);\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    if (!fFailTest) // Vista includes this format even for empty PIDL array\n        BOOST_CHECK(fFoundShellIdList);\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format\n    BOOST_CHECK((fFailTest) ? !fFoundDescriptor : fFoundDescriptor);\n\n    // Test CFSTR_FILECONTENTS (IStream)\n    BOOST_CHECK((fFailTest) ? !fFoundContents : fFoundContents);\n}\n\n/**\n * Perform our enumerator tests for both SetData() and GetData() enums.\n */\nstatic void _testBothEnumerators(\n    comet::com_ptr<IDataObject> data_object, bool fFailTest=false)\n{\n    HRESULT hr;\n\n    // Test enumerator of GetData() formats\n    comet::com_ptr<IEnumFORMATETC> enum_get;\n    hr = data_object->EnumFormatEtc(DATADIR_GET, enum_get.out());\n    BOOST_REQUIRE_OK(hr);\n    _testEnumerator(enum_get, fFailTest);\n\n    // Test enumerator of SetData() formats\n    comet::com_ptr<IEnumFORMATETC> enum_set;\n    hr = data_object->EnumFormatEtc(DATADIR_SET, enum_set.out());\n    BOOST_REQUIRE_OK(hr);\n    _testEnumerator(enum_set, fFailTest);\n}\n\n#endif\n"
  },
  {
    "path": "test/shell_folder/file_group_descriptor_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for classes derived from basic_pidl.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/shell_folder/data_object/FileGroupDescriptor.hpp\"  // subject\n\n#include <test/common_boost/helpers.hpp>\n\n#include <boost/test/unit_test.hpp>\n#include <boost/shared_ptr.hpp>  // shared_ptr\n\n#include <cstring>  // memset, memcpy\n\nusing namespace swish::shell_folder::data_object;\nusing boost::shared_ptr;\n\nnamespace {\n\n    class FgdFixture\n    {\n    public:\n\n        static const size_t TEST_ALLOC_SIZE =\n            sizeof(FILEGROUPDESCRIPTOR) + sizeof(FILEDESCRIPTOR);\n\n        /**\n         * Allocate a fake FILEGROUPDESCRIPTOR with space for two\n         * FILEDESCRIPTORS.\n         */\n        FgdFixture() :\n            m_hglobal(\n                ::GlobalAlloc(GMEM_MOVEABLE, TEST_ALLOC_SIZE), ::GlobalFree)\n        {\n            BOOST_REQUIRE(m_hglobal.get());\n\n            FILEGROUPDESCRIPTOR* fgd =\n                static_cast<FILEGROUPDESCRIPTOR*>(\n                    ::GlobalLock(m_hglobal.get()));\n            BOOST_REQUIRE(fgd);\n\n            fgd->cItems = 2;\n\n            FILEDESCRIPTOR fd1;\n            std::memset(&fd1, 0, sizeof(fd1));\n            wcscpy_s(fd1.cFileName, L\"test\\\\item\\\\path\");\n\n            FILEDESCRIPTOR fd2;\n            std::memset(&fd2, 0, sizeof(fd2));\n            wcscpy_s(fd2.cFileName, L\"test\\\\item\\\\bob\");\n\n            fgd->fgd[0] = fd1;\n            fgd->fgd[1] = fd2;\n\n            BOOST_REQUIRE(::GlobalUnlock(fgd));\n        }\n\n        /**\n         * Return pointer to chunk of memory intialised with fake\n         * FILEGROUPDESCRIPTOR.\n         */\n        HGLOBAL get() const\n        {\n            return m_hglobal.get();\n        }\n\n    private:\n        shared_ptr<void> m_hglobal; // HGLOBAL\n    };\n\n\n}\n\n#pragma region FileGroupDescriptor tests\nBOOST_FIXTURE_TEST_SUITE(file_group_descriptor_tests, FgdFixture)\n\n/**\n * Constructor doesn't throw.\n */\nBOOST_AUTO_TEST_CASE( create )\n{\n    FileGroupDescriptor fgd(get());\n}\n\n/**\n * Counting contained descriptors gives the expected value of 2.\n */\nBOOST_AUTO_TEST_CASE( size )\n{\n    FileGroupDescriptor fgd(get());\n    size_t size = fgd.size();\n    BOOST_REQUIRE_EQUAL(size, 2U);\n}\n\n/**\n * Accessing descriptors renders the expected data.\n */\nBOOST_AUTO_TEST_CASE( access )\n{\n    FileGroupDescriptor fgd(get());\n    BOOST_CHECK_EQUAL(fgd[0].path(), L\"test\\\\item\\\\path\");\n    BOOST_CHECK_EQUAL(fgd[1].path(), L\"test\\\\item\\\\bob\");\n    BOOST_CHECK_EQUAL(fgd[0].path(), L\"test\\\\item\\\\path\");\n}\n\n/**\n * Accessing an out-of-bounds descriptor throws an exception.\n */\nBOOST_AUTO_TEST_CASE( bounds_error )\n{\n    FileGroupDescriptor fgd(get());\n    BOOST_REQUIRE_THROW(fgd[2], std::out_of_range);\n}\n\n/**\n * The lifetime of a descriptor outlives that of its parent group.\n */\nBOOST_AUTO_TEST_CASE( descriptor_lifetime )\n{\n    FileGroupDescriptor fgd(get());\n    Descriptor d = fgd[0];\n\n    {\n        FileGroupDescriptor scoped_fgd(get());\n        d = scoped_fgd[1];\n    }\n\n    BOOST_CHECK_EQUAL(d.path(), L\"test\\\\item\\\\bob\");\n}\n\n/**\n * Changing a descriptor outside the FileGroupDescriptor should leave the\n * copy in the FGD unchanged.  This checks that descriptors point at copies\n * not references to the original memory.\n */\nBOOST_AUTO_TEST_CASE( descriptor_independence )\n{\n    FileGroupDescriptor fgd(get());\n    Descriptor d = fgd[1];\n    d.path(L\"replaced/path\");\n\n    BOOST_CHECK_EQUAL(d.path(), L\"replaced\\\\path\");\n    BOOST_CHECK_EQUAL(fgd[1].path(), L\"test\\\\item\\\\bob\");\n}\n\n/**\n * Changing a descriptor in the FileGroupDescriptor directly should change\n * the value returned in subsequent accesses.  This checks that the FGD\n * [] accessor returns the descriptors by reference.\n */\nBOOST_AUTO_TEST_CASE( descriptor_access_byref )\n{\n    FileGroupDescriptor fgd(get());\n    fgd[1].path(L\"replaced/path\");\n    Descriptor d = fgd[1];\n\n    BOOST_CHECK_EQUAL(d.path(), L\"replaced\\\\path\");\n    BOOST_CHECK_EQUAL(fgd[1].path(), L\"replaced\\\\path\");\n}\n\n/**\n * A copy of an FGD should give the expected data from its accessors.\n * This checks that the copied FGD has access to sensible data but does\n * *not* check that it points to the same copy of the data as the original.\n */\nBOOST_AUTO_TEST_CASE( copy_construct )\n{\n    FileGroupDescriptor fgd_orig(get());\n    FileGroupDescriptor fgd = fgd_orig;\n    BOOST_CHECK_EQUAL(fgd[0].path(), L\"test\\\\item\\\\path\");\n    BOOST_CHECK_EQUAL(fgd[1].path(), L\"test\\\\item\\\\bob\");\n    BOOST_REQUIRE_EQUAL(fgd.size(), 2U);\n}\n\n/**\n * A copy of an FGD should point to the same memory as the original.\n * Therefore, changes to one should affect the other.\n */\nBOOST_AUTO_TEST_CASE( copies_are_linked )\n{\n    FileGroupDescriptor fgd_orig(get());\n    FileGroupDescriptor fgd = fgd_orig;\n\n    fgd[1].path(L\"replaced/path\");\n\n    BOOST_CHECK_EQUAL(fgd_orig[1].path(), L\"replaced\\\\path\");\n}\n\n/**\n * Descriptor field are initialised to zero.\n */\nBOOST_AUTO_TEST_CASE( descriptor_zero_init )\n{\n    Descriptor d;\n    const FILEDESCRIPTOR fd = d.get();\n\n    FILEDESCRIPTOR fd_zero;\n    std::memset(&fd_zero, 0, sizeof(fd_zero));\n\n    BOOST_REQUIRE(!std::memcmp(&fd, &fd_zero, sizeof(fd)));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/shell_folder/global_lock_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for the locked HGLOBAL wrapper.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/atl.hpp\"\n\n#include \"swish/shell_folder/data_object/GlobalLocker.hpp\"  // Test subject\n\n#include \"test/common_boost/fixtures.hpp\"\n\n#include <boost/test/unit_test.hpp>\n#include <boost/shared_ptr.hpp>  // shared_ptr\n#include <boost/system/system_error.hpp>  // system_error\n\n#include <string>\n\nusing boost::shared_ptr;\nusing boost::system::system_error;\nusing std::string;\n\nnamespace { // private\n\n    typedef swish::shell_folder::data_object::GlobalLocker<char> \n        GlobalStringLock;\n\n    /**\n     * Put the test string into global memory and return a smart pointer to it.\n     */\n    shared_ptr<void> global_test_data(const string& data)\n    {\n        shared_ptr<void> global(\n            ::GlobalAlloc(GMEM_MOVEABLE, data.size()+1), ::GlobalFree);\n        char* buf = static_cast<char*>(::GlobalLock(global.get()));\n\n        ::CopyMemory(buf, data.c_str(), data.size());\n        buf[data.size()] = '\\0';\n\n        ::GlobalUnlock(buf);\n        return global;\n    }\n}\n\nBOOST_AUTO_TEST_SUITE( global_lock_test )\n\n/**\n * Get locked data and check that it isn't unexpectedly different.\n */\nBOOST_AUTO_TEST_CASE( lock )\n{\n    shared_ptr<void> global = global_test_data(\"lorem ipsum\");\n\n    GlobalStringLock lock(global.get());\n    char* data = lock.get();\n\n    BOOST_REQUIRE_EQUAL(data, \"lorem ipsum\");\n}\n\n/**\n * Create on an invalid HGLOBAL.\n * This should fail and throw an exception.\n */\nBOOST_AUTO_TEST_CASE( lock_fail )\n{\n    BOOST_REQUIRE_THROW(GlobalStringLock lock(NULL), system_error);\n}\n\n/**\n * Copy a GlobalLock using the copy contructor.\n * The pointers returned from get() should be identical by *address*.\n */\nBOOST_AUTO_TEST_CASE( lock_copy )\n{\n    shared_ptr<void> global = global_test_data(\"lorem ipsum\");\n\n    GlobalStringLock lock(global.get());\n    void* data1 = lock.get();\n\n    GlobalStringLock lock_copy(lock);\n    void* data2 = lock_copy.get();\n\n    BOOST_REQUIRE_EQUAL(data1, data2); // Compare address, not strings\n}\n\n/**\n * Copy a GlobalLock using copy assignment.\n * The pointers returned from get() should be identical by *address*.\n */\nBOOST_AUTO_TEST_CASE( lock_copy_assign )\n{\n    // Create first lock on global data\n    shared_ptr<void> global1 = global_test_data(\"lorem ipsum\");\n    GlobalStringLock lock1(global1.get());\n\n    // Create a lock on *other* global data\n    shared_ptr<void> global2 = global_test_data(\"dolor sit amet\");\n    GlobalStringLock lock2(global2.get());\n\n    // Assign second lock to first which should point both locks to second data\n    lock1 = lock2;\n\n    char* data1 = lock1.get();\n    char* data2 = lock2.get();\n\n    // Compare addresses and make sure it points to the *second* string\n    BOOST_REQUIRE_EQUAL(static_cast<void*>(data1), static_cast<void*>(data2));\n    BOOST_REQUIRE_EQUAL(data1, \"dolor sit amet\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/shell_folder/remote_folder_test.cpp",
    "content": "// Copyright 2011, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"swish/shell_folder/RemoteFolder.h\" // test subject\n\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n\n#include \"test/common_boost/helpers.hpp\"  // BOOST_REQUIRE_OK\n#include \"test/common_boost/fixtures.hpp\" // ComFixture\n#include \"test/fixtures/provider_fixture.hpp\"\n\n#include <washer/shell/pidl.hpp>  // apidl_t\n#include <washer/shell/shell.hpp> // strret_to_string\n\n#include <comet/datetime.h>      // datetime_t\n#include <comet/enum_iterator.h> // enum_iterator\n#include <comet/error.h>         // com_error\n#include <comet/ptr.h>           // com_ptr\n\n#include <boost/bind.hpp>         // bind\n#include <boost/lexical_cast.hpp> // lexical_cast\n#include <boost/test/unit_test.hpp>\n\n#include <algorithm> // find_if\n\nusing test::fixtures::provider_fixture;\n\nusing swish::remote_folder::remote_itemid_view;\nusing swish::utils::Utf8StringToWideString;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\nusing washer::shell::strret_to_string;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::datetime_t;\nusing comet::enum_iterator;\n\nusing ssh::filesystem::path;\n\nusing boost::lexical_cast;\nusing boost::test_tools::predicate_result;\n\nusing std::find_if;\nusing std::wstring;\n\nnamespace comet\n{\n\ntemplate <>\nstruct comtype<IEnumIDList>\n{\n    static const IID& uuid() throw()\n    {\n        return IID_IEnumIDList;\n    }\n    typedef IUnknown base;\n};\n\ntemplate <>\nstruct enumerated_type_of<IEnumIDList>\n{\n    typedef PITEMID_CHILD is;\n};\n\n/**\n * Copy-policy for use by enumerators of child PIDLs.\n */\ntemplate <>\nstruct impl::type_policy<PITEMID_CHILD>\n{\n    static void init(PITEMID_CHILD& t, const cpidl_t& s)\n    {\n        s.copy_to(t);\n    }\n\n    static void clear(PITEMID_CHILD& t)\n    {\n        ::ILFree(t);\n    }\n};\n}\n\nnamespace\n{ // private\n\nclass RemoteFolderFixture : public provider_fixture, public test::ComFixture\n{\nprivate:\n    com_ptr<IShellFolder> m_folder;\n\npublic:\n    RemoteFolderFixture()\n        : m_folder(CRemoteFolder::Create(\n              sandbox_pidl().get(),\n              boost::bind(&RemoteFolderFixture::consumer_factory, this, _1)))\n    {\n    }\n\n    com_ptr<IShellFolder> folder() const\n    {\n        return m_folder;\n    }\n\n    comet::com_ptr<ISftpConsumer> consumer_factory(HWND)\n    {\n        return Consumer();\n    }\n};\n\nvoid test_enum(com_ptr<IEnumIDList> pidls, SHCONTF flags)\n{\n    PITEMID_CHILD pidl;\n    ULONG fetched;\n    HRESULT hr = pidls->Next(1, &pidl, &fetched);\n    BOOST_REQUIRE_OK(hr);\n    BOOST_CHECK_EQUAL(fetched, 1U);\n\n    do\n    {\n        remote_itemid_view itemid(pidl);\n\n        // Check REMOTEPIDLness\n        BOOST_REQUIRE(itemid.valid());\n\n        // Check filename\n        BOOST_CHECK_GT(itemid.filename().size(), 0U);\n        if (!(flags & SHCONTF_INCLUDEHIDDEN))\n            BOOST_CHECK_NE(itemid.filename(), L\".\");\n\n        // Check folderness\n        if (!(flags & SHCONTF_FOLDERS))\n            BOOST_CHECK(!itemid.is_folder());\n        if (!(flags & SHCONTF_NONFOLDERS))\n            BOOST_CHECK(itemid.is_folder());\n\n        // Check group and owner exist\n        BOOST_CHECK_GT(itemid.owner().size(), 0U);\n        BOOST_CHECK_GT(itemid.group().size(), 0U);\n\n        // Check date validity\n        BOOST_CHECK(itemid.date_modified().good());\n\n        hr = pidls->Next(1, &pidl, &fetched);\n    } while (hr == S_OK);\n\n    BOOST_CHECK_EQUAL(hr, S_FALSE);\n    BOOST_CHECK_EQUAL(fetched, 0U);\n}\n\nvoid test_enum(ATL::CComPtr<IEnumIDList> pidls, SHCONTF flags)\n{\n    test_enum(com_ptr<IEnumIDList>(pidls.p), flags);\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(remote_folder_tests, RemoteFolderFixture)\n\n/**\n * When a remote directory is empty, the remote folder's enumerator must\n * be empty.\n */\nBOOST_AUTO_TEST_CASE(enum_empty)\n{\n    SHCONTF flags =\n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    com_ptr<IEnumIDList> listing;\n    HRESULT hr = folder()->EnumObjects(NULL, flags, listing.out());\n    BOOST_REQUIRE_OK(hr);\n\n    ULONG fetched = 1;\n    PITEMID_CHILD pidl;\n    BOOST_CHECK_EQUAL(listing->Next(1, &pidl, &fetched), S_FALSE);\n    BOOST_CHECK_EQUAL(fetched, 0U);\n}\n\n/**\n * Requesting everything should return folder and dotted files as well.\n */\nBOOST_AUTO_TEST_CASE(enum_everything)\n{\n    path file1 = new_file_in_sandbox();\n    path file2 = new_file_in_sandbox();\n    path folder1 = sandbox() / L\"folder1\";\n    create_directory(filesystem(), folder1);\n    path folder2 = sandbox() / L\"folder2\";\n    create_directory(filesystem(), folder2);\n\n    SHCONTF flags =\n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    com_ptr<IEnumIDList> listing;\n    HRESULT hr = folder()->EnumObjects(NULL, flags, listing.out());\n    BOOST_REQUIRE_OK(hr);\n\n    test_enum(listing, flags);\n}\n\nnamespace\n{\n\nbool pidl_matches_filename(PCUITEMID_CHILD remote_pidl, wstring name)\n{\n    remote_itemid_view item(remote_pidl);\n    return item.filename() == name;\n}\n\ncpidl_t pidl_for_file(com_ptr<IShellFolder> folder, wstring name)\n{\n    SHCONTF flags =\n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    com_ptr<IEnumIDList> listing;\n    HRESULT hr = folder->EnumObjects(NULL, flags, listing.out());\n    BOOST_REQUIRE_OK(hr);\n\n    enum_iterator<IEnumIDList> pos = std::find_if(\n        enum_iterator<IEnumIDList>(listing), enum_iterator<IEnumIDList>(),\n        bind(pidl_matches_filename, _1, name));\n    BOOST_REQUIRE_MESSAGE(pos != enum_iterator<IEnumIDList>(),\n                          \"PIDL not found\");\n\n    return *pos;\n}\n\npredicate_result display_name_matches(com_ptr<IShellFolder> folder,\n                                      SHGDNF flags, const path& filename,\n                                      const wstring& expected_display_name)\n{\n    cpidl_t pidl = pidl_for_file(folder, filename.wstring());\n\n    STRRET strret;\n    HRESULT hr = folder->GetDisplayNameOf(pidl.get(), flags, &strret);\n    BOOST_REQUIRE_OK(hr);\n\n    wstring display_name = strret_to_string<wchar_t>(strret);\n    if (display_name != expected_display_name)\n    {\n        predicate_result res(false);\n        res.message() << L\"Display name for '\" << filename << L\"' unexpected: [\"\n                      << display_name << L\" != \" << expected_display_name\n                      << L\"]\";\n        return res;\n    }\n\n    return true;\n}\n}\n\n/**\n * Request the display name for a file.\n *\n * This is the name of the file in a form suitable displaying to the user\n * anywhere in Windows and therefore may need disambiguation information\n * included.  For example 'filename on host' rather than just 'filename'.\n *\n * The result may or may not include the extension depending on the user's\n * settings, so we accept either as a successful result.\n *\n * Currently we don't support disambiguation information in Swish.\n *\n * This name does not have to be parseable.\n */\nBOOST_AUTO_TEST_CASE(display_name_file)\n{\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL;\n    wstring expected_with = L\"testfile.txt\";\n    wstring expected_without = L\"testfile\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected_with) ||\n        display_name_matches(folder(), flags, file.filename(),\n                             expected_without));\n}\n\n/**\n * Request the display name for a Unix 'hidden' file.\n *\n * On Unix files are considered to be hidden if they start with a full-stop.\n * We adhere to this convention and should not treat an initial dot dot as part\n * of the extension.\n *\n * The result may or may not include the extension depending on the user's\n * settings, so we accept either as a successful result.\n */\nBOOST_AUTO_TEST_CASE(display_name_hidden_file)\n{\n    path file1 = new_file_in_sandbox(L\".hidden\");\n    path file2 = new_file_in_sandbox(L\".testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL;\n    wstring expected1 = L\".hidden\";\n    wstring expected2_with = L\".testfile.txt\";\n    wstring expected2_without = L\".testfile\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file1.filename(), expected1));\n    BOOST_CHECK(display_name_matches(folder(), flags, file2.filename(),\n                                     expected2_with) ||\n                display_name_matches(folder(), flags, file2.filename(),\n                                     expected2_without));\n}\n\n/**\n * Request the editing name for a file as though it were being edited elsewhere\n * than within its parent folder view.\n * I'm not sure how this situation would work but I don't think it matters for\n * us so we just return the usual editing name.\n */\nBOOST_AUTO_TEST_CASE(editing_name_file)\n{\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL | SHGDN_FOREDITING;\n    wstring expected = L\"testfile.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Request the name for a file as though it were shown in the address bar\n * somewhere that isn't necessarily the parent folder.\n */\nBOOST_AUTO_TEST_CASE(address_bar_name_file)\n{\n    BOOST_WARN_MESSAGE(false,\n                       \"skipping - testing full address bar requires \"\n                       \"registration and knowledge of the parent host folder\");\n    return; // Leaving code here in case we find a way round this\n\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL | SHGDN_FORADDRESSBAR;\n    wstring expected = L\"sftp://\" + wuser() + L\"@\" + whost() + L\":\" +\n                       lexical_cast<wstring>(port()) + L\"/\" + file.wstring();\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Check the display name for a file as it should be shown in a listing\n * of its containing folder.\n * In particular, this doesn't need disambiguation information that relates\n * to the folder it is in as this name is only used within the parent folder.\n *\n * The result may or may not include the extension depending on the user's\n * settings, so we accept either as a successful result.\n *\n * This name does not have to be parseable.\n */\nBOOST_AUTO_TEST_CASE(in_folder_display_name_file)\n{\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_INFOLDER;\n    wstring expected_with = L\"testfile.txt\";\n    wstring expected_without = L\"testfile\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected_with) ||\n        display_name_matches(folder(), flags, file.filename(),\n                             expected_without));\n}\n\n/**\n * Check the display name for a file of unregistered type as it should be\n * shown in a listing of its containing folder.\n * In particular, this doesn't need disambiguation information that relates\n * to the folder it is in as this name is only used within the parent folder.\n *\n * This test differs from in_folder_display_name_file in that the file\n * extension is of an unregistered type.  These should always show the\n * extension.\n *\n * This name does not have to be parseable.\n */\nBOOST_AUTO_TEST_CASE(in_folder_display_name_unknown_file)\n{\n    // May fail if .xyz is actually a registered type\n    path file = new_file_in_sandbox(L\"testfile.xyz\");\n\n    SHGDNF flags = SHGDN_INFOLDER;\n    wstring expected = L\"testfile.xyz\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Check the parsing name of a file relative to its containing folder.\n * In other words, return the name of the file in such a form that it can\n * be uniquely identified given that we know the folder it is in.\n * Effectively, this means return the filename with its extension but any\n * decorative text that isn't part of its real name should be removed.\n *\n * Our files over SFTP don't have any decorative text but we do have to deal\n * with the extension.\n *\n * The FORPARSING flag forces the file extension to be included, regardless\n * of any user setting.\n */\nBOOST_AUTO_TEST_CASE(in_folder_parsing_name_file)\n{\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_INFOLDER | SHGDN_FORPARSING;\n    wstring expected = L\"testfile.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Request the editing name for a file as though it were being renamed in-place.\n * Normally in Windows this is different from the in-folder parsing name in that\n * it wouldn't include the extension but we tweak this slightly so that\n * renaming a file shows the extension even if that isn't the default user\n * setting.\n */\nBOOST_AUTO_TEST_CASE(in_folder_editing_name_file)\n{\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_INFOLDER | SHGDN_FOREDITING;\n    wstring expected = L\"testfile.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n// NORMAL + FORPARSING = ABSOLUTE\n//\n// ... or so it would seem\n\n/**\n * Request the absolute name of a file as shown in the address bar.\n *\n * This should be a 'pretty' version of the name rather than the\n * truly parseable version that includes GUIDs etc.\n */\nBOOST_AUTO_TEST_CASE(absolute_address_bar_parsing_name_file)\n{\n    BOOST_WARN_MESSAGE(false,\n                       \"skipping - testing absolute parsing name requires \"\n                       \"registration and knowledge of the parent\");\n    return; // Leaving code here in case we find a way round this\n\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL | SHGDN_FORADDRESSBAR | SHGDN_FORPARSING;\n    wstring expected = L\"Computer\\\\Swish\\\\sftp://\" + wuser() + L\"@\" + whost() +\n                       L\":\" + lexical_cast<wstring>(port()) + L\"/\" +\n                       file.wstring();\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Request the absolute parsing name for a file.\n *\n * It must be possible to pass this to the @b desktop folder's ParseDisplayName\n * and get back a pidl for this item.\n */\nBOOST_AUTO_TEST_CASE(absolute_parsing_name_file)\n{\n    BOOST_WARN_MESSAGE(false,\n                       \"skipping - testing absolute parsing name requires \"\n                       \"registration and knowledge of the parent\");\n    return; // Leaving code here in case we find a way round this\n\n    path file = new_file_in_sandbox(L\"testfile.txt\");\n\n    SHGDNF flags = SHGDN_NORMAL | SHGDN_FORPARSING;\n    wstring expected = L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n                       L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\\\\sftp://\" +\n                       wuser() + L\"@\" + whost() + L\":\" +\n                       lexical_cast<wstring>(port()) + L\"/\" + file.wstring();\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, file.filename(), expected));\n}\n\n/**\n * Request the display name for a folder.\n *\n * This is the name of the file in a form suitable displaying to the user\n * anywhere in Windows and therefore may need disambiguation information\n * included.  For example 'folder on host' rather than just 'folder'.\n *\n * Currently we don't support disambiguation information in Swish.\n *\n * This name does not have to be parseable.\n */\nBOOST_AUTO_TEST_CASE(display_name_folder)\n{\n    path directory = sandbox() / L\"testfolder\";\n    create_directory(filesystem(), directory);\n\n    SHGDNF flags = SHGDN_NORMAL;\n    wstring expected = L\"testfolder\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, directory.filename(), expected));\n}\n\n/**\n * Request the display name for a folder within its parent folder view.\n *\n * This name does not have to be parseable.\n */\nBOOST_AUTO_TEST_CASE(in_folder_name_folder)\n{\n    path directory = sandbox() / L\"testfolder\";\n    create_directory(filesystem(), directory);\n\n    SHGDNF flags = SHGDN_INFOLDER;\n    wstring expected = L\"testfolder\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, directory.filename(), expected));\n}\n\n/**\n * Request the display name for a folder that looks like it has an extension.\n *\n * Dots in a folder don't really indicate an extension so we should return\n * the whole thing.\n */\nBOOST_AUTO_TEST_CASE(display_name_folder_with_extension)\n{\n    path directory = sandbox() / L\"testfolder.txt\";\n    create_directory(filesystem(), directory);\n\n    SHGDNF flags = SHGDN_NORMAL;\n    wstring expected = L\"testfolder.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, directory.filename(), expected));\n}\n\n/**\n * Request the display name for a folder that looks like it has an extension\n * in a form for use within its parent folder view.\n *\n * Dots in a folder don't really indicate an extension so we should return\n * the whole thing.\n */\nBOOST_AUTO_TEST_CASE(in_folder_name_folder_with_extension)\n{\n    path directory = sandbox() / L\"testfolder.txt\";\n    create_directory(filesystem(), directory);\n\n    SHGDNF flags = SHGDN_INFOLDER;\n    wstring expected = L\"testfolder.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, directory.filename(), expected));\n}\n\n/**\n * Request the display name for a Unix 'hidden' directory.\n *\n * On Unix files are considered to be hidden if they start with a full-stop.\n * Although we shouldn't treat any part of a folder name as an extension, we\n * test the initial-dot case here specially just to make sure.\n */\nBOOST_AUTO_TEST_CASE(display_name_hidden_folder)\n{\n    path dir1 = sandbox() / L\".hidden\";\n    create_directory(filesystem(), dir1);\n    path dir2 = sandbox() / L\".testfolder.txt\";\n    create_directory(filesystem(), dir2);\n\n    SHGDNF flags = SHGDN_NORMAL;\n    wstring expected1 = L\".hidden\";\n    wstring expected2 = L\".testfolder.txt\";\n\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, dir1.filename(), expected1));\n    BOOST_CHECK(\n        display_name_matches(folder(), flags, dir2.filename(), expected2));\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/shell_folder/sftp_data_object_nasty_old_test.cpp",
    "content": "/**\n    @file\n\n    Testing DataObject implementation.\n\n    @if license\n\n    Copyright (C) 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"test/common_boost/fixtures.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/MockProvider.hpp\"\n#include \"test/common_boost/SwishPidlFixture.hpp\"\n#include \"exercise_data_object.h\"\n\n#include \"swish/host_folder/host_pidl.hpp\"     // create_host_itemid\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view\n#include \"swish/shell_folder/SftpDataObject.h\"\n\n#include <washer/shell/pidl.hpp> // cpidl_t, apidl_t\n\n#include <comet/bstr.h> // bstr_t\n#include <comet/ptr.h>  // com_ptr\n\n#include <boost/test/unit_test.hpp>\n\n#include <vector>\n\nusing swish::host_folder::create_host_itemid;\nusing swish::provider::sftp_provider;\nusing swish::remote_folder::remote_itemid_view;\n\nusing boost::shared_ptr;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::bstr_t;\nusing comet::com_ptr;\n\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\nnamespace comet\n{\n\ntemplate <>\nstruct comtype<IDataObject>\n{\n    static const IID& uuid()\n    {\n        return IID_IDataObject;\n    }\n    typedef ::IUnknown base;\n};\n}\n\nnamespace test\n{\n\nnamespace\n{\n\nclass TestFixture : public ComFixture, public SwishPidlFixture\n{\npublic:\n    TestFixture()\n    {\n        // Create mock object coclass instances\n        m_pProvider = shared_ptr<sftp_provider>(new MockProvider());\n    }\n\nprotected:\n    shared_ptr<sftp_provider> m_pProvider;\n};\n}\n\n// HACK:\n// A lot of these tests rely on SwishPidlFixture creating a host PIDL with\n// path `/tmp` and a remote root PIDL with path `swish`.\n\nBOOST_FIXTURE_TEST_SUITE(sftp_data_object_nasty_old_tests, TestFixture)\n\nBOOST_AUTO_TEST_CASE(Create)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCUITEMID_CHILD pidl_array[] = {pidl.get()};\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(1, pidl_array, root.get(), m_pProvider);\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    cpidl_t root_child = root.last_item();\n    remote_itemid_view folder(root_child);\n    _testShellPIDLFolder(data_object, folder.filename());\n    _testShellPIDL(data_object, remote_itemid_view(pidl).filename(), 0);\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format\n    _testFileDescriptor(data_object, L\"testswishfile.ext\", 0);\n\n    // Test CFSTR_FILECONTENTS (IStream) format\n    _testStreamContents(data_object, L\"/tmp/swish/testswishfile.ext\", 0);\n}\n\nBOOST_AUTO_TEST_CASE(CreateMulti)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(3, aPidl, root.get(), m_pProvider);\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format\n    cpidl_t root_child = root.last_item();\n    remote_itemid_view folder(root_child);\n    _testShellPIDLFolder(data_object, folder.filename());\n    _testShellPIDL(data_object, remote_itemid_view(pidl1).filename(), 0);\n    _testShellPIDL(data_object, remote_itemid_view(pidl2).filename(), 1);\n    _testShellPIDL(data_object, remote_itemid_view(pidl3).filename(), 2);\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format\n    _testFileDescriptor(data_object, L\"testswishfile.ext\", 0);\n    _testFileDescriptor(data_object, L\"testswishfile.txt\", 1);\n    _testFileDescriptor(data_object, L\"testswishFile\", 2);\n\n    // Test CFSTR_FILECONTENTS (IStream) format\n    _testStreamContents(data_object, L\"/tmp/swish/testswishfile.ext\", 0);\n    _testStreamContents(data_object, L\"/tmp/swish/testswishfile.txt\", 1);\n    _testStreamContents(data_object, L\"/tmp/swish/testswishFile\", 2);\n}\n\n/**\n * Test that QueryGetData fails for all our formats when created with\n * empty PIDL list.\n */\nBOOST_AUTO_TEST_CASE(QueryFormatsEmpty)\n{\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(0, NULL, NULL, m_pProvider);\n\n    // Perform query tests\n    _testQueryFormats(data_object, true);\n}\n\n/**\n * Test that none of our expected formats are in the enumerator when\n * created with empty PIDL list.\n */\nBOOST_AUTO_TEST_CASE(EnumFormatsEmpty)\n{\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(0, NULL, NULL, m_pProvider);\n\n    // Test enumerators of both GetData() and SetData() formats\n    _testBothEnumerators(data_object, true);\n}\n\n/**\n * Test that QueryGetData responds successfully for all our formats.\n */\nBOOST_AUTO_TEST_CASE(QueryFormats)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCUITEMID_CHILD pidl_array[] = {pidl.get()};\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(1, pidl_array, root.get(), m_pProvider);\n\n    // Perform query tests\n    _testQueryFormats(data_object);\n}\n\n/**\n * Test that all our expected formats are in the enumeration.\n */\nBOOST_AUTO_TEST_CASE(EnumFormats)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n\n    PCUITEMID_CHILD pidl_array[] = {pidl.get()};\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(1, pidl_array, root.get(), m_pProvider);\n\n    // Test enumerators of both GetData() and SetData() formats\n    _testBothEnumerators(data_object);\n}\n\n/**\n * Test that QueryGetData responds successfully for all our formats when\n * initialised with multiple PIDLs.\n */\nBOOST_AUTO_TEST_CASE(QueryFormatsMulti)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(3, aPidl, root.get(), m_pProvider);\n\n    // Perform query tests\n    _testQueryFormats(data_object);\n}\n\n/**\n * Test that all our expected formats are in the enumeration when\n * initialised with multiple PIDLs.\n */\nBOOST_AUTO_TEST_CASE(EnumFormatsMulti)\n{\n    apidl_t root = create_dummy_root_pidl();\n    cpidl_t pidl1 = create_dummy_remote_itemid(L\"testswishfile.ext\", false);\n    cpidl_t pidl2 = create_dummy_remote_itemid(L\"testswishfile.txt\", false);\n    cpidl_t pidl3 = create_dummy_remote_itemid(L\"testswishFile\", false);\n\n    PCITEMID_CHILD aPidl[3];\n    aPidl[0] = pidl1.get();\n    aPidl[1] = pidl2.get();\n    aPidl[2] = pidl3.get();\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(3, aPidl, root.get(), m_pProvider);\n\n    // Test enumerators of both GetData() and SetData() formats\n    _testBothEnumerators(data_object);\n}\n\nBOOST_AUTO_TEST_CASE(FullDirectoryTree)\n{\n    // This has to start at / rather than /tmp\n    apidl_t root =\n        fake_swish_pidl() + create_host_itemid(L\"test.example.com\", L\"user\",\n                                               L\"/\", 22, L\"Test PIDL\");\n\n    cpidl_t pidl = create_dummy_remote_itemid(L\"tmp\", true);\n\n    PCUITEMID_CHILD pidl_array[] = {pidl.get()};\n\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(1, pidl_array, root.get(), m_pProvider);\n\n    // Test CFSTR_SHELLIDLIST (PIDL array) format.\n    _testShellPIDLFolder(data_object, L\"/\");\n    _testShellPIDLCount(data_object, 1);\n    _testShellPIDL(data_object, L\"tmp\", 0);\n\n    // Build list of paths in entire expected hierarchy\n\n    // HACK:\n    // These paths depend on the paths generated in the MockProvider. Any\n    // slight change there kills this test\n    vector<wstring> testfiles;\n    testfiles.push_back(L\"tmp\");\n    testfiles.push_back(L\"tmp/.qtmp\");\n    testfiles.push_back(L\"tmp/.testtmphiddenfile\");\n    testfiles.push_back(L\"tmp/.testtmphiddenfolder\");\n    testfiles.push_back(L\"tmp/Testtmpfolder\");\n    testfiles.push_back(L\"tmp/another linktmpfolder\");\n    testfiles.push_back(L\"tmp/linktmpfolder\");\n    testfiles.push_back(L\"tmp/ptmp\");\n    testfiles.push_back(L\"tmp/swish\");\n    testfiles.push_back(L\"tmp/swish/.qswish\");\n    testfiles.push_back(L\"tmp/swish/.testswishhiddenfile\");\n    testfiles.push_back(L\"tmp/swish/.testswishhiddenfolder\");\n    testfiles.push_back(L\"tmp/swish/Testswishfolder\");\n    testfiles.push_back(L\"tmp/swish/another linkswishfolder\");\n    testfiles.push_back(L\"tmp/swish/linkswishfolder\");\n    testfiles.push_back(L\"tmp/swish/pswish\");\n    testfiles.push_back(L\"tmp/swish/testswishFile\");\n    testfiles.push_back(L\"tmp/swish/testswishfile\");\n    testfiles.push_back(L\"tmp/swish/testswishfile with \\\"quotes\\\" and spaces\");\n    testfiles.push_back(L\"tmp/swish/testswishfile with spaces\");\n    testfiles.push_back(L\"tmp/swish/testswishfile..\");\n    testfiles.push_back(L\"tmp/swish/testswishfile.ext\");\n    testfiles.push_back(L\"tmp/swish/testswishfile.ext.txt\");\n    testfiles.push_back(L\"tmp/swish/testswishfile.txt\");\n    testfiles.push_back(L\"tmp/swish/testswishfolder with spaces\");\n    testfiles.push_back(L\"tmp/swish/testswishfolder.bmp\");\n    testfiles.push_back(L\"tmp/swish/testswishfolder.ext\");\n    testfiles.push_back(L\"tmp/swish/this_link_is_broken_swish\");\n    testfiles.push_back(L\"tmp/testtmpFile\");\n    testfiles.push_back(L\"tmp/testtmpfile\");\n    testfiles.push_back(L\"tmp/testtmpfile with \\\"quotes\\\" and spaces\");\n    testfiles.push_back(L\"tmp/testtmpfile with spaces\");\n    testfiles.push_back(L\"tmp/testtmpfile..\");\n    testfiles.push_back(L\"tmp/testtmpfile.ext\");\n    testfiles.push_back(L\"tmp/testtmpfile.ext.txt\");\n    testfiles.push_back(L\"tmp/testtmpfile.txt\");\n    testfiles.push_back(L\"tmp/testtmpfolder with spaces\");\n    testfiles.push_back(L\"tmp/testtmpfolder.bmp\");\n    testfiles.push_back(L\"tmp/testtmpfolder.ext\");\n    testfiles.push_back(L\"tmp/this_link_is_broken_tmp\");\n\n    // Test CFSTR_FILEDESCRIPTOR (FILEGROUPDESCRIPTOR) format.  The\n    // descriptor should include every item in the entire hierarchy\n    // generated by CMockSftpProvider.\n    for (UINT i = 0; i < testfiles.size(); ++i)\n    {\n        _testFileDescriptor(data_object, testfiles[i], i);\n    }\n\n    // Test CFSTR_FILECONTENTS (IStream) format.  The dummy streams should\n    // contain the absolute path to the file as a string\n    for (UINT i = 0; i < testfiles.size(); ++i)\n    {\n        _testStreamContents(data_object, wstring(L\"/\") + testfiles[i], i);\n    }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n\n} // namespace test\n"
  },
  {
    "path": "test/shell_folder/sftp_data_object_test.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2012, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * @file\n *\n * Unlike the tests in drop_target_test.cpp, these tests do not exercise\n * the CDropTarget component alone.  Nor do they exercise it directly.\n * Instead the simulate the calls the shell itself would make to drag\n * a file making use of the whole Shell Namespace Folder hierarchy.\n */\n\n#include \"swish/shell_folder/SftpDataObject.h\" // test subject\n#include \"swish/shell_folder/data_object/FileGroupDescriptor.hpp\" // accessor\n#include \"swish/shell_folder/data_object/ShellDataObject.hpp\"     // accessor\n#include \"swish/shell_folder/data_object/StorageMedium.hpp\"       // accessor\n\n#include \"test/common_boost/helpers.hpp\" // BOOST_REQUIRE_OK\n#include \"test/fixtures/provider_fixture.hpp\"\n\n#include <washer/shell/pidl.hpp>       // apidl_t, cpidl_t\n#include <washer/shell/shell_item.hpp> // pidl_shell_item\n\n#include <comet/ptr.h> // com_ptr\n\n#include <ssh/filesystem.hpp>\n#include <ssh/stream.hpp>\n\n#pragma warning(push)\n#pragma warning(disable : 4244) // conversion from uint64_t to uint32_t\n#include <boost/date_time/posix_time/conversion.hpp> // from_time_t\n#pragma warning(pop)\n#include <boost/date_time/posix_time/posix_time_io.hpp> // ptime stringerising\n#include <boost/numeric/conversion/cast.hpp>            // numeric_cast\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/throw_exception.hpp> // BOOST_THROW_EXCEPTION\n\n#include <string>\n#include <vector>\n\nusing namespace swish::shell_folder::data_object;\n\nusing test::fixtures::provider_fixture;\n\nusing namespace washer::shell::pidl;\nusing washer::shell::pidl_shell_item;\n\nusing comet::com_ptr;\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::path;\nusing ssh::filesystem::perms;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::numeric_cast;\nusing boost::system::system_error;\nusing boost::system::get_system_category;\nusing boost::posix_time::from_time_t;\nusing boost::test_tools::predicate_result;\n\nusing std::string;\nusing std::wstring;\nusing std::vector;\nusing std::istreambuf_iterator;\n\nnamespace comet\n{\n\ntemplate <>\nstruct comtype<::IDataObject>\n{\n    static const ::IID& uuid() throw()\n    {\n        return ::IID_IDataObject;\n    }\n    typedef ::IUnknown base;\n};\n}\n\nnamespace\n{ // private\n\nclass DataObjectFixture : public provider_fixture\n{\npublic:\n    vector<path> make_test_files(bool readonly = false)\n    {\n        vector<path> files;\n        files.push_back(new_file_in_sandbox(\"second\"));\n        files.push_back(new_file_in_sandbox(\"first\"));\n\n        {\n            ofstream s(filesystem(), files.at(0));\n            s << \"blah\";\n        }\n        {\n            ofstream s(filesystem(), files.at(1));\n            s << \"more stuff\";\n        }\n\n        if (readonly)\n        {\n            permissions(filesystem(), files.at(0), perms::owner_read);\n            permissions(filesystem(), files.at(1), perms::owner_read);\n        }\n\n        return files;\n    }\n};\n\n/**\n * Check that two PIDLS refer to the same item.\n */\npredicate_result pidl_equivalence(const apidl_t& pidl1, const apidl_t& pidl2)\n{\n    wstring name1 = pidl_shell_item(pidl1).parsing_name();\n    wstring name2 = pidl_shell_item(pidl2).parsing_name();\n\n    if (name1 != name2)\n    {\n        predicate_result res(false);\n        res.message() << \"PIDLs resolve to different items [\" << name1\n                      << \" != \" << name2 << \"]\";\n        return res;\n    }\n\n    return true;\n}\n\n/**\n * Check that two PIDLS have the same representation.\n */\npredicate_result pidl_equality(const apidl_t& pidl1, const apidl_t& pidl2)\n{\n    if (!::ILIsEqual(pidl1.get(), pidl2.get()))\n    {\n        predicate_result res(false);\n        res.message() << \"PIDLs have different representations\";\n        return res;\n    }\n\n    return true;\n}\n\n/**\n * Check that the contents of a file and a stream are the same\n */\npredicate_result file_stream_equivalence(sftp_filesystem& filesystem,\n                                         const path& file,\n                                         const com_ptr<IStream>& stream)\n{\n    // Read in from file\n    ifstream in_stream(filesystem, file);\n    string file_contents = string(istreambuf_iterator<char>(in_stream),\n                                  istreambuf_iterator<char>());\n\n    // Read in from stream\n    ULARGE_INTEGER stream_size;\n    LARGE_INTEGER zero = {0};\n    BOOST_REQUIRE_OK(stream->Seek(zero, STREAM_SEEK_END, &stream_size));\n    BOOST_REQUIRE_OK(stream->Seek(zero, STREAM_SEEK_SET, NULL));\n    vector<char> buf(stream_size.LowPart);\n    string stream_contents;\n    if (buf.size())\n    {\n        ULONG cbRead = 0;\n        BOOST_REQUIRE_OK(\n            stream->Read(&buf[0], numeric_cast<ULONG>(buf.size()), &cbRead));\n\n        stream_contents = string(&buf[0], cbRead);\n    }\n\n    if (file_contents != stream_contents)\n    {\n        predicate_result res(false);\n        res.message() << \"File and IStream contents do not match [\"\n                      << file_contents << \" != \" << stream_contents << \"]\";\n        return res;\n    }\n\n    return true;\n}\n\nconst CLIPFORMAT CF_FILEDESCRIPTORW =\n    static_cast<CLIPFORMAT>(::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW));\nconst CLIPFORMAT CF_FILECONTENTS =\n    static_cast<CLIPFORMAT>(::RegisterClipboardFormat(CFSTR_FILECONTENTS));\n}\n\n#pragma region SftpDataObject tests\nBOOST_FIXTURE_TEST_SUITE(sftp_data_object_tests, DataObjectFixture)\n\nBOOST_AUTO_TEST_CASE(create)\n{\n    com_ptr<IDataObject> data_object =\n        new CSftpDataObject(0, NULL, sandbox_pidl().get(), Provider());\n    BOOST_REQUIRE(data_object);\n}\n\n/**\n * Ask for the SHELLIDLIST format.\n * This should hold the PIDLs that the DataObject was originally initialised\n * with.\n */\nBOOST_AUTO_TEST_CASE(pidls)\n{\n    make_test_files();\n\n    vector<cpidl_t> pidls = pidls_in_sandbox();\n\n    com_ptr<IDataObject> data_object = data_object_from_sandbox();\n    PidlFormat format(data_object);\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), pidls.size());\n    BOOST_REQUIRE(pidl_equality(format.file(0), sandbox_pidl() + pidls[0]));\n    BOOST_REQUIRE(pidl_equality(format.file(1), sandbox_pidl() + pidls[1]));\n    BOOST_REQUIRE(pidl_equality(format.parent_folder(), sandbox_pidl()));\n}\n\n/**\n * Ask for the HDROP format.\n * This should fail as the SftpDataObject can't render such a format.\n */\nBOOST_AUTO_TEST_CASE(hdrop)\n{\n    make_test_files();\n\n    com_ptr<IDataObject> data_object = data_object_from_sandbox();\n\n    FORMATETC fetc = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};\n\n    StorageMedium medium;\n    HRESULT hr = data_object->GetData(&fetc, medium.out());\n\n    BOOST_REQUIRE(FAILED(hr));\n}\n\nvoid do_filedescriptor_test(const com_ptr<IDataObject>& data_object,\n                            sftp_filesystem& filesystem,\n                            const vector<path>& files)\n{\n    FORMATETC fetc = {CF_FILEDESCRIPTORW, NULL, DVASPECT_CONTENT, -1,\n                      TYMED_HGLOBAL};\n\n    StorageMedium medium;\n    HRESULT hr = data_object->GetData(&fetc, medium.out());\n    BOOST_REQUIRE_OK(hr);\n\n    FileGroupDescriptor fgd(medium.get().hGlobal);\n    BOOST_REQUIRE_EQUAL(fgd.size(), files.size());\n    for (size_t i = 0; i < files.size(); ++i)\n    {\n        BOOST_REQUIRE_EQUAL(fgd[i].path(), files[i].filename());\n        BOOST_REQUIRE_EQUAL(fgd[i].file_size(),\n                            file_size(filesystem, files[i]));\n        BOOST_REQUIRE_EQUAL(fgd[i].last_write_time(),\n                            from_time_t(last_write_time(filesystem, files[i])));\n    }\n}\n\nvoid do_filecontents_test(const com_ptr<IDataObject>& data_object,\n                          sftp_filesystem& filesystem,\n                          const vector<path>& files, size_t index)\n{\n    FORMATETC fetc = {CF_FILECONTENTS, NULL, DVASPECT_CONTENT,\n                      numeric_cast<LONG>(index), TYMED_ISTREAM};\n\n    StorageMedium medium;\n    HRESULT hr = data_object->GetData(&fetc, medium.out());\n    BOOST_REQUIRE_OK(hr);\n\n    com_ptr<IStream> stream = medium.get().pstm;\n    BOOST_REQUIRE(file_stream_equivalence(filesystem, files.at(index), stream));\n}\n\n/**\n * Ask for the FILEDESCRIPTOR format.\n * This should provide streams to the test files via the SSH connection.\n */\nBOOST_AUTO_TEST_CASE(file_descriptor)\n{\n    vector<path> files = make_test_files();\n\n    com_ptr<IDataObject> data_object = data_object_from_sandbox();\n\n    do_filedescriptor_test(data_object, filesystem(), files);\n\n    for (size_t i = 0; i < files.size(); ++i)\n        do_filecontents_test(data_object, filesystem(), files, i);\n}\n\n/**\n * Ask for the FILEDESCRIPTOR format.\n * This should provide streams to the test files via the SSH connection.\n */\nBOOST_AUTO_TEST_CASE(file_descriptor_readonly)\n{\n    vector<path> files = make_test_files(true);\n\n    com_ptr<IDataObject> data_object = data_object_from_sandbox();\n\n    do_filedescriptor_test(data_object, filesystem(), files);\n\n    for (size_t i = 0; i < files.size(); ++i)\n        do_filecontents_test(data_object, filesystem(), files, i);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/shell_folder/sftp_directory_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for the SftpDirector class.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/shell_folder/SftpDirectory.h\"  // test subject\n\n#include \"swish/atl.hpp\"   // Common ATL setup\n#include \"swish/host_folder/host_pidl.hpp\" // create_host_itemid\n#include \"swish/remote_folder/remote_pidl.hpp\" // remote_itemid_view,\n                                               // create_remote_itemid\n\n#include \"test/common_boost/helpers.hpp\"  // BOOST_REQUIRE_OK\n#include \"test/common_boost/MockConsumer.hpp\" // MockConsumer\n#include \"test/common_boost/MockProvider.hpp\" // MockProvider\n\n#include <washer/shell/pidl.hpp> // apidl_t, cpidl_t\n\n#include <comet/datetime.h> // datetime_t\n#include <comet/enum_iterator.h> // enum_iterator\n#include <comet/error.h> // com_error\n#include <comet/ptr.h>  // com_ptr\n\n#include <boost/make_shared.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n#include <vector>\n\nusing test::MockProvider;\nusing test::MockConsumer;\n\nusing swish::host_folder::create_host_itemid;\nusing swish::remote_folder::create_remote_itemid;\nusing swish::remote_folder::remote_itemid_view;\n\nusing washer::shell::pidl::apidl_t;\nusing washer::shell::pidl::cpidl_t;\n\nusing comet::com_error;\nusing comet::com_ptr;\nusing comet::datetime_t;\nusing comet::enum_iterator;\n\nusing boost::make_shared;\nusing boost::shared_ptr;\n\nusing std::vector;\nusing std::wstring;\n\n\nnamespace comet {\n\ntemplate<> struct comtype<IEnumIDList>\n{\n    static const IID& uuid() throw() { return IID_IEnumIDList; }\n    typedef IUnknown base;\n};\n\ntemplate<> struct enumerated_type_of<IEnumIDList>\n{ typedef PITEMID_CHILD is; };\n\n/**\n * Copy-policy for use by enumerators of child PIDLs.\n */\ntemplate<> struct impl::type_policy<PITEMID_CHILD>\n{\n    static void init(PITEMID_CHILD& t, const cpidl_t& s) \n    {\n        s.copy_to(t);\n    }\n\n    static void clear(PITEMID_CHILD& t)\n    {\n        ::ILFree(t);\n    }    \n};\n\n}\n\nnamespace { // private\n\n    apidl_t test_pidl()\n    {\n        return apidl_t() + create_host_itemid(\n            L\"testhost\", L\"testuser\", L\"/tmp\", 22);\n    }\n\n    class SftpDirectoryFixture\n    {\n    private:\n        shared_ptr<MockProvider> m_provider;\n        com_ptr<MockConsumer> m_consumer;\n\n    public:\n\n        SftpDirectoryFixture()\n            : m_provider(make_shared<MockProvider>()),\n            m_consumer(new MockConsumer()) {}\n\n        CSftpDirectory directory()\n        {\n            return directory(test_pidl());\n        }\n\n        CSftpDirectory directory(const apidl_t& pidl)\n        {\n            return CSftpDirectory(pidl, provider());\n        }\n\n        shared_ptr<MockProvider> provider()\n        {\n            return m_provider;\n        }\n\n        com_ptr<MockConsumer> consumer()\n        {\n            return m_consumer;\n        }\n    };\n\n    void test_enum(com_ptr<IEnumIDList> pidls, SHCONTF flags)\n    {\n        PITEMID_CHILD pidl;\n        ULONG fetched;\n        HRESULT hr = pidls->Next(1, &pidl, &fetched);\n        BOOST_REQUIRE_OK(hr);\n        BOOST_CHECK_EQUAL(fetched, 1U);\n\n        do {\n            remote_itemid_view itemid(pidl);\n\n            // Check filename\n            BOOST_CHECK_GT(itemid.filename().size(), 0U);\n            if (!(flags & SHCONTF_INCLUDEHIDDEN))\n                BOOST_CHECK_NE(itemid.filename(), L\".\");\n\n            // Check folderness\n            if (!(flags & SHCONTF_FOLDERS))\n                BOOST_CHECK(!itemid.is_folder());\n            if (!(flags & SHCONTF_NONFOLDERS))\n                BOOST_CHECK(itemid.is_folder());\n\n            // Check group and owner exist\n            BOOST_CHECK_GT(itemid.owner().size(), 0U);\n            BOOST_CHECK_GT(itemid.group().size(), 0U);\n\n            // Check date validity\n            BOOST_CHECK(itemid.date_modified().good());\n\n            hr = pidls->Next(1, &pidl, &fetched);\n        } while (hr == S_OK);\n\n        BOOST_CHECK_EQUAL(hr, S_FALSE);\n        BOOST_CHECK_EQUAL(fetched, 0U);\n    }\n\n    void test_enum(ATL::CComPtr<IEnumIDList> pidls, SHCONTF flags)\n    {\n        test_enum(com_ptr<IEnumIDList>(pidls.p), flags);\n    }\n\n    cpidl_t create_test_pidl(const wstring& filename)\n    {\n        return create_remote_itemid(\n            filename, false, false, L\"\", L\"\", 0, 0, 040666, 42, datetime_t(),\n            datetime_t());\n    }\n\n    void standard_checks(remote_itemid_view itemid)\n    {\n        // Check filename is sensible\n        BOOST_CHECK_GT(itemid.filename().size(), 0U);\n\n        // Check group and owner exist\n        BOOST_CHECK_GT(itemid.owner().size(), 0U);\n        BOOST_CHECK_GT(itemid.group().size(), 0U);\n\n        // Check date validity\n        BOOST_CHECK(itemid.date_modified().good());\n    }\n\n    template<size_t size>\n    void expected_filenames(\n        com_ptr<IEnumIDList> listing, const wchar_t* (&expected)[size])\n    {\n        vector<wstring> sorted_expected(expected, expected + size);\n        std::sort(sorted_expected.begin(), sorted_expected.end());\n\n        vector<wstring> actual;\n        enum_iterator<IEnumIDList> e(listing);\n        for (; e != enum_iterator<IEnumIDList>(); ++e)\n        {\n            actual.push_back(remote_itemid_view(*e).filename());\n        }\n        std::sort(actual.begin(), actual.end());\n\n        BOOST_CHECK_EQUAL_COLLECTIONS(\n            actual.begin(), actual.end(), sorted_expected.begin(),\n            sorted_expected.end());\n    }\n}\n\n#pragma region SftpDirectory tests\nBOOST_FIXTURE_TEST_SUITE(sftp_directory_tests, SftpDirectoryFixture)\n\n/**\n * When a provider returns no files, the SftpDirectory mustn't either.\n */\nBOOST_AUTO_TEST_CASE( empty )\n{\n    SHCONTF flags = \n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n    provider()->set_listing_behaviour(MockProvider::EmptyListing);\n\n    com_ptr<IEnumIDList> listing = directory().GetEnum(flags);\n\n    ULONG fetched = 1;\n    PITEMID_CHILD pidl;\n    BOOST_CHECK_EQUAL(listing->Next(1, &pidl, &fetched), S_FALSE);\n    BOOST_CHECK_EQUAL(fetched, 0U);\n}\n\n/**\n * Requesting everything should return folder and dotted files as well.\n */\nBOOST_AUTO_TEST_CASE( everything )\n{\n    SHCONTF flags = \n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    enum_iterator<IEnumIDList> e(directory().GetEnum(flags));\n    for (; e != enum_iterator<IEnumIDList>(); ++e)\n    {\n        standard_checks(remote_itemid_view(*e));\n    }\n}\n\n/**\n * Check that link are correctly PIDLed.\n */\nBOOST_AUTO_TEST_CASE( links )\n{\n    SHCONTF flags = \n        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    // Keep list of what is a link to test against\n    vector<wstring> link_names;\n    link_names.push_back(L\"linktmpfolder\");\n    link_names.push_back(L\"another linktmpfolder\");\n    link_names.push_back(L\"ptmp\");\n    link_names.push_back(L\".qtmp\");\n    link_names.push_back(L\"this_link_is_broken_tmp\");\n\n    enum_iterator<IEnumIDList> e(directory().GetEnum(flags));\n    for (; e != enum_iterator<IEnumIDList>(); ++e)\n    {\n        remote_itemid_view itemid(*e);\n\n        if (std::find(\n            link_names.begin(), link_names.end(), itemid.filename()) !=\n            link_names.end())\n        {\n            BOOST_CHECK_MESSAGE(\n                itemid.is_link(),\n                itemid.filename() + L\" is not recognised as a link\");\n        }\n        else\n        {\n            BOOST_CHECK_MESSAGE(\n                !itemid.is_link(),\n                itemid.filename() + L\" is incorrectly recognised as a link\");\n        }\n    }\n}\n\n/**\n * Requesting just folders must only return folders but must return links\n * that target folders.\n */\nBOOST_AUTO_TEST_CASE( only_folder )\n{\n    SHCONTF flags = SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    enum_iterator<IEnumIDList> e(directory().GetEnum(flags));\n    for (; e != enum_iterator<IEnumIDList>(); ++e)\n    {\n        remote_itemid_view itemid(*e);\n        BOOST_CHECK(itemid.is_folder());\n        standard_checks(itemid);\n    }\n\n    const wchar_t* expected[] = {\n        L\"Testtmpfolder\", L\"testtmpfolder.ext\", L\"testtmpfolder.bmp\",\n        L\"testtmpfolder with spaces\", L\".testtmphiddenfolder\", L\"linktmpfolder\",\n        L\"another linktmpfolder\", L\"swish\"};\n\n    expected_filenames(directory().GetEnum(flags), expected);\n}\n\n/**\n * Requesting just files must only return files.\n */\nBOOST_AUTO_TEST_CASE( only_files )\n{\n    SHCONTF flags = SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;\n\n    enum_iterator<IEnumIDList> e(directory().GetEnum(flags));\n    for (; e != enum_iterator<IEnumIDList>(); ++e)\n    {\n        remote_itemid_view itemid(*e);\n        BOOST_CHECK(!itemid.is_folder());\n        standard_checks(itemid);\n    }\n\n    const wchar_t* expected[] = {\n        L\"testtmpfile\", L\"testtmpFile\", L\"testtmpfile.ext\", L\"testtmpfile.txt\",\n        L\"testtmpfile with spaces\", L\"testtmpfile with \\\"quotes\\\" and spaces\",\n        L\"testtmpfile.ext.txt\", L\"testtmpfile..\", L\".testtmphiddenfile\",\n        L\"ptmp\", L\".qtmp\", L\"this_link_is_broken_tmp\"};\n        // broken link is considered a file\n\n    expected_filenames(directory().GetEnum(flags), expected);\n}\n\n/**\n * If hidden items aren't requested, they mustn't be included.\n */\nBOOST_AUTO_TEST_CASE( no_hidden )\n{\n    SHCONTF flags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;\n\n    const wchar_t* expected[] = {\n        L\"Testtmpfolder\", L\"testtmpfolder.ext\", L\"testtmpfolder.bmp\",\n        L\"testtmpfolder with spaces\", L\"linktmpfolder\",\n        L\"another linktmpfolder\", L\"swish\",\n        L\"testtmpfile\", L\"testtmpFile\", L\"testtmpfile.ext\", L\"testtmpfile.txt\",\n        L\"testtmpfile with spaces\", L\"testtmpfile with \\\"quotes\\\" and spaces\",\n        L\"testtmpfile.ext.txt\", L\"testtmpfile..\", \n        L\"ptmp\", L\"this_link_is_broken_tmp\"};\n\n    expected_filenames(directory().GetEnum(flags), expected);\n}\n\n/**\n * If hidden items aren't requested, they mustn't be included even when only\n * folders are requested.\n */\nBOOST_AUTO_TEST_CASE( no_hidden_only_folders )\n{\n    SHCONTF flags = SHCONTF_FOLDERS;\n\n    const wchar_t* expected[] = {\n        L\"Testtmpfolder\", L\"testtmpfolder.ext\", L\"testtmpfolder.bmp\",\n        L\"testtmpfolder with spaces\", L\"linktmpfolder\",\n        L\"another linktmpfolder\", L\"swish\"};\n\n    expected_filenames(directory().GetEnum(flags), expected);\n}\n\n/**\n * If hidden items aren't requested, they mustn't be included even when only\n * files are requested.\n */\nBOOST_AUTO_TEST_CASE( no_hidden_only_files )\n{\n    SHCONTF flags = SHCONTF_NONFOLDERS;\n\n    const wchar_t* expected[] = {\n        L\"testtmpfile\", L\"testtmpFile\", L\"testtmpfile.ext\", L\"testtmpfile.txt\",\n        L\"testtmpfile with spaces\", L\"testtmpfile with \\\"quotes\\\" and spaces\",\n        L\"testtmpfile.ext.txt\", L\"testtmpfile..\", \n        L\"ptmp\", L\"this_link_is_broken_tmp\"};\n\n    expected_filenames(directory().GetEnum(flags), expected);\n}\n\n/**\n * Rename a file where to provider doesn't request confirmation (i.e. acts\n * as though the new name doesn't already exist.  Check that it reports\n * that nothing was overwritten.\n */\nBOOST_AUTO_TEST_CASE( rename )\n{\n    provider()->set_rename_behaviour(MockProvider::RenameOK);\n\n    // PIDL of old file.  Would normally come from GetEnum()\n    cpidl_t pidl = create_test_pidl(L\"testtmpfile\");\n\n    BOOST_CHECK_EQUAL(\n        directory().Rename(pidl, L\"renamed to\", consumer()), false);\n}\n\n/**\n * Rename a file where there are multiple segments to the path.\n */\nBOOST_AUTO_TEST_CASE( rename_in_subfolder )\n{\n    provider()->set_rename_behaviour(MockProvider::RenameOK);\n\n    // PIDL of old file.  Would normally come from GetEnum()\n    cpidl_t pidl = create_test_pidl(L\"testswishfile\");\n\n    BOOST_CHECK_EQUAL(\n        directory(\n            apidl_t() + create_host_itemid(\n                L\"testhost\", L\"testuser\", L\"/tmp/swish\", 22)).Rename(\n                    pidl, L\"renamed to\", consumer()),\n        false);\n}\n\n/**\n * Rename a file but make the provider request confirmation and the consumer\n * grant permission.  Check that it reports that the file was overwritten.\n */\nBOOST_AUTO_TEST_CASE( rename_with_confirmation_granted )\n{\n    provider()->set_rename_behaviour(MockProvider::ConfirmOverwrite);\n    consumer()->set_confirm_overwrite_behaviour(MockConsumer::AllowOverwrite);\n\n    cpidl_t pidl = create_test_pidl(L\"testtmpfile\");\n\n    BOOST_CHECK_EQUAL(\n        directory().Rename(pidl, L\"renamed to\", consumer()), true);\n    BOOST_CHECK(consumer()->was_asked_to_confirm_overwrite());\n}\n\nnamespace {\n\n    bool is_com_abort(const com_error& error)\n    {    \n        return error.hr() == E_ABORT;\n    }\n\n    bool is_com_fail(const com_error& error)\n    {    \n        return error.hr() == E_FAIL;\n    }\n}\n\n/**\n * Rename a file but make the provider request confirmation but the consumer\n * denies permission.  Check that it reports that nothing was overwritten.\n */\nBOOST_AUTO_TEST_CASE( rename_with_confirmation_denied )\n{\n    provider()->set_rename_behaviour(MockProvider::ConfirmOverwrite);\n    consumer()->set_confirm_overwrite_behaviour(MockConsumer::PreventOverwrite);\n\n    cpidl_t pidl = create_test_pidl(L\"testtmpfile\");\n\n    BOOST_CHECK_EXCEPTION(\n        directory().Rename(pidl, L\"renamed to\", consumer()),\n        com_error, is_com_abort);\n    BOOST_CHECK(consumer()->was_asked_to_confirm_overwrite());\n}\n\n/**\n * Handle error case where we tried to rename a file but the provider aborted.\n */\nBOOST_AUTO_TEST_CASE( rename_provider_aborts )\n{\n    provider()->set_rename_behaviour(MockProvider::AbortRename);\n\n    cpidl_t pidl = create_test_pidl(L\"testtmpfile\");\n\n    BOOST_CHECK_EXCEPTION(\n        directory().Rename(pidl, L\"renamed to\", consumer()),\n        com_error, is_com_abort);\n    BOOST_CHECK(!consumer()->was_asked_to_confirm_overwrite());\n}\n\n/**\n * Handle error case where we tried to rename a file but the provider failed.\n */\nBOOST_AUTO_TEST_CASE( rename_provider_fail )\n{\n    provider()->set_rename_behaviour(MockProvider::FailRename);\n\n    cpidl_t pidl = create_test_pidl(L\"testtmpfile\");\n\n    BOOST_CHECK_EXCEPTION(\n        directory().Rename(pidl, L\"renamed to\", consumer()),\n        com_error, is_com_fail);\n    BOOST_CHECK(!consumer()->was_asked_to_confirm_overwrite());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/shell_folder/shell_data_object_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for the ShellDataObject wrapper.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/shell_folder/data_object/ShellDataObject.hpp\" // Test subject\n#include \"swish/shell_folder/data_object/StorageMedium.hpp\"   // Test subject\n#include \"swish/shell/shell.hpp\" // data_object_for_*\n\n#include \"test/fixtures/local_sandbox_fixture.hpp\"\n#include \"test/common_boost/fixtures.hpp\"\n#include \"test/common_boost/helpers.hpp\"\n#include \"test/common_boost/data_object_utils.hpp\" // DataObject zip stuff\n\n#include <boost/test/unit_test.hpp>\n#include <boost/filesystem.hpp>\n\n#include <algorithm>\n#include <vector>\n#include <string>\n\nusing swish::shell_folder::data_object::ShellDataObject; // test subject\nusing swish::shell_folder::data_object::PidlFormat;      // test subject\nusing swish::shell_folder::data_object::StorageMedium;   // test subject\nusing swish::shell::data_object_for_file;\nusing swish::shell::data_object_for_directory;\n\nusing washer::shell::pidl::apidl_t;\n\nusing test::ComFixture;\nusing test::data_object_utils::create_test_zip_file;\nusing test::data_object_utils::data_object_for_zipfile;\nusing test::fixtures::local_sandbox_fixture;\n\nusing boost::filesystem::path;\nusing boost::test_tools::predicate_result;\n\nusing std::vector;\nusing std::wstring;\n\nnamespace\n{ // private\n\n/**\n * Check that a PIDL and a filesystem path refer to the same item.\n */\npredicate_result pidl_path_equivalence(apidl_t pidl, path path)\n{\n    vector<wchar_t> name(MAX_PATH);\n    ::SHGetPathFromIDListW(pidl.get(), &name[0]);\n\n    if (!equivalent(path, &name[0]))\n    {\n        predicate_result res(false);\n        res.message() << \"Different items [\" << wstring(&name[0])\n                      << \" != \" << path.string() << \"]\";\n        return res;\n    }\n\n    return true;\n}\n\nclass DataObjectFixture : public ComFixture, public local_sandbox_fixture\n{\n};\n}\n\n#pragma region StorageMedium tests\nBOOST_AUTO_TEST_SUITE(storage_medium_tests)\n\n/**\n * Create and destroy an instance of the StorageMedium helper object.\n * Check a few members to see if they are initialsed properly.\n */\nBOOST_AUTO_TEST_CASE(storage_medium_lifecycle)\n{\n    {\n        StorageMedium medium;\n\n        BOOST_REQUIRE(medium.empty());\n        BOOST_REQUIRE_EQUAL(medium.get().hGlobal, static_cast<HGLOBAL>(NULL));\n        BOOST_REQUIRE_EQUAL(medium.get().pstm, static_cast<IStream*>(NULL));\n        BOOST_REQUIRE_EQUAL(medium.get().pUnkForRelease,\n                            static_cast<IUnknown*>(NULL));\n    }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n\n#pragma region ShellDataObject tests\nBOOST_FIXTURE_TEST_SUITE(shell_data_object_tests, DataObjectFixture)\n\n/**\n * Detecting the CF_HDROP format for a filesystem item.\n *\n * The shell data object should always have this format for items that are\n * backed by a real filesystem (i.e. not virtual).  This is a test of whether\n * we can recognise that or not.\n */\nBOOST_AUTO_TEST_CASE(cf_hdrop_format)\n{\n    path file = new_file_in_local_sandbox();\n    ShellDataObject data_object(data_object_for_file(file).get());\n\n    BOOST_REQUIRE(data_object.has_hdrop_format());\n}\n\n/**\n * Detecting the CF_HDROP format for virtual items.\n *\n * A data object should not have this format for virtual items as they have no\n * filesystem path.  This is a test of whether we can recognise that or not.\n */\nBOOST_AUTO_TEST_CASE(cf_hdrop_format_virtual)\n{\n    path zip_file = create_test_zip_file(local_sandbox());\n    ShellDataObject data_object(data_object_for_zipfile(zip_file).get());\n\n    BOOST_REQUIRE(!data_object.has_hdrop_format());\n}\n\n/**\n * Detecting the CFSTR_SHELLIDLIST format for a filesystem item.\n *\n * The shell data object should always have this format.  This is a test\n * of whether we can recognise that or not.\n *\n * @todo  Unset the format and test a negative result.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_format)\n{\n    path file = new_file_in_local_sandbox();\n    ShellDataObject data_object(data_object_for_file(file).get());\n\n    BOOST_REQUIRE(data_object.has_pidl_format());\n}\n\n/**\n * Detecting the CFSTR_SHELLIDLIST format for virtual items.\n *\n * The shell data object should always have this format.  This is a test\n * of whether we can recognise that or not.\n *\n * @todo  Unset the format and test a negative result.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_format_virtual)\n{\n    path zip_file = create_test_zip_file(local_sandbox());\n    ShellDataObject data_object(data_object_for_zipfile(zip_file).get());\n\n    BOOST_REQUIRE(data_object.has_pidl_format());\n}\n\n/**\n * Detecting the CFSTR_FILEDESCRIPTOR format for virtual items.\n *\n * This format is expected for data objects holding virtual items.  This is a\n * test of whether we can recognise that or not.\n */\nBOOST_AUTO_TEST_CASE(cf_file_group_descriptor_format_virtual)\n{\n    path zip_file = create_test_zip_file(local_sandbox());\n    ShellDataObject data_object(data_object_for_zipfile(zip_file).get());\n\n    BOOST_REQUIRE(data_object.has_file_group_descriptor_format());\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n\n#pragma region PidlFormat tests\nBOOST_FIXTURE_TEST_SUITE(pidl_format_tests, DataObjectFixture)\n\n/**\n * Get a PIDL from a shell data object.\n *\n * Create the DataObject with one item, the test file in the sandbox.  Get\n * the item from the data object as a PIDL and check that it can be resolved\n * back the to filename from which the data object was created.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_item)\n{\n    path file = new_file_in_local_sandbox();\n    PidlFormat format(data_object_for_file(file));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 1U);\n\n    apidl_t pidl = format.file(0);\n\n    BOOST_REQUIRE(pidl_path_equivalence(pidl, file));\n}\n\n/**\n * Get a PIDL's parent from a shell data object.\n *\n * Create the DataObject with one item, the test file in the sandbox.  Get\n * the parent folder of this item (the sandbox) from the data object as a\n * PIDL and check that it can be resolved back the sandbox's path.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_parent)\n{\n    path file = new_file_in_local_sandbox();\n    PidlFormat format(data_object_for_file(file));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 1U);\n\n    apidl_t folder_pidl = format.parent_folder();\n\n    BOOST_REQUIRE(pidl_path_equivalence(folder_pidl, file.parent_path()));\n}\n\n/**\n * Try to get a non-existent PIDL from the data object.\n *\n * Create the DataObject with one item.  Attempt to get the @b second item\n * from the data object.  As there is no second item this should fail by\n * throwing an range_error exception.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_item_fail)\n{\n    path file = new_file_in_local_sandbox();\n    PidlFormat format(data_object_for_file(file));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 1U);\n    BOOST_REQUIRE_THROW(format.file(1), std::range_error)\n}\n\n/**\n * Get PIDLs from a shell data object with more than one.\n *\n * Create the DataObject with three items, test files in the sandbox.  Get\n * the items from the data object as PIDLs and check that they can be resolved\n * back the to the filenames from which the data object was created.\n */\nBOOST_AUTO_TEST_CASE(cfstr_shellidlist_multiple_items)\n{\n    vector<path> files;\n    files.push_back(new_file_in_local_sandbox());\n    files.push_back(new_file_in_local_sandbox());\n    files.push_back(new_file_in_local_sandbox());\n    sort(files.begin(), files.end());\n\n    PidlFormat format(data_object_for_directory(local_sandbox()));\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 3U);\n\n    apidl_t pidl1 = format.file(0);\n    apidl_t pidl2 = format.file(1);\n    apidl_t pidl3 = format.file(2);\n\n    BOOST_REQUIRE(pidl_path_equivalence(pidl1, files[0]));\n    BOOST_REQUIRE(pidl_path_equivalence(pidl2, files[1]));\n    BOOST_REQUIRE(pidl_path_equivalence(pidl3, files[2]));\n\n    BOOST_REQUIRE_THROW(format.file(4), std::range_error)\n}\n\n/**\n * The format should respond sensibly even if created with a NULL pointer.\n *\n * The behaviour should act as though this NULL pointer were an empty\n * DataObject; this is the meaning the shell gives it - for instance when\n * nothing is selected in a folder a NULL pointer is returned as the\n * DataObject.\n */\nBOOST_AUTO_TEST_CASE(null_dataobject)\n{\n    PidlFormat format(NULL);\n\n    BOOST_REQUIRE_EQUAL(format.pidl_count(), 0U);\n    BOOST_REQUIRE_THROW(format.file(0), std::range_error);\n    BOOST_REQUIRE_THROW(format.relative_file(0), std::range_error);\n    BOOST_REQUIRE_THROW(format.parent_folder(), std::logic_error);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n#pragma endregion\n"
  },
  {
    "path": "test/shell_folder/utils_test.cpp",
    "content": "/**\n    @file\n\n    Tests common utils.\n\n    @if license\n\n    Copyright (C) 2009, 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"swish/utils.hpp\"  // Test subject\n\n#include \"test/common_boost/helpers.hpp\"\n\n#include <boost/filesystem.hpp> // path\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\nusing boost::filesystem::path;\n\nusing std::string;\nusing std::wstring;\n\nBOOST_AUTO_TEST_SUITE(SwishUtils)\n\n/**\n * Narrow a wide string.\n */\nBOOST_AUTO_TEST_CASE( narrowing_string )\n{\n    wstring wide = L\"This was a wide-char string\";\n    string narrow = \"This was a wide-char string\";\n\n    string out = swish::utils::WideStringToUtf8String(wide);\n\n    BOOST_REQUIRE_EQUAL(out, narrow);\n}\n\n/**\n * Narrowing an empty string produces an empty string.\n */\nBOOST_AUTO_TEST_CASE( narrowing_empty_string )\n{\n    wstring wide = L\"\";\n    string narrow = \"\";\n\n    string out = swish::utils::WideStringToUtf8String(wide);\n\n    BOOST_REQUIRE_EQUAL(out, narrow);\n}\n\n/**\n * Widening a narrow string.\n */\nBOOST_AUTO_TEST_CASE( widening_string )\n{\n    wstring wide = L\"This was a wide-char string\";\n    string narrow = \"This was a wide-char string\";\n\n    wstring out = swish::utils::Utf8StringToWideString(narrow);\n\n    BOOST_REQUIRE_EQUAL(out, wide);\n}\n\n/**\n * Widening an empty string produces an empty string.\n */\nBOOST_AUTO_TEST_CASE( widening_empty_string )\n{\n    wstring wide = L\"\";\n    string narrow = \"\";\n\n    wstring out = swish::utils::Utf8StringToWideString(narrow);\n\n    BOOST_REQUIRE_EQUAL(out, wide);\n}\n\n/**\n * Test getting current user's username.\n */\nBOOST_AUTO_TEST_CASE( get_current_user )\n{\n    wstring name = swish::utils::current_user();\n\n    BOOST_REQUIRE_GE(name.size(), wstring(L\"a\").size());\n}\n\n/**\n * Test getting current user's username (ANSI version).\n */\nBOOST_AUTO_TEST_CASE( get_current_user_a )\n{\n    string name = swish::utils::current_user_a();\n\n    BOOST_REQUIRE_GE(name.size(), string(\"a\").size());\n}\n\n/**\n * Test getting current user's home directory (ANSI).\n */\nBOOST_AUTO_TEST_CASE( get_homedir )\n{\n    path home = swish::utils::home_directory<path>();\n\n    BOOST_CHECK(!home.empty());\n    BOOST_CHECK(is_directory(home));\n}\n\n/**\n * Test getting current user's home directory (Unicode).\n */\nBOOST_AUTO_TEST_CASE( get_homedir_w )\n{\n    path home = swish::utils::home_directory<path>();\n\n    BOOST_CHECK(!home.empty());\n    BOOST_CHECK(is_directory(home));\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/shell_folder-com_dll/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  CppUnitExtensions.h\n  HostFolder_test.cpp\n  main.cpp\n  Module.cpp\n  pidl.cpp\n  RemoteFolder_test.cpp\n  pidl.hpp)\n\nswish_test_suite(\n  SUBJECT shell_folder-com_dll\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES})\n\nswish_copy_fixture_files(test-shell_folder-com_dll)\n"
  },
  {
    "path": "test/shell_folder-com_dll/CppUnitExtensions.h",
    "content": "/**\n    @file\n\n    Swish-specific extensions to the CppUnit facilities\n*/\n\n#pragma once\n\n#pragma warning(push)\n#pragma warning(disable:4512) // assignment operator could not be generated\n#include <cppunit/extensions/HelperMacros.h>\n#pragma warning(pop)\n\n#include \"swish/atl.hpp\"    // Common ATL setup\n#include <atlstr.h>         // CString\n\nCPPUNIT_NS_BEGIN\n/**\n * Provides CPPUNIT_ASSERT_EQUAL capability for CStrings.\n *\n * @example\n * CString x = \"bob\"\n * CString y = \"sally\"\n * CPPUNIT_ASSERT_EQUAL( x, y )\n */\ntemplate <>\nstruct assertion_traits<ATL::CString>\n{  \n    static bool equal(ATL::CString x, ATL::CString y)\n    {\n        return x == y;\n    }\n\n    static std::string toString(ATL::CString x)\n    {\n        ATL::CStringA y(x);\n        std::string s(y);\n        return s;\n    }\n};\n\n#define STRMESSAGE_CASE(hr) case hr: strMessage = #hr; break;\ninline std::string GetErrorFromHResult(HRESULT hResult)\n{\n    std::string strMessage;\n    switch (hResult)\n    {\n        STRMESSAGE_CASE(S_OK);\n        STRMESSAGE_CASE(S_FALSE);\n        STRMESSAGE_CASE(E_ABORT);\n        STRMESSAGE_CASE(E_ACCESSDENIED);\n        STRMESSAGE_CASE(E_UNEXPECTED);\n        STRMESSAGE_CASE(E_NOTIMPL);\n        STRMESSAGE_CASE(E_OUTOFMEMORY);\n        STRMESSAGE_CASE(E_INVALIDARG);\n        STRMESSAGE_CASE(E_NOINTERFACE);\n        STRMESSAGE_CASE(E_POINTER);\n        STRMESSAGE_CASE(E_HANDLE);\n        STRMESSAGE_CASE(E_FAIL);\n        STRMESSAGE_CASE(E_PENDING);\n    default:\n        strMessage = \"<unknown>\";\n    }\n\n    char *pszMessage = NULL;\n    if (::FormatMessageA(\n        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,\n        hResult, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \n        reinterpret_cast<char*>(&pszMessage), 0, NULL) && (pszMessage != NULL))\n    {\n        strMessage += \": \";\n        strMessage += pszMessage;\n        ::LocalFree(reinterpret_cast<HLOCAL>(pszMessage));\n    }\n\n    return strMessage;\n}\n\n/**\n * COM HRESULT-specific assertions\n * @{\n */\n#define CPPUNIT_ASSERT_OK(hresult) \\\n    do { \\\n        HRESULT hrCopy; \\\n        hrCopy = hresult; \\\n        std::string str(\"COM return code was \"); \\\n        str += CPPUNIT_NS::GetErrorFromHResult(hrCopy); \\\n        CPPUNIT_ASSERT_MESSAGE(str, hrCopy == S_OK); \\\n    } while(0);\n#define CPPUNIT_ASSERT_SUCCEEDED(hresult) CPPUNIT_ASSERT(SUCCEEDED(hresult))\n#define CPPUNIT_ASSERT_FAILED(hresult) CPPUNIT_ASSERT(FAILED(hresult))\n/* @} */\n\n/**\n * Convenience macros\n * @{\n */\ntemplate <class T>\nvoid assertZero( const T& actual,\n                 SourceLine sourceLine,\n                 const std::string &message )\n{\n    CPPUNIT_NS::assertEquals(static_cast<T>(0), actual, sourceLine, message);\n}\n#define CPPUNIT_ASSERT_ZERO(actual) \\\n    do { \\\n        CPPUNIT_NS::assertZero((actual), CPPUNIT_SOURCELINE(), \\\n            \"\"#actual \" != 0\"); \\\n    } while(0);\n/* @} */\n\nCPPUNIT_NS_END\n"
  },
  {
    "path": "test/shell_folder-com_dll/HostFolder_test.cpp",
    "content": "/**\n    @file\n\n    Testing CHostFolder via the external COM interfaces.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n\n#include \"pidl.hpp\"  // Custom PIDL functions\n\n#include \"swish/shell_folder/Swish.h\" // for HostFolder UUID\n\n#include \"swish/atl.hpp\"  // Common ATL setup\n\n#include <mshtml.h>  // For IHTMLDOMTextNode2\n\nnamespace test {\nnamespace swish {\nnamespace com_dll {\n\nusing pidl::MakeHostPidl;\n\nusing ATL::CComPtr;\nusing ATL::CComQIPtr;\nusing ATL::CString;\n\nclass CHostFolderPreInitialize_test : public CPPUNIT_NS::TestFixture\n{\n    CPPUNIT_TEST_SUITE( CHostFolderPreInitialize_test );\n        CPPUNIT_TEST( testQueryInterface );\n        CPPUNIT_TEST( testGetCLSID );\n        CPPUNIT_TEST( testInitialize );\n    CPPUNIT_TEST_SUITE_END();\n\npublic:\n    CHostFolderPreInitialize_test() : m_pFolder(NULL)\n    {\n        HRESULT hr;\n\n        // Start up COM\n        hr = ::CoInitialize(NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // One-off tests\n\n        // Store HostFolder CLSID\n        CLSID CLSID_CHostFolder;\n        hr = ::CLSIDFromProgID(OLESTR(\"Swish.HostFolder\"), &CLSID_CHostFolder);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Check that CLSID was correctly constructed from ProgID\n        LPOLESTR pszUuid = NULL;\n        hr = ::StringFromCLSID( CLSID_CHostFolder, &pszUuid );\n        CString strExpectedUuid = L\"{b816a83a-5022-11dc-9153-0090f5284f85}\";\n        CString strActualUuid = pszUuid;\n        ::CoTaskMemFree(pszUuid);\n        CPPUNIT_ASSERT_EQUAL(\n            strExpectedUuid.MakeLower(), strActualUuid.MakeLower());\n\n        // Shut down COM\n        ::CoUninitialize();\n    }\n\n    void setUp()\n    {\n        HRESULT hr;\n\n        // Start up COM\n        hr = ::CoInitialize(NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Create instance of folder using CLSID\n        hr = m_spFolder.CoCreateInstance(__uuidof(CHostFolder));\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Copy to regular interface pointer so we can test for memory\n        // leaks in tearDown()\n        m_spFolder.CopyTo(&m_pFolder);\n    }\n\n    void tearDown()\n    {\n        try\n        {\n            m_spFolder.Release();\n\n            if (m_pFolder) // Test for leaking refs\n            {\n                CPPUNIT_ASSERT_ZERO(m_pFolder->Release());\n                m_pFolder = NULL;\n            }\n        }\n        catch(...)\n        {\n            // Shut down COM\n            ::CoUninitialize();\n            throw;\n        }\n\n        // Shut down COM\n        ::CoUninitialize();\n    }\n\nprotected:\n    /**\n     * Test that the class responds to IUnknown::QueryInterface correctly.\n     *\n     * This test will be roughly the same for *any* valid COM object except\n     * one that implementa IHTMLDOMTextNode2 as this has been chosen to test\n     * failure.\n     * The cases being tested are based on those explained by Raymond Chen:\n     * http://blogs.msdn.com/oldnewthing/archive/2004/03/26/96777.aspx\n     */\n    void testQueryInterface()\n    {\n        HRESULT hr;\n\n        // Supports IUnknown (valid COM object)?\n        IUnknown *pUnk;\n        hr = m_pFolder->QueryInterface(&pUnk);\n        CPPUNIT_ASSERT_OK(hr);\n        pUnk->Release();\n\n        // Supports IShellFolder2 (valid self!)?\n        IShellFolder2 *pFolder;\n        hr = m_pFolder->QueryInterface(&pFolder);\n        CPPUNIT_ASSERT_OK(hr);\n        pFolder->Release();\n\n        // Says no properly (Very unlikely to support this - must return NULL)\n        IHTMLDOMTextNode2 *pShell = (IHTMLDOMTextNode2 *)this;\n        hr = m_pFolder->QueryInterface(&pShell);\n        if (SUCCEEDED(hr))\n        {\n            pShell->Release();\n            CPPUNIT_ASSERT(FAILED(hr));\n        }\n        CPPUNIT_ASSERT(pShell == NULL);\n    }\n\n    void testGetCLSID()\n    {\n        CComQIPtr<IPersist> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        CLSID clsid;\n        HRESULT hr = spPersist->GetClassID(&clsid);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Check that CLSID is correct\n        LPOLESTR pszUuid = NULL;\n        ::StringFromCLSID(clsid, &pszUuid);\n        CString strExpectedUuid = L\"{b816a83a-5022-11dc-9153-0090f5284f85}\";\n        CString strActualUuid = pszUuid;\n        ::CoTaskMemFree(pszUuid);\n        CPPUNIT_ASSERT_EQUAL(\n            strExpectedUuid.MakeLower(), strActualUuid.MakeLower());\n    }\n\n    void testInitialize()\n    {\n        CComQIPtr<IPersistFolder> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        // Get Swish PIDL as HostFolder root\n        PIDLIST_ABSOLUTE pidlSwish = _GetSwishPidl();\n\n        // Initialise HostFolder with its PIDL\n        HRESULT hr = spPersist->Initialize(pidlSwish);\n        ::ILFree(pidlSwish);\n        CPPUNIT_ASSERT_OK(hr);\n    }\n\n    void testGetPIDL()\n    {\n        HRESULT hr;\n\n        CComQIPtr<IPersistFolder2> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        // Get Swish PIDL as HostFolder root\n        PIDLIST_ABSOLUTE pidlSwish = _GetSwishPidl();\n        // We will leak this PIDL if the tests below fail\n\n        // Initialise HostFolder with its PIDL\n        hr = spPersist->Initialize(pidlSwish);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Read the PIDL back - should be identical\n        PIDLIST_ABSOLUTE pidl;\n        hr = spPersist->GetCurFolder(&pidl);\n        CPPUNIT_ASSERT_OK(hr);\n        CPPUNIT_ASSERT(::ILIsEqual(pidl, pidlSwish));\n        ::ILFree(pidl);\n        ::ILFree(pidlSwish);\n    }\n\n    /**\n     * Get the PIDL which represents the HostFolder (Swish icon) in Explorer.\n     */\n    PIDLIST_ABSOLUTE _GetSwishPidl()\n    {\n        HRESULT hr;\n        PIDLIST_ABSOLUTE pidl;\n        CComPtr<IShellFolder> spDesktop;\n        hr = ::SHGetDesktopFolder(&spDesktop);\n        CPPUNIT_ASSERT_OK(hr);\n        hr = spDesktop->ParseDisplayName(NULL, NULL,\n            L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n            L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\", NULL,\n            reinterpret_cast<PIDLIST_RELATIVE*>(&pidl), NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        return pidl;\n    }\n\n    CComPtr<IShellFolder2> m_spFolder;\n    IShellFolder2 *m_pFolder;\n};\n\n\nclass CHostFolderPostInitialize_test : public CHostFolderPreInitialize_test\n{\npublic:\n    CHostFolderPostInitialize_test() : CHostFolderPreInitialize_test() {}\n\n    void setUp()\n    {\n        __super::setUp();\n\n        // Initialise HostFolder with its PIDL\n        CComQIPtr<IPersistFolder> spPersist(m_spFolder);\n        PIDLIST_ABSOLUTE pidlSwish = _GetSwishPidl();\n        HRESULT hr = spPersist->Initialize(pidlSwish);\n        ::ILFree(pidlSwish);\n        CPPUNIT_ASSERT_OK(hr);\n    }\n};\n\n// Tests for following configuration:\n//     ComputerPIDL\\SwishPIDL\\HOSTPIDL\n// where this RemoteFolder is rooted at:\n//     ComputerPIDL\\SwishPIDL\n\nstatic const wchar_t *DN_FRIENDLY_RELATIVE = L\"Test PIDL\";\nstatic const wchar_t *DN_FRIENDLY_ABSOLUTE =\n    L\"sftp://user@test.example.com//home/user/dir\";\n\nstatic const wchar_t *DN_PARSING_RELATIVE =\n    L\"sftp://user@test.example.com:22//home/user/dir\";\nstatic const wchar_t *DN_PARSING_ABSOLUTE =\n    L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n    L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\\\\\"\n    L\"sftp://user@test.example.com:22//home/user/dir\";\n\nstatic const wchar_t *DN_ADDRESSBAR_RELATIVE =\n    L\"sftp://user@test.example.com//home/user/dir\";\nstatic const wchar_t *DN_ADDRESSBAR_ABSOLUTE =\n    L\"sftp://user@test.example.com//home/user/dir\";\n\nstatic const wchar_t *DN_PARSINGADDRESSBAR_RELATIVE =\n    L\"sftp://user@test.example.com:22//home/user/dir\";\nstatic const wchar_t *DN_PARSINGADDRESSBAR_ABSOLUTE =\n    L\"Computer\\\\Swish\\\\sftp://user@test.example.com:22//home/user/dir\";\n\nstatic const wchar_t *DN_EDITING_RELATIVE = L\"Test PIDL\";\nstatic const wchar_t *DN_EDITING_ABSOLUTE = L\"Test PIDL\";\n\nclass CHostFolderDisplayName_test : public CHostFolderPostInitialize_test\n{\n    CPPUNIT_TEST_SUITE( CHostFolderDisplayName_test );\n        CPPUNIT_TEST( testDisplayNormal );\n        CPPUNIT_TEST( testDisplayInFolder );\n        CPPUNIT_TEST( testParsingNormal );\n        CPPUNIT_TEST( testParsingInFolder );\n        CPPUNIT_TEST( testAddressbarNormal );\n        CPPUNIT_TEST( testAddressbarInFolder );\n        CPPUNIT_TEST( testEditingNormal );\n        CPPUNIT_TEST( testEditingInFolder );\n        CPPUNIT_TEST( testParsingAddressbarNormal );\n        CPPUNIT_TEST( testParsingAddressbarInFolder );\n        CPPUNIT_TEST( testParseDisplayName );\n    CPPUNIT_TEST_SUITE_END();\n\npublic:\n    CHostFolderDisplayName_test() : CHostFolderPostInitialize_test() {}\n\nprotected:\n\n    void testDisplayNormal()\n    {\n        _testName(DN_FRIENDLY_ABSOLUTE, SHGDN_NORMAL);\n    }\n\n    void testDisplayInFolder()\n    {\n        _testName(DN_FRIENDLY_RELATIVE, SHGDN_INFOLDER);\n    }\n\n    void testParsingNormal()\n    {\n        _testName(DN_PARSING_ABSOLUTE, SHGDN_FORPARSING);\n    }\n\n    void testParsingInFolder()\n    {\n        _testName(DN_PARSING_RELATIVE, SHGDN_INFOLDER | SHGDN_FORPARSING);\n    }\n\n    void testAddressbarNormal()\n    {\n        _testName(DN_ADDRESSBAR_ABSOLUTE, SHGDN_FORADDRESSBAR);\n    }\n\n    void testAddressbarInFolder()\n    {\n        _testName(\n            DN_ADDRESSBAR_RELATIVE, SHGDN_INFOLDER | SHGDN_FORADDRESSBAR);\n    }\n\n    void testEditingNormal()\n    {\n        _testName(DN_EDITING_ABSOLUTE, SHGDN_FOREDITING);\n    }\n\n    void testEditingInFolder()\n    {\n        _testName(DN_EDITING_RELATIVE, SHGDN_INFOLDER | SHGDN_FOREDITING);\n    }\n\n    void testParsingAddressbarNormal()\n    {\n        _testName(\n            DN_PARSINGADDRESSBAR_ABSOLUTE,\n            SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\n    void testParsingAddressbarInFolder()\n    {\n        _testName(\n            DN_PARSINGADDRESSBAR_RELATIVE,\n            SHGDN_INFOLDER | SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\n    void testParseDisplayName()\n    {\n        HRESULT hr;\n\n        PIDLIST_RELATIVE pidl = NULL;\n        wchar_t wszDisplayName[MAX_PATH];\n        wcscpy_s(wszDisplayName, ARRAYSIZE(wszDisplayName), DN_PARSING_RELATIVE);\n        hr = m_spFolder->ParseDisplayName(\n            NULL, NULL, wszDisplayName, NULL, &pidl, NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Check return against PIDL\n        PITEMID_CHILD pidlTest = _CreateTestPidl();\n        BOOL fPidlsMatch = ::ILIsEqual(\n            reinterpret_cast<PIDLIST_ABSOLUTE>(pidl),\n            reinterpret_cast<PIDLIST_ABSOLUTE>(pidlTest));\n        ::ILFree(pidl);\n        ::ILFree(pidlTest);\n        CPPUNIT_ASSERT(fPidlsMatch);\n    }\n\n\nprivate:\n\n    void _testName(PCWSTR pwszName, SHGDNF uFlags)\n    {\n        CString strActual(_GetDisplayName(uFlags));\n        CString strExpected(pwszName);\n\n        CPPUNIT_ASSERT_EQUAL(strExpected, strActual);\n    }\n\n    PITEMID_CHILD _CreateTestPidl()\n    {\n        // Create test HOSTPIDL\n        return MakeHostPidl(\n            L\"user\", L\"test.example.com\", L\"/home/user/dir\", 22, L\"Test PIDL\");\n    }\n\n    CString _GetDisplayName(SHGDNF uFlags)\n    {\n        HRESULT hr;\n\n        // Create test HOSTPIDL\n        PITEMID_CHILD pidl = _CreateTestPidl();\n\n        STRRET strret;\n        hr = m_spFolder->GetDisplayNameOf(pidl, uFlags, &strret);\n        CPPUNIT_ASSERT_OK(hr);\n\n        PWSTR pwszName;\n        hr = ::StrRetToStr(&strret, pidl, &pwszName);\n        CPPUNIT_ASSERT_OK(hr);\n        CString strName(pwszName);\n        ::CoTaskMemFree(pwszName);\n\n        if (strret.uType == STRRET_WSTR)\n            ::CoTaskMemFree(strret.pOleStr);\n\n        return strName;\n    }\n};\n\nCPPUNIT_TEST_SUITE_REGISTRATION( CHostFolderPreInitialize_test );\nCPPUNIT_TEST_SUITE_REGISTRATION( CHostFolderDisplayName_test );\n\n}}} // namespace test::swish::com_dll\n"
  },
  {
    "path": "test/shell_folder-com_dll/Module.cpp",
    "content": "/**\n    @file\n\n    ATL Module required for ATL support.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"swish/atl.hpp\"\n\nnamespace test {\nnamespace swish {\nnamespace com_dll {\n\nusing ATL::CAtlModule;\n\n/**\n * ATL module needed to use ATL-based objects, e.g. CMockSftpConsumer.\n */\nclass CModule : public CAtlModule\n{\npublic :\n    \n    virtual HRESULT AddCommonRGSReplacements(IRegistrarBase*) throw()\n    {\n        return S_OK;\n    }\n};\n\n}}} // namespace test::swish::com_dll\n\ntest::swish::com_dll::CModule _AtlModule; ///< Global module instance"
  },
  {
    "path": "test/shell_folder-com_dll/RemoteFolder_test.cpp",
    "content": "/**\n    @file\n\n    Testing CRemoteFolder via the external COM interfaces.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"pidl.hpp\"  // Custom PIDL functions\n\n#include \"test/common/CppUnitExtensions.h\"\n\n#include \"swish/shell_folder/Swish.h\" // for HostFolder UUID\n\n#include \"swish/atl.hpp\"  // Common ATL setup\n\n#include <mshtml.h>  // For IHTMLDOMTextNode2\n\nnamespace test {\nnamespace swish {\nnamespace com_dll {\n\nusing pidl::MakeHostPidl;\nusing pidl::MakeRemotePidl;\n\nusing ATL::CComPtr;\nusing ATL::CComQIPtr;\nusing ATL::CString;\n\nclass CRemoteFolderPreInitialize_test : public CPPUNIT_NS::TestFixture\n{\n    CPPUNIT_TEST_SUITE( CRemoteFolderPreInitialize_test );\n        CPPUNIT_TEST( testQueryInterface );\n        CPPUNIT_TEST( testGetCLSID );\n        CPPUNIT_TEST( testInitialize );\n    CPPUNIT_TEST_SUITE_END();\n\npublic:\n    CRemoteFolderPreInitialize_test() : m_pFolder(NULL)\n    {\n        HRESULT hr;\n\n        // Start up COM\n        hr = ::CoInitialize(NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // One-off tests\n\n        // Store RemoteFolder CLSID\n        CLSID CLSID_Folder;\n        hr = ::CLSIDFromProgID(OLESTR(\"Swish.RemoteFolder\"), &CLSID_Folder);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Check that CLSID was correctly constructed from ProgID\n        LPOLESTR pszUuid = NULL;\n        hr = ::StringFromCLSID( CLSID_Folder, &pszUuid );\n        CString strExpectedUuid = L\"{b816a83c-5022-11dc-9153-0090f5284f85}\";\n        CString strActualUuid = pszUuid;\n        ::CoTaskMemFree(pszUuid);\n        CPPUNIT_ASSERT_EQUAL(\n            strExpectedUuid.MakeLower(), strActualUuid.MakeLower());\n\n        // Check that CLSID was correctly constructed from __uuidof()\n        pszUuid = NULL;\n        hr = ::StringFromCLSID( __uuidof(CRemoteFolder), &pszUuid );\n        strExpectedUuid = L\"{b816a83c-5022-11dc-9153-0090f5284f85}\";\n        strActualUuid = pszUuid;\n        ::CoTaskMemFree(pszUuid);\n        CPPUNIT_ASSERT_EQUAL(\n            strExpectedUuid.MakeLower(), strActualUuid.MakeLower());\n\n        // Shut down COM\n        ::CoUninitialize();\n    }\n\n    void setUp()\n    {\n        HRESULT hr;\n\n        // Start up COM\n        hr = ::CoInitialize(NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Create instance of folder using CLSID\n        hr = m_spFolder.CoCreateInstance(__uuidof(CRemoteFolder));\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Copy to regular interface pointer so we can test for memory \n        // leaks in tearDown()\n        m_spFolder.CopyTo(&m_pFolder);\n    }\n\n    void tearDown()\n    {\n        try\n        {\n            m_spFolder.Release();\n\n            if (m_pFolder) // Test for leaking refs\n            {\n                CPPUNIT_ASSERT_ZERO(m_pFolder->Release());\n                m_pFolder = NULL;\n            }\n        }\n        catch(...)\n        {\n            // Shut down COM\n            ::CoUninitialize();\n            throw;\n        }\n\n        // Shut down COM\n        ::CoUninitialize();\n    }\n\nprotected:\n    /**\n     * Test that the class responds to IUnknown::QueryInterface correctly.\n     *\n     * This test will be roughly the same for *any* valid COM object except\n     * one that implements IHTMLDOMTextNode2 as this has been chosen to test \n     * failure. \n     * The cases being tested are based on those explained by Raymond Chen:\n     * http://blogs.msdn.com/oldnewthing/archive/2004/03/26/96777.aspx\n     */\n    void testQueryInterface()\n    {\n        HRESULT hr;\n\n        // Supports IUnknown (valid COM object)?\n        IUnknown *pUnk;\n        hr = m_pFolder->QueryInterface(&pUnk);\n        CPPUNIT_ASSERT_OK(hr);\n        pUnk->Release();\n\n        // Supports IShellFolder2 (valid self!)?\n        IShellFolder2 *pFolder;\n        hr = m_pFolder->QueryInterface(&pFolder);\n        CPPUNIT_ASSERT_OK(hr);\n        pFolder->Release();\n\n        // Says no properly (Very unlikely to support this - must return NULL)\n        IHTMLDOMTextNode2 *pShell = (IHTMLDOMTextNode2 *)this;\n        hr = m_pFolder->QueryInterface(&pShell);\n        if (SUCCEEDED(hr))\n        {\n            pShell->Release();\n            CPPUNIT_ASSERT(FAILED(hr));\n        }\n        CPPUNIT_ASSERT(pShell == NULL);\n    }\n\n    void testGetCLSID()\n    {\n        CComQIPtr<IPersist> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        CLSID clsid;\n        HRESULT hr = spPersist->GetClassID(&clsid);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Check that CLSID is correct\n        LPOLESTR pszUuid = NULL;\n        ::StringFromCLSID(clsid, &pszUuid);\n        CString strExpectedUuid = L\"{b816a83c-5022-11dc-9153-0090f5284f85}\";\n        CString strActualUuid = pszUuid;\n        ::CoTaskMemFree(pszUuid);\n        CPPUNIT_ASSERT_EQUAL(\n            strExpectedUuid.MakeLower(), strActualUuid.MakeLower());\n    }\n\n    void testInitialize()\n    {\n        CComQIPtr<IPersistFolder> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        // Get Swish PIDL + HOSTPIDL + REMOTEPIDL as RemoteFolder root\n        PIDLIST_ABSOLUTE pidl = _CreateRootPidl();\n\n        // Initialise RemoteFolder with its PIDL\n        HRESULT hr = spPersist->Initialize(pidl);\n        ::ILFree(pidl);\n        CPPUNIT_ASSERT_OK(hr);\n    }\n\n    void testGetPIDL()\n    {\n        HRESULT hr;\n\n        CComQIPtr<IPersistFolder2> spPersist(m_spFolder);\n        CPPUNIT_ASSERT(spPersist);\n\n        // Get Swish PIDL + HOSTPIDL + REMOTEPIDL as RemoteFolder root\n        PIDLIST_ABSOLUTE pidlRoot = _CreateRootPidl();\n        // We will leak this PIDL if the tests below fail\n\n        // Initialise RemoteFolder with its PIDL\n        hr = spPersist->Initialize(pidlRoot);\n        CPPUNIT_ASSERT_OK(hr);\n\n        // Read the PIDL back - should be identical\n        PIDLIST_ABSOLUTE pidl;\n        hr = spPersist->GetCurFolder(&pidl);\n        CPPUNIT_ASSERT_OK(hr);\n        CPPUNIT_ASSERT(::ILIsEqual(pidl, pidlRoot));\n        ::ILFree(pidl);\n        ::ILFree(pidlRoot);\n    }\n\n\n    /**\n     * Get root PIDL appropriate for current test.\n     */\n    virtual PIDLIST_ABSOLUTE _CreateRootPidl()\n    {\n        return _CreateRootRemotePidl();\n    }\n\n    /**\n     * Get the PIDL which represents the HostFolder (Swish icon) in Explorer.\n     */\n    PIDLIST_ABSOLUTE _GetSwishPidl()\n    {\n        HRESULT hr;\n        PIDLIST_ABSOLUTE pidl;\n        CComPtr<IShellFolder> spDesktop;\n        hr = ::SHGetDesktopFolder(&spDesktop);\n        CPPUNIT_ASSERT_OK(hr);\n        hr = spDesktop->ParseDisplayName(NULL, NULL, \n            L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n            L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\", NULL, \n            reinterpret_cast<PIDLIST_RELATIVE*>(&pidl), NULL);\n        CPPUNIT_ASSERT_OK(hr);\n\n        return pidl;\n    }\n\n    /**\n     * Get an absolute PIDL that ends in a REMOTEPIDL to root RemoteFolder on.\n     */\n    PIDLIST_ABSOLUTE _CreateRootRemotePidl()\n    {\n        // Create test absolute HOSTPIDL\n        PIDLIST_ABSOLUTE pidlHost = _CreateRootHostPidl();\n        \n        // Create root child REMOTEPIDL\n        PITEMID_CHILD pidlRemote = MakeRemotePidl(\n            L\"dir\", true, L\"owner\", L\"group\", 1001, 1002, false, 0677, 1024);\n\n        // Concatenate to make absolute pidl to RemoteFolder root\n        PIDLIST_ABSOLUTE pidl = ::ILCombine(pidlHost, pidlRemote);\n        ::ILFree(pidlRemote);\n        ::ILFree(pidlHost);\n        return pidl;\n    }\n\n    /**\n     * Get an absolute PIDL that ends in a HOSTPIDL to root RemoteFolder on.\n     */\n    PIDLIST_ABSOLUTE _CreateRootHostPidl()\n    {\n        // Create absolute PIDL to Swish icon\n        PIDLIST_ABSOLUTE pidlSwish = _GetSwishPidl();\n        \n        // Create test child HOSTPIDL\n        PITEMID_CHILD pidlHost = MakeHostPidl(\n            L\"user\", L\"test.example.com\", L\"/home/user\", 22, L\"Test PIDL\");\n\n        // Concatenate to make absolute pidl to RemoteFolder root\n        PIDLIST_ABSOLUTE pidl = ::ILCombine(pidlSwish, pidlHost);\n        ::ILFree(pidlSwish);\n        ::ILFree(pidlHost);\n        return pidl;\n    }\n\n    CComPtr<IShellFolder2> m_spFolder;\n    IShellFolder2 *m_pFolder;\n};\n\n\nclass CRemoteFolderPostInitialize_test : public CRemoteFolderPreInitialize_test\n{\npublic:\n    CRemoteFolderPostInitialize_test() : CRemoteFolderPreInitialize_test() {}\n\n    void setUp()\n    {\n        __super::setUp();\n\n        // Initialise RemoteFolder with its PIDL\n        CComQIPtr<IPersistFolder> spPersist(m_spFolder);\n        PIDLIST_ABSOLUTE pidl = _CreateRootPidl();\n        HRESULT hr = spPersist->Initialize(pidl);\n        ::ILFree(pidl);\n        CPPUNIT_ASSERT_OK(hr);\n    }\n};\n\n/**\n * Base class of display name tests.\n */\nclass CRemoteFolderDisplayName_test : public CRemoteFolderPostInitialize_test\n{\npublic:\n    CRemoteFolderDisplayName_test() : CRemoteFolderPostInitialize_test() {}\n\n    void _testName(PCWSTR pwszName, SHGDNF uFlags)\n    {\n        CString strActual(_GetDisplayName(uFlags));\n        CString strExpected(pwszName);\n\n        CPPUNIT_ASSERT_EQUAL(strExpected, strActual);\n    }\n\n    CString _GetDisplayName(SHGDNF uFlags)\n    {\n        HRESULT hr;\n\n        // Create test PIDL\n        PITEMID_CHILD pidl = _CreateTestPidl();\n\n        STRRET strret;\n        hr = m_spFolder->GetDisplayNameOf(pidl, uFlags, &strret);\n        CPPUNIT_ASSERT_OK(hr);\n\n        PWSTR pwszName;\n        hr = ::StrRetToStr(&strret, pidl, &pwszName);\n        CPPUNIT_ASSERT_OK(hr);\n        CString strName(pwszName);\n        ::CoTaskMemFree(pwszName);\n\n        if (strret.uType == STRRET_WSTR)\n            ::CoTaskMemFree(strret.pOleStr);\n\n        return strName;\n    }\n\n    virtual PITEMID_CHILD _CreateTestPidl() PURE;\n};\n\n// Tests for following configuration:\n//     ComputerPIDL\\SwishPIDL\\HOSTPIDL\\REMOTEPIDL\\REMOTEPIDL\n// where this RemoteFolder is rooted at:\n//     ComputerPIDL\\SwishPIDL\\HOSTPIDL\\REMOTEPIDL\n\nstatic const wchar_t *DN1_FRIENDLY_RELATIVE = L\"TestFile\";\nstatic const wchar_t *DN1_FRIENDLY_ABSOLUTE = L\"TestFile\";\n\nstatic const wchar_t *DN1_PARSING_RELATIVE = L\"TestFile.bmp\";\nstatic const wchar_t *DN1_PARSING_ABSOLUTE = \n    L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n    L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\\\\\"\n    L\"sftp://user@test.example.com:22//home/user/dir/TestFile.bmp\";\n\nstatic const wchar_t *DN1_ADDRESSBAR_RELATIVE = L\"TestFile\";\nstatic const wchar_t *DN1_ADDRESSBAR_ABSOLUTE = \n    L\"sftp://user@test.example.com//home/user/dir/TestFile\";\n\nstatic const wchar_t *DN1_PARSINGADDRESSBAR_RELATIVE = L\"TestFile.bmp\";\nstatic const wchar_t *DN1_PARSINGADDRESSBAR_ABSOLUTE = \n    L\"Computer\\\\Swish\\\\sftp://user@test.example.com:22/\"\n    L\"/home/user/dir/TestFile.bmp\";\n\nstatic const wchar_t *DN1_EDITING_RELATIVE = L\"TestFile.bmp\";\nstatic const wchar_t *DN1_EDITING_ABSOLUTE = L\"TestFile.bmp\";\n\nclass CRemoteFolderDisplayName1_test : public CRemoteFolderDisplayName_test\n{\n    CPPUNIT_TEST_SUITE( CRemoteFolderDisplayName1_test );\n        CPPUNIT_TEST( testDisplayNormal );\n        CPPUNIT_TEST( testDisplayInFolder );\n        CPPUNIT_TEST( testParsingNormal );\n        CPPUNIT_TEST( testParsingInFolder );\n        CPPUNIT_TEST( testAddressbarNormal );\n        CPPUNIT_TEST( testAddressbarInFolder );\n        CPPUNIT_TEST( testEditingNormal );\n        CPPUNIT_TEST( testEditingInFolder );\n        CPPUNIT_TEST( testParsingAddressbarNormal );\n        CPPUNIT_TEST( testParsingAddressbarInFolder );\n    CPPUNIT_TEST_SUITE_END();\n\npublic:\n    CRemoteFolderDisplayName1_test() : CRemoteFolderDisplayName_test() {}\n\nprotected:\n\n    void testDisplayNormal()\n    {\n        _testName(DN1_FRIENDLY_ABSOLUTE, SHGDN_NORMAL);\n    }\n\n    void testDisplayInFolder()\n    {\n        _testName(DN1_FRIENDLY_RELATIVE, SHGDN_INFOLDER);\n    }\n\n    void testParsingNormal()\n    {\n        _testName(DN1_PARSING_ABSOLUTE, SHGDN_FORPARSING);\n    }\n\n    void testParsingInFolder()\n    {\n        _testName(DN1_PARSING_RELATIVE, SHGDN_INFOLDER | SHGDN_FORPARSING);\n    }\n\n    void testAddressbarNormal()\n    {\n        _testName(DN1_ADDRESSBAR_ABSOLUTE, SHGDN_FORADDRESSBAR);\n    }\n\n    void testAddressbarInFolder()\n    {\n        _testName(\n            DN1_ADDRESSBAR_RELATIVE, SHGDN_INFOLDER | SHGDN_FORADDRESSBAR);\n    }\n\n    void testEditingNormal()\n    {\n        _testName(DN1_EDITING_ABSOLUTE, SHGDN_FOREDITING);\n    }\n\n    void testEditingInFolder()\n    {\n        _testName(DN1_EDITING_RELATIVE, SHGDN_INFOLDER | SHGDN_FOREDITING);\n    }\n\n    void testParsingAddressbarNormal()\n    {\n        _testName(\n            DN1_PARSINGADDRESSBAR_ABSOLUTE, \n            SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\n    void testParsingAddressbarInFolder()\n    {\n        _testName(\n            DN1_PARSINGADDRESSBAR_RELATIVE, \n            SHGDN_INFOLDER | SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\nprivate:\n\n    PITEMID_CHILD _CreateTestPidl()\n    {\n        // Create test REMOTEPIDL\n        return MakeRemotePidl(\n            L\"TestFile.bmp\", false, L\"me\", L\"us\", 1001, 1002, false, \n            0677, 511, NULL);\n    }\n};\n\n// Tests for following configuration:\n//     ComputerPIDL\\SwishPIDL\\HOSTPIDL\\REMOTEPIDL\n// where this RemoteFolder is rooted at:\n//     ComputerPIDL\\SwishPIDL\\HOSTPIDL\n\nstatic const wchar_t *DN2_FRIENDLY_RELATIVE = L\"TestDirectory.ext\";\nstatic const wchar_t *DN2_FRIENDLY_ABSOLUTE = L\"TestDirectory.ext\";\n\nstatic const wchar_t *DN2_PARSING_RELATIVE = L\"TestDirectory.ext\";\nstatic const wchar_t *DN2_PARSING_ABSOLUTE = \n    L\"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\"\n    L\"::{B816A83A-5022-11DC-9153-0090F5284F85}\\\\\"\n    L\"sftp://user@test.example.com:22//home/user/TestDirectory.ext\";\n\nstatic const wchar_t *DN2_ADDRESSBAR_RELATIVE = L\"TestDirectory.ext\";\nstatic const wchar_t *DN2_ADDRESSBAR_ABSOLUTE = \n    L\"sftp://user@test.example.com//home/user/TestDirectory.ext\";\n\nstatic const wchar_t *DN2_PARSINGADDRESSBAR_RELATIVE = L\"TestDirectory.ext\";\nstatic const wchar_t *DN2_PARSINGADDRESSBAR_ABSOLUTE = \n    L\"Computer\\\\Swish\\\\sftp://user@test.example.com:22/\"\n    L\"/home/user/TestDirectory.ext\";\n\nstatic const wchar_t *DN2_EDITING_RELATIVE = L\"TestDirectory.ext\";\nstatic const wchar_t *DN2_EDITING_ABSOLUTE = L\"TestDirectory.ext\";\n\nclass CRemoteFolderDisplayName2_test : public CRemoteFolderDisplayName_test\n{\n    CPPUNIT_TEST_SUITE( CRemoteFolderDisplayName2_test );\n        CPPUNIT_TEST( testDisplayNormal );\n        CPPUNIT_TEST( testDisplayInFolder );\n        CPPUNIT_TEST( testParsingNormal );\n        CPPUNIT_TEST( testParsingInFolder );\n        CPPUNIT_TEST( testAddressbarNormal );\n        CPPUNIT_TEST( testAddressbarInFolder );\n        CPPUNIT_TEST( testEditingNormal );\n        CPPUNIT_TEST( testEditingInFolder );\n        CPPUNIT_TEST( testParsingAddressbarNormal );\n        CPPUNIT_TEST( testParsingAddressbarInFolder );\n    CPPUNIT_TEST_SUITE_END();\n\npublic:\n    CRemoteFolderDisplayName2_test() : CRemoteFolderDisplayName_test() {}\n\nprotected:\n\n    void testDisplayNormal()\n    {\n        _testName(DN2_FRIENDLY_ABSOLUTE, SHGDN_NORMAL);\n    }\n\n    void testDisplayInFolder()\n    {\n        _testName(DN2_FRIENDLY_RELATIVE, SHGDN_INFOLDER);\n    }\n\n    void testParsingNormal()\n    {\n        _testName(DN2_PARSING_ABSOLUTE, SHGDN_FORPARSING);\n    }\n\n    void testParsingInFolder()\n    {\n        _testName(DN2_PARSING_RELATIVE, SHGDN_INFOLDER | SHGDN_FORPARSING);\n    }\n\n    void testAddressbarNormal()\n    {\n        _testName(DN2_ADDRESSBAR_ABSOLUTE, SHGDN_FORADDRESSBAR);\n    }\n\n    void testAddressbarInFolder()\n    {\n        _testName(\n            DN2_ADDRESSBAR_RELATIVE, SHGDN_INFOLDER | SHGDN_FORADDRESSBAR);\n    }\n\n    void testEditingNormal()\n    {\n        _testName(DN2_EDITING_ABSOLUTE, SHGDN_FOREDITING);\n    }\n\n    void testEditingInFolder()\n    {\n        _testName(DN2_EDITING_RELATIVE, SHGDN_INFOLDER | SHGDN_FOREDITING);\n    }\n\n    void testParsingAddressbarNormal()\n    {\n        _testName(\n            DN2_PARSINGADDRESSBAR_ABSOLUTE, \n            SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\n    void testParsingAddressbarInFolder()\n    {\n        _testName(\n            DN2_PARSINGADDRESSBAR_RELATIVE, \n            SHGDN_INFOLDER | SHGDN_FORADDRESSBAR | SHGDN_FORPARSING);\n    }\n\nprivate:\n\n    PITEMID_CHILD _CreateTestPidl()\n    {\n        // Create test REMOTEPIDL\n        return MakeRemotePidl(\n            L\"TestDirectory.ext\", true, L\"me\", L\"us\", 1001, 1002, false, \n            0677, 511, NULL);\n    }\n\n    /**\n     * Get root PIDL appropriate for current test.\n     */\n    virtual PIDLIST_ABSOLUTE _CreateRootPidl()\n    {\n        return _CreateRootHostPidl();\n    }\n};\n\nCPPUNIT_TEST_SUITE_REGISTRATION( CRemoteFolderPreInitialize_test );\nCPPUNIT_TEST_SUITE_REGISTRATION( CRemoteFolderDisplayName1_test );\nCPPUNIT_TEST_SUITE_REGISTRATION( CRemoteFolderDisplayName2_test );\n\n}}} // namespace test::swish::com_dll"
  },
  {
    "path": "test/shell_folder-com_dll/main.cpp",
    "content": "/**\n    @file\n\n    Main test runner implementation.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <cppunit/TextTestProgressListener.h>\n#include <cppunit/CompilerOutputter.h>\n#include <cppunit/extensions/TestFactoryRegistry.h>\n#include <cppunit/TestResult.h>\n#include <cppunit/TestResultCollector.h>\n#include <cppunit/TestRunner.h>\n\n/**\n * Run all tests displaying their names and status.\n *\n * If a test fails, display the error in a compiler compatible form.\n */\nint main()\n{\n    // Create the event manager and test controller\n    CPPUNIT_NS::TestResult controller;\n\n    // Add a listener that collects test result\n    CPPUNIT_NS::TestResultCollector result;\n    controller.addListener(&result);        \n\n    // Add a listener that print dots as test run.\n    CPPUNIT_NS::TextTestProgressListener progress;\n    controller.addListener(&progress);      \n\n    // Add the top suite to the test runner\n    CPPUNIT_NS::TestRunner runner;\n    runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());\n    runner.run(controller);\n\n    // Print test in a compiler compatible format.\n    CPPUNIT_NS::CompilerOutputter outputter(&result, CPPUNIT_NS::stdCOut());\n    outputter.write(); \n\n    return 0;\n}"
  },
  {
    "path": "test/shell_folder-com_dll/pidl.cpp",
    "content": "/**\n    @file\n\n    Custom PIDL functions for use only by tests.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include \"pidl.hpp\"\n\nnamespace { // private\n\n    #include <pshpack1.h>\n    /** \n     * Duplicate of HostItemId defined in HostPidl.h.\n     * These must be kept in sync.\n     */\n    struct HostItemId\n    {\n        USHORT cb;\n        DWORD dwFingerprint;\n        WCHAR wszLabel[MAX_LABEL_LENZ];\n        WCHAR wszUser[MAX_USERNAME_LENZ];\n        WCHAR wszHost[MAX_HOSTNAME_LENZ];\n        WCHAR wszPath[MAX_PATH_LENZ];\n        USHORT uPort;\n        \n        static const DWORD FINGERPRINT = 0x496c1066;\n    };\n\n    /** \n     * Duplicate of RemoteItemId defined in RemotePidl.h.\n     * These must be kept in sync.\n     */\n    struct RemoteItemId\n    {\n        USHORT cb;\n        DWORD dwFingerprint;\n        bool fIsFolder;\n        bool fIsLink;\n        WCHAR wszFilename[MAX_FILENAME_LENZ];\n        WCHAR wszOwner[MAX_USERNAME_LENZ];\n        WCHAR wszGroup[MAX_USERNAME_LENZ];\n        ULONG uUid;\n        ULONG uGid;\n        DWORD dwPermissions;\n        //WORD wPadding;\n        ULONGLONG uSize;\n        DATE dateModified;\n        DATE dateAccessed;\n\n        static const DWORD FINGERPRINT = 0x533aaf69;\n    };\n    #include <poppack.h>\n}\n\nnamespace test {\nnamespace swish {\nnamespace com_dll {\nnamespace pidl {\n\nPITEMID_CHILD MakeHostPidl(\n    PCWSTR user, PCWSTR host, PCWSTR path, USHORT port, PCWSTR label)\n{\n    // Allocate enough memory to hold HostItemId structure & terminator\n    static size_t cbItem = sizeof(HostItemId) + sizeof(USHORT);\n    HostItemId* item = static_cast<HostItemId*>(::CoTaskMemAlloc(cbItem));\n    ::ZeroMemory(item, cbItem);\n\n    // Fill members of the PIDL with data\n    item->cb = sizeof(*item);\n    item->dwFingerprint = HostItemId::FINGERPRINT; // Sign with fingerprint\n    ::wcscpy_s(item->wszUser, ARRAYSIZE(item->wszUser), user);\n    ::wcscpy_s(item->wszHost, ARRAYSIZE(item->wszHost), host);\n    item->uPort = port;\n    ::wcscpy_s(item->wszPath, ARRAYSIZE(item->wszPath), path);\n    ::wcscpy_s(item->wszLabel, ARRAYSIZE(item->wszLabel), label);\n\n    return reinterpret_cast<PITEMID_CHILD>(item);\n}\n\nPITEMID_CHILD MakeRemotePidl(\n    PCWSTR filename, bool fIsFolder, PCWSTR owner, PCWSTR group, ULONG uUid,\n    ULONG uGid, bool fIsLink, DWORD dwPermissions, ULONGLONG uSize,\n    DATE dateModified, DATE dateAccessed)\n{\n    // Allocate enough memory to hold RemoteItemId structure & terminator\n    static size_t cbItem = sizeof RemoteItemId + sizeof USHORT;\n    RemoteItemId* item = static_cast<RemoteItemId*>(\n        ::CoTaskMemAlloc(cbItem));\n    ::ZeroMemory(item, cbItem);\n\n    // Fill members of the PIDL with data\n    item->cb = sizeof(*item);\n    item->dwFingerprint = RemoteItemId::FINGERPRINT; // Sign with fprint\n    ::wcscpy_s(item->wszFilename, ARRAYSIZE(item->wszFilename), filename);\n    ::wcscpy_s(item->wszOwner, ARRAYSIZE(item->wszOwner), owner);\n    ::wcscpy_s(item->wszGroup, ARRAYSIZE(item->wszGroup), group);\n    item->uUid = uUid;\n    item->uGid = uGid;\n    item->dwPermissions = dwPermissions;\n    item->uSize = uSize;\n    item->dateModified = dateModified;\n    item->dateAccessed = dateAccessed;\n    item->fIsFolder = fIsFolder;\n    item->fIsLink = fIsLink;\n\n    return reinterpret_cast<PITEMID_CHILD>(item);\n}\n\n}}}} // namespace test::swish::com_dll::pidl"
  },
  {
    "path": "test/shell_folder-com_dll/pidl.hpp",
    "content": "/**\n    @file\n\n    Custom PIDL functions for use only by tests.\n\n    @if license\n\n    Copyright (C) 2009  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#define STRICT_TYPED_ITEMIDS ///< Better type safety for PIDLs (must be \n                             ///< before <shtypes.h> or <shlobj.h>).\n#include <shlobj.h>  // For PIDLs and PIDL-handling functions\n\nnamespace test {\nnamespace swish {\nnamespace com_dll {\n/**\n * PIDL-making helpers so that tests have no dependencies other than\n * the COM interfaces to CHostFolder and CRemoteFolder in swish.h.\n *\n * @note\n * These functions omit most error-checking so must only be used for tests.\n */\nnamespace pidl {\n\n    PITEMID_CHILD MakeHostPidl(\n        PCWSTR user, PCWSTR host, PCWSTR path, \n        USHORT port=22, PCWSTR label=L\"\");\n\n    PITEMID_CHILD MakeRemotePidl(\n        PCWSTR filename, bool fIsFolder=false, PCWSTR owner=L\"\", \n        PCWSTR group=L\"\", ULONG uUid=0, ULONG uGid=0, bool fIsLink=false,\n        DWORD dwPermissions=0, ULONGLONG uSize=0,\n        DATE dateModified=0, DATE dateAccessed=0);\n\n}}}} // namespace test::swish::com_dll::pidl"
  },
  {
    "path": "test/shell_folder-dialogue/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  # Need to get resources from shell_folder project:\n  # http://stackoverflow.com/a/1631078/67013\n  KbdInteractiveDialog_test.cpp)\n\nswish_test_suite(\n  SUBJECT shell_folder VARIANT dialogue\n  SOURCES ${SOURCES}\n  LIBRARIES ${CMAKE_BINARY_DIR}/swish/shell_folder/shell_folder.dir/${CMAKE_CFG_INTDIR}/shell_folder.res\n  ${Boost_LIBRARIES}\n  LABELS gui)\n"
  },
  {
    "path": "test/shell_folder-dialogue/KbdInteractiveDialog_test.cpp",
    "content": "/**\n    @file\n\n    Basic testing of the 'Keyboard-interactive Authentication' dialogue box.\n\n    @if license\n\n    Copyright (C) 2009, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    @endif\n*/\n\n#include <washer/error.hpp>\n\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n#include <utility> // pair, make_pair\n#include <vector>\n\n#include \"swish/shell_folder/KbdInteractiveDialog.h\"\n\nusing washer::last_error;\n\nusing std::make_pair;\nusing std::pair;\nusing std::string;\nusing std::vector;\n\nBOOST_AUTO_TEST_SUITE( KbdInteractiveDialog_tests )\n\nnamespace {\n\n    const DWORD CLICK_DELAY = 700;\n\n    /**\n     * Sends a button click to the Cancel button of the dialog programmatically.\n     */\n    DWORD WINAPI ClickCancelThread(LPVOID lpThreadParam)\n    {\n        CKbdInteractiveDialog *pDlg = ((CKbdInteractiveDialog *)lpThreadParam);\n        ::Sleep(CLICK_DELAY);\n\n        // Post left mouse button up/down directly to Cancel button\n        ::PostMessage(\n            pDlg->GetDlgItem(IDCANCEL), WM_LBUTTONDOWN, MK_LBUTTON, NULL);\n        ::PostMessage(pDlg->GetDlgItem(IDCANCEL), WM_LBUTTONUP, NULL, NULL);\n        return 0;\n    }\n\n    /**\n     * Sends a button click to the OK button of the dialog programmatically.\n     */\n    DWORD WINAPI ClickOKThread(LPVOID lpThreadParam)\n    {\n        CKbdInteractiveDialog *pDlg = ((CKbdInteractiveDialog *)lpThreadParam);\n        ::Sleep(CLICK_DELAY);\n\n        // Post left mouse button up/down directly to OK button\n        ::PostMessage(\n            pDlg->GetDlgItem(IDOK), WM_LBUTTONDOWN, MK_LBUTTON, NULL);\n        ::PostMessage(pDlg->GetDlgItem(IDOK), WM_LBUTTONUP, NULL, NULL);\n        return 0;\n    }\n\n    void _testModalDisplay(CKbdInteractiveDialog& dlg, bool fClickCancel = true)\n    {\n        // Launch thread which will send button click to dialog\n        DWORD dwThreadId;\n        HANDLE hClickThread = ::CreateThread(\n            NULL, 0,\n            (fClickCancel) ? ClickCancelThread : ClickOKThread,\n            &dlg, 0, &dwThreadId\n            );\n        BOOST_REQUIRE( hClickThread );\n\n        // Launch dialog (blocks until dialog ends) and check button ID\n        INT_PTR rc = dlg.DoModal();\n\t\tif (rc == -1)\n\t\t\tBOOST_THROW_EXCEPTION(last_error());\n\n\n\t\tBOOST_CHECK_EQUAL(rc, (INT_PTR) ((fClickCancel) ? IDCANCEL : IDOK));\n\n        // Check that thread terminated\n        ::Sleep(CLICK_DELAY);\n        DWORD dwThreadStatus;\n        ::GetExitCodeThread(hClickThread, &dwThreadStatus);\n        BOOST_CHECK( STILL_ACTIVE != dwThreadStatus );\n\n        // Cleanup\n        BOOST_CHECK( ::CloseHandle(hClickThread) );\n        hClickThread = NULL;\n    }\n}\n\nBOOST_AUTO_TEST_CASE( single_prompt )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt:\"), true));\n\n    CKbdInteractiveDialog dlg(\n        \"server-sent name\", \"server-sent instruction\", prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( single_prompt_no_instruction )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt:\"), true));\n\n    CKbdInteractiveDialog dlg(\"server-sent name\", \"\", prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( single_prompt_no_instruction_nor_name )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt:\"), true));\n\n    CKbdInteractiveDialog dlg(\"\", \"\", prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( long_instruction )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt:\"), true));\n\n    CKbdInteractiveDialog dlg(\n        \"server-sent name\",\n        \"A very very very very long instruction which, as permitted \"\n        \"by the [IETF RFC 4256] SFTP specification, can contain \"\n        \"linebreaks in\\r\\n\"\n        \"Windows style\\r\\nUnix style\\nlegacy MacOS style\\rall of which \"\n        \"should behave correctly.\",\n        prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( multiple_prompts )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt 1:\"), true));\n    prompts.push_back(make_pair(string(\"Test prompt 2:\"), false));\n    prompts.push_back(make_pair(string(\"Test prompt 3:\"), true));\n\n    CKbdInteractiveDialog dlg(\"\", \"\", prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( long_prompt )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt 1:\"), true));\n    prompts.push_back(\n        make_pair(string(\"Test prompt 2 which is much longer than all the \"\n        \"other prompts:\"), false));\n    prompts.push_back(make_pair(string(\"Test prompt 3:\"), true));\n\n    CKbdInteractiveDialog dlg(\"\", \"\", prompts);\n\n    _testModalDisplay(dlg);\n}\n\nBOOST_AUTO_TEST_CASE( empty_responses_ok_clicked )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt 1:\"), true));\n    prompts.push_back(make_pair(string(\"Test prompt 2:\"), false));\n    prompts.push_back(make_pair(string(\"Test prompt 3:\"), true));\n\n    CKbdInteractiveDialog dlg(\"\", \"\", prompts);\n\n    _testModalDisplay(dlg, false);\n\n    vector<string> responses = dlg.GetResponses();\n\n    BOOST_CHECK_EQUAL(responses.size(), 3U);\n    BOOST_CHECK_EQUAL(responses[0], \"\");\n    BOOST_CHECK_EQUAL(responses[1], \"\");\n    BOOST_CHECK_EQUAL(responses[2], \"\");\n}\n\nBOOST_AUTO_TEST_CASE( empty_responses_cancel_clicked )\n{\n    vector<pair<string, bool>> prompts;\n    prompts.push_back(make_pair(string(\"Test prompt 1:\"), true));\n    prompts.push_back(make_pair(string(\"Test prompt 2:\"), false));\n    prompts.push_back(make_pair(string(\"Test prompt 3:\"), true));\n\n    CKbdInteractiveDialog dlg(\"\", \"\", prompts);\n\n    _testModalDisplay(dlg, true);\n\n    vector<string> responses = dlg.GetResponses();\n\n    BOOST_CHECK_EQUAL(responses.size(), 0U);\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "test/ssh/CMakeLists.txt",
    "content": "# Copyright 2015, 2016 Alexander Lamaison\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nhunter_add_package(Boost.Process)\n\n# Underscores to distinguish from same-name Swish fixtures.  Can be renamed when\n# we split the projects.\nadd_library(openssh_fixture_\n  openssh_fixture.cpp\n  openssh_fixture.hpp)\ntarget_link_libraries(openssh_fixture_\n  PUBLIC ${Boost_LIBRARIES}\n  PRIVATE Boost::Process)\n\nadd_library(session_fixture_\n  session_fixture.cpp\n  session_fixture.hpp)\ntarget_link_libraries(session_fixture_\n  PUBLIC openssh_fixture_ ssh)\n\nadd_library(sftp_fixture_\n  sftp_fixture.cpp\n  sftp_fixture.hpp)\ntarget_link_libraries(sftp_fixture_\n  PUBLIC session_fixture_)\n\nset(INTEGRATION_TESTS\n  auth_test\n  filesystem_test\n  filesystem_construction_test\n  host_key_test\n  session_test\n  input_stream_test\n  output_stream_test\n  stream_threading_test\n  io_stream_test)\n\nset(UNIT_TESTS\n  knownhost_test\n  path_test)\n\nset(TEST_RUNNER_ARGUMENTS\n  --result_code=yes --build_info=yes --log_level=test_suite)\n\nset(TEST_DATA_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\ninclude(CMakeParseArguments)\n# ssh_test_suite(SUBJECT test-target TESTS ... LIBRARIES ... [LABELS ...])\nfunction(SSH_TEST_SUITE)\n  set(options)\n  set(oneValueArgs SUBJECT)\n  set(multiValueArgs TESTS LIBRARIES LABELS)\n  cmake_parse_arguments(SSH_TEST_SUITE\n    \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  foreach(_TEST_NAME ${SSH_TEST_SUITE_TESTS})\n    set(_TEST_EXE_NAME \"test-${SSH_TEST_SUITE_SUBJECT}-${_TEST_NAME}\")\n    set(_TEST_SOURCE_FILE \"${_TEST_NAME}.cpp\")\n\n    add_executable(${_TEST_EXE_NAME} module.cpp ${_TEST_SOURCE_FILE})\n\n    target_link_libraries(${_TEST_EXE_NAME}\n      PRIVATE ${SSH_TEST_SUITE_SUBJECT} ${SSH_TEST_SUITE_LIBRARIES})\n\n    add_dependencies(BUILD_ALL_TESTS ${_TEST_EXE_NAME})\n\n    add_test(\n      NAME ${_TEST_EXE_NAME}\n      COMMAND ${_TEST_EXE_NAME} ${TEST_RUNNER_ARGUMENTS}\n      WORKING_DIRECTORY \"${TEST_DATA_DIR}\")\n\n    if(MEMORY_LEAKS_ARE_FAILURES)\n      # Don't hide memory leak detection.  The detector can't change the error\n      # code so the test appears successful otherwise.\n      set_tests_properties(${_TEST_EXE_NAME} PROPERTIES\n\tFAIL_REGULAR_EXPRESSION \"Detected memory leaks\")\n    endif()\n\n    if(SSH_TEST_SUITE_LABELS)\n      set_tests_properties(\n\t${_TEST_EXE_NAME} PROPERTIES LABELS \"${SSH_TEST_SUITE_LABELS}\")\n    endif()\n  endforeach()\nendfunction()\n\nssh_test_suite(\n  SUBJECT ssh\n  TESTS ${INTEGRATION_TESTS}\n  LIBRARIES ${Boost_LIBRARIES} openssh_fixture_ session_fixture_ sftp_fixture_\n  LABELS integration)\n\nssh_test_suite(\n  SUBJECT ssh VARIANT unit\n  TESTS ${UNIT_TESTS}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n"
  },
  {
    "path": "test/ssh/auth_test.cpp",
    "content": "/**\n    @file\n\n    Tests for session authentication.\n\n    @if license\n\n    Copyright (C) 2010, 2012, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"session_fixture.hpp\" // session_fixture\n\n#include <ssh/session.hpp> // test subject\n\n#include <boost/concept_check.hpp> // BOOST_CONCEPT_ASSERT\n#include <boost/move/move.hpp>\n#include <boost/range/concepts.hpp> // RandomAccessRangeConcept\n#include <boost/range/size.hpp>\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <exception>\n#include <memory>\n#include <string>\n#include <vector>\n\nusing boost::RandomAccessRangeConcept;\nusing boost::move;\nusing boost::size;\nusing boost::system::system_error;\n\nusing ssh::session;\nusing ssh::agent_identities;\nusing ssh::identity;\n\nusing test::ssh::session_fixture;\n\nusing std::auto_ptr;\nusing std::exception;\nusing std::string;\nusing std::vector;\n\nBOOST_FIXTURE_TEST_SUITE(auth_tests, session_fixture)\n\nBOOST_AUTO_TEST_CASE( available_auth_methods )\n{\n    session& s = test_session();\n    \n    vector<string> methods = s.authentication_methods(user());\n    // 'publickey' is the only required method\n    BOOST_REQUIRE(\n        find(methods.begin(), methods.end(), \"publickey\") != methods.end());\n}\n\n/**\n * New sessions must not be authenticated.\n *\n * Assumes the server doesn't support authentication method 'none'.\n */\nBOOST_AUTO_TEST_CASE( intial_state )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n}\n\n// The next two test cases, password and kb-int are very limited.  We can't set\n// the password or kb-int responses that the Cygwin OpenSSH server is expecting\n// so we only test the failure case.  Would love to know a way round this!\n\n/**\n * Try password authentication.\n *\n * This will fail as we can't set a password on our fixture server.\n *\n * @todo  Find a way to test the success case with the fixture server.\n */\nBOOST_AUTO_TEST_CASE( password_fail )\n{\n    session& s = test_session();\n    \n    vector<string> methods = s.authentication_methods(user());\n    BOOST_REQUIRE(\n        find(methods.begin(), methods.end(), \"password\") != methods.end());\n\n    BOOST_CHECK(!s.authenticate_by_password(user(), \"dummy password\"));\n    BOOST_CHECK(!s.authenticated());\n}\n\nnamespace {\n\n    /**\n     * Callback for interactive authentication that responds with nonsense to\n     * every request.\n     */\n    class nonsense_interactor\n    {\n    public:\n\n        template<typename PromptRange>\n        vector<string> operator()(\n            const string& /* request_name */, const string& /* instructions */,\n            PromptRange prompts)\n        {\n            BOOST_CONCEPT_ASSERT((RandomAccessRangeConcept<PromptRange>));\n            return vector<string>(size(prompts), \"gobbleygook\");\n        }\n    };\n    \n    /**\n     * Callback for interactive authentication that responds with too few\n     * responses.\n     */\n    class short_interactor\n    {\n    public:\n\n        template<typename PromptRange>\n        vector<string> operator()(\n            const string& /* request_name */, const string& /* instructions */,\n            PromptRange /*prompts*/)\n        {\n            BOOST_CONCEPT_ASSERT((RandomAccessRangeConcept<PromptRange>));\n            return vector<string>();\n        }\n    };\n\n    class bob_exception {};\n    \n    /**\n     * Callback for interactive authentication that responds with too few\n     * responses.\n     */\n    class exception_interactor\n    {\n    public:\n\n        template<typename PromptRange>\n        vector<string> operator()(\n            const string& /* request_name */, const string& /* instructions */,\n            PromptRange /*prompts*/)\n        {\n            // Use custom exception so we can identify that the correct\n            // exception is bubbled up in the test\n            throw bob_exception();\n        }\n    };\n}\n\n/**\n * Try keyboard-interactive authentication but give the wrong responses.\n *\n * This will fail as we can't get Cygwin OpenSSH to use kb-int\n * authentication.  The server will say it is supported when it isn't.\n *\n * @todo  Find a way to test the case with the fixture server.\n */\nBOOST_AUTO_TEST_CASE( kbint_fail_wrong )\n{\n    session& s = test_session();\n\n    vector<string> methods = s.authentication_methods(user());\n    BOOST_REQUIRE(\n        find(methods.begin(), methods.end(), \"keyboard-interactive\")\n        != methods.end());\n\n    // FIXME: Will throw because Cygwin server refuses kb-int after claiming\n    // to support it.  Suppressing test that, currently, cannot pass.\n    //BOOST_CHECK(!s.authenticate_interactively(user(), nonsense_interactor()));\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try keyboard-interactive authentication but return no responses.\n *\n * This will fail as we can't get Cygwin OpenSSH to use kb-int\n * authentication.  The server will say it is supported when it isn't.\n *\n * @todo  Find a way to test the case with the fixture server.\n */\nBOOST_AUTO_TEST_CASE( kbint_fail_short )\n{\n    session& s = test_session();\n\n    vector<string> methods = s.authentication_methods(user());\n    BOOST_REQUIRE(\n        find(methods.begin(), methods.end(), \"keyboard-interactive\")\n        != methods.end());\n\n    // FIXME: Will throw because Cygwin server refuses kb-int after claiming\n    // to support it.  Suppressing test that, currently, cannot pass.\n    //BOOST_CHECK(!s.authenticate_interactively(user(), short_interactor()));\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try keyboard-interactive authentication but return no responses.\n *\n * This will fail as we can't get Cygwin OpenSSH to use kb-int\n * authentication.  The server will say it is supported when it isn't.\n *\n * @todo  Find a way to test the case with the fixture server.\n */\nBOOST_AUTO_TEST_CASE( kbint_fail_exception )\n{\n    session& s = test_session();\n\n    vector<string> methods = s.authentication_methods(user());\n    BOOST_REQUIRE(\n        find(methods.begin(), methods.end(), \"keyboard-interactive\")\n        != methods.end());\n\n    BOOST_CHECK_THROW(\n        s.authenticate_interactively(user(), exception_interactor()),\n        // bob_exception);\n        exception);\n    // FIXME: Will throw wrong kind of exception because Cygwin server refuses\n    // kb-int after claiming to support it.  Suppressing test that, currently,\n    // cannot pass.\n\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try pubkey authentication with public key that should fail.\n */\nBOOST_AUTO_TEST_CASE( pubkey_wrong_public )\n{\n    session& s = test_session();\n\n    BOOST_CHECK_THROW(\n        s.authenticate_by_key_files(\n            user(), wrong_public_key_path(), private_key_path(), \"\"),\n        system_error);\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try pubkey authentication with private key that should fail.\n */\nBOOST_AUTO_TEST_CASE( pubkey_wrong_private )\n{\n    session& s = test_session();\n\n    BOOST_CHECK_THROW(\n        s.authenticate_by_key_files(\n            user(), public_key_path(), wrong_private_key_path(), \"\"),\n        system_error);\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try pubkey authentication with both keys (but matching pair!) that\n * should fail.\n */\nBOOST_AUTO_TEST_CASE( pubkey_wrong_pair )\n{\n    session& s = test_session();\n\n    BOOST_CHECK_THROW(\n        s.authenticate_by_key_files(\n            user(), wrong_public_key_path(), wrong_private_key_path(), \"\"),\n        system_error);\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try pubkey authentication with a key public that can't be parsed.\n */\nBOOST_AUTO_TEST_CASE( pubkey_invalid_public )\n{\n    session& s = test_session();\n\n    BOOST_CHECK_THROW(\n        s.authenticate_by_key_files(\n            user(), private_key_path(), private_key_path(), \"\"), system_error);\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Try pubkey authentication with a key private that can't be parsed.\n */\nBOOST_AUTO_TEST_CASE( pubkey_invalid_private )\n{\n    session& s = test_session();\n\n    BOOST_CHECK_THROW(\n        s.authenticate_by_key_files(\n            user(), public_key_path(), public_key_path(), \"\"), system_error);\n    BOOST_CHECK(!s.authenticated());\n}\n\n/**\n * Pubkey authentication with correct keys.\n */\nBOOST_AUTO_TEST_CASE( pubkey )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(), \"\");\n    BOOST_CHECK(s.authenticated());\n}\n\n/**\n * Authentication carries across to move-constructed session.\n */\nBOOST_AUTO_TEST_CASE( move_construct_after_auth )\n{\n    session& s = test_session();\n\n    s.authenticate_by_key_files(\n        user(), public_key_path(), private_key_path(), \"\");\n\n    session t(move(s));\n    BOOST_CHECK(t.authenticated());\n}\n\n/**\n * Authentication carries across to move-assigned session.\n */\nBOOST_AUTO_TEST_CASE( move_assign_after_auth )\n{\n    session& s = test_session();\n\n    s.authenticate_by_key_files(\n        user(), public_key_path(), private_key_path(), \"\");\n\n    auto_ptr<boost::asio::ip::tcp::socket> socket(connect_additional_socket());\n\n    session t(socket->native());\n\n    BOOST_CHECK(!t.authenticated());\n    t = move(s);\n    BOOST_CHECK(t.authenticated());\n}\n\n/**\n * Request connection to agent.  Allowed to fail but not catastrophically.\n */\nBOOST_AUTO_TEST_CASE( agent )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n\n    try\n    {\n        agent_identities identities = s.agent_identities();\n\n        BOOST_FOREACH(identity i, identities)\n        {\n            try\n            {\n                i.authenticate(user());\n                BOOST_CHECK(s.authenticated());\n                return;\n            }\n            catch(const system_error&) {}\n\n            BOOST_CHECK(!s.authenticated());\n        }\n    }\n    catch (system_error&) { /* agent not running - failure ok */ }\n}\n\n/**\n * Agent copy behaviour.\n */\nBOOST_AUTO_TEST_CASE( agent_copy )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n\n    try\n    {\n        agent_identities identities = s.agent_identities();\n        agent_identities identities2 = identities;\n\n        BOOST_FOREACH(identity i, identities)\n        {\n        }\n        BOOST_FOREACH(identity i, identities2)\n        {\n        }\n    }\n    catch (system_error&) { /* agent not running - failure ok */ }\n}\n\n/**\n * Agent idempotence - creating the object more than once should be ok.\n */\nBOOST_AUTO_TEST_CASE( agent_idempotence )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n\n    try\n    {\n        agent_identities identities = s.agent_identities();\n        agent_identities identities2 = s.agent_identities();\n\n        BOOST_FOREACH(identity i, identities)\n        {\n        }\n        BOOST_FOREACH(identity i, identities2)\n        {\n        }\n    } \n    catch (system_error&) { /* agent not running - failure ok */ }\n}\n\n/**\n * Agent move-construct behaviour.\n */\nBOOST_AUTO_TEST_CASE( agent_move_construct )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n\n    try\n    {\n        agent_identities identities = s.agent_identities();\n        agent_identities identities2(move(identities));\n\n        BOOST_FOREACH(identity i, identities2)\n        {\n        }\n    }\n    catch (system_error&) { /* agent not running - failure ok */ }\n}\n\n/**\n * Agent move-assign behaviour.\n */\nBOOST_AUTO_TEST_CASE( agent_move_assign )\n{\n    session& s = test_session();\n\n    BOOST_CHECK(!s.authenticated());\n\n    try\n    {\n        agent_identities identities = s.agent_identities();\n        agent_identities identities2 = s.agent_identities();\n\n        identities2 = move(identities);\n\n        BOOST_FOREACH(identity i, identities2)\n        {\n        }\n    }\n    catch (system_error&) { /* agent not running - failure ok */ }\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/filesystem_construction_test.cpp",
    "content": "// Copyright 2010, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"session_fixture.hpp\"\n\n#include <ssh/filesystem.hpp> // test subject\n#include <ssh/stream.hpp>\n\n#include <boost/move/move.hpp>\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <memory> // auto_ptr\n\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::sftp_filesystem;\nusing ssh::session;\n\nusing boost::move;\nusing boost::system::system_error;\n\nusing test::ssh::session_fixture;\n\nusing std::auto_ptr;\n\nBOOST_AUTO_TEST_SUITE(filesystem_construction_test)\n\nBOOST_FIXTURE_TEST_CASE(construct_fail, session_fixture)\n{\n    session& s = test_session();\n\n    // Session not authenticated so SFTP not possible\n    BOOST_CHECK_THROW(s.connect_to_filesystem(), system_error);\n}\n\n// This tests the very basic requirements of any sensible relationship between\n// a filesystem and a session.  It must be possible to create a filesystem\n// before moving the session.  That's it.\n//\n// In particular, we destroy the filesystem before moving the object because\n// we don't want to test an added requirement that the filesystem's lifetime\n// can extend beyond the session's move.  Whatever else we might decide the\n// semantics of the session-filesystem relationship should be now or in the\n// future, this tests must pass.  Anything else would mean moving depends on\n// what you've used the session for in the past, which would just be broken.\n//\n// In other words, even the most careful caller would run into trouble\n// if this test failed.\nBOOST_FIXTURE_TEST_CASE(move_session_after_connecting_filesystem,\n                        session_fixture)\n{\n    session& s = test_session();\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n\n    {\n        sftp_filesystem(s.connect_to_filesystem());\n    }\n\n    session(move(s));\n}\n\n// This builds slightly on the previous test by checking that the filesystem\n// can be destroyed _after_ the session is moved.  It still isn't a test that\n// the filesystem is usable afterwards (though we probably want that\n// property too), just that the object is valid (can be destroyed).\n//\n// In an earlier version, the filesystem destructor tried to use the moved\n// session causing a crash.  It's very hard sometimes to ensure the filesystem\n// is destroyed before the exact (non-moved) session it came from, so its\n// important we allow destruction to happen after moving the session (but\n// before moved-to session is destroyed).\nBOOST_FIXTURE_TEST_CASE(move_session_with_live_filesystem_connection,\n                        session_fixture)\n{\n    session& s = test_session();\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n\n    sftp_filesystem fs = s.connect_to_filesystem();\n    session s2(move(s));\n\n    // The rules are that the last session must outlive the last FS so moving\n    // FS to inner scope ensures this\n    sftp_filesystem(move(fs));\n}\n\n// This is the third part of the session-movement tests. It strengthens the\n// requirements a bit more to ensure the filesystem is not just valid for\n// destruction but also still functions as a filesystem connection.\nBOOST_FIXTURE_TEST_CASE(moving_session_leaves_working_filesystem,\n                        session_fixture)\n{\n    session& s = test_session();\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n\n    sftp_filesystem fs = s.connect_to_filesystem();\n    session s2(move(s));\n\n    // The rules are that the last session must outlive the last FS so moving\n    // FS to inner scope ensures this\n    sftp_filesystem fs2(move(fs));\n    ofstream(fs2, \"/tmp/bob.txt\").close();\n\n    BOOST_CHECK(exists(fs2, \"/tmp/bob.txt\"));\n}\n\nBOOST_FIXTURE_TEST_CASE(swap_session_with_live_filesystem_connection,\n                        session_fixture)\n{\n    // Both sockets must outlive both session objects\n    auto_ptr<boost::asio::ip::tcp::socket> socket1(connect_additional_socket());\n    auto_ptr<boost::asio::ip::tcp::socket> socket2(connect_additional_socket());\n\n    session s(socket1->native());\n    session t(socket2->native());\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n    t.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n\n    sftp_filesystem fs = s.connect_to_filesystem();\n\n    boost::swap(t, s);\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/filesystem_test.cpp",
    "content": "// Copyright 2010, 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"session_fixture.hpp\"\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/filesystem.hpp> // test subject\n#include <ssh/stream.hpp>\n\n#include <boost/bind.hpp>    // bind\n#include <boost/cstdint.hpp> // uintmax_t\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/move/move.hpp>\n#include <boost/system/system_error.hpp>\n#include <boost/test/predicate_result.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/future.hpp>\n#include <boost/thread/thread.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\n#include <algorithm> // find, sort, transform\n#include <string>\n#include <utility>\n#include <vector>\n\nusing ssh::filesystem::directory_iterator;\nusing ssh::filesystem::file_attributes;\nusing ssh::filesystem::file_status;\nusing ssh::filesystem::file_type;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::overwrite_behaviour;\nusing ssh::filesystem::path;\nusing ssh::filesystem::perms;\nusing ssh::filesystem::sftp_file;\nusing ssh::filesystem::sftp_filesystem;\nusing ssh::session;\n\nusing boost::bind;\nusing boost::move;\nusing boost::packaged_task;\nusing boost::system::system_error;\nusing boost::test_tools::predicate_result;\nusing boost::thread;\nusing boost::uintmax_t;\nusing boost::uuids::random_generator;\n\nusing test::ssh::session_fixture;\nusing test::ssh::sftp_fixture;\n\nusing std::auto_ptr;\nusing std::find;\nusing std::make_pair;\nusing std::pair;\nusing std::string;\nusing std::vector;\n\nnamespace\n{\n\npredicate_result directory_is_empty(sftp_filesystem& fs,\n                                    const ::ssh::filesystem::path& p)\n{\n    predicate_result result(true);\n\n    directory_iterator it = fs.directory_iterator(p);\n\n    size_t entry_count = 0;\n    while (it != fs.directory_iterator())\n    {\n        if (it->path().filename() != \".\" && it->path().filename() != \"..\")\n        {\n            ++entry_count;\n        }\n\n        ++it;\n    }\n\n    if (entry_count != 0)\n    {\n        result = false;\n\n        result.message() << \"Directory is not empty; contains \" << entry_count\n                         << \" entries\";\n    }\n\n    return result;\n}\n}\n\nnamespace\n{\n\nclass filesystem_fixture : public sftp_fixture\n{\npublic:\n    // The following functions return the link and target path as a pair.  Both\n    // paths are relative to the sandbox, regardless of whether the symlink was\n    // created with a relative or absolute path\n\n    pair<path, path> create_relative_symlink_in_sandbox()\n    {\n        path link = sandbox() / \"link\";\n        path target = new_file_in_sandbox().filename();\n        create_symlink(link, target);\n        return make_pair(link.filename(), target.filename());\n    }\n\n    pair<path, path> create_absolute_symlink_in_sandbox()\n    {\n        path link = sandbox() / \"link\";\n        path target = absolute_sandbox() / new_file_in_sandbox().filename();\n        create_symlink(link, target);\n        return make_pair(link.filename(), target.filename());\n    }\n\n    pair<path, path> create_broken_symlink_in_sandbox()\n    {\n        path link = sandbox() / \"link\";\n        path target = \"i don't exist\";\n        create_symlink(link, target);\n        return make_pair(link.filename(), target.filename());\n    }\n};\n}\n\n// Tests assume an authenticated session and established SFTP filesystem\nBOOST_FIXTURE_TEST_SUITE(channel_running_tests, filesystem_fixture)\n\n/**\n * List an empty directory.\n *\n * Will contain . and ..\n */\nBOOST_AUTO_TEST_CASE(empty_dir)\n{\n    sftp_filesystem& c = filesystem();\n\n    BOOST_CHECK(directory_is_empty(c, sandbox()));\n}\n\n/**\n * List a directory that doesn't exist.  Must throw.\n */\nBOOST_AUTO_TEST_CASE(missing_dir)\n{\n    sftp_filesystem& c = filesystem();\n    BOOST_CHECK_THROW(c.directory_iterator(\"/i/dont/exist\"), system_error);\n}\n\nBOOST_AUTO_TEST_CASE(swap_filesystems)\n{\n    sftp_filesystem& fs1 = filesystem();\n    sftp_filesystem fs2 = test_session().connect_to_filesystem();\n\n    boost::swap(fs1, fs2);\n\n    BOOST_CHECK(directory_is_empty(fs1, sandbox()));\n    BOOST_CHECK(directory_is_empty(fs2, sandbox()));\n}\n\nBOOST_AUTO_TEST_CASE(move_construct)\n{\n    sftp_filesystem& c = filesystem();\n    sftp_filesystem d(move(c));\n\n    BOOST_CHECK(directory_is_empty(d, sandbox()));\n}\n\nBOOST_AUTO_TEST_CASE(move_assign)\n{\n    sftp_filesystem& c = filesystem();\n    sftp_filesystem d(test_session().connect_to_filesystem());\n\n    d = move(c);\n\n    BOOST_CHECK(directory_is_empty(d, sandbox()));\n}\n\nnamespace\n{\nstring filename_getter(const sftp_file& directory_entry)\n{\n    return directory_entry.path().filename().native();\n}\n}\n\nBOOST_AUTO_TEST_CASE(dir_with_one_file)\n{\n    path test_file = new_file_in_sandbox();\n\n    vector<string> files;\n    transform(filesystem().directory_iterator(sandbox()),\n              filesystem().directory_iterator(), back_inserter(files),\n              filename_getter);\n    sort(files.begin(), files.end());\n\n    vector<string> expected;\n    expected.push_back(test_file.filename().native());\n    BOOST_CHECK_EQUAL_COLLECTIONS(files.begin(), files.end(), expected.begin(),\n                                  expected.end());\n}\n\nBOOST_AUTO_TEST_CASE(dir_with_multiple_files)\n{\n    path test_file1 = new_file_in_sandbox();\n    path test_file2 = new_file_in_sandbox();\n\n    vector<sftp_file> files(filesystem().directory_iterator(sandbox()),\n                            filesystem().directory_iterator());\n    sort(files.begin(), files.end());\n\n    vector<sftp_file>::const_iterator it = files.begin();\n\n    BOOST_CHECK(it->path().filename() ==\n                    path(test_file1.filename().wstring()) ||\n                it->path().filename() == path(test_file2.filename().wstring()));\n    it++;\n\n    BOOST_CHECK(it->path().filename() ==\n                    path(test_file1.filename().wstring()) ||\n                it->path().filename() == path(test_file2.filename().wstring()));\n\n    it++;\n}\n\nBOOST_AUTO_TEST_CASE(move_construct_iterator)\n{\n    path test_file1 = new_file_in_sandbox();\n    path test_file2 = new_file_in_sandbox();\n\n    directory_iterator it = filesystem().directory_iterator(sandbox());\n\n    string path_before_move = it->path();\n\n    directory_iterator itm(move(it));\n\n    BOOST_CHECK_EQUAL(itm->path(), path_before_move);\n\n    itm++;\n\n    BOOST_CHECK_NE(itm->path(), path_before_move);\n\n    itm++;\n\n    BOOST_CHECK(itm == filesystem().directory_iterator());\n}\n\nBOOST_AUTO_TEST_CASE(can_create_relative_symlink)\n{\n    path link = create_relative_symlink_in_sandbox().first;\n    BOOST_CHECK(exists(filesystem(), sandbox() / link));\n    BOOST_CHECK_EQUAL(find_file_in_sandbox(link).attributes().type(),\n                      file_attributes::symbolic_link);\n}\n\nBOOST_AUTO_TEST_CASE(can_create_absolute_symlink)\n{\n    path link = create_relative_symlink_in_sandbox().first;\n    BOOST_CHECK(exists(filesystem(), sandbox() / link));\n    BOOST_CHECK_EQUAL(find_file_in_sandbox(link).attributes().type(),\n                      file_attributes::symbolic_link);\n}\n\nBOOST_AUTO_TEST_CASE(can_create_broken_symlink)\n{\n    path link = create_broken_symlink_in_sandbox().first;\n    BOOST_CHECK(exists(filesystem(), sandbox() / link));\n    BOOST_CHECK_EQUAL(find_file_in_sandbox(link).attributes().type(),\n                      file_attributes::symbolic_link);\n}\n\nBOOST_AUTO_TEST_CASE(relative_symlinks_are_resolved_to_their_relative_target)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n\n    path resolved_target =\n        filesystem().resolve_link_target(sandbox() / ends.first);\n    BOOST_CHECK_EQUAL(resolved_target, ends.second);\n}\n\nBOOST_AUTO_TEST_CASE(absolute_symlinks_are_resolved_to_their_absolute_target)\n{\n    pair<path, path> ends = create_absolute_symlink_in_sandbox();\n\n    path resolved_target =\n        filesystem().resolve_link_target(sandbox() / ends.first);\n    BOOST_CHECK_EQUAL(resolved_target, absolute_sandbox() / ends.second);\n}\n\nBOOST_AUTO_TEST_CASE(broken_symlinks_are_resolved_to_their_non_existent_target)\n{\n    pair<path, path> ends = create_broken_symlink_in_sandbox();\n\n    path resolved_target =\n        filesystem().resolve_link_target(sandbox() / ends.first);\n    BOOST_CHECK_EQUAL(resolved_target, ends.second);\n}\n\n/**\n * Resolve a symlink to a symlink.  The result should be the path of the\n * middle symlink, rather than the middle symlink's target.\n */\nBOOST_AUTO_TEST_CASE(resolving_symlink_to_symlink_returns_middle_link)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n\n    path target = ends.second;\n    path middle_link = ends.first;\n    path link_to_link = sandbox() / \"link2\";\n    create_symlink(link_to_link, middle_link);\n\n    path resolved_target = filesystem().resolve_link_target(link_to_link);\n\n    BOOST_CHECK_EQUAL(resolved_target, middle_link);\n}\n\nBOOST_AUTO_TEST_CASE(canonicalising_relative_symlink_returns_absolute_path)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n\n    path canonical_target = filesystem().canonical_path(sandbox() / ends.first);\n    BOOST_CHECK_EQUAL(canonical_target, absolute_sandbox() / ends.second);\n}\n\nBOOST_AUTO_TEST_CASE(canonicalising_absolute_symlink_returns_absolute_path)\n{\n    pair<path, path> ends = create_absolute_symlink_in_sandbox();\n\n    path canonical_target = filesystem().canonical_path(sandbox() / ends.first);\n    BOOST_CHECK_EQUAL(canonical_target, absolute_sandbox() / ends.second);\n}\n\nBOOST_AUTO_TEST_CASE(\n    canonicalising_symlink_to_symlink_return_absolute_path_of_final_target)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n\n    path target = ends.second;\n    path middle_link = ends.first;\n    path link_to_link = sandbox() / \"link2\";\n    create_symlink(link_to_link, middle_link);\n\n    path canonical_target = filesystem().canonical_path(link_to_link);\n\n    BOOST_CHECK_EQUAL(canonical_target, absolute_sandbox() / target);\n}\n\nBOOST_AUTO_TEST_CASE(attributes_file)\n{\n    path subject = new_file_in_sandbox();\n\n    file_attributes attrs = filesystem().attributes(subject, false);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::normal_file);\n\n    attrs = filesystem().attributes(subject, true);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::normal_file);\n}\n\nBOOST_AUTO_TEST_CASE(attributes_directory)\n{\n    path subject = sandbox() / \"testdir\";\n    create_directory(filesystem(), subject);\n\n    file_attributes attrs = filesystem().attributes(subject, false);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::directory);\n\n    attrs = filesystem().attributes(subject, true);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::directory);\n}\n\nBOOST_AUTO_TEST_CASE(attributes_link)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n    path link = sandbox() / ends.first;\n\n    file_attributes attrs = filesystem().attributes(link, false);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::symbolic_link);\n\n    attrs = filesystem().attributes(link, true);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::normal_file);\n}\n\nBOOST_AUTO_TEST_CASE(attributes_double_link)\n{\n    pair<path, path> ends = create_relative_symlink_in_sandbox();\n\n    path middle_link = ends.first;\n    path link_to_link = sandbox() / \"link2\";\n    create_symlink(link_to_link, middle_link);\n\n    file_attributes attrs = filesystem().attributes(link_to_link, true);\n\n    BOOST_CHECK_EQUAL(attrs.type(), file_attributes::normal_file);\n}\n\nBOOST_AUTO_TEST_CASE(attributes_broken_link)\n{\n    pair<path, path> ends = create_broken_symlink_in_sandbox();\n\n    BOOST_CHECK_THROW(filesystem().attributes(ends.first, true), system_error);\n}\n\nBOOST_AUTO_TEST_CASE(default_directory)\n{\n    path resolved_target = filesystem().canonical_path(\"\");\n    BOOST_CHECK_EQUAL(resolved_target, \"/home/swish\");\n}\n\nBOOST_AUTO_TEST_CASE(remove_nothing)\n{\n    path target = \"gibberish\";\n\n    bool already_existed = remove(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK(!already_existed);\n}\n\nBOOST_AUTO_TEST_CASE(remove_file)\n{\n    path target = new_file_in_sandbox();\n\n    bool already_existed = remove(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK(already_existed);\n}\n\nBOOST_AUTO_TEST_CASE(remove_empty_dir)\n{\n    path target = new_directory_in_sandbox();\n\n    bool already_existed = remove(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK(already_existed);\n}\n\nBOOST_AUTO_TEST_CASE(remove_non_empty_dir)\n{\n    path target = new_directory_in_sandbox();\n    create_directory(filesystem(), target / \"bob\");\n\n    BOOST_CHECK_THROW(remove(filesystem(), target), system_error);\n\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(remove_link)\n{\n    path target = new_file_in_sandbox();\n    path link = sandbox() / \"link\";\n    create_symlink(link, target);\n\n    bool already_existed = remove(filesystem(), link);\n\n    BOOST_CHECK(!exists(filesystem(), link));\n    BOOST_CHECK(exists(filesystem(), target)); // should only delete the link\n    BOOST_CHECK(already_existed);\n}\n\nBOOST_AUTO_TEST_CASE(remove_nothing_recursive)\n{\n    path target = \"gibberish\";\n\n    uintmax_t count = remove_all(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK_EQUAL(count, 0U);\n}\n\nBOOST_AUTO_TEST_CASE(remove_file_recursive)\n{\n    path target = new_file_in_sandbox();\n\n    uintmax_t count = remove_all(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK_EQUAL(count, 1U);\n}\n\nBOOST_AUTO_TEST_CASE(remove_empty_dir_recursive)\n{\n    path target = new_directory_in_sandbox();\n\n    uintmax_t count = remove_all(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK_EQUAL(count, 1U);\n}\n\nBOOST_AUTO_TEST_CASE(remove_non_empty_dir_recursive)\n{\n    path target = new_directory_in_sandbox();\n    create_directory(filesystem(), target / \"bob\");\n    ofstream(filesystem(), target / \"bob\" / \"sally\");\n    ofstream(filesystem(),\n             target / \"alice\"); // Either side of bob alphabetically\n    ofstream(filesystem(), target / \"jim\");\n\n    uintmax_t count = remove_all(filesystem(), target);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n    BOOST_CHECK_EQUAL(count, 5U);\n}\n\nBOOST_AUTO_TEST_CASE(remove_link_recursive)\n{\n    path target = new_directory_in_sandbox();\n    create_directory(filesystem(), target / \"bob\");\n    path link = sandbox() / \"link\";\n    create_symlink(link, target);\n\n    uintmax_t count = remove_all(filesystem(), link);\n\n    BOOST_CHECK(!exists(filesystem(), link));\n    BOOST_CHECK(exists(filesystem(), target)); // should only delete the link\n    BOOST_CHECK(\n        exists(filesystem(), target / \"bob\")); // should only delete the link\n    BOOST_CHECK_EQUAL(count, 1U);\n}\n\nBOOST_AUTO_TEST_CASE(rename_file)\n{\n    path test_file = new_file_in_sandbox();\n    path target = sandbox() / \"target\";\n\n    rename(filesystem(), test_file, target,\n           overwrite_behaviour::prevent_overwrite);\n    BOOST_CHECK(!exists(filesystem(), test_file));\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(rename_file_obstacle_no_overwrite)\n{\n    path test_file = new_file_in_sandbox();\n\n    path target = new_file_in_sandbox(\"target\");\n\n    BOOST_CHECK_THROW(rename(filesystem(), test_file, target,\n                             overwrite_behaviour::prevent_overwrite),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), test_file));\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(rename_file_obstacle_allow_overwrite)\n{\n    path test_file = new_file_in_sandbox();\n\n    path target = new_file_in_sandbox(\"target\");\n\n    // Using OpenSSH server which only supports SFTP 3 (no overwrite) so\n    // failure expected\n    BOOST_CHECK_THROW(rename(filesystem(), test_file, target,\n                             overwrite_behaviour::allow_overwrite),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), test_file));\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(rename_file_obstacle_atomic_overwrite)\n{\n    path test_file = new_file_in_sandbox();\n\n    path target = new_file_in_sandbox(\"target\");\n\n    // Using OpenSSH server which only supports SFTP 3 (no overwrite) so\n    // failure expected\n    BOOST_CHECK_THROW(rename(filesystem(), test_file, target,\n                             overwrite_behaviour::atomic_overwrite),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), test_file));\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(exists_true)\n{\n    path test_file = new_file_in_sandbox();\n\n    BOOST_CHECK(exists(filesystem(), test_file));\n}\n\nBOOST_AUTO_TEST_CASE(exists_false)\n{\n    path test_file = sandbox() / \"I do not exist\";\n    BOOST_CHECK(!exists(filesystem(), test_file));\n}\n\nBOOST_AUTO_TEST_CASE(is_directory_returns_true_for_directories)\n{\n    path target = new_directory_in_sandbox();\n\n    BOOST_CHECK(is_directory(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(is_directory_returns_false_for_files)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK(!is_directory(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(is_directory_returns_false_for_non_existent_path)\n{\n    BOOST_CHECK(!is_directory(filesystem(), \"i do not exist\"));\n}\n\nBOOST_AUTO_TEST_CASE(is_regular_file_returns_false_for_directories)\n{\n    path target = new_directory_in_sandbox();\n\n    BOOST_CHECK(!is_regular_file(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(is_regular_file_returns_false_for_files)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK(is_regular_file(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(is_regular_file_returns_false_for_non_existent_path)\n{\n    BOOST_CHECK(!is_regular_file(filesystem(), \"i do not exist\"));\n}\n\nBOOST_AUTO_TEST_CASE(new_directory)\n{\n    path target = new_directory_in_sandbox();\n    remove(filesystem(), target);\n\n    BOOST_CHECK(create_directory(filesystem(), target));\n    BOOST_CHECK(exists(filesystem(), target));\n    BOOST_CHECK(is_directory(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(new_directory_already_there)\n{\n    path target = new_directory_in_sandbox();\n\n    BOOST_CHECK(!create_directory(filesystem(), target));\n    BOOST_CHECK(exists(filesystem(), target));\n    BOOST_CHECK(is_directory(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(new_directory_already_there_wrong_type)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK_THROW(create_directory(filesystem(), target), system_error);\n    BOOST_CHECK(exists(filesystem(), target));\n    BOOST_CHECK(!is_directory(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(status_returns_correct_file_permissions)\n{\n    path target = new_file_in_sandbox();\n    perms permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(permissions, perms::owner_read | perms::owner_write |\n                                       perms::group_read | perms::others_read);\n}\n\nBOOST_AUTO_TEST_CASE(status_returns_correct_file_type)\n{\n    path target = new_file_in_sandbox();\n    file_type type = status(filesystem(), target).type();\n    BOOST_CHECK_EQUAL(type, file_type::regular);\n}\n\nBOOST_AUTO_TEST_CASE(status_returns_correct_directory_permissions)\n{\n    path target = new_directory_in_sandbox();\n    perms permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(permissions, perms::owner_all | perms::group_read |\n                                       perms::group_exec | perms::others_read |\n                                       perms::others_exec);\n}\n\nBOOST_AUTO_TEST_CASE(status_returns_correct_directory_type)\n{\n    path target = new_directory_in_sandbox();\n    file_type type = status(filesystem(), target).type();\n    BOOST_CHECK_EQUAL(type, file_type::directory);\n}\n\nBOOST_AUTO_TEST_CASE(status_does_not_throw_if_file_doesnt_exist)\n{\n    path target = \"i don't exist\";\n    file_status s = status(filesystem(), target);\n    BOOST_CHECK_EQUAL(s.permissions(), perms::unknown);\n    BOOST_CHECK_EQUAL(s.type(), file_type::not_found);\n}\n\nBOOST_AUTO_TEST_CASE(can_set_file_permissions_exactly)\n{\n    path target = new_file_in_sandbox();\n    permissions(filesystem(), target, perms::group_write);\n    perms new_permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(new_permissions, perms::group_write);\n}\n\nBOOST_AUTO_TEST_CASE(can_set_file_permissions_to_none)\n{\n    path target = new_file_in_sandbox();\n    permissions(filesystem(), target, perms::none);\n    perms new_permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(new_permissions, perms::none);\n}\n\nBOOST_AUTO_TEST_CASE(can_add_file_permissions)\n{\n    path target = new_file_in_sandbox();\n    permissions(filesystem(), target, perms::add_perms | perms::group_write);\n    perms new_permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(new_permissions, perms::group_write | perms::owner_read |\n                                           perms::owner_write |\n                                           perms::group_read |\n                                           perms::others_read);\n}\n\nBOOST_AUTO_TEST_CASE(can_remove_file_permissions)\n{\n    path target = new_file_in_sandbox();\n    permissions(filesystem(), target, perms::remove_perms | perms::group_read);\n    perms new_permissions = status(filesystem(), target).permissions();\n    BOOST_CHECK_EQUAL(new_permissions, perms::owner_read | perms::owner_write |\n                                           perms::others_read);\n}\n\nBOOST_AUTO_TEST_CASE(file_size_is_returned_with_sensible_value)\n{\n    string data = \"mary had a little lamb\";\n    path target = new_file_in_sandbox_containing_data(data);\n    BOOST_CHECK_EQUAL(file_size(filesystem(), target), data.size());\n}\n\nBOOST_AUTO_TEST_CASE(file_size_of_non_file_throws_error)\n{\n    BOOST_CHECK_THROW(file_size(filesystem(), \"/dev/console\"), system_error);\n}\n\nBOOST_AUTO_TEST_CASE(last_write_time_returns_sensible_timestamp)\n{\n    path target = new_file_in_sandbox();\n    time_t write_time = last_write_time(filesystem(), target);\n    time_t now = time(0);\n    BOOST_CHECK_LE(write_time, now);\n    BOOST_CHECK_GT(write_time, now - 5);\n}\n\nBOOST_AUTO_TEST_CASE(last_write_time_of_non_file_throws_error)\n{\n    BOOST_CHECK_THROW(last_write_time(filesystem(), \"/dev/console\"),\n                      system_error);\n}\n\nBOOST_AUTO_TEST_CASE(empty_file_is_empty)\n{\n    path target = new_file_in_sandbox();\n    BOOST_CHECK(is_empty(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(non_empty_file_is_not_empty)\n{\n    string data = \"mary had a little lamb\";\n    path target = new_file_in_sandbox_containing_data(data);\n    BOOST_CHECK(!is_empty(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(empty_directory_is_empty)\n{\n    path target = new_directory_in_sandbox();\n    BOOST_CHECK(is_empty(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(non_empty_directory_is_not_empty)\n{\n    new_file_in_sandbox();\n    BOOST_CHECK(!is_empty(filesystem(), sandbox()));\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/fixture_dsakey",
    "content": "-----BEGIN DSA PRIVATE KEY-----\nMIIBvAIBAAKBgQCtiYdgpPvFtfi7Ba44DiB+1x8kojjT0nRvn2hU2aa4p4fXI8kd\n6Hc57VQO/lLhR9eFpxjP7m+jGwF468Q6NU8xiC71ucep0OoXS7u8RcoIoWfLDtZi\nDDlahnZTW04mB5fFxo2y7dYl31vE4TPdSxhqpkvnIBIstMFh2M7Dl0w8/QIVAP95\nu6dg1OW6gGsRgiircsy1A9tzAoGBAIzwc5FCnJnzAJm9Hjv0AFV5l/i/DQulZ9pu\nEILkNiHCfDR+lTJ8VxAR7J3pgjmvYzeeRvi519ez1YriktDt66kIknQOcHB8ghyg\nU+dff79SkDcpg8LnX5xb3cVMgABujA0sSpaW1wwm64RXdvmoQvWu6ympUT0l0dEd\noYVkb4ytAoGAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6\nFzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbU\nfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7GooCFQCyaA6T\ntkJunHP+F0Xg/WAUV6tcqA==\n-----END DSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssh/fixture_dsakey.pub",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty\n"
  },
  {
    "path": "test/ssh/fixture_hostkey",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6\ncvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99\nfnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB\neNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMA\nwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQK\nypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbwIBIwKCAQAd9Cu9heWrs+UAinvv\nIwmq/EhnDGQijJoOt1zEMrpXSekyq7mQDgN0SZdJLPeSlSRQ5nVq5/dZroYB3A5i\nE7N3F7nibcJskWq5rcMyGjQHwod8wqfMiGcL6mjeZu2jLXprm0NDpJ3DyicbCA2G\nEhnpoHmktIBE5FsslI/nHer2o6OA/kVWSEjak+pvI1pm22T8QOBBfY0yAX7B0ebk\n8o4lB4cdLf3In7Q0ahpHNOwIPdRvQ2c4Tm/DcfUBkTW2ZYGUd45cFsyHqXZscNNy\nGX2Wcy/FLEvQ6zBFJsNLpxCYsUyBxfSDygn9dx9RQfiWFXjdRaRPpyRAr+BTXkLU\nyvabAoGBANt7sxfjvu/SLkRc7TnBoJ0h/AL7Mcuu9PJmOnis4boyF9ZxqbiRiS3J\nyK+EKxfC0S+xf5WJ5uf7dVGnOXHXKaRl4xH90iRtryNlvtILZwHw1DTqRFxv9jtz\ntTRrYMEHAnMKzadgDfV/lv4iJ6nwFzK76GQ7RQNZYiGTMEh3pUNjAoGBAMvNLGpz\nFxhpIh+fVvRjawKgGVP87T482WOUdsF18EEPFMe6D7DO5xpLuJi+C7QkvMI8WjvD\n/3RGvaSh9Wt7ikLZpeogiSJy121HsEqheTR5hTx2t72ClrjZvIhLbQMRu6PqGPu/\nHOC2urEGGYm7O2vnftwpuG3zIVVLM2KstPCFAoGBAM7w+VEJ7opYdMQdGi8kRvqN\nwbmrAxCAY0ryrCijALbexgS0T5DDu9q28Gr49W4stpquq35dc0/BNBnJje7+EVHc\naGFrqOCErHHU9b66Sy23LnsIxBykFAwrRHNAq66u1mx35nk9Tv1pq58nhHun21u4\nfAa7ijZblwm2qd3tJsqBAoGAEXf8ficfPJtMEVbM8GBLADmbxV7Sga1xuBQKLdbo\ntR6MwKmMUPvKqnuE2eRnZzZZUnoznrkHRHsXkcS9Q7ohyzc6G2Hf3mGdb8RQ8HQ9\nlsiWZESwqdf+SlvOVNND27EQFV01V2gnC/JnxgfWTaJVjOf07ky4CWycdQZyHmaT\nKo8CgYB58jOyXMdo2ggOCG/HX2H92KPPpFUBFCX27fCue8BZLD5quIltpXupx5oj\nEyltgvPcmNDgvdSadkHvP5s6nykS+n5we+d9yIIJF/BfETWsXjR3ooip+trqiirw\n0aHqUDFcYn9unm2wtrMYYViiDLRijNwLZ2sG0JIU4JHyseh+NA==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssh/fixture_hostkey.pub",
    "content": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrBeNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMAwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbw== awl03@bounty\n"
  },
  {
    "path": "test/ssh/fixture_rsakey",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJ\nevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBp\nvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5\nRqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNX\nb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYIL\njzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3QIBIwKCAQB1Hpyhoi2LXCIVfXPM\nAU6AtWvRY12PtdSl8uqr+nX2JATNBZlUCTaUE6qQJNxEZyDeMNvzZdxV5gkzQ2Fi\nTpQIyRddbH01fJKoTxzlHzbLfAeCj9mFqicahOkHAMN8K6Ddqxe89zhD60w0SgjW\n91tLzZQ2sxE70RxBdPQOpbaZLxmUZSVxRgf5djotyZqB4CcGblKCEZYJ9ZemgCnF\ngEcSsqcn0Jxfu+aEJ4WinN2orWs+okfgsUu9G9Ozwcy9Ptq1LkIzcwwTIpL7TTDd\nLMvhql39a07SysepjFRHxjvXh8Gv+SsLvKQPJHheVv8XoG0dZd+9/Eden9rHKoVm\nvGPLAoGBANGDQtv5K/md/3sRGeJ6Ir3/Ao+WMe8C5onck+hW4y/2yQqm3ZLzyZon\nKdWRj2q4dnxFZyoyDgX0UHLpM4aSsMRjn4C6vcPLcYaZ9CGB5FWPGZrq+q6vuMGK\nV9/fo4ZNFkNK3wo4WCSgxC1Y8XUJc3klOvPVjsmVxZaeZnkukkAFAoGBAMCkqe/S\nhrKITzjZuyGN90a2Nq+3xMNGuc400Qvoi27D1OcSn7SJ/K3tVWbENOH3CAlkmlZT\n46IM2SRRmM0bxF3aThEwnsD5yPqgz+tcweX+gK3nXnP5JZfYF1kArXk80/eYhNE9\nPwnJNXDQMoxaM0/X6BVgQyt03/Q12lH9u0j5AoGAR9U7fp6Su/uoDO/rnhs/HJHy\nP9u5WULSsuyKe4uBF8JTjp+cbOXeuIJ0vkCI8WPQ2iZsg37gPI5Hd9rtGDJLPATm\nOsOuxslowG9MY0J6K/aMb6EFfbiXHckIL3/gS02hO6SkPgSwgZY0odVaGX+VThtk\nq18ppDNZr/vLXL+CmZsCgYEAlJxIlG80tZxaXw5dKIN1nPL2/JUUIZz1vFShQ7Nk\nP4EglP+9B52lqr5mc9kwHAe1vhpobns6kvP393IlawbKrsz6ZQg/8/PkLw5XQIli\nYPeH1pyKsTyKtvcn9DO5BcE1zaGLB9ApULEpOcUuTwPBLvcDfjRREuUhywT44Coi\nw0MCgYAX5yc7/Z3R6M30rGsrgb1Y2siHYsi2LCygUj7TDGQYpaZN4afPJOT5H/Nr\n7x7dgZkbOR6PQFm00VgML0XxKih59t0dcQ+2qk1LX5JDKRF/1kER3np6dpceteDu\ncC+MEHB/KvijnviAtBZGvD0O7oZgvbkKHESu2igXpAnfXPZFvw==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssh/fixture_rsakey.pub",
    "content": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty\n"
  },
  {
    "path": "test/ssh/fixture_wrong_dsakey",
    "content": "-----BEGIN DSA PRIVATE KEY-----\nMIIBuwIBAAKBgQCE1v/lL1VvjlJMyG7q0wAgl2tqVMzy5h1RVOtDS8bTlXLJg7ks\nT63wTmXlp2HedgKkfHCu7AKsjPyg1CTrvRBa8BFEvMoUDARonMwql34aiKVMy/t0\n/ehnmCQV+ZMFpsVFnphJpZuXLTW1F3pnEbSNud5sACjbWb51uly5AUynuwIVAOhj\nrbNOaAtC1oYki8CVwpkQ8rHhAoGAYSepXRF3GJSjseYgJ2bCgcJS0L9agcvKAf+F\ndc+ZDJOchhnZC/hGHsjAfg62KowwKuOYsbcR3S4LJxiERcmRabww+kUIL1E8bLaQ\nRbOygNsHU8LyBdSx3WqC2WEOpVkTAjYDWTkbN+qkb53IBoI0GwFt5P9GHvQcAGkj\nGJQAWWYCgYAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMi\na0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/\nTq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pAIVAKV45RLf\nhg2GNXvO68Q4tt3F6kSP\n-----END DSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssh/fixture_wrong_dsakey.pub",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAITW/+UvVW+OUkzIburTACCXa2pUzPLmHVFU60NLxtOVcsmDuSxPrfBOZeWnYd52AqR8cK7sAqyM/KDUJOu9EFrwEUS8yhQMBGiczCqXfhqIpUzL+3T96GeYJBX5kwWmxUWemEmlm5ctNbUXemcRtI253mwAKNtZvnW6XLkBTKe7AAAAFQDoY62zTmgLQtaGJIvAlcKZEPKx4QAAAIBhJ6ldEXcYlKOx5iAnZsKBwlLQv1qBy8oB/4V1z5kMk5yGGdkL+EYeyMB+DrYqjDAq45ixtxHdLgsnGIRFyZFpvDD6RQgvUTxstpBFs7KA2wdTwvIF1LHdaoLZYQ6lWRMCNgNZORs36qRvncgGgjQbAW3k/0Ye9BwAaSMYlABZZgAAAIAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMia0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/Tq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pA== awl03@bounty\n"
  },
  {
    "path": "test/ssh/host_key_test.cpp",
    "content": "/**\n    @file\n\n    Tests for host_key object.\n\n    @if license\n\n    Copyright (C) 2010, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"session_fixture.hpp\" // session_fixture\n\n#include <ssh/detail/session_state.hpp>\n#include <ssh/host_key.hpp> // test subject\n#include <ssh/session.hpp> // session\n\n#include <boost/make_shared.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/system/error_code.hpp>\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n\n#include <libssh2.h>\n\nusing ssh::detail::session_state;\nusing ssh::host_key;\nusing ssh::session;\n\nusing test::ssh::session_fixture;\n\nusing boost::make_shared;\nusing boost::shared_ptr;\nusing boost::system::error_code;\nusing boost::system::system_error;\n\nusing std::string;\n\nconst string EXPECTED_HOSTKEY =\n    \"AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKU\"\n    \"UoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7B\"\n    \"Am99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB\"\n    \"eNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMAwQU9\"\n    \"i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQ\"\n    \"gkIIbCU1A31+4ExvcIVoxLQw/aTSbw==\";\n\nBOOST_FIXTURE_TEST_SUITE(host_key_tests, session_fixture)\n\nnamespace {\n\n    string base64_decode(const string& input)\n    {\n        shared_ptr<session_state> session = make_shared<session_state>();\n\n        char* data;\n        unsigned int data_len;\n        int rc = libssh2_base64_decode(\n            session->session_ptr(), &data, &data_len, input.data(),\n            input.size());\n        if (rc)\n        {\n            string message;\n            error_code ec = ssh::detail::last_error_code(\n                session->session_ptr(), message);\n\n            BOOST_THROW_EXCEPTION(system_error(ec, message));\n        }\n\n        string out(data, data_len);\n        free(data);\n        return out;\n    }\n}\n\n/**\n * Server hostkey corresponds to test key when connected.\n */\nBOOST_AUTO_TEST_CASE( hostkey )\n{\n    session& s = test_session();\n    host_key key = s.hostkey();\n\n    string expected = base64_decode(EXPECTED_HOSTKEY);\n    BOOST_CHECK_EQUAL(key.key(), expected);\n    BOOST_CHECK_EQUAL(key.algorithm(), ssh::hostkey_type::ssh_rsa);\n    BOOST_CHECK_EQUAL(key.algorithm_name(), \"ssh-rsa\");\n    BOOST_CHECK(!key.is_base64());\n}\n\n/**\n * Hashed (MD5) hostkey should print as:\n *    0C 0E D1 A5 BB 10 27 5F 76 92 4C E1 87 CE 5C 5E\n * in hex.\n */\nBOOST_AUTO_TEST_CASE( hostkey_md5 )\n{\n    host_key key = test_session().hostkey();\n\n    string hex_hash(ssh::hexify(key.md5_hash(), \" \", true));\n\n    BOOST_CHECK_EQUAL(\n        hex_hash, \"0C 0E D1 A5 BB 10 27 5F 76 92 4C E1 87 CE 5C 5E\");\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/input_stream_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/stream.hpp> // test subject\n\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\n#include <string>\n#include <vector>\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::openmode;\nusing ssh::filesystem::path;\nusing ssh::filesystem::perms;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::uuids::random_generator;\nusing boost::system::system_error;\n\nusing test::ssh::sftp_fixture;\n\nusing std::runtime_error;\nusing std::string;\nusing std::vector;\n\nnamespace\n{\n\n// the large data must fill more than one stream buffer (currently set to\n// 32768 (see DEFAULT_BUFFER_SIZE)\n\nstring large_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('m');\n        data.push_back('z');\n    }\n\n    return data;\n}\n\nstring large_binary_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('\\n');\n        data.push_back('\\0');\n        data.push_back('\\r');\n        data.push_back('\\n');\n        data.push_back(-1);\n    }\n\n    return data;\n}\n\nvoid make_file_read_only(sftp_filesystem& filesystem, const path& target)\n{\n    permissions(filesystem, target, perms::owner_read);\n}\n\nconst wchar_t WIDE_STRING1[] = L\"\\x92e\\x939\\x938\\x941\\x938\";\n}\n\nBOOST_FIXTURE_TEST_SUITE(ifstream_tests, sftp_fixture)\n\nBOOST_AUTO_TEST_CASE(input_stream_multiple_streams)\n{\n    path target1 = new_file_in_sandbox();\n    path target2 = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream s1(chan, target1);\n    ifstream s2(chan, target2);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_multiple_streams_to_same_file)\n{\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream s1(chan, target);\n    ifstream s2(chan, target);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_readable)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n    BOOST_CHECK(!(s >> bob));\n    BOOST_CHECK(s.eof());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_unicode_readable)\n{\n    path target =\n        new_file_in_sandbox_containing_data(WIDE_STRING1, \"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n    BOOST_CHECK(!(s >> bob));\n    BOOST_CHECK(s.eof());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_readable_multiple_buffers)\n{\n    // large enough to span multiple buffers\n    string expected_data(large_data());\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream input_stream(chan, target);\n\n    string bob;\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\n// Test with Boost.IOStreams buffer disabled.\n// Should call directly to libssh2\nBOOST_AUTO_TEST_CASE(input_stream_readable_no_buffer)\n{\n    string expected_data(\"gobbeldy gook\");\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream input_stream(chan, target, openmode::in, 0);\n\n    string bob;\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_readable_binary_data)\n{\n    string expected_data(\"gobbledy gook\\0after-null\\x12\\11\", 26);\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream input_stream(chan, target);\n\n    string bob;\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_readable_binary_data_multiple_buffers)\n{\n    // large enough to span multiple buffers\n    string expected_data(large_binary_data());\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream input_stream(chan, target);\n\n    string bob;\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_readable_binary_data_stream_op)\n{\n    string expected_data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream input_stream(chan, target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n\n    BOOST_CHECK(input_stream >> bob);\n    const char* gn = \"gook\\0after-null\\x12\\x11\";\n    BOOST_CHECK_EQUAL_COLLECTIONS(bob.begin(), bob.end(), gn, gn + 17);\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_does_not_create_by_default)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_REQUIRE(!exists(filesystem(), target));\n    BOOST_CHECK_THROW(ifstream(filesystem(), target), system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\n/* FIXME: find why this is failing in libssh2\nBOOST_AUTO_TEST_CASE(\n    input_stream_does_not_create_with_ridiculously_large_filename )\n{\n    // We intentionally pass a large amount of data as the filename.\n    // When we did this accidentally, we found it was not getting an error code\nbut hit an assertion because opening the file failed.\n    path target = large_data();\n    BOOST_REQUIRE(!exists(filesystem(), target));\n    BOOST_CHECK_THROW(\n        ifstream(filesystem(), target), system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n*/\n\nBOOST_AUTO_TEST_CASE(input_stream_opens_read_only_by_default)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    ifstream(filesystem(), target);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_in_flag_does_not_create)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, openmode::in),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_std_in_flag_does_not_create)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, std::ios_base::in),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_in_flag_opens_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    ifstream(filesystem(), target, openmode::in);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_out_flag_does_not_create)\n{\n    // Because ifstream forces in as well as out an in suppresses creation\n\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, openmode::out),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_out_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, openmode::out),\n                      system_error);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_out_trunc_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ifstream input_stream(filesystem(), target,\n                          openmode::out | openmode::trunc);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_std_out_trunc_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ifstream input_stream(filesystem(), target,\n                          std::ios_base::out | std::ios_base::trunc);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_out_trunc_nocreate_flag_fails)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, openmode::out |\n                                                         openmode::trunc |\n                                                         openmode::nocreate),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_out_trunc_noreplace_flag_fails)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK_THROW(ifstream(filesystem(), target, openmode::out |\n                                                         openmode::trunc |\n                                                         openmode::noreplace),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_seek_input_absolute)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n    s.seekg(1, std::ios_base::beg);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"obbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_seek_input_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n    s.seekg(1, std::ios_base::cur);\n    s.seekg(1, std::ios_base::cur);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"bbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_seek_input_end)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n    s.seekg(-3, std::ios_base::end);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"ook\");\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_seek_input_too_far_absolute)\n{\n    path target = new_file_in_sandbox();\n\n    ifstream s(filesystem(), target);\n    s.exceptions(std::ios_base::badbit | std::ios_base::eofbit |\n                 std::ios_base::failbit);\n    s.seekg(1, std::ios_base::beg);\n\n    string bob;\n    BOOST_CHECK_THROW(s >> bob, runtime_error);\n}\n\nBOOST_AUTO_TEST_CASE(input_stream_seek_input_too_far_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ifstream s(filesystem(), target);\n    s.exceptions(std::ios_base::badbit | std::ios_base::eofbit |\n                 std::ios_base::failbit);\n    s.seekg(9, std::ios_base::cur);\n    s.seekg(4, std::ios_base::cur);\n\n    string bob;\n    BOOST_CHECK_THROW(s >> bob, runtime_error);\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/io_stream_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/stream.hpp> // test subject\n\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n#include <vector>\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::fstream;\nusing ssh::filesystem::openmode;\nusing ssh::filesystem::path;\nusing ssh::filesystem::perms;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::system::system_error;\n\nusing test::ssh::sftp_fixture;\n\nusing std::runtime_error;\nusing std::string;\nusing std::vector;\n\nnamespace\n{\n\n// the large data must fill more than one stream buffer (currently set to\n// 32768 (see DEFAULT_BUFFER_SIZE)\n\nstring large_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('m');\n        data.push_back('z');\n    }\n\n    return data;\n}\n\nvoid make_file_read_only(sftp_filesystem& filesystem, const path& target)\n{\n    permissions(filesystem, target, perms::owner_read);\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(io_stream_test, sftp_fixture)\n\nBOOST_AUTO_TEST_CASE(io_stream_multiple_streams)\n{\n    path target1 = new_file_in_sandbox();\n    path target2 = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream s1(chan, target1);\n    fstream s2(chan, target2);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_multiple_streams_to_same_file)\n{\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream s1(chan, target);\n    fstream s2(chan, target);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_fails_to_open_read_only_by_default)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(fstream(filesystem(), target), system_error);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_out_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(fstream(filesystem(), target, openmode::out),\n                      system_error);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_in_out_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(\n        fstream(filesystem(), target, openmode::in | openmode::out),\n        system_error);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_in_flag_opens_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    fstream(filesystem(), target, openmode::in);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_readable)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n    BOOST_CHECK(!(s >> bob));\n    BOOST_CHECK(s.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_readable_binary_data)\n{\n    string expected_data(\"gobbledy gook\\0after-null\\x12\\11\", 26);\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target);\n\n    string bob;\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(io_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_readable_binary_data_stream_op)\n{\n    string expected_data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox_containing_data(expected_data);\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target);\n\n    string bob;\n\n    BOOST_CHECK(io_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n\n    BOOST_CHECK(io_stream >> bob);\n    const char* gn = \"gook\\0after-null\\x12\\x11\";\n    BOOST_CHECK_EQUAL_COLLECTIONS(bob.begin(), bob.end(), gn, gn + 17);\n    BOOST_CHECK(!(io_stream >> bob));\n    BOOST_CHECK(io_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_writeable)\n{\n    path target = new_file_in_sandbox();\n\n    {\n        fstream io_stream(filesystem(), target);\n\n        io_stream << \"gobbledy gook\";\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_write_multiple_buffers)\n{\n    // large enough to span multiple buffers\n    string data(large_data());\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target);\n    BOOST_CHECK(io_stream.write(data.data(), data.size()));\n    io_stream.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\n// Test with Boost.IOStreams buffer disabled.\n// Should call directly to libssh2\nBOOST_AUTO_TEST_CASE(io_stream_write_no_buffer)\n{\n    string data(\"gobbeldy gook\");\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target, openmode::in | openmode::out, 0);\n    BOOST_CHECK(io_stream.write(data.data(), data.size()));\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\n// An IO stream may be able to open a read-only file when given the in flag,\n// but it should still fail to write to it\nBOOST_AUTO_TEST_CASE(io_stream_read_only_write_fails)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    fstream s(filesystem(), target, openmode::in);\n\n    BOOST_CHECK(s << \"gobbledy gook\");\n    BOOST_CHECK(!s.flush()); // Failure happens on the flush\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK_EQUAL(bob, string());\n    BOOST_CHECK(input_stream.eof());\n}\n\n// Flush is not called explicitly so failure will happen in destructor\nBOOST_AUTO_TEST_CASE(io_stream_read_only_write_fails_no_flush)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    {\n        fstream s(filesystem(), target, openmode::in);\n\n        BOOST_CHECK(s << \"gobbledy gook\");\n\n        // No explicit flush\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK_EQUAL(bob, string());\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_write_binary_data)\n{\n    string data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target);\n    BOOST_CHECK(io_stream.write(data.data(), data.size()));\n    io_stream.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_write_binary_data_stream_op)\n{\n    string data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    fstream io_stream(chan, target);\n    BOOST_CHECK(io_stream << data);\n    io_stream.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_input_absolute)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekg(1, std::ios_base::beg);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"obbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_input_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekg(1, std::ios_base::cur);\n    s.seekg(1, std::ios_base::cur);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"bbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_input_end)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekg(-3, std::ios_base::end);\n\n    string bob;\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"ook\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_input_too_far_absolute)\n{\n    path target = new_file_in_sandbox();\n\n    fstream s(filesystem(), target);\n    s.exceptions(std::ios_base::badbit | std::ios_base::eofbit |\n                 std::ios_base::failbit);\n    s.seekg(1, std::ios_base::beg);\n\n    string bob;\n    BOOST_CHECK_THROW(s >> bob, runtime_error);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_input_too_far_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.exceptions(std::ios_base::badbit | std::ios_base::eofbit |\n                 std::ios_base::failbit);\n    s.seekg(9, std::ios_base::cur);\n    s.seekg(4, std::ios_base::cur);\n\n    string bob;\n    BOOST_CHECK_THROW(s >> bob, runtime_error);\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_output_absolute)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekp(1, std::ios_base::beg);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"grbbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_output_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekp(1, std::ios_base::cur);\n    s.seekp(1, std::ios_base::cur);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gorbledy\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_output_end)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekp(-3, std::ios_base::end);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"grok\");\n}\n\nBOOST_AUTO_TEST_CASE(io_stream_seek_interleaved)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    fstream s(filesystem(), target);\n    s.seekp(1, std::ios_base::beg);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.seekg(2, std::ios_base::cur);\n\n    string bob;\n\n    BOOST_CHECK(s >> bob);\n    // not \"bbledy\" because read and write head are combined\n    BOOST_CHECK_EQUAL(bob, \"ledy\");\n\n    s.seekp(-4, std::ios_base::end);\n\n    BOOST_CHECK(s << \"ahh\");\n\n    BOOST_CHECK(s >> bob);\n    BOOST_CHECK_EQUAL(bob, \"k\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"grbbledy\");\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"ahhk\");\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/knownhost_test.cpp",
    "content": "/**\n    @file\n\n    Tests for ssh knownhost interface.\n\n    @if license\n\n    Copyright (C) 2010, 2011, 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"ssh/knownhost.hpp\"\n\n#include <boost/assign/list_of.hpp> // list_of()\n#include <boost/foreach.hpp> // BOOST_FOREACH\n#include <boost/shared_ptr.hpp> // shared_ptr\n#include <boost/test/unit_test.hpp>\n\n#include <string>\n#include <vector>\n\n#include <libssh2.h>\n\nusing ssh::knownhost_search_result;\nusing ssh::knownhost_collection;\nusing ssh::knownhost;\nusing ssh::knownhost_iterator;\nusing ssh::openssh_knownhost_collection;\n\nusing boost::assign::list_of;\nusing boost::filesystem::path;\nusing boost::filesystem::ifstream;\nusing boost::shared_ptr;\nusing boost::test_tools::predicate_result;\n\nusing std::string;\nusing std::vector;\n\nnamespace {\n    struct test_datum\n    {\n        test_datum(\n            string name, string ip, string key_algo, string key,\n            string fail_key, string comment)\n            : name(name), ip(ip), key_algo(key_algo), key(key),\n              fail_key(fail_key), comment(comment) {}\n\n        string name;\n        string ip;\n        string key_algo;\n        string key;\n        string fail_key;\n        string comment;\n    };\n}\n\nconst string KEY_A =\n    \"AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZni\"\n    \"x8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nC\"\n    \"qtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbo\"\n    \"dUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEl\"\n    \"dwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4Q\"\n    \"FeigFf3QY7rGUgBEm/wMgxggdvLUCQ==\";\n\nconst string KEY_B = \n    \"AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezs\"\n    \"HRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEY\"\n    \"ho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaM\"\n    \"VMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS\"\n    \"51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yD\"\n    \"FZYbAkXz8QjhGL/qTywA7Afglyt5/w==\";\n\nconst string KEY_C = \n    \"AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb\"\n    \"2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt4\"\n    \"02tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgs\"\n    \"rs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYD\"\n    \"UvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WG\"\n    \"I3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAa\"\n    \"kCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z\"\n    \"+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/I\"\n    \"QcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w==\";\n\nconst string KEY_UNKNOWN_FORMAT =\n    \"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/q\"\n    \"Jfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1\"\n    \"E7E=\";\n\nconst vector<const test_datum> test_data = list_of\n    (test_datum(\n        \"host1.example.com\", \"192.168.0.1\", \"ssh-rsa\", KEY_A, KEY_B, \n        \"test@swish\"))\n    // The next key is not recognised by libssh2 (yet).  We use it to test that\n    // unrecognised keys are handled gracefully.  Added in the middle to catch\n    // if it halts processing silently - the later keys won't be processed.\n    // Key format name is \"unknown\" because actual format name isn't exposed\n    (test_datum(\n        \"unrecognisedkey.example.com\", \"192.168.2.1\", \"unknown\",\n        KEY_UNKNOWN_FORMAT, KEY_A, \"test@swish\"))\n    (test_datum(\n        \"host2.example.com\", \"10.0.0.1\", \"ssh-rsa\", KEY_B, KEY_C, \"\"))\n    (test_datum(\n        \"host3.example.com\", \"192.168.1.1\", \"ssh-dss\", KEY_C, KEY_A,\n        \"test@swish\"));\n\nconst string FAIL_HOST = \"i-dontexist-in-the-host-file.example.com\";\n\nBOOST_AUTO_TEST_SUITE(knownhost_tests)\n\n/**\n * Create and destroy without leaking.\n */\nBOOST_AUTO_TEST_CASE( create )\n{\n    knownhost_collection kh;\n}\n\nBOOST_AUTO_TEST_SUITE(openssh_knownhost_tests)\n\n/**\n * Initialise with known_host entries.\n */\nBOOST_AUTO_TEST_CASE( init_from_file )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n}\n\n/**\n * Initialise with hashed known_host entries.\n */\nBOOST_AUTO_TEST_CASE( init_from_hashed_file )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts_hashed\");\n}\n\n/**\n * Initialise with a file that doesn't exist.\n */\nBOOST_AUTO_TEST_CASE( init_fail )\n{\n    path bad_path = \"i-dont-exist\";\n    BOOST_REQUIRE(!exists(bad_path));\n    BOOST_CHECK_THROW(\n        (openssh_knownhost_collection(bad_path)), std::runtime_error);\n    BOOST_CHECK(!exists(bad_path));\n}\n\n/**\n * Saved file lines should match original except with each entry on its\n * own line.  I.e.:\n *     host3.example.com,192.168.1.1 ssh-dss <key>\n * becomes:\n *     192.168.1.1 ssh-dss <key>\n *     host3.example.com ssh-dss <key>\n */\nBOOST_AUTO_TEST_CASE( roundtrip )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n\n    vector<string> lines;\n    kh.save(kh.begin(), kh.end(), back_inserter(lines));\n\n    ifstream file(\"test_known_hosts_out\");\n    BOOST_REQUIRE_EQUAL_COLLECTIONS(\n        lines.begin(), lines.end(), \n        std::istream_iterator<ssh::detail::line>(file),\n        std::istream_iterator<ssh::detail::line>());\n}\n\nnamespace {\n\n    /**\n     * Check known_host matches the expected data.\n     */\n    template<typename T>\n    predicate_result entry_matches_impl(\n        const T& actual, const test_datum& expected, bool use_name,\n        bool hashed_name)\n    {\n        predicate_result res(false);\n\n        string actual_name = actual.name();\n        string expected_name;\n        \n        if (hashed_name)\n            expected_name = \"\";\n        else\n            expected_name = (use_name) ? expected.name : expected.ip;\n\n        if (actual_name != expected_name)\n            res.message()\n                << \"Host names or IPs don't match [\" << actual_name\n                << \" != \" << expected_name << \"]\";\n        else if (actual.key() != expected.key)\n            res.message()\n                << \"Keys don't match [\" << actual.key()\n                << \" != \" << expected.key << \"]\";\n        /*\n         TODO: test the comments once the libssh2 API is fixed.\n\n        else if (actual.comment() != expected.comment)\n            res.message()\n                << \"Comments don't match [\" << actual.comment()\n                << \" != \" << expected.comment << \"]\";\n        */\n        else if (actual.key_algo() != expected.key_algo)\n            res.message()\n                << \"Algorithms don't match [\" << actual.key_algo()\n                << \" != \" << expected.key_algo << \"]\";\n        else if (!hashed_name && !actual.is_name_plain())\n            res.message() << \"Should be plain-text\";\n        else if (hashed_name && actual.is_name_plain())\n            res.message() << \"Shouldn't be plain-text\";\n        else if (!hashed_name && actual.is_name_sha1())\n            res.message() << \"Should be SHA1-encoded\";\n        else if (hashed_name && !actual.is_name_sha1())\n            res.message() << \"Shouldn't be SHA1-encoded\";\n        else if (actual.is_name_custom())\n            res.message() << \"Shouldn't be custom encoded\";\n        else\n            return true;\n\n        return res;\n    }\n\n    /**\n     * Check that a known_host matches the expected data.\n     *\n     * The host name is expected to be the IP address..\n     */\n    template<typename T>\n    predicate_result entry_matches_ip(\n        const T& entry, const test_datum& expected)\n    {\n        return entry_matches_impl(entry, expected, false, false);\n    }\n\n    /**\n     * Check that a known_host matches the expected data.\n     *\n     * The host name is expected to be the IP address..\n     */\n    template<typename T>\n    predicate_result entry_matches(\n        const T& entry, const test_datum& expected)\n    {\n        return entry_matches_impl(entry, expected, true, false);\n    }\n\n    /**\n     * Check that a hashed known_host matches the expected data.\n     */\n    template<typename T>\n    predicate_result hashed_entry_matches(\n        const T& entry, const test_datum& expected)\n    {\n        return entry_matches_impl(entry, expected, false, true);\n    }\n}\n\n/**\n * Initialise with known_host entries and test retrieval.\n *\n * The iterator should keep working after the collection is destroyed\n * (this isn't strictly needed but as its easy for is to implement, its\n * a nice feature to enforce).\n */\nBOOST_AUTO_TEST_CASE( iterate_entries )\n{\n    knownhost_iterator it;\n    knownhost_iterator end;\n    {\n        openssh_knownhost_collection kh(\"test_known_hosts\");\n        it = kh.begin();\n        end = kh.end();\n    }\n\n    // There should be one entry for IP and one for hostname\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        BOOST_REQUIRE(it != end);\n        BOOST_CHECK(entry_matches_ip(*it++, datum));\n\n        BOOST_REQUIRE(it != end);\n        BOOST_CHECK(entry_matches(*it++, datum));\n    }\n    \n    BOOST_REQUIRE(it == end);\n}\n\n/**\n * Initialise with *hashed* known_host entries and test retrieval.\n */\nBOOST_AUTO_TEST_CASE( iterate_hashed_entries )\n{\n    knownhost_iterator it;\n    knownhost_iterator end;\n    {\n        openssh_knownhost_collection kh(\"test_known_hosts_hashed\");\n        it = kh.begin();\n        end = kh.end();\n    }\n\n    // Two entries per host even though we cannot see which is IP and\n    // which hostname\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        BOOST_REQUIRE(it != end);\n        BOOST_CHECK(hashed_entry_matches(*it++, datum));\n\n        BOOST_REQUIRE(it != end);\n        BOOST_CHECK(hashed_entry_matches(*it++, datum));\n    }\n\n    BOOST_REQUIRE(it == end);\n}\n\n/**\n * Iterators should no affect each other.\n */\nBOOST_AUTO_TEST_CASE( iterator_independence )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n\n    knownhost_iterator it1 = kh.begin();\n\n    BOOST_CHECK(entry_matches_ip(*it1++, test_data[0]));\n\n        knownhost_iterator it2 = kh.begin();\n\n    BOOST_CHECK(entry_matches(*it1++, test_data[0]));\n\n        BOOST_CHECK(entry_matches_ip(*it2++, test_data[0]));\n        BOOST_CHECK(entry_matches(*it2++, test_data[0]));\n        BOOST_CHECK(entry_matches_ip(*it2++, test_data[1]));\n        BOOST_CHECK(entry_matches(*it2++, test_data[1]));\n    \n    BOOST_CHECK(entry_matches_ip(*it1++, test_data[1]));\n    BOOST_CHECK(entry_matches(*it1++, test_data[1]));\n    BOOST_CHECK(entry_matches_ip(*it1++, test_data[2]));\n    BOOST_CHECK(entry_matches(*it1++, test_data[2]));\n\n        BOOST_CHECK(entry_matches_ip(*it2++, test_data[2]));\n        BOOST_CHECK(entry_matches(*it2++, test_data[2]));\n\n    BOOST_CHECK(entry_matches_ip(*it1++, test_data[3]));\n    BOOST_CHECK(entry_matches(*it1++, test_data[3]));\n\n    BOOST_REQUIRE(it1 == kh.end());\n\n        BOOST_CHECK(entry_matches_ip(*it2++, test_data[3]));\n        BOOST_CHECK(entry_matches(*it2++, test_data[3]));\n\n        BOOST_REQUIRE(it2 == kh.end());\n}\n\nnamespace {\n\n    /**\n     * Return first knownhost in file.\n     */\n    knownhost get_host_but_destroy_collection_and_iterator()\n    {\n        openssh_knownhost_collection kh(\"test_known_hosts\");\n\n        return *(kh.begin());\n    }\n}\n\n/**\n * knownhosts should outlive their iterator and collection.\n *\n * They do this by keeping the raw libssh2 collection alive inside them.  Ooo,\n * spooky!\n */\nBOOST_AUTO_TEST_CASE( knownhost_lifetime )\n{\n    knownhost host = get_host_but_destroy_collection_and_iterator();\n\n    BOOST_CHECK(entry_matches_ip(host, test_data[0]));\n}\n\nvoid do_find_match_test(\n    const boost::filesystem::path& file, bool is_hashed)\n{\n    openssh_knownhost_collection kh(file);\n\n    // Find each datum twice, once by IP once by name\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        knownhost_search_result result = kh.find(datum.name, datum.key, true);\n        \n        BOOST_CHECK(result.match());\n        BOOST_CHECK(!result.mismatch());\n        BOOST_CHECK(!result.not_found());\n\n        BOOST_REQUIRE(result.host() != kh.end());\n        if (!is_hashed)\n            BOOST_CHECK_EQUAL(result.host()->name(), datum.name);\n        BOOST_CHECK_EQUAL(result.host()->key(), datum.key);\n\n        result = kh.find(datum.ip, datum.key, true);\n        \n        BOOST_CHECK(result.match());\n        BOOST_CHECK(!result.mismatch());\n        BOOST_CHECK(!result.not_found());\n\n        BOOST_REQUIRE(result.host() != kh.end());\n        if (!is_hashed)\n            BOOST_CHECK_EQUAL(result.host()->name(), datum.ip);\n        BOOST_CHECK_EQUAL(result.host()->key(), datum.key);\n    }\n}\n\n/**\n * Search for all the test entries.  Each one should result in a match.\n */\nBOOST_AUTO_TEST_CASE( find_match )\n{\n    do_find_match_test(\"test_known_hosts\", false);\n}\n\n/**\n * Search for all the test entries in hashed collection.\n * Each one should result in a match.\n */\nBOOST_AUTO_TEST_CASE( find_match_hashed )\n{\n    do_find_match_test(\"test_known_hosts_hashed\", true);\n}\n\nvoid do_find_mismatch_test(\n    const boost::filesystem::path& file, bool is_hashed)\n{\n    openssh_knownhost_collection kh(file);\n\n    // Find each datum twice, once by IP once by name\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        knownhost_search_result result = kh.find(datum.name, datum.fail_key, true);\n        \n        BOOST_CHECK(!result.match());\n        BOOST_CHECK(result.mismatch());\n        BOOST_CHECK(!result.not_found());\n\n        BOOST_REQUIRE(result.host() != kh.end());\n        if (!is_hashed)\n            BOOST_CHECK_EQUAL(result.host()->name(), datum.name);\n        BOOST_CHECK_EQUAL(result.host()->key(), datum.key);\n\n        result = kh.find(datum.ip, datum.fail_key, true);\n        \n        BOOST_CHECK(!result.match());\n        BOOST_CHECK(result.mismatch());\n        BOOST_CHECK(!result.not_found());\n\n        BOOST_REQUIRE(result.host() != kh.end());\n        if (!is_hashed)\n            BOOST_CHECK_EQUAL(result.host()->name(), datum.ip);\n        BOOST_CHECK_EQUAL(result.host()->key(), datum.key);\n    }\n}\n\n/**\n * Search for each test host with a key that doesn't match.\n */\nBOOST_AUTO_TEST_CASE( find_mismatch )\n{\n    do_find_mismatch_test(\"test_known_hosts\", false);\n}\n\n/**\n * Search for each test host with a key that doesn't match.\n */\nBOOST_AUTO_TEST_CASE( find_mismatch_hashed )\n{\n    do_find_mismatch_test(\"test_known_hosts_hashed\", true);\n}\n\n/**\n * Search for a non-existent hostname in the collection.\n * This should fail to find a match.\n */\nBOOST_AUTO_TEST_CASE( find_fail )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n    knownhost_search_result result = kh.find(FAIL_HOST, KEY_A, true);\n\n    BOOST_CHECK(!result.match());\n    BOOST_CHECK(!result.mismatch());\n    BOOST_CHECK(result.not_found());\n    BOOST_CHECK(result.host() == kh.end());\n}\n\n/**\n * Search for a non-existent hostname in the collection.\n * This should fail to find a match.\n */\nBOOST_AUTO_TEST_CASE( find_fail_hashed )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts_hashed\");\n    knownhost_search_result result = kh.find(FAIL_HOST, KEY_A, true);\n\n    BOOST_CHECK(!result.match());\n    BOOST_CHECK(!result.mismatch());\n    BOOST_CHECK(result.not_found());\n    BOOST_CHECK(result.host() == kh.end());\n}\n\nvoid do_erase_test(\n    const openssh_knownhost_collection& kh, const test_datum& datum,\n    bool is_hashed)\n{\n    std::string expected_ip = (is_hashed) ? \"\" : datum.ip;\n    std::string expected_name = (is_hashed) ? \"\" : datum.name;\n\n    // find target entry by IP address\n    knownhost_search_result ip_result = kh.find(datum.ip, datum.key, true);\n    BOOST_CHECK_EQUAL(ip_result.host()->name(), expected_ip);\n    BOOST_CHECK_EQUAL(ip_result.host()->key(), datum.key);\n\n    // erase it which should give us pointer to next entry\n    // (the hostname version of the entry)\n    knownhost_iterator next = ip_result.host();\n    ++next;\n    BOOST_CHECK(erase(ip_result.host()) == next);\n    BOOST_CHECK_EQUAL(next->name(), expected_name);\n    BOOST_CHECK_EQUAL(next->key(), datum.key);\n\n    // searching for this host entry should also work and give an\n    // equal iterator\n    knownhost_search_result host_result = kh.find(datum.name, datum.key, true);\n    BOOST_CHECK(host_result.match());\n    BOOST_CHECK_EQUAL(host_result.host()->name(), expected_name);\n    BOOST_CHECK_EQUAL(host_result.host()->key(), datum.key);\n    BOOST_CHECK(next == host_result.host());\n     \n    // but searching for the IP entry we just deleted should fail to\n    // find anything\n    ip_result = kh.find(datum.ip, datum.key, true);\n    BOOST_CHECK(ip_result.not_found());\n\n    // erase host entry as well\n    erase(host_result.host());\n\n    // searching for it again should fail this time\n    host_result = kh.find(datum.name, datum.key, true);\n    BOOST_CHECK(host_result.not_found());\n}\n\nvoid do_erase_test_loop(const boost::filesystem::path& file, bool is_hashed)\n{\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        openssh_knownhost_collection kh(file);\n\n        do_erase_test(kh, datum, is_hashed);\n    }\n}\n\n/**\n * Erase one item from a collection.\n *\n * We test this for all entries with a fresh collection each time.\n *\n * The item in question should be gone but the other items should still\n * exist.\n\n * @warning  Strictly speaking we erase two items at a time due to the\n * relationship between host and IP entries.  This may be fragile.\n */\nBOOST_AUTO_TEST_CASE( erase_plain )\n{\n    do_erase_test_loop(\"test_known_hosts\", false);\n}\n\n/**\n * Erase one item from a collection of hashed entries.\n */\nBOOST_AUTO_TEST_CASE( erase_hashed )\n{\n    do_erase_test_loop(\"test_known_hosts_hashed\", true);\n}\n\n/**\n * Erase all items from a collection.\n *\n * The item in question should be gone but the other items should still\n * exist.\n\n * @warning  Strictly speaking we erase two items at a time due to the\n * relationship between host and IP entries.  This may be fragile.\n */\nBOOST_AUTO_TEST_CASE( erase_all )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n\n    BOOST_FOREACH(const test_datum& datum, test_data)\n    {\n        do_erase_test(kh, datum, false);\n    }\n\n    BOOST_CHECK(kh.begin() == kh.end());\n}\n\n/**\n * Erase the last item in the collection.\n */\nBOOST_AUTO_TEST_CASE( erase_last )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n    knownhost_search_result result =\n        kh.find(\n            test_data[test_data.size() - 1].name,\n            test_data[test_data.size() - 1].key, true);\n\n    BOOST_REQUIRE(result.host() != kh.end());\n\n    knownhost_iterator next = erase(result.host());\n    BOOST_REQUIRE(next == kh.end());\n\n    result = kh.find(\n        test_data[test_data.size() - 1].name,\n        test_data[test_data.size() - 1].key, true);\n    BOOST_CHECK(result.not_found());\n}\n\n/**\n * Add an item to the collection.\n */\nBOOST_AUTO_TEST_CASE( add )\n{\n    openssh_knownhost_collection kh(\"test_known_hosts\");\n\n    kh.add(\"new.example.com\", KEY_B, ssh::hostkey_type::ssh_dss, true);\n\n    knownhost_search_result result = kh.find(\"new.example.com\", KEY_B, true);\n\n    BOOST_CHECK(result.match());\n    BOOST_REQUIRE(result.host() != kh.end());\n    BOOST_CHECK_EQUAL(result.host()->name(), \"new.example.com\");\n    BOOST_CHECK_EQUAL(result.host()->key(), KEY_B);\n}\n\n/**\n * Lines must be written back exactly as they are read with exception of:\n *  - comma-separated host names being split into separate lines\n *  - newlines stripped\n *  - tabs replaced with spaces\n */\nBOOST_AUTO_TEST_CASE( load_save )\n{\n    const vector<string> lines = list_of\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host.example.com,192.0.32.10 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"hostalias1,hostalias2 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\\t\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\\n\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== \\n\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test@swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\\ttest swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish\\n\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish \\n\")\n        (\"host.example.com unknown-key-format blahblahblahkey test swish \\n\")\n        (\"host.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT\"\n         \"YAAAAIbmlzdHAyNTYAAABBBClh5K95Rz/k4WSPZ9rc8UnFSSSPtsu5z+hs19xbpusWB\"\n         \"2MwAU3+PYOjEUZZ9XuRMA+yKxOy1Qc/08uQWs1tyX8= swish@default\")\n        // Hashed version of the above\n        (\"|1|FI75NN2BtS542+iqaY9PWHJlXfc=|vGXV2w0kCLXWOqRF8uf+njij1MI= \"\n         \"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzd\"\n         \"HAyNTYAAABBBClh5K95Rz/k4WSPZ9rc8UnFSSSPtsu5z+hs19xbpusWB2MwAU\"\n         \"3+PYOjEUZZ9XuRMA+yKxOy1Qc/08uQWs1tyX8= swish@default\")\n        (\"|1|wWleTRHpe2S17RMX0bNldkfB/6Y=|8KTu5EjSLKwlkr0JoNo2QA3uhJs= \"\n         \"ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        // this one will fail with libssh2 < 1.2.8\n        (\"host1,host2,host3,192.168.1.1 ssh-rsa AAAAB3NzaC1yc2EAA==\");\n    const vector<string> expected_output = list_of\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"192.0.32.10 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"hostalias2 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"hostalias1 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test@swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish\")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish \")\n        (\"host.example.com ssh-rsa AAAAB3NzaC1yc2EAA== test swish \")\n        (\"host.example.com unknown-key-format blahblahblahkey test swish \")\n        (\"host.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT\"\n         \"YAAAAIbmlzdHAyNTYAAABBBClh5K95Rz/k4WSPZ9rc8UnFSSSPtsu5z+hs19xbpusWB\"\n         \"2MwAU3+PYOjEUZZ9XuRMA+yKxOy1Qc/08uQWs1tyX8= swish@default\")\n        (\"|1|FI75NN2BtS542+iqaY9PWHJlXfc=|vGXV2w0kCLXWOqRF8uf+njij1MI= \"\n         \"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzd\"\n         \"HAyNTYAAABBBClh5K95Rz/k4WSPZ9rc8UnFSSSPtsu5z+hs19xbpusWB2MwAU\"\n         \"3+PYOjEUZZ9XuRMA+yKxOy1Qc/08uQWs1tyX8= swish@default\")\n        (\"|1|wWleTRHpe2S17RMX0bNldkfB/6Y=|8KTu5EjSLKwlkr0JoNo2QA3uhJs= \"\n         \"ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"192.168.1.1 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host3 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host2 ssh-rsa AAAAB3NzaC1yc2EAA==\")\n        (\"host1 ssh-rsa AAAAB3NzaC1yc2EAA==\");\n\n    openssh_knownhost_collection kh(lines.begin(), lines.end());\n\n    vector<string> output;\n    kh.save(kh.begin(), kh.end(), back_inserter(output));\n\n    for (size_t i = 0; i < output.size(); ++i)\n    {\n        string o = output[i];\n        string e = expected_output[i];\n        BOOST_CHECK_EQUAL(o, e);\n        BOOST_CHECK_EQUAL_COLLECTIONS(\n            o.begin(), o.end(), e.begin(), e.end());\n    }\n}\n\n\nBOOST_AUTO_TEST_SUITE_END();\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/module.cpp",
    "content": "/**\n    @file\n\n    Unit test module.\n\n    @if license\n\n    Copyright (C) 2010  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the \n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it, \n    with unchanged license). You may copy and distribute such a system \n    following the terms of the GNU GPL for this program and the licenses \n    of the other code concerned. The GNU General Public License gives \n    permission to release a modified version without this exception; this \n    exception also makes it possible to release a modified version which \n    carries forward this exception.\n\n    @endif\n*/\n\n#define BOOST_TEST_MODULE ssh tests\n#include <boost/test/unit_test.hpp>\n"
  },
  {
    "path": "test/ssh/openssh_fixture.cpp",
    "content": "// Copyright 2009, 2010, 2011, 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"openssh_fixture.hpp\"\n\n#include <boost/assign/list_of.hpp>\n#include <boost/date_time/posix_time/posix_time_duration.hpp>\n#include <boost/foreach.hpp>\n#include <boost/io/detail/quoted_manip.hpp>\n#include <boost/optional.hpp>\n#include <boost/process/context.hpp>\n#include <boost/process/environment.hpp>\n#include <boost/process/operations.hpp> // find_executable_in_path\n#include <boost/process/pistream.hpp>\n#include <boost/process/self.hpp>\n#include <boost/process/stream_behavior.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/thread.hpp> // this_thread\n\n#include <iterator>\n#include <map>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\nusing boost::assign::list_of;\nusing boost::io::quoted;\nusing boost::filesystem::path;\nusing boost::optional;\nusing boost::process::behavior::pipe;\nusing boost::process::child;\nusing boost::process::context;\nusing boost::process::environment;\nusing boost::process::find_executable_in_path;\nusing boost::process::pistream;\nusing boost::process::self;\nusing boost::process::stderr_id;\nusing boost::process::stdout_id;\n\nusing std::map;\nusing std::ostream_iterator;\nusing std::ostringstream;\nusing std::runtime_error;\nusing std::string;\nusing std::vector;\nusing std::wstring;\n\nnamespace\n{ // private\n\nconst string SSHD_PRIVATE_KEY_FILE = \"fixture_dsakey\";\nconst string SSHD_PUBLIC_KEY_FILE = \"fixture_dsakey.pub\";\nconst string SSHD_WRONG_PRIVATE_KEY_FILE = \"fixture_wrong_dsakey\";\nconst string SSHD_WRONG_PUBLIC_KEY_FILE = \"fixture_wrong_dsakey.pub\";\n\ntemplate <typename ArgSequence>\nstring error_message_from_stderr(const string& command,\n                                 const ArgSequence& arguments, child& process)\n{\n    pistream command_stderr(process.get_handle(stderr_id));\n\n    ostringstream message;\n    message << quoted(command) << \" \";\n    copy(arguments.begin(), arguments.end(),\n         ostream_iterator<string>(message, \" \"));\n    message << \" failed: \";\n    message << command_stderr.rdbuf() << std::flush;\n\n    return message.str();\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_executable(const path& executable,\n                                 const ArgSequence& arguments)\n{\n    context ctx;\n    ctx.env = self::get_environment();\n    ctx.streams[stdout_id] = pipe();\n    ctx.streams[stderr_id] = pipe();\n\n    child process = create_child(executable.string(), arguments, ctx);\n\n    pistream command_stdout(process.get_handle(stdout_id));\n    Out out;\n    command_stdout >> out;\n\n    int status = process.wait();\n    // TODO: Check if process exited, with WIFEXITED - may need to upgrade\n    // Boost.Process to the 2012 version to get mitigate.hpp, which handles this\n    // portably.\n    if (status == 0)\n    {\n        return out;\n    }\n    else\n    {\n        BOOST_THROW_EXCEPTION(runtime_error(error_message_from_stderr(\n            executable.string(), arguments, process)));\n    }\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_command(const string& command,\n                              const ArgSequence& arguments)\n{\n    path command_executable = find_executable_in_path(command);\n\n    return single_value_from_executable<Out>(command_executable, arguments);\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_docker_command(const ArgSequence& arguments)\n{\n    return single_value_from_command<Out>(\"docker\", arguments);\n}\n\ntemplate <typename Out, typename ArgSequence>\nOut single_value_from_docker_machine_command(const ArgSequence& arguments)\n{\n    return single_value_from_command<Out>(\"docker-machine\", arguments);\n}\n\ntemplate <typename ArgSequence>\nvoid run_docker_command(const ArgSequence& arguments)\n{\n    single_value_from_docker_command<string>(arguments);\n}\n\noptional<string> docker_machine_name()\n{\n    const string docker_machine_name_variable = \"DOCKER_MACHINE_NAME\";\n\n    boost::process::environment environment = self::get_environment();\n    if (environment.count(docker_machine_name_variable) == 1)\n    {\n        return environment[docker_machine_name_variable];\n    }\n    else\n    {\n        return optional<string>();\n    }\n}\n}\n\nextern \"C\" {\nextern void ERR_free_strings();\nextern void ERR_remove_state(unsigned long);\nextern void EVP_cleanup();\nextern void CRYPTO_cleanup_all_ex_data();\nextern void ENGINE_cleanup();\nextern void CONF_modules_unload(int);\nextern void CONF_modules_free();\nextern void RAND_cleanup();\n}\n\nnamespace\n{\n\nstruct global_fixture\n{\n    global_fixture()\n    {\n        // Ensure the docker image has been built\n        vector<string> build_command = (list_of(string(\"build\")), \"-t\",\n                                        \"ssh2pp/openssh_server\", \"ssh_server\");\n        run_docker_command(build_command);\n    }\n\n    ~global_fixture()\n    {\n        // We call this here as a bit of a hack to stop memory-leak\n        // detection incorrectly detecting OpenSSL global data as a\n        // leak\n        ::RAND_cleanup();\n        ::ENGINE_cleanup();\n        ::CONF_modules_unload(1);\n        ::CONF_modules_free();\n        ::EVP_cleanup();\n        ::ERR_free_strings();\n        ::ERR_remove_state(0);\n        ::CRYPTO_cleanup_all_ex_data();\n    }\n};\n}\n\nBOOST_GLOBAL_FIXTURE(global_fixture);\n\nnamespace test\n{\nnamespace ssh\n{\n\nopenssh_fixture::openssh_fixture()\n{\n    vector<string> docker_command =\n        (list_of(string(\"run\")), \"--detach\", \"-P\", \"ssh2pp/openssh_server\");\n    m_container_id = single_value_from_docker_command<string>(docker_command);\n    m_host = ask_docker_for_host();\n    m_port = ask_docker_for_port();\n}\n\nopenssh_fixture::~openssh_fixture()\n{\n    try\n    {\n        vector<string> stop_command = (list_of(string(\"stop\")), m_container_id);\n        run_docker_command(stop_command);\n    }\n    catch (...)\n    {\n    }\n}\n\nstring openssh_fixture::host() const\n{\n    return m_host;\n}\n\nstring openssh_fixture::ask_docker_for_host() const\n{\n    optional<string> active_docker_machine = docker_machine_name();\n    if (active_docker_machine)\n    {\n        // This can be flaky when tests run in parallel (see\n        // https://github.com/docker/machine/issues/2612), so we retry a few\n        // times with exponential backoff if it fails\n        int attempt_no = 0;\n        boost::posix_time::milliseconds wait_time(100);\n        while (true)\n        {\n            ++attempt_no;\n            try\n            {\n                vector<string> machine_ip_command =\n                    (list_of(string(\"ip\")), active_docker_machine);\n                return single_value_from_docker_machine_command<string>(\n                    machine_ip_command);\n            }\n            catch (const runtime_error&)\n            {\n                if (attempt_no > 5)\n                {\n                    throw;\n                }\n                else\n                {\n                    wait_time *= 2;\n                }\n            }\n            boost::this_thread::sleep(wait_time);\n        }\n    }\n    else\n    {\n        vector<string> inspect_host_command =\n            (list_of(string(\"inspect\")), \"--format\",\n             \"{{ .NetworkSettings.IPAddress }}\", m_container_id);\n        return single_value_from_docker_command<string>(inspect_host_command);\n    }\n}\n\nstring openssh_fixture::user() const\n{\n    return \"swish\";\n}\n\nint openssh_fixture::port() const\n{\n    return m_port;\n}\n\nint openssh_fixture::ask_docker_for_port() const\n{\n    vector<string> inspect_host_command =\n        (list_of(string(\"inspect\")), \"--format\",\n         \"{{ index (index (index .NetworkSettings.Ports \\\"22/tcp\\\") \"\n         \"0) \\\"HostPort\\\" }}\",\n         m_container_id);\n    return single_value_from_docker_command<int>(inspect_host_command);\n}\n\n/**\n * The private half of a key-pair that is expected to authenticate successfully\n * with the fixture server.\n */\npath openssh_fixture::private_key_path() const\n{\n    return SSHD_PRIVATE_KEY_FILE;\n}\n\n/**\n * The public half of a key-pair that is expected to authenticate successfully\n * with the fixture server.\n */\npath openssh_fixture::public_key_path() const\n{\n    return SSHD_PUBLIC_KEY_FILE;\n}\n\n/**\n * The private half of a key-pair that is expected to fail to authenticate\n * with the fixture server.\n *\n * This must be in the same format as the successful key-pair so that the key\n * mismatches rather than format mismatches are the cause of authentication\n * failure regardless of which combination of keys is passed.\n */\npath openssh_fixture::wrong_private_key_path() const\n{\n    return SSHD_WRONG_PRIVATE_KEY_FILE;\n}\n\n/**\n * The public half of a key-pair that is expected to fail to authenticate\n * with the fixture server.\n *\n * This must be in the same format as the successful key-pair so that the\n * key mismatches rather than format mismatches are the cause of\n * authentication\n * failure regardless of which combination of keys is passed.\n */\npath openssh_fixture::wrong_public_key_path() const\n{\n    return SSHD_WRONG_PUBLIC_KEY_FILE;\n}\n}\n} // namespace test::ssh\n"
  },
  {
    "path": "test/ssh/openssh_fixture.hpp",
    "content": "// Copyright 2009, 2010, 2012, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SSH_OPENSSH_FIXTURE_HPP\n#define SSH_OPENSSH_FIXTURE_HPP\n\n#include <boost/filesystem.hpp> // path\n\n#include <string>\n\nnamespace test\n{\nnamespace ssh\n{\n\n/**\n * Fixture that starts and stops an OpenSSH server.\n */\nclass openssh_fixture\n{\npublic:\n    openssh_fixture();\n    virtual ~openssh_fixture();\n\n    std::string host() const;\n    std::string user() const;\n    int port() const;\n    boost::filesystem::path private_key_path() const;\n    boost::filesystem::path public_key_path() const;\n    boost::filesystem::path wrong_private_key_path() const;\n    boost::filesystem::path wrong_public_key_path() const;\n\nprivate:\n    std::string m_container_id;\n    std::string m_host;\n    int m_port;\n\n    std::string ask_docker_for_host() const;\n    int ask_docker_for_port() const;\n};\n}\n} // namespace test::ssh\n\n#endif\n"
  },
  {
    "path": "test/ssh/output_stream_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/stream.hpp> // test subject\n\n#include <boost/system/system_error.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\n#include <string>\n#include <vector>\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::openmode;\nusing ssh::filesystem::path;\nusing ssh::filesystem::perms;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::uuids::random_generator;\nusing boost::system::system_error;\n\nusing test::ssh::sftp_fixture;\n\nusing std::string;\nusing std::vector;\n\nnamespace\n{\n\n// the large data must fill more than one stream buffer (currently set to\n// 32768 (see DEFAULT_BUFFER_SIZE)\n\nstring large_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('m');\n        data.push_back('z');\n    }\n\n    return data;\n}\n\nstring large_binary_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('\\n');\n        data.push_back('\\0');\n        data.push_back('\\r');\n        data.push_back('\\n');\n        data.push_back(-1);\n    }\n\n    return data;\n}\n\nvoid make_file_read_only(sftp_filesystem& filesystem, const path& target)\n{\n    permissions(filesystem, target, perms::owner_read);\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(ofstream_tests, sftp_fixture)\n\nBOOST_AUTO_TEST_CASE(output_stream_multiple_streams)\n{\n    path target1 = new_file_in_sandbox();\n    path target2 = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    ofstream s1(chan, target1);\n    ofstream s2(chan, target2);\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_multiple_streams_to_same_file)\n{\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    ofstream s1(chan, target);\n    ofstream s2(chan, target);\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_is_writeable)\n{\n    path target = new_file_in_sandbox();\n\n    {\n        ofstream output_stream(filesystem(), target);\n        output_stream << \"gobbledy gook\";\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_write_multiple_buffers)\n{\n    // large enough to span multiple buffers\n    string data(large_data());\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    {\n        ofstream output_stream(chan, target);\n        BOOST_CHECK(output_stream.write(data.data(), data.size()));\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\n// Test with Boost.IOStreams buffer disabled.\n// Should call directly to libssh2\nBOOST_AUTO_TEST_CASE(output_stream_write_no_buffer)\n{\n    string data(\"gobbeldy gook\");\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    ofstream output_stream(chan, target, openmode::out, 0);\n    BOOST_CHECK(output_stream.write(data.data(), data.size()));\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_write_binary_data)\n{\n    string data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    {\n        ofstream output_stream(chan, target);\n        BOOST_CHECK(output_stream.write(data.data(), data.size()));\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_write_binary_data_multiple_buffers)\n{\n    // large enough to span multiple buffers\n    string data(large_binary_data());\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    {\n        ofstream output_stream(chan, target);\n        BOOST_CHECK(output_stream.write(data.data(), data.size()));\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_write_binary_data_stream_op)\n{\n    string data(\"gobbledy gook\\0after-null\\x12\\x11\", 26);\n\n    path target = new_file_in_sandbox();\n\n    sftp_filesystem& chan = filesystem();\n\n    {\n        ofstream output_stream(chan, target);\n        BOOST_CHECK(output_stream << data);\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    vector<char> buffer(data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(), data.begin(),\n                                  data.end());\n\n    BOOST_CHECK(!input_stream.read(&buffer[0], buffer.size()));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_creates_by_default)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_nocreate_flag)\n{\n    path target = new_file_in_sandbox();\n\n    ofstream(filesystem(), target, openmode::nocreate);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_nocreate_flag_fails)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target, openmode::nocreate),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_noreplace_flag)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream(filesystem(), target, openmode::noreplace);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_noreplace_flag_fails)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target, openmode::noreplace),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target, openmode::out);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_flag_truncates)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    {\n        ofstream output_stream(filesystem(), target, openmode::out);\n        BOOST_CHECK(exists(filesystem(), target));\n\n        BOOST_CHECK(output_stream << \"abcdef\");\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"abcdef\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_nocreate_flag)\n{\n    path target = new_file_in_sandbox();\n\n    ofstream output_stream(filesystem(), target,\n                           openmode::out | openmode::nocreate);\n\n    BOOST_CHECK(output_stream << \"abcdef\");\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_nocreate_flag_fails)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(\n        ofstream(filesystem(), target, openmode::out | openmode::nocreate),\n        system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_noreplace_flag)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target,\n                           openmode::out | openmode::noreplace);\n\n    BOOST_CHECK(exists(filesystem(), target));\n    BOOST_CHECK(output_stream << \"abcdef\");\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_noreplace_flag_fails)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK_THROW(\n        ofstream(filesystem(), target, openmode::out | openmode::noreplace),\n        system_error);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_flag_does_not_create)\n{\n    // In flag suppresses creation.  Matches standard lib ofstream.\n\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target, openmode::in),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_out_does_not_create)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(\n        ofstream(filesystem(), target, openmode::in | openmode::out),\n        system_error);\n\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_out_flag_updates)\n{\n    // Unlike the out flag for output-only streams, which truncates, the\n    // out flag on an input stream leaves the existing contents because the\n    // input stream forces the in flag and in|out means update existing\n\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    {\n        ofstream output_stream(filesystem(), target,\n                               openmode::in | openmode::out);\n        BOOST_CHECK(exists(filesystem(), target));\n\n        BOOST_CHECK(output_stream << \"abcdef\");\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"abcdefdy\");\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gook\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target,\n                           openmode::out | openmode::trunc);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_nocreate_flag)\n{\n    path target = new_file_in_sandbox();\n\n    ofstream output_stream(filesystem(), target, openmode::out |\n                                                     openmode::trunc |\n                                                     openmode::nocreate);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_nocreate_flag_fails)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    BOOST_CHECK_THROW(ofstream output_stream(filesystem(), target,\n                                             openmode::out | openmode::trunc |\n                                                 openmode::nocreate),\n                      system_error);\n    BOOST_CHECK(!exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_noreplace_flag)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target, openmode::out |\n                                                     openmode::trunc |\n                                                     openmode::noreplace);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_noreplace_flag_fails)\n{\n    path target = new_file_in_sandbox();\n\n    BOOST_CHECK_THROW(ofstream output_stream(filesystem(), target,\n                                             openmode::out | openmode::trunc |\n                                                 openmode::noreplace),\n                      system_error);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_trunc_flag_truncates)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    {\n        ofstream output_stream(filesystem(), target,\n                               openmode::out | openmode::trunc);\n\n        BOOST_CHECK(output_stream << \"abcdef\");\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"abcdef\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_out_trunc_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target,\n                           openmode::in | openmode::out | openmode::trunc);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_out_trunc_flag_truncates)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    {\n        ofstream output_stream(filesystem(), target,\n                               openmode::in | openmode::out | openmode::trunc);\n\n        BOOST_CHECK(output_stream << \"abcdef\");\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"abcdef\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_append_flag_creates)\n{\n    random_generator generator;\n    path target = to_string(generator());\n\n    ofstream output_stream(filesystem(), target, openmode::out | openmode::app);\n    BOOST_CHECK(exists(filesystem(), target));\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_append_flag_appends)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    {\n        ofstream output_stream(filesystem(), target,\n                               openmode::out | openmode::app);\n\n        BOOST_CHECK(output_stream << \"abcdef\");\n    }\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    /* If the tests fail here, the version of OpenSSH being used\n       is probably old and doesn't support FXF_APPEND */\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gookabcdef\");\n\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK(input_stream.eof());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_fails_to_open_read_only_by_default)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target), system_error);\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_out_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target, openmode::out),\n                      system_error);\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_in_out_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(\n        ofstream(filesystem(), target, openmode::in | openmode::out),\n        system_error);\n}\n\n// Because output streams force out flag, they can't open read-only files\nBOOST_AUTO_TEST_CASE(output_stream_in_flag_fails_to_open_read_only)\n{\n    path target = new_file_in_sandbox();\n    make_file_read_only(filesystem(), target);\n\n    BOOST_CHECK_THROW(ofstream(filesystem(), target, openmode::in),\n                      system_error);\n}\n\n// By default ostreams overwrite the file so seeking will cause subsequent\n// output to write after the file end.  The skipped bytes should be filled\n// with NUL\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_absolute_overshoot)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target);\n    s.seekp(2, std::ios_base::beg);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string expected_data(\"\\0\\0r\", 3);\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_absolute)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target, openmode::in);\n    s.seekp(1, std::ios_base::beg);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"grbbledy\");\n}\n\n// By default ostreams overwrite the file so seeking will cause subsequent\n// output to write after the file end.  The skipped bytes should be filled\n// with NUL\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_relative_overshoot)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target);\n    s.seekp(1, std::ios_base::cur);\n    s.seekp(1, std::ios_base::cur);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string expected_data(\"\\0\\0r\", 3);\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_relative)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target, openmode::in);\n    s.seekp(1, std::ios_base::cur);\n    s.seekp(1, std::ios_base::cur);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gorbledy\");\n}\n\n// By default ostreams overwrite the file.  Seeking TO the end of this empty\n// file will just start writing from the beginning.  No NUL bytes are\n// inserted anywhere\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_end)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target);\n    s.seekp(0, std::ios_base::end);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"r\");\n    BOOST_CHECK(!(input_stream >> bob));\n    BOOST_CHECK_EQUAL(bob, \"r\");\n}\n\n// By default ostreams overwrite the file.  Seeking past the end  will cause\n// subsequent output to write after the file end.  The skipped bytes will\n// be filled with NUL.\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_end_overshoot)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target);\n    s.seekp(3, std::ios_base::end);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string expected_data(\"\\0\\0\\0r\", 4);\n\n    vector<char> buffer(expected_data.size());\n    BOOST_CHECK(input_stream.read(&buffer[0], buffer.size()));\n\n    BOOST_CHECK_EQUAL_COLLECTIONS(buffer.begin(), buffer.end(),\n                                  expected_data.begin(), expected_data.end());\n}\n\nBOOST_AUTO_TEST_CASE(output_stream_seek_output_before_end)\n{\n    path target = new_file_in_sandbox_containing_data(\"gobbledy gook\");\n\n    ofstream s(filesystem(), target, openmode::in);\n    s.seekp(-3, std::ios_base::end);\n\n    BOOST_CHECK(s << \"r\");\n\n    s.flush();\n\n    ifstream input_stream(filesystem(), target);\n\n    string bob;\n\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"gobbledy\");\n    BOOST_CHECK(input_stream >> bob);\n    BOOST_CHECK_EQUAL(bob, \"grok\");\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/path_test.cpp",
    "content": "/**\n    @file\n\n    Tests for ssh filesystem path.\n\n    @if license\n\n    Copyright (C) 2015 Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#include <ssh/filesystem/path.hpp>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/locale.hpp>\n#include <boost/test/unit_test.hpp>\n\n#include <iostream>\n#include <sstream>\n#include <string>\n\nusing ssh::filesystem::path;\n\nusing boost::locale::conv::from_utf;\nusing boost::locale::util::get_system_locale;\n//using boost::filesystem::path;\n\nusing std::string;\nusing std::stringstream;\nusing std::wstring;\nusing std::wstringstream;\n\nnamespace std {\n\n    ostream& operator<<(ostream& out, const wstring& wide_in)\n    {\n        out << from_utf(wide_in, get_system_locale());\n        return out;\n    }\n\n    inline ostream& operator<<(ostream& out, const wchar_t* wide_in)\n    {\n        out << wstring(wide_in);\n        return out;\n    }\n}\n\n\nBOOST_AUTO_TEST_SUITE(path_tests)\n\nBOOST_AUTO_TEST_CASE( default_path_is_empty )\n{\n    const path p;\n    BOOST_CHECK(p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_filename_is_empty )\n{\n    const path p;\n    BOOST_CHECK(p.filename().empty());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_equal_to_itself )\n{\n    const path p;\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_equal_to_another_default_path )\n{\n    const path p;\n    const path q;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_equal_to_a_constructed_copy )\n{\n    const path p;\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_equal_to_an_assigned_copy )\n{\n    const path p;\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_different_to_a_single_segment_path )\n{\n    const path p;\n    const path q(\"other path\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_converts_explicity_to_empty_string )\n{\n    const path p;\n    BOOST_CHECK_EQUAL(p.native(), path::string_type());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_streams_nothing_to_narrow_string )\n{\n    const path p;\n    stringstream ss;\n    ss << p;\n    BOOST_CHECK_EQUAL(ss.str(), string());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_streams_nothing_to_wide_string )\n{\n    const path p;\n    wstringstream ss;\n    ss << p;\n    BOOST_CHECK_EQUAL(ss.str(), wstring());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_converts_implicitly_to_empty_string )\n{\n    const path p;\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, path::string_type());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_at_end_of_iteration )\n{\n    const path p;\n    BOOST_CHECK(p.begin() == p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 0);\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_relative )\n{\n    const path p;\n    BOOST_CHECK(p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_is_not_absolute )\n{\n    const path p;\n    BOOST_CHECK(!p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_has_no_parent_path )\n{\n    const path p;\n    BOOST_CHECK(!p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_parent_path_is_empty )\n{\n    const path p;\n    BOOST_CHECK(p.parent_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_has_no_relative_path )\n{\n    const path p;\n    BOOST_CHECK(!p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( default_path_relative_path_is_empty )\n{\n    const path p;\n    BOOST_CHECK(p.relative_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_not_empty )\n{\n    const path p(\"/\");\n    BOOST_CHECK(!p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_equal_to_itself )\n{\n    const path p(\"/\");\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_filename_is_the_root_path )\n{\n    const path p(\"/\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_equal_to_another_root_path )\n{\n    const path p(\"/\");\n    const path q(\"/\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_different_to_a_non_root_relative_path )\n{\n    const path p(\"/\");\n    const path q(\"foo\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_different_to_a_non_root_absolute_path )\n{\n    const path p(\"/\");\n    const path q(\"/foo\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_different_to_a_default_path )\n{\n    const path p(\"/\");\n    const path q;\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_equal_to_a_constructed_copy )\n{\n    const path p(\"/\");\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_equal_to_an_assigned_copy )\n{\n    const path p(\"/\");\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_converts_explicity_to_original_string )\n{\n    const path p(\"/\");\n    BOOST_CHECK_EQUAL(p.native(), \"/\");\n}\n\nBOOST_AUTO_TEST_CASE( root_path_converts_implicitly_to_original_string )\n{\n    const path p(\"/\");\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, \"/\");\n}\n\nBOOST_AUTO_TEST_CASE( root_path_can_iterate_once )\n{\n    const path p(\"/\");\n    BOOST_CHECK(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 1);\n}\n\nBOOST_AUTO_TEST_CASE( root_path_iterator_produces_original_path )\n{\n    const path p(\"/\");\n    BOOST_REQUIRE(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(*(p.begin()), path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( root_path_iteration_is_bidirectional )\n{\n    const path p(\"/\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = --p.end();\n    BOOST_CHECK_EQUAL(*it, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_not_relative )\n{\n    const path p(\"/\");\n    BOOST_CHECK(!p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_is_absolute )\n{\n    const path p(\"/\");\n    BOOST_CHECK(p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_has_no_parent_path )\n{\n    const path p(\"/\");\n    BOOST_CHECK(!p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_parent_path_is_empty )\n{\n    const path p(\"/\");\n    BOOST_CHECK(p.parent_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_has_no_relative_path )\n{\n    const path p(\"/\");\n    BOOST_CHECK(!p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( root_path_relative_path_is_empty )\n{\n    const path p(\"/\");\n    BOOST_CHECK(p.relative_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_not_empty )\n{\n    const path p(\".\");\n    BOOST_CHECK(!p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_equal_to_itself )\n{\n    const path p(\".\");\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_filename_is_the_dot_path )\n{\n    const path p(\".\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\".\"));\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_equal_to_another_dot_path )\n{\n    const path p(\".\");\n    const path q(\".\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_different_to_a_non_dot_path )\n{\n    const path p(\".\");\n    const path q(\"foo\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_different_to_the_root_path )\n{\n    const path p(\".\");\n    const path q(\"/\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_different_to_a_directory_path )\n{\n    const path p(\".\");\n    const path q(\"foo/\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_different_to_a_default_path )\n{\n    const path p(\".\");\n    const path q;\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_equal_to_a_constructed_copy )\n{\n    const path p(\".\");\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_equal_to_an_assigned_copy )\n{\n    const path p(\".\");\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_converts_explicity_to_dot_string )\n{\n    const path p(\".\");\n    BOOST_CHECK_EQUAL(p.native(), \".\");\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_converts_implicitly_to_dot_string )\n{\n    const path p(\".\");\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, \".\");\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_can_iterate_once )\n{\n    const path p(\".\");\n    BOOST_CHECK(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 1);\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_iterator_produces_original_path )\n{\n    const path p(\".\");\n    BOOST_REQUIRE(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(*(p.begin()), path(\".\"));\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_iteration_is_bidirectional )\n{\n    const path p(\".\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = --p.end();\n    BOOST_CHECK_EQUAL(*it, path(\".\"));\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_relative )\n{\n    const path p(\".\");\n    BOOST_CHECK(p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_not_absolute )\n{\n    const path p(\".\");\n    BOOST_CHECK(!p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_has_no_parent_path )\n{\n    const path p(\".\");\n    BOOST_CHECK(!p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_parent_path_is_empty )\n{\n    const path p(\".\");\n    BOOST_CHECK(p.parent_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_has_relative_path )\n{\n    const path p(\".\");\n    BOOST_CHECK(p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( dot_path_is_its_own_relative_path )\n{\n    const path p(\".\");\n    BOOST_CHECK_EQUAL(p.relative_path(), p);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_not_empty )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(!p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_filename_excludes_root )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\"Test Filename.txt\"));\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_equal_to_itself )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_equal_to_another_path_from_equal_source )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_different_to_another_path_from_different_source )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(\"/Test Filename.txp\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_different_to_similar_relative_path )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(\"Test Filename.txt\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_equality_is_case_sensitive )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(\"/Test filename.txt\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_equal_to_a_constructed_copy )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_equal_to_an_assigned_copy )\n{\n    const path p(\"/Test Filename.txt\");\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_less_than_lexi_greater_source )\n{\n    const path p(\"/Test Filename.txs\");\n    const path q(\"/Test Filename.txt\");\n    BOOST_CHECK_LT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_greater_than_lexi_less_source )\n{\n    const path p(\"/Test Filename.txt\");\n    const path q(\"/Test Filename.txs\");\n    BOOST_CHECK_GT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_converts_explicity_to_original_string )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.native(), \"/Test Filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_converts_implicitly_to_original_string )\n{\n    const path p(\"/Test Filename.txt\");\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, \"/Test Filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_can_iterate_twice )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 2);\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_iterator_produces_root_and_filename_single_segment_paths )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = p.begin();\n    BOOST_CHECK_EQUAL(*it++, path(\"/\"));\n    BOOST_CHECK_EQUAL(*it++, path(\"Test Filename.txt\"));\n    BOOST_CHECK(it == p.end());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_iteration_is_bidirectional )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = --p.end();\n    BOOST_CHECK_EQUAL(*it--, path(\"Test Filename.txt\"));\n    BOOST_CHECK_EQUAL(*it, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_not_relative )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(!p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_is_absolute )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_has_parent_path )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_parent_path_is_root_path )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.parent_path(), path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_has_relative_path )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK(p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_absolute_path_relative_path_is_filename_path )\n{\n    const path p(\"/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.relative_path(), p.filename());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_relative_path_filename_is_the_single_segment )\n{\n    const path p(\"foo\");\n    BOOST_CHECK_EQUAL(p, path(\"foo\"));\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_relative_path_has_no_parent_path )\n{\n    const path p(\"foo\");\n    BOOST_CHECK(!p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_relative_path_parent_path_is_empty )\n{\n    const path p(\"foo\");\n    BOOST_CHECK(p.parent_path().empty());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_relative_path_has_relative_path )\n{\n    const path p(\"foo\");\n    BOOST_CHECK(p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( single_segment_is_its_own_relative_path )\n{\n    const path p(\"foo\");\n    BOOST_CHECK_EQUAL(p.relative_path(), p);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_not_empty )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(!p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_filename_is_last_segment )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\"Test Filename.txt\"));\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_equal_to_itself )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_equal_to_another_path_from_equal_source )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path q(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_different_to_another_path_with_same_dir_different_file )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path q(\"Test Dir/Test Filename.txp\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_different_to_another_path_with_different_dir_same_file )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path q(\"Test Dir 2/Test Filename.txt\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_equality_is_case_sensitive )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path q(\"Test Dir/Test filename.txt\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_equal_to_a_constructed_copy )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_equal_to_an_assigned_copy )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_compares_less_than_lexi_by_segment )\n{\n    const path p(\"a/ad\");\n    const path q(\"a+/c\");\n    BOOST_CHECK_LT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_compares_greater_than_lexi_by_segment )\n{\n    const path p(\"a+/c\");\n    const path q(\"a/ad\");\n    BOOST_CHECK_GT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_converts_explicity_to_original_string )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.native(), \"Test Dir/Test Filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_converts_implicitly_to_original_string )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, \"Test Dir/Test Filename.txt\");\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_can_iterate_twice )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 2);\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_iterator_produces_dir_and_file_single_segment_paths )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = p.begin();\n    BOOST_CHECK_EQUAL(*it++, path(\"Test Dir\"));\n    BOOST_CHECK_EQUAL(*it++, path(\"Test Filename.txt\"));\n    BOOST_CHECK(it == p.end());\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_iteration_is_bidirectional )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = --p.end();\n    BOOST_CHECK_EQUAL(*it--, path(\"Test Filename.txt\"));\n    BOOST_CHECK_EQUAL(*it, path(\"Test Dir\"));\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_relative )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_is_not_absolute )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(!p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_has_parent_path )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE(\n    multi_segment_relative_path_parent_path_omits_last_segment )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.parent_path(), path(\"Test Dir\"));\n}\n\nBOOST_AUTO_TEST_CASE( multi_segment_relative_path_has_relative_path )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK(p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE(\n    multi_segment_relative_path_is_its_own_relative_path )\n{\n    const path p(\"Test Dir/Test Filename.txt\");\n    BOOST_CHECK_EQUAL(p.relative_path(), p);\n}\n\n// NOTE: This behaviour seems very odd an anti-STL (non-interchangeable equal\n// values) however it seems to be required by the current C++ Standard proposal\n// (iteration ignores multiple separators, equality based on iteration).\n// Boost.Filesystem in 1.49 did _not_ have this beahviour, but I suspect the\n// recent changes to bring it into line with the standard may have introduced\n// that behaviour.\n//\n// TODO: Test what latest Boost.Filesystem behaviour is and, if necessary file a\n// bug with Boost and/or the C++ standard.\nBOOST_AUTO_TEST_CASE( multiple_adjacent_separators_do_not_affect_path_equality )\n{\n    const path p(\"foo//bar\");\n    BOOST_CHECK_EQUAL(p, path(\"foo//bar\"));\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar\"));\n    BOOST_CHECK_EQUAL(p, path(\"foo///bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( multiple_adjacent_separators_do_not_affect_iteration )\n{\n    const path p(\"foo//bar\");\n\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = p.begin();\n    BOOST_CHECK_EQUAL(*it++, path(\"foo\"));\n    BOOST_CHECK_EQUAL(*it++, path(\"bar\"));\n    BOOST_CHECK(it == p.end());\n}\n\nBOOST_AUTO_TEST_CASE( multiple_adjacent_separators_do_not_affect_filename )\n{\n    const path p(\"foo//bar\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\"bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_not_empty )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(!p.empty());\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_filename_is_dot )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\".\"));\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_equal_to_itself )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p, p);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_not_equal_to_similar_file_path )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"foo/bar\");\n    BOOST_CHECK_NE(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_equal_to_another_path_from_equal_source )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_equal_to_a_constructed_copy )\n{\n    const path p(\"foo/bar/\");\n    const path q(p);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_equal_to_an_assigned_copy )\n{\n    const path p(\"foo/bar/\");\n    path q;\n    q = p;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_less_than_lexi_greater_source )\n{\n    const path p(\"foo/baq/\");\n    const path q(\"foo/bar/\");\n    BOOST_CHECK_LT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_greater_than_lexi_less_source )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"foo/baq/\");\n    BOOST_CHECK_GT(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_converts_explicity_to_original_string )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p.native(), \"foo/bar/\");\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_converts_implicitly_to_original_string )\n{\n    const path p(\"foo/bar/\");\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, \"foo/bar/\");\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_iterates_once_more_than_number_of_names )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(p.begin() != p.end());\n    BOOST_CHECK_EQUAL(std::distance(p.begin(), p.end()), 3);\n}\n\nBOOST_AUTO_TEST_CASE(\n    directory_path_iterator_produces_filename_single_segments_followed_by_dot )\n{\n    const path p(\"foo/bar/\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = p.begin();\n    BOOST_CHECK_EQUAL(*it++, path(\"foo\"));\n    BOOST_CHECK_EQUAL(*it++, path(\"bar\"));\n    BOOST_CHECK_EQUAL(*it++, path(\".\"));\n    BOOST_CHECK(it == p.end());\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_iteration_is_bidirectional )\n{\n    const path p(\"foo/bar/\");\n    BOOST_REQUIRE(p.begin() != p.end());\n\n    path::iterator it = --p.end();\n    BOOST_CHECK_EQUAL(*it--, path(\".\"));\n    BOOST_CHECK_EQUAL(*it--, path(\"bar\"));\n    BOOST_CHECK_EQUAL(*it, path(\"foo\"));\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_has_parent_path )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_parent_path_omit_trailing_slash )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p.parent_path(), path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_has_relative_path )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(p.has_relative_path());\n}\n\nBOOST_AUTO_TEST_CASE( directory_path_is_its_own_relative_path )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK_EQUAL(p.relative_path(), p);\n}\n\nBOOST_AUTO_TEST_CASE( dotted_directory_path_has_parent_path )\n{\n    const path p(\"foo/bar/.\");\n    BOOST_CHECK(p.has_parent_path());\n}\n\nBOOST_AUTO_TEST_CASE( dotted_directory_path_filename_is_dot )\n{\n    const path p(\"foo/bar/.\");\n    BOOST_CHECK_EQUAL(p.filename(), path(\".\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    dotted_directory_path_parent_path_omit_trailing_slash_and_dot )\n{\n    const path p(\"foo/bar/.\");\n    BOOST_CHECK_EQUAL(p.parent_path(), path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( relative_directory_path_is_relative )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( absolute_directory_path_is_not_relative )\n{\n    const path p(\"/foo/bar/\");\n    BOOST_CHECK(!p.is_relative());\n}\n\nBOOST_AUTO_TEST_CASE( relative_directory_path_is_not_absolute )\n{\n    const path p(\"foo/bar/\");\n    BOOST_CHECK(!p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( absolute_directory_path_is_absolute )\n{\n    const path p(\"/foo/bar/\");\n    BOOST_CHECK(p.is_absolute());\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_relative_paths_returns_concantenation )\n{\n    const path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    BOOST_CHECK_EQUAL(p / q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_relative_paths_leaves_both_operands_unchanged )\n{\n    const path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar\"));\n    BOOST_CHECK_EQUAL(q, path(\"baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_relative_paths_inserts_single_separator )\n{\n    const path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    BOOST_CHECK_EQUAL((p / q).native(), \"foo/bar/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_path_to_another_returns_concatenation )\n{\n    path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_path_to_another_changes_latter_to_concatenation )\n{\n    path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_path_to_another_leaves_former_unchanged )\n{\n    path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_path_to_another_inserts_single_separator )\n{\n    path p(\"foo/bar\");\n    const path q(\"baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p.native(), \"foo/bar/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_paths_returns_concatenation )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    BOOST_CHECK_EQUAL(p / q, path(\"foo/bar/baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_paths_leaves_both_unchanged )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/\"));\n    BOOST_CHECK_EQUAL(q, path(\"baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_paths_inserts_single_separator )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    BOOST_CHECK_EQUAL((p / q).native(), path(\"foo/bar/baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_directory_path_to_another_returns_concatenation )\n{\n    path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"foo/bar/baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_directory_path_to_another_changes_latter_to_concatenation )\n{\n    path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_directory_path_to_another_leaves_former_unchanged )\n{\n    path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"baz/woz/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_directory_path_to_another_inserts_single_separator )\n{\n    path p(\"foo/bar/\");\n    const path q(\"baz/woz/\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q.native(), \"baz/woz/\");\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_and_absolute_returns_concatenation )\n{\n    const path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL(p / q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_and_absolute_leaves_both_unchanged )\n{\n    const path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar\"));\n    BOOST_CHECK_EQUAL(q, path(\"/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_and_absolute_doesnt_insert_separator )\n{\n    const path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL((p / q).native(), \"foo/bar/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE( appending_absolute_to_relative_returns_concatenation )\n{\n    path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_changes_latter_to_concatenation )\n{\n    path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_another_leaves_former_unchanged )\n{\n    path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_doesnt_insert_separator )\n{\n    path p(\"foo/bar\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q.native(), \"/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_and_absolute_returns_concatenation )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL(p / q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_and_absolute_leaves_both_unchanged )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/\"));\n    BOOST_CHECK_EQUAL(q, path(\"/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    concatenating_relative_directory_and_absolute_collapses_adjoining_separators )\n{\n    const path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL((p / q).native(), \"foo/bar/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_directory_returns_concatenation )\n{\n    path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_directory_changes_latter_to_concatenation )\n{\n    path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"foo/bar/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_directory_leaves_former_unchanged )\n{\n    path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"/baz/woz\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_absolute_to_relative_directory_collapses_adjoining_separators )\n{\n    path p(\"foo/bar/\");\n    const path q(\"/baz/woz\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p.native(), \"foo/bar/baz/woz\");\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_default_and_relative_returns_the_latter )\n{\n    const path p;\n    const path q(\"foo/bar\");\n    BOOST_CHECK_EQUAL(p / q, q);\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_default_and_relative_leaves_both_unchanged )\n{\n    const path p;\n    const path q(\"foo/bar\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path());\n    BOOST_CHECK_EQUAL(q, path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_to_default_returns_the_former )\n{\n    path p;\n    const path q(\"foo/bar\");\n    BOOST_CHECK_EQUAL(p /= q, q);\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_to_default_changes_latter_to_equal_former )\n{\n    path p;\n    const path q(\"foo/bar\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_to_default_leaves_former_unchanged )\n{\n    path p;\n    const path q(\"foo/bar\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_root_and_relative_returns_concatenation )\n{\n    const path p(\"/\");\n    const path q(\"foo/bar\");\n    BOOST_CHECK_EQUAL(p / q, path(\"/foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE(concatenating_root_and_relative_leaves_both_unchanged )\n{\n    const path p(\"/\");\n    const path q(\"foo/bar\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"/\"));\n    BOOST_CHECK_EQUAL(q, path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_root_and_relative_doesnt_insert_separator )\n{\n    const path p(\"/\");\n    const path q(\"foo/bar\");\n    BOOST_CHECK_EQUAL((p / q).native(), \"/foo/bar\");\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_to_root_returns_concatenation )\n{\n    path p(\"/\");\n    const path q(\"foo/bar\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"/foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_relative_to_root_changes_latter_to_concatenation )\n{\n    path p(\"/\");\n    const path q(\"foo/bar\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"/foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_to_root_leaves_former_unchanged )\n{\n    path p(\"/\");\n    const path q(\"foo/bar\");\n    p /= q;\n    BOOST_CHECK_EQUAL(q, path(\"foo/bar\"));\n}\n\nBOOST_AUTO_TEST_CASE( appending_relative_to_root_doesnt_insert_separator )\n{\n    path p(\"/\");\n    const path q(\"foo/bar\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p.native(), \"/foo/bar\");\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_root_and_root_paths_returns_root_path )\n{\n    const path p(\"/\");\n    const path q(\"/\");\n    BOOST_CHECK_EQUAL(p / q, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_root_and_root_paths_leaves_both_unchanged )\n{\n    const path p(\"/\");\n    const path q(\"/\");\n    p / q;\n    BOOST_CHECK_EQUAL(p, path(\"/\"));\n    BOOST_CHECK_EQUAL(q, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_root_and_root_paths_collapses_separators )\n{\n    const path p(\"/\");\n    const path q(\"/\");\n    BOOST_CHECK_EQUAL((p / q).native(), \"/\");\n}\n\nBOOST_AUTO_TEST_CASE( appending_root_path_to_root_path_returns_root_path )\n{\n    path p(\"/\");\n    const path q(\"/\");\n    BOOST_CHECK_EQUAL(p /= q, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_root_path_to_root_path_leaves_both_operands_unchanged )\n{\n    path p(\"/\");\n    const path q(\"/\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p, path(\"/\"));\n    BOOST_CHECK_EQUAL(q, path(\"/\"));\n}\n\nBOOST_AUTO_TEST_CASE(\n    appending_root_path_to_root_path_collapses_separators )\n{\n    path p(\"/\");\n    const path q(\"/\");\n    p /= q;\n    BOOST_CHECK_EQUAL(p.native(), \"/\");\n}\n\nnamespace {\n    const char UTF8_STRING1[] =\n        \"\\xe0\\xa4\\xae\\xe0\\xa4\\xb9\\xe0\\xa4\\xb8\\xe0\\xa5\\x81\\xe0\\xa4\\xb8\";\n    const wchar_t WIDE_STRING1[] = L\"\\x92e\\x939\\x938\\x941\\x938\";\n\n    const char UTF8_STRING2[] = \"\\xe4\\xb8\\xad\\xe5\\x9c\\x8b\";\n    const wchar_t WIDE_STRING2[] = L\"\\x4e2d\\x570b\";\n\n    const char UTF8_CONCATENATION[] =\n        \"\\xe0\\xa4\\xae\\xe0\\xa4\\xb9\\xe0\\xa4\\xb8\\xe0\\xa5\\x81\\xe0\\xa4\\xb8/\"\n        \"\\xe4\\xb8\\xad\\xe5\\x9c\\x8b\";\n    const wchar_t WIDE_CONCATENATION[] =\n        L\"\\x92e\\x939\\x938\\x941\\x938/\\x4e2d\\x570b\";\n}\n\nBOOST_AUTO_TEST_CASE( path_created_from_wide_string_is_equal_to_another )\n{\n    const path p(WIDE_STRING1);\n    const path q(WIDE_STRING1);\n    BOOST_CHECK_EQUAL(p, q);\n}\n\nBOOST_AUTO_TEST_CASE(\n    path_created_from_ascii_wide_string_is_equal_to_narrow_equivalent )\n{\n    // A non-ASCII narrow path may or may not be interpreted as the same string\n    // as the wide path, depending on OS.  On Windows, narrow strings are in the\n    // local code page\n    const path p(L\"hello.txt\");\n    const path q(\"hello.txt\");\n    BOOST_CHECK_EQUAL(p, q);\n}\n\n// TODO: Create and test a constructor that takes a std::locale parameter to\n// guide how the string is interpreted.  Allows passing UTF8 char strings on\n// Windows\n\nBOOST_AUTO_TEST_CASE(\n    path_created_from_wide_string_converts_explicitly_to_original_string )\n{\n    const path p(WIDE_STRING1);\n    BOOST_CHECK_EQUAL(p.wstring(), WIDE_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE(\n    path_created_from_wide_string_converts_explicitly_to_utf8_string )\n{\n    const path p(WIDE_STRING1);\n    BOOST_CHECK_EQUAL(p.u8string(), UTF8_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE( native_string_is_utf8 )\n{\n    const path p(WIDE_STRING1);\n    BOOST_CHECK_EQUAL(p.native(), UTF8_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE( narrow_string_accessor_is_utf8 )\n{\n    const path p(WIDE_STRING1);\n    string narrow(p.string<string::value_type, string::traits_type>());\n    BOOST_CHECK_EQUAL(narrow, UTF8_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE( wide_string_accessor_preserves_wide_string )\n{\n    const path p(WIDE_STRING1);\n    wstring wide(p.string<wstring::value_type, wstring::traits_type>());\n    BOOST_CHECK_EQUAL(wide, WIDE_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE( string_conversion_to_local_codepage_works )\n{\n    // Can't test non-ASCII conversion because the chars may not be supported in\n    // the local codepage\n    const path p(\"hello\");\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( implicit_string_conversion_is_utf8 )\n{\n    const path p(WIDE_STRING1);\n    const path::string_type s = p;\n    BOOST_CHECK_EQUAL(s, UTF8_STRING1);\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_implicitly_from_narrow_pointer )\n{\n    const char* s = \"hello\";\n    path p = s;\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_implicitly_from_wide_pointer )\n{\n    const wchar_t* s = L\"hello\";\n    path p = s;\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_implicitly_from_narrow_string )\n{\n    const string s = \"hello\";\n    path p = s;\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_implicitly_from_wide_string )\n{\n    const wstring s = L\"hello\";\n    path p = s;\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_from_narrow_range )\n{\n    const string s = \"hello\";\n    path p(s.begin(), s.end());\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( path_contructs_from_wide_range )\n{\n    const wstring s = L\"hello\";\n    path p(s.begin(), s.end());\n    BOOST_CHECK_EQUAL(p.string(), \"hello\");\n}\n\nBOOST_AUTO_TEST_CASE( appending_wide_string_to_path_extends_path )\n{\n    path p(WIDE_STRING1);\n    p /= WIDE_STRING2;\n    const path q(WIDE_CONCATENATION);\n    BOOST_CHECK_EQUAL(p, q);\n    BOOST_CHECK_EQUAL(p.u8string(), UTF8_CONCATENATION);\n}\n\nBOOST_AUTO_TEST_CASE( concatenating_wide_string_and_path_returns_concatenation )\n{\n    const path p(WIDE_STRING1);\n    const path q(WIDE_CONCATENATION);\n    BOOST_CHECK_EQUAL(p / WIDE_STRING2, q);\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/session_fixture.cpp",
    "content": "// Copyright 2010, 2011, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"session_fixture.hpp\"\n\n#include <boost/system/system_error.hpp> // system_error\n#include <boost/throw_exception.hpp>     // BOOST_THROW_EXCEPTION\n\n#include <locale>\n#include <sstream>   // basic_ostringstream\n#include <stdexcept> // logic_error\n#include <string>\n\nusing ssh::session;\n\nusing boost::system::system_error;\n\nusing std::auto_ptr;\nusing std::locale;\nusing std::string;\nusing std::ostringstream;\nusing std::logic_error;\n\nnamespace test\n{\nnamespace ssh\n{\n\nnamespace\n{\n\n/**\n * Locale-independent port number to port string conversion.\n */\nstring port_to_string(long port)\n{\n    ostringstream stream;\n    stream.imbue(locale::classic()); // force locale-independence\n    stream << port;\n    if (!stream)\n        BOOST_THROW_EXCEPTION(\n            logic_error(\"Unable to convert port number to string\"));\n\n    return stream.str();\n}\n}\n\nvoid open_socket_to_host(boost::asio::io_service& io,\n                         boost::asio::ip::tcp::socket& socket,\n                         const string& host_name, int port)\n{\n    using boost::asio::ip::tcp;\n\n    tcp::resolver resolver(io);\n    typedef tcp::resolver::query Lookup;\n    Lookup query(host_name, port_to_string(port));\n\n    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);\n    tcp::resolver::iterator end;\n\n    boost::system::error_code error = boost::asio::error::host_not_found;\n    while (error && endpoint_iterator != end)\n    {\n        socket.close();\n        socket.connect(*endpoint_iterator++, error);\n    }\n    if (error)\n        BOOST_THROW_EXCEPTION(system_error(error));\n}\n\nsession_fixture::session_fixture()\n    : m_io(0),\n      m_socket(m_io),\n      m_session(session(open_socket(host(), port()).native()))\n{\n}\n\nsession& session_fixture::test_session()\n{\n    return m_session;\n}\n\nauto_ptr<boost::asio::ip::tcp::socket>\nsession_fixture::connect_additional_socket()\n{\n    auto_ptr<boost::asio::ip::tcp::socket> socket(\n        new boost::asio::ip::tcp::socket(m_io));\n\n    open_socket_to_host(m_io, *socket, host(), port());\n\n    return socket;\n}\n\nboost::asio::ip::tcp::socket&\nsession_fixture::open_socket(const string& host_name, int port)\n{\n    open_socket_to_host(m_io, m_socket, host_name, port);\n\n    return m_socket;\n}\n}\n} // namespace test::ssh\n"
  },
  {
    "path": "test/ssh/session_fixture.hpp",
    "content": "// Copyright 2010, 2011, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef SSH_TEST_FIXTURES_SESSION_FIXTURE_HPP\n#define SSH_TEST_FIXTURES_SESSION_FIXTURE_HPP\n\n#include \"openssh_fixture.hpp\"\n\n#include <ssh/session.hpp>\n\n#include <boost/asio/ip/tcp.hpp> // Boost sockets\n\n#include <string>\n\nnamespace test\n{\nnamespace ssh\n{\n\n/**\n * Fixture serving ssh::session objects connected to a running server.\n */\nclass session_fixture : virtual public openssh_fixture\n{\npublic:\n    session_fixture();\n\n    ::ssh::session& test_session();\n\n    std::auto_ptr<boost::asio::ip::tcp::socket> connect_additional_socket();\n\nprivate:\n    boost::asio::ip::tcp::socket& open_socket(const std::string& host_name,\n                                              int port);\n\n    boost::asio::io_service m_io; ///< Boost IO system\n    boost::asio::ip::tcp::socket m_socket;\n    ::ssh::session m_session;\n};\n\nvoid open_socket_to_host(boost::asio::io_service& io,\n                         boost::asio::ip::tcp::socket& socket,\n                         const std::string& host_name, int port);\n}\n} // namespace test::ssh\n\n#endif\n"
  },
  {
    "path": "test/ssh/session_test.cpp",
    "content": "/**\n    @file\n\n    Tests for session existence.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n    In addition, as a special exception, the the copyright holders give you\n    permission to combine this program with free software programs or the\n    OpenSSL project's \"OpenSSL\" library (or with modified versions of it,\n    with unchanged license). You may copy and distribute such a system\n    following the terms of the GNU GPL for this program and the licenses\n    of the other code concerned. The GNU General Public License gives\n    permission to release a modified version without this exception; this\n    exception also makes it possible to release a modified version which\n    carries forward this exception.\n\n    @endif\n*/\n\n#include \"openssh_fixture.hpp\"\n#include \"session_fixture.hpp\" // open_socket_to_host\n\n#include <ssh/session.hpp> // test subject\n\n#include <boost/move/move.hpp>\n#include <boost/test/unit_test.hpp>\n\nusing ssh::session;\n\nusing test::ssh::openssh_fixture;\nusing test::ssh::open_socket_to_host;\n\nusing boost::asio::io_service;\nusing boost::asio::ip::tcp;\nusing boost::move;\n\nBOOST_FIXTURE_TEST_SUITE(session_tests, openssh_fixture)\n\nBOOST_AUTO_TEST_CASE(default_message)\n{\n    io_service io;\n    tcp::socket socket(io);\n    open_socket_to_host(io, socket, host(), port());\n    session s(socket.native());\n}\n\nBOOST_AUTO_TEST_CASE(custom_message)\n{\n    io_service io;\n    tcp::socket socket(io);\n    open_socket_to_host(io, socket, host(), port());\n    session s(socket.native(), \"blah\");\n}\n\nBOOST_AUTO_TEST_CASE(swap)\n{\n    io_service io;\n\n    // BOTH sockets must be created before first session.  Once swapped,\n    // second socket is used by first session, so must outlive it.\n    tcp::socket socket1(io);\n    tcp::socket socket2(io);\n\n    open_socket_to_host(io, socket1, host(), port());\n    session s1(socket1.native());\n\n    open_socket_to_host(io, socket2, host(), port());\n    session s2(socket2.native());\n\n    boost::swap(s1, s2);\n}\n\nBOOST_AUTO_TEST_CASE(move_construct)\n{\n    io_service io;\n    tcp::socket socket(io);\n    open_socket_to_host(io, socket, host(), port());\n    session s1(socket.native());\n\n    session s2(move(s1));\n}\n\nBOOST_AUTO_TEST_CASE(move_assign)\n{\n    io_service io;\n\n    tcp::socket socket1(io);\n    open_socket_to_host(io, socket1, host(), port());\n    session s1(socket1.native());\n\n    tcp::socket socket2(io);\n    open_socket_to_host(io, socket2, host(), port());\n    session s2(socket2.native());\n\n    s2 = move(s1);\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/sftp_fixture.cpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n#include <ssh/stream.hpp>\n\n#include <boost/bind.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/uuid/uuid_generators.hpp> // random_generator\n#include <boost/uuid/uuid_io.hpp>         // to_string\n\n#include <string>\n\nusing ssh::filesystem::directory_iterator;\nusing ssh::filesystem::ofstream;\nusing ssh::filesystem::path;\nusing ssh::filesystem::sftp_file;\nusing ssh::filesystem::sftp_filesystem;\nusing ssh::session;\n\nusing boost::bind;\nusing boost::uuids::random_generator;\n\nusing test::ssh::session_fixture;\n\nusing std::string;\n\nnamespace\n{\n\nbool filename_matches(const string& filename, const sftp_file& remote_file)\n{\n    return filename == remote_file.path().filename();\n}\n}\n\nnamespace test\n{\nnamespace ssh\n{\n\nsftp_fixture::sftp_fixture() : m_filesystem(authenticate_and_create_sftp())\n{\n}\n\nsftp_filesystem& sftp_fixture::filesystem()\n{\n    return m_filesystem;\n}\n\npath sftp_fixture::sandbox() const\n{\n    return \"sandbox\";\n}\n\npath sftp_fixture::absolute_sandbox() const\n{\n    return \"/home/swish/sandbox\";\n}\n\nsftp_file sftp_fixture::find_file_in_sandbox(const string& filename)\n{\n    directory_iterator it = filesystem().directory_iterator(sandbox());\n    directory_iterator pos = find_if(it, filesystem().directory_iterator(),\n                                     bind(filename_matches, filename, _1));\n    BOOST_REQUIRE(pos != filesystem().directory_iterator());\n\n    return *pos;\n}\n\npath sftp_fixture::new_file_in_sandbox()\n{\n    random_generator generator;\n\n    path filename = to_string(generator());\n    return new_file_in_sandbox(filename);\n}\n\npath sftp_fixture::new_file_in_sandbox(const path& filename)\n{\n    path file = sandbox() / filename;\n    ofstream(filesystem(), file).close();\n    return file;\n}\n\npath sftp_fixture::new_file_in_sandbox_containing_data(const string& data)\n{\n    path p = new_file_in_sandbox();\n    ofstream s(filesystem(), p);\n\n    s.write(data.data(), data.size());\n\n    return p;\n}\n\npath sftp_fixture::new_file_in_sandbox_containing_data(const path& name,\n                                                       const string& data)\n{\n    path p = new_file_in_sandbox(name);\n    ofstream s(filesystem(), p);\n\n    s.write(data.data(), data.size());\n\n    return p;\n}\n\npath sftp_fixture::new_directory_in_sandbox()\n{\n    random_generator generator;\n\n    path directory_name = to_string(generator());\n    path directory = sandbox() / directory_name;\n    create_directory(filesystem(), directory);\n    return directory;\n}\n\nvoid sftp_fixture::create_symlink(const path& link, const path& target)\n{\n    // Passing arguments in the wrong order to work around OpenSSH bug\n    ::ssh::filesystem::create_symlink(filesystem(), target, link);\n}\n\nsftp_filesystem sftp_fixture::authenticate_and_create_sftp()\n{\n    session& s = test_session();\n    s.authenticate_by_key_files(user(), public_key_path(), private_key_path(),\n                                \"\");\n    return s.connect_to_filesystem();\n}\n}\n}\n"
  },
  {
    "path": "test/ssh/sftp_fixture.hpp",
    "content": "// Copyright 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#ifndef TEST_SSH_SFTP_FIXTURE_HPP\n#define TEST_SSH_SFTP_FIXTURE_HPP\n\n#include \"session_fixture.hpp\"\n\n#include <ssh/filesystem.hpp>\n\n#include <string>\n\nnamespace test\n{\nnamespace ssh\n{\nclass sftp_fixture : public session_fixture\n{\npublic:\n    sftp_fixture();\n\n    ::ssh::filesystem::sftp_filesystem& filesystem();\n\n    ::ssh::filesystem::path sandbox() const;\n\n    ::ssh::filesystem::path absolute_sandbox() const;\n\n    ::ssh::filesystem::sftp_file\n    find_file_in_sandbox(const std::string& filename);\n\n    ::ssh::filesystem::path new_file_in_sandbox();\n\n    ::ssh::filesystem::path\n    new_file_in_sandbox(const ::ssh::filesystem::path& filename);\n\n    ::ssh::filesystem::path\n    new_file_in_sandbox_containing_data(const std::string& data);\n\n    ::ssh::filesystem::path\n    new_file_in_sandbox_containing_data(const ::ssh::filesystem::path& name,\n                                        const std::string& data);\n\n    ::ssh::filesystem::path new_directory_in_sandbox();\n\n    void create_symlink(const ::ssh::filesystem::path& link,\n                        const ::ssh::filesystem::path& target);\n\nprivate:\n    ::ssh::filesystem::sftp_filesystem authenticate_and_create_sftp();\n\n    ::ssh::filesystem::sftp_filesystem m_filesystem;\n};\n}\n}\n\n#endif\n"
  },
  {
    "path": "test/ssh/ssh_server/Dockerfile",
    "content": "# Copyright 2016 Alexander Lamaison\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU 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 General Public License for more details.\n\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http:#www.gnu.org/licenses/>.\n\nFROM debian:jessie\n\nRUN apt-get update \\\n && apt-get install -y openssh-server \\\n && apt-get clean \\\n && rm -rf /var/lib/apt/lists/*\nRUN mkdir /var/run/sshd\n\n# Chmodding because, when building on Windows, files are copied in with\n# -rwxr-xr-x permissions.\n#\n# Copying to a temp location, then moving because chmodding the copied file has\n# no effect (Docker AUFS-related bug maybe?)\nCOPY ssh_host_rsa_key /tmp/etc/ssh/ssh_host_rsa_key\nRUN mv /tmp/etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key\nRUN chmod 600 /etc/ssh/ssh_host_rsa_key\n\nRUN adduser --disabled-password --gecos 'Test user for Swish integration tests' swish\nRUN echo 'swish:my test password' | chpasswd\n\nRUN sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config\n\n# SSH login fix. Otherwise user is kicked off after login\nRUN sed 's@session\\s*required\\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\nUSER swish\n\nRUN mkdir -p /home/swish/.ssh\nRUN mkdir -p /home/swish/sandbox\n\nCOPY authorized_keys /tmp/swish/.ssh/authorized_keys\nRUN cp /tmp/swish/.ssh/authorized_keys /home/swish/.ssh/authorized_keys\nRUN chmod 600 /home/swish/.ssh/authorized_keys\n\nUSER root\n\nEXPOSE 22\n# # -e gives logs via 'docker logs'\nCMD [\"/usr/sbin/sshd\", \"-D\", \"-e\"]\n"
  },
  {
    "path": "test/ssh/ssh_server/authorized_keys",
    "content": "ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty\nssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty\n"
  },
  {
    "path": "test/ssh/ssh_server/ssh_host_rsa_key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6\ncvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99\nfnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB\neNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMA\nwQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQK\nypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbwIBIwKCAQAd9Cu9heWrs+UAinvv\nIwmq/EhnDGQijJoOt1zEMrpXSekyq7mQDgN0SZdJLPeSlSRQ5nVq5/dZroYB3A5i\nE7N3F7nibcJskWq5rcMyGjQHwod8wqfMiGcL6mjeZu2jLXprm0NDpJ3DyicbCA2G\nEhnpoHmktIBE5FsslI/nHer2o6OA/kVWSEjak+pvI1pm22T8QOBBfY0yAX7B0ebk\n8o4lB4cdLf3In7Q0ahpHNOwIPdRvQ2c4Tm/DcfUBkTW2ZYGUd45cFsyHqXZscNNy\nGX2Wcy/FLEvQ6zBFJsNLpxCYsUyBxfSDygn9dx9RQfiWFXjdRaRPpyRAr+BTXkLU\nyvabAoGBANt7sxfjvu/SLkRc7TnBoJ0h/AL7Mcuu9PJmOnis4boyF9ZxqbiRiS3J\nyK+EKxfC0S+xf5WJ5uf7dVGnOXHXKaRl4xH90iRtryNlvtILZwHw1DTqRFxv9jtz\ntTRrYMEHAnMKzadgDfV/lv4iJ6nwFzK76GQ7RQNZYiGTMEh3pUNjAoGBAMvNLGpz\nFxhpIh+fVvRjawKgGVP87T482WOUdsF18EEPFMe6D7DO5xpLuJi+C7QkvMI8WjvD\n/3RGvaSh9Wt7ikLZpeogiSJy121HsEqheTR5hTx2t72ClrjZvIhLbQMRu6PqGPu/\nHOC2urEGGYm7O2vnftwpuG3zIVVLM2KstPCFAoGBAM7w+VEJ7opYdMQdGi8kRvqN\nwbmrAxCAY0ryrCijALbexgS0T5DDu9q28Gr49W4stpquq35dc0/BNBnJje7+EVHc\naGFrqOCErHHU9b66Sy23LnsIxBykFAwrRHNAq66u1mx35nk9Tv1pq58nhHun21u4\nfAa7ijZblwm2qd3tJsqBAoGAEXf8ficfPJtMEVbM8GBLADmbxV7Sga1xuBQKLdbo\ntR6MwKmMUPvKqnuE2eRnZzZZUnoznrkHRHsXkcS9Q7ohyzc6G2Hf3mGdb8RQ8HQ9\nlsiWZESwqdf+SlvOVNND27EQFV01V2gnC/JnxgfWTaJVjOf07ky4CWycdQZyHmaT\nKo8CgYB58jOyXMdo2ggOCG/HX2H92KPPpFUBFCX27fCue8BZLD5quIltpXupx5oj\nEyltgvPcmNDgvdSadkHvP5s6nykS+n5we+d9yIIJF/BfETWsXjR3ooip+trqiirw\n0aHqUDFcYn9unm2wtrMYYViiDLRijNwLZ2sG0JIU4JHyseh+NA==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test/ssh/stream_threading_test.cpp",
    "content": "// Copyright 2013, 2016 Alexander Lamaison\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU 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 General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sftp_fixture.hpp\"\n\n#include <ssh/stream.hpp> // test subject\n\n#include <boost/bind/bind.hpp>\n#include <boost/test/unit_test.hpp>\n#include <boost/thread/future.hpp> // packaged_task\n#include <boost/thread/thread.hpp>\n\n#include <string>\n\nusing ssh::filesystem::ifstream;\nusing ssh::filesystem::fstream;\nusing ssh::filesystem::path;\nusing ssh::filesystem::sftp_filesystem;\n\nusing boost::bind;\nusing boost::packaged_task;\nusing boost::thread;\n\nusing test::ssh::sftp_fixture;\n\nusing std::string;\n\nnamespace\n{\n\n// the large data must fill more than one stream buffer (currently set to\n// 32768 (see DEFAULT_BUFFER_SIZE)\n\nstring large_data()\n{\n    string data;\n    for (int i = 0; i < 32000; ++i)\n    {\n        data.push_back('a');\n        data.push_back('m');\n        data.push_back('z');\n    }\n\n    return data;\n}\n}\n\nBOOST_FIXTURE_TEST_SUITE(stream_threading_test, sftp_fixture)\n\nnamespace\n{\n\nstring get_first_token(ifstream& stream)\n{\n    string r;\n    stream >> r;\n    return r;\n}\n}\n\nBOOST_AUTO_TEST_CASE(stream_read_on_different_threads)\n{\n    path target1 = new_file_in_sandbox_containing_data(\"humpty dumpty sat\");\n    path target2 = new_file_in_sandbox_containing_data(\"on the wall\");\n\n    sftp_filesystem& chan = filesystem();\n\n    ifstream s1(chan, target1);\n    ifstream s2(chan, target2);\n\n    packaged_task<string> p1(boost::bind(get_first_token, boost::ref(s1)));\n    packaged_task<string> p2(boost::bind(get_first_token, boost::ref(s2)));\n\n    thread(boost::ref(p1)).detach();\n    thread(boost::ref(p2)).detach();\n\n    BOOST_CHECK_EQUAL(p1.get_future().get(), \"humpty\");\n    BOOST_CHECK_EQUAL(p2.get_future().get(), \"on\");\n}\n\n// There was a bug in our session locking that meant we locked the session\n// when opening a file but didn't when closing it (because it happened in\n// the shared_ptr destructor).  This test case triggers that bug by opening\n// a file (locks and unlocks session), starting to read from a second file\n// (locks) session and then closing the first file.  This will cause all\n// sorts of bad behaviour of the closure doesn't lock the session so we can\n// detect it if it regresses.\nBOOST_AUTO_TEST_CASE(parallel_file_closing)\n{\n    string data = large_data();\n\n    path read_me = new_file_in_sandbox_containing_data(data);\n    path test_me = new_file_in_sandbox();\n\n    ifstream stream1(filesystem(), read_me);\n    ifstream stream2(filesystem(), test_me);\n\n    // Using a long-running stream read operation to make sure the session\n    // is still locked when we try to close the other file\n    packaged_task<string> ps(bind(get_first_token, boost::ref(stream1)));\n    thread(boost::ref(ps)).detach();\n\n    thread(bind(&ifstream::close, &stream2)).detach();\n\n    BOOST_CHECK_EQUAL(ps.get_future().get(), data);\n}\n\nBOOST_AUTO_TEST_SUITE_END();\n"
  },
  {
    "path": "test/ssh/test_known_hosts",
    "content": "host1.example.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\nunrecognisedkey.example.com,192.168.2.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\nhost2.example.com,10.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\nhost3.example.com,192.168.1.1 ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\n"
  },
  {
    "path": "test/ssh/test_known_hosts_hashed",
    "content": "# There are two entries for each host, first the hashed IP address, second\n# the hashed host.  If you regenerate this file from test_known_hosts\n# using ssh-keygen -H and the erasure tests start failing, ssh-keygen probably\n# reordered them so that the host has is first.  Just swap each pair of lines\n# to fix this.\n|1|zgCaq7/YEx5K8JidOuvMUgLen5M=|2E8Pgkzd8Izo5yUCTkdUHJYxmOE= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ==\n|1|t3DdA7tkFZOFbi/s0eZ7J5+EFoo=|pZVcJk4rGYIZTdmM56xF75BUBIA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ==\n|1|omREKWLfSo4emtLIvwB/TQrLwAk=|banp/Ra/d10UwM5WwLtzMUeKd7M= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n|1|TdmIYPmnBFnDMkYhDtKjnoIyW8M=|tsCyyCykyxVRnt2NejdekCgEqFo= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n|1|QoctyHBi5b2zysdvpnPn/nh7jXI=|UhxbMpvA8OkmFMJBm5ym3Enfurc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n|1|sf6lAuYow3JBSdiZQ99BP4Qnbqc=|LeWkRRPlTMoztEws6an/K6vHwNY= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n|1|MNSfXoAF6FmLI6GUb1hOh+rzd40=|eM/07xIcp1edKt3h4kUsnzyjKjY= ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w==\n|1|oS2oUoU87z0tnrn6xoZlietQCyo=|k1m5Wo5oSXL5ddO0LgnMtXwg5FY= ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w==\n"
  },
  {
    "path": "test/ssh/test_known_hosts_out",
    "content": "192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\nhost1.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA9QcrMH117S7SNIzhExJJmbKlCqxcIt2QQ5B4gZnix8RJci8U/z2P1noALl+oJ59gD9IuJZBXxjDQhxCRHWuvwNPax4BvtZwew0VnXlrs75nCqtFVwcWPUlSU5ycp958YJ3uKQs9yQffgu+LDU29QJ+r7yQSx/YJPgD+DpVeWG1YNqRbodUYQKWktto3OFJi4cO8t7fAteK+u+x26JQdMtplj/xrR8FNNghMyT7Rckh54/KrEdbEldwXTbp1bm9zDny9OSK6cwVjAk8zdNHCLx9/uurlSNcDRZXCDx3yRJiv8Q4ne0kmbMm4QFeigFf3QY7rGUgBEm/wMgxggdvLUCQ== test@swish\n192.168.2.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\nunrecognisedkey.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOnHj5Uw9UIVhG/qJfqz4MXX0AqaIvsX/cO3Y2rR6qRo6HUDS4mD3QPLQxw2tDTs12Iji5v/mWUerKPwnRx1E7E=\n10.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\nhost2.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvKS1ply6S6xcb/pxnJQQEB+y123axJUKsYEk2ezsHRNZP920FNM1KXGMmm+i7KugMk7dz46pkE/p4qJ4qVfoeDKojR4GiP1WleKQniTIdgEYho7OmopOUszST1Qo5PK9e2gvVQcsyE6xEJkBdMlBWqfm/2vfyr92IPW1wtR3j3YYCcaMVMdpo0tHiK4qmVJIGcs4BRYRSeWzSFaFdmkhEM7iRxCgQDLykjQEZcKmF5KUEf+SxfNS51B0O4D2aoamsYaAC849HBJgMS/I5CxLAah2uMQXnZwJrCIUZcZDUQrC7LnSgd86P+yDFZYbAkXz8QjhGL/qTywA7Afglyt5/w==\n192.168.1.1 ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\nhost3.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAL+sTKUuo0M9zhbDq414IEA8S3FJWliDJNaO3isqDuh3aEEb2wyDrsTf5b6R73RsrAD6K5b3xfMox7LhjwET3D63OpNmU+SUEJl3oJ/yujPHE87aOkt402tB82+yed6V2/Wy4eLcihi4r4VJie9WaBbezvxYbB+hV8YpaoktvI5PAAAAFQCmyKgsrs/7HtA/WVk2iT4av4dmuQAAAIB4hWeAov90067UdbadIq67v7JM8gFBHRertp33nSYDUvMwqCguiTEnBiOCvdKqGRy6RnnmXgMFqqqE6mHDOMZRQdVCn6M402CYJQ0+HefsC3WGI3DLIygHJgAjUswb8qg83ddYhcgLqF4vGqoqUr4Cxsgy3k9zOXEH+NoCylXW9gAAAIAakCvnTYROP7rqRx7zAlHElQnbjH7D1/6yBvt2JmkPHxmsxQPhiwrlTJqkkCztunLmvO4Z+BoB23HQ6utyC4ZBA40dB/Bpq+jbQUq1RLmhlHULqVT/2Z9QLHHcygBddKrUZznsk1/IQcyLHk77/cxQn6dW+B/7G7AdBc4MYMGM/w== test@swish\n"
  },
  {
    "path": "test/versions/CMakeLists.txt",
    "content": "# Copyright (C) 2015  Alexander Lamaison <swish@lammy.co.uk>\n#\n# This program is free software: you can redistribute it and/or modify it under\n# the terms of the GNU General Public License as published by the Free Software\n# Foundation, either version 3 of the License, or (at your option) any later\n# version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program.  If not, see <http://www.gnu.org/licenses/>.\n\nset(SOURCES\n  version_test.cpp)\n\nswish_test_suite(\n  SUBJECT versions\n  SOURCES ${SOURCES}\n  LIBRARIES ${Boost_LIBRARIES}\n  LABELS unit)\n"
  },
  {
    "path": "test/versions/version_test.cpp",
    "content": "/**\n    @file\n\n    Unit tests for version info.\n\n    @if license\n\n    Copyright (C) 2013  Alexander Lamaison <awl03@doc.ic.ac.uk>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU 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 General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n    If you modify this Program, or any covered work, by linking or\n    combining it with the OpenSSL project's OpenSSL library (or a\n    modified version of that library), containing parts covered by the\n    terms of the OpenSSL or SSLeay licenses, the licensors of this\n    Program grant you additional permission to convey the resulting work.\n\n    @endif\n*/\n\n#include <boost/test/unit_test.hpp>\n\n#include <swish/versions/version.hpp>\n\n#include <string>\n\nusing std::string;\n\nBOOST_AUTO_TEST_SUITE( version_test )\n\n/**\n * Sensible snapshot version result.\n */\nBOOST_AUTO_TEST_CASE( snapshot )\n{\n    string version = swish::snapshot_version();\n    \n    BOOST_WARN_MESSAGE(\n        !version.empty(), \"Legal, but unfortunate, snapshot description\");\n}\n\nBOOST_AUTO_TEST_CASE( release_numeric )\n{\n    swish::structured_version version = swish::release_version();\n    \n    BOOST_CHECK_LT(version.major(), 50);\n    BOOST_CHECK_GE(version.major(), 0);\n\n    BOOST_CHECK_LT(version.minor(), 500);\n    BOOST_CHECK_GE(version.minor(), 0);\n\n    BOOST_CHECK_LT(version.bugfix(), 500);\n    BOOST_CHECK_GE(version.bugfix(), 0);\n}\n\n/**\n * Sensible release version string result.\n */\nBOOST_AUTO_TEST_CASE( release_string )\n{\n    string version = swish::release_version().as_string();\n    \n    BOOST_CHECK_MESSAGE(\n        !version.empty(), \"Release version not allowed to be empty\");\n}\n\nBOOST_AUTO_TEST_CASE( timestamp )\n{\n    BOOST_CHECK_MESSAGE(\n        !swish::build_date().empty(), \"Build date not allowed to be empty\");\n    BOOST_CHECK_MESSAGE(\n        !swish::build_time().empty(), \"Build time not allowed to be empty\");\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialog.h",
    "content": "#if !defined(AFX_TASKDIALOG_H__20073232_5AA0_1E88_3A6D_0080AD509054__INCLUDED_)\r\n#define AFX_TASKDIALOG_H__20073232_5AA0_1E88_3A6D_0080AD509054__INCLUDED_\r\n\r\n#if _MSC_VER > 1000\r\n#pragma once\r\n#endif // _MSC_VER > 1000\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// CTask98Dialog - Task Dialog for legacy Windows platforms\r\n//\r\n// Written by Bjarke Viksoe (bjarke@viksoe.dk)\r\n// Copyright (c) 2007 Bjarke Viksoe.\r\n//\r\n// Thanks to David Brown and Yarp of senosoft.com \r\n// for submitting several fixes.\r\n//\r\n// This code may be used in compiled form in any way you desire. This\r\n// source file may be redistributed by any means PROVIDING it is \r\n// not sold for profit without the authors written consent, and \r\n// providing that this notice and the authors name is included. \r\n//\r\n// This file is provided \"as is\" with no expressed or implied warranty.\r\n// The author accepts no liability if it causes any damage to you or your\r\n// computer whatsoever. It's free, so don't hassle me about it.\r\n//\r\n// Beware of bugs.\r\n\r\n\r\n// Dual licensed under the GPL with permission:\r\n//\r\n// Copyright (c) 2007 Bjarke Viksoe <bjarke@viksoe.dk>\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 General Public License as published by\r\n// the Free Software Foundation; either version 2 of the License, or\r\n// (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 General Public License for more details.\r\n// \r\n// You should have received a copy of the GNU General Public License along\r\n// with this program; if not, write to the Free Software Foundation, Inc.,\r\n// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r\n\r\n#include \"icons.h\"\r\n\r\n#include \"atlapp.h\"\r\n#include \"atlctrls.h\"\r\n#include \"atldlgs.h\"\r\n\r\n#define USER32_IDS_OK      800\r\n#define USER32_IDS_CANCEL  801\r\n#define USER32_IDS_RETRY   803\r\n#define USER32_IDS_YES     805\r\n#define USER32_IDS_NO      806\r\n#define USER32_IDS_CLOSE   807\r\n\r\n/////////////////////////////////////////////////////////////////////////\r\n// TaskDialog declares\r\n//\r\n\r\n#if _WIN32_WINNT < 0x0501\r\n\r\n#ifdef _WIN32\r\n#include <pshpack1.h>\r\n#endif // _WIN32\r\n\r\n#define MAX_LINKID_TEXT 48\r\n\r\n#define L_MAX_URL_LENGTH (2048 + 32 + sizeof(\"://\"))\r\n\r\ntypedef struct tagLITEM\r\n{\r\n  UINT  mask;\r\n  int   iLink;\r\n  UINT  state;\r\n  UINT  stateMask;\r\n  WCHAR szID[MAX_LINKID_TEXT];\r\n  WCHAR szUrl[L_MAX_URL_LENGTH];\r\n} LITEM, *PLITEM;\r\n\r\ntypedef struct tagNMLINK\r\n{\r\n  NMHDR hdr;\r\n  LITEM item;\r\n} NMLINK, *PNMLINK;\r\n\r\nclass CLinkCtrl\r\n{\r\npublic:\r\n   static LPCTSTR GetWndClassName()\r\n   {\r\n      return _T(\"SysLink\");\r\n   }\r\n};\r\n\r\n#define BCM_FIRST               0x1600      // Button control messages\r\n#define BCM_SETIMAGELIST        (BCM_FIRST + 0x0002)\r\n\r\ntypedef struct\r\n{\r\n    HIMAGELIST  himl;\r\n    RECT        margin;\r\n    UINT        uAlign;\r\n} BUTTON_IMAGELIST, *PBUTTON_IMAGELIST;\r\n\r\n\r\n#ifdef _WIN32\r\n#include <poppack.h>\r\n#endif // _WIN32\r\n\r\n#endif // _WIN32_WINNT\r\n\r\n\r\n#if _WIN32_WINNT < 0x0600 && !defined(TD_WARNING_ICON)\r\n\r\n#ifdef _WIN32\r\n#include <pshpack1.h>\r\n#endif // _WIN32\r\n\r\ntypedef HRESULT (CALLBACK *PFTASKDIALOGCALLBACK)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData);\r\n\r\nenum _TASKDIALOG_FLAGS\r\n{\r\n    TDF_ENABLE_HYPERLINKS               = 0x0001,\r\n    TDF_USE_HICON_MAIN                  = 0x0002,\r\n    TDF_USE_HICON_FOOTER                = 0x0004,\r\n    TDF_ALLOW_DIALOG_CANCELLATION       = 0x0008,\r\n    TDF_USE_COMMAND_LINKS               = 0x0010,\r\n    TDF_USE_COMMAND_LINKS_NO_ICON       = 0x0020,\r\n    TDF_EXPAND_FOOTER_AREA              = 0x0040,\r\n    TDF_EXPANDED_BY_DEFAULT             = 0x0080,\r\n    TDF_VERIFICATION_FLAG_CHECKED       = 0x0100,\r\n    TDF_SHOW_PROGRESS_BAR               = 0x0200,\r\n    TDF_SHOW_MARQUEE_PROGRESS_BAR       = 0x0400,\r\n    TDF_CALLBACK_TIMER                  = 0x0800,\r\n    TDF_POSITION_RELATIVE_TO_WINDOW     = 0x1000,\r\n    TDF_RTL_LAYOUT                      = 0x2000,\r\n    TDF_NO_DEFAULT_RADIO_BUTTON         = 0x4000,\r\n    TDF_CAN_BE_MINIMIZED                = 0x8000\r\n};\r\ntypedef int TASKDIALOG_FLAGS;   \r\n\r\ntypedef enum _TASKDIALOG_MESSAGES\r\n{\r\n    TDM_NAVIGATE_PAGE                       = WM_USER + 101,\r\n    TDM_CLICK_BUTTON                        = WM_USER + 102,\r\n    TDM_SET_MARQUEE_PROGRESS_BAR            = WM_USER + 103,\r\n    TDM_SET_PROGRESS_BAR_STATE              = WM_USER + 104,\r\n    TDM_SET_PROGRESS_BAR_RANGE              = WM_USER + 105,\r\n    TDM_SET_PROGRESS_BAR_POS                = WM_USER + 106,\r\n    TDM_SET_PROGRESS_BAR_MARQUEE            = WM_USER + 107,\r\n    TDM_SET_ELEMENT_TEXT                    = WM_USER + 108,\r\n    TDM_CLICK_RADIO_BUTTON                  = WM_USER + 110,\r\n    TDM_ENABLE_BUTTON                       = WM_USER + 111,\r\n    TDM_ENABLE_RADIO_BUTTON                 = WM_USER + 112,\r\n    TDM_CLICK_VERIFICATION                  = WM_USER + 113,\r\n    TDM_UPDATE_ELEMENT_TEXT                 = WM_USER + 114,\r\n    TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE = WM_USER + 115,\r\n    TDM_UPDATE_ICON                         = WM_USER + 116 \r\n} TASKDIALOG_MESSAGES;\r\n\r\ntypedef enum _TASKDIALOG_NOTIFICATIONS\r\n{\r\n    TDN_CREATED                         = 0,\r\n    TDN_NAVIGATED                       = 1,\r\n    TDN_BUTTON_CLICKED                  = 2,\r\n    TDN_HYPERLINK_CLICKED               = 3,\r\n    TDN_TIMER                           = 4,\r\n    TDN_DESTROYED                       = 5,\r\n    TDN_RADIO_BUTTON_CLICKED            = 6,\r\n    TDN_DIALOG_CONSTRUCTED              = 7,\r\n    TDN_VERIFICATION_CLICKED            = 8,\r\n    TDN_HELP                            = 9,\r\n    TDN_EXPANDO_BUTTON_CLICKED          = 10\r\n} TASKDIALOG_NOTIFICATIONS;\r\n\r\ntypedef struct _TASKDIALOG_BUTTON\r\n{\r\n    int     nButtonID;\r\n    PCWSTR  pszButtonText;\r\n} TASKDIALOG_BUTTON;\r\n\r\ntypedef enum _TASKDIALOG_ELEMENTS\r\n{\r\n    TDE_CONTENT,\r\n    TDE_EXPANDED_INFORMATION,\r\n    TDE_FOOTER,\r\n    TDE_MAIN_INSTRUCTION\r\n} TASKDIALOG_ELEMENTS;\r\n\r\ntypedef enum _TASKDIALOG_ICON_ELEMENTS\r\n{\r\n    TDIE_ICON_MAIN,\r\n    TDIE_ICON_FOOTER\r\n} TASKDIALOG_ICON_ELEMENTS;\r\n\r\n#define TD_WARNING_ICON         MAKEINTRESOURCEW(-1)\r\n#define TD_ERROR_ICON           MAKEINTRESOURCEW(-2)\r\n#define TD_INFORMATION_ICON     MAKEINTRESOURCEW(-3)\r\n#define TD_SHIELD_ICON          MAKEINTRESOURCEW(-4)\r\n\r\nenum _TASKDIALOG_COMMON_BUTTON_FLAGS\r\n{\r\n    TDCBF_OK_BUTTON            = 0x0001, // selected control return value IDOK\r\n    TDCBF_YES_BUTTON           = 0x0002, // selected control return value IDYES\r\n    TDCBF_NO_BUTTON            = 0x0004, // selected control return value IDNO\r\n    TDCBF_CANCEL_BUTTON        = 0x0008, // selected control return value IDCANCEL\r\n    TDCBF_RETRY_BUTTON         = 0x0010, // selected control return value IDRETRY\r\n    TDCBF_CLOSE_BUTTON         = 0x0020  // selected control return value IDCLOSE\r\n};\r\ntypedef int TASKDIALOG_COMMON_BUTTON_FLAGS;\r\n\r\ntypedef struct _TASKDIALOGCONFIG\r\n{\r\n    UINT        cbSize;\r\n    HWND        hwndParent;\r\n    HINSTANCE   hInstance;                              // used for MAKEINTRESOURCE() strings\r\n    TASKDIALOG_FLAGS                dwFlags;            // TASKDIALOG_FLAGS (TDF_XXX) flags\r\n    TASKDIALOG_COMMON_BUTTON_FLAGS  dwCommonButtons;    // TASKDIALOG_COMMON_BUTTON (TDCBF_XXX) flags\r\n    PCWSTR      pszWindowTitle;                         // string or MAKEINTRESOURCE()\r\n    union\r\n    {\r\n        HICON   hMainIcon;\r\n        PCWSTR  pszMainIcon;\r\n    };\r\n    PCWSTR      pszMainInstruction;\r\n    PCWSTR      pszContent;\r\n    UINT        cButtons;\r\n    const TASKDIALOG_BUTTON  *pButtons;\r\n    int         nDefaultButton;\r\n    UINT        cRadioButtons;\r\n    const TASKDIALOG_BUTTON  *pRadioButtons;\r\n    int         nDefaultRadioButton;\r\n    PCWSTR      pszVerificationText;\r\n    PCWSTR      pszExpandedInformation;\r\n    PCWSTR      pszExpandedControlText;\r\n    PCWSTR      pszCollapsedControlText;\r\n    union\r\n    {\r\n        HICON   hFooterIcon;\r\n        PCWSTR  pszFooterIcon;\r\n    };\r\n    PCWSTR      pszFooter;\r\n    PFTASKDIALOGCALLBACK pfCallback;\r\n    LONG_PTR    lpCallbackData;\r\n    UINT        cxWidth;\r\n} TASKDIALOGCONFIG;\r\n\r\n#ifdef _WIN32\r\n#include <poppack.h>\r\n#endif // _WIN32\r\n\r\n#endif // _WIN32_WINNT\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////\r\n// TaskDialog dialog implementation\r\n//\r\n\r\ntemplate< class T >\r\nclass CTask98DialogImpl : public WTL::CIndirectDialogImpl<T>, public WTL::CDialogBaseUnits\r\n{\r\npublic:\r\n   enum\r\n   {\r\n      IDC_TASKDLG_OK = IDOK,\r\n      IDC_TASKDLG_YES = IDYES,\r\n      IDC_TASKDLG_NO = IDNO,\r\n      IDC_TASKDLG_CANCEL = IDCANCEL,\r\n      IDC_TASKDLG_RETRY = IDRETRY,\r\n      IDC_TASKDLG_CLOSE = IDCLOSE,\r\n      //\r\n      IDC_TASKDLG_TITLETEXT = 0x7400,\r\n      IDC_TASKDLG_INSTRUCTIONSTEXT,\r\n      IDC_TASKDLG_INSTRUCTIONSICON,\r\n      IDC_TASKDLG_CONTENTTEXT,\r\n      IDC_TASKDLG_VERIFYBUTTON,\r\n      IDC_TASKDLG_EXPANDERICON,\r\n      IDC_TASKDLG_EXPANDERTEXT,\r\n      IDC_TASKDLG_EXPANDERHIDDENBUTTON,\r\n      IDC_TASKDLG_EXTRATEXT,\r\n      IDC_TASKDLG_FOOTERTEXT,\r\n      IDC_TASKDLG_FOOTERICON,\r\n      IDC_TASKDLG_PROGRESSBAR,\r\n      //\r\n      IDC_TASKDLG_CUSTOMBUTTON_FIRST,\r\n      IDC_TASKDLG_CUSTOMBUTTON_LAST = IDC_TASKDLG_CUSTOMBUTTON_FIRST + 20,\r\n      IDC_TASKDLG_RADIOBUTTON_FIRST,\r\n      IDC_TASKDLG_RADIOBUTTON_LAST = IDC_TASKDLG_RADIOBUTTON_FIRST + 20,\r\n   };\r\n\r\n   enum \r\n   { \r\n      TIMERID_TIGGER = 4050,\r\n      TIMERID_HOVERLINK = 4051,\r\n      WIDTH_PROBE = 99999,\r\n   };\r\n\r\n   enum { MAX_TEXT_LENGTH = 2048 };\r\n\r\n   CTask98DialogImpl() : m_bNavigated(false)\r\n   {\r\n      ::ZeroMemory(&m_cfg, sizeof(m_cfg));\r\n      m_cfg.cbSize = sizeof(TASKDIALOGCONFIG);\r\n      m_cfg.hInstance = ModuleHelper::GetResourceInstance();\r\n      m_cfg.pfCallback = T::TaskDialogCallback;\r\n      m_cfg.lpCallbackData = (LONG_PTR) static_cast<T*>(this);\r\n      _Reset();\r\n   }\r\n\r\n   // Message map\r\n\r\n   BEGIN_MSG_MAP(CTask98DialogImpl)\r\n      MESSAGE_HANDLER(WM_INITDIALOG, OnWmInitDialog)\r\n      MESSAGE_HANDLER(WM_DESTROY, OnWmDestroy)\r\n      MESSAGE_HANDLER(WM_HELP, OnWmHelp)\r\n      MESSAGE_HANDLER(WM_TIMER, OnWmTimer)\r\n      MESSAGE_HANDLER(WM_ERASEBKGND, OnWmEraseBkgnd)\r\n      MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnWmCtlColor)\r\n      MESSAGE_HANDLER(WM_CTLCOLORBTN, OnWmCtlColor)\r\n      MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)\r\n      MESSAGE_HANDLER(WM_DRAWITEM, OnWmDrawItem)\r\n      MESSAGE_HANDLER(TDM_CLICK_BUTTON, OnMsgClickButton)\r\n      MESSAGE_HANDLER(TDM_CLICK_VERIFICATION, OnMsgClickVerification)\r\n      MESSAGE_HANDLER(TDM_CLICK_RADIO_BUTTON, OnMsgClickRadioButton)\r\n      MESSAGE_HANDLER(TDM_ENABLE_BUTTON, OnMsgEnableButton)\r\n      MESSAGE_HANDLER(TDM_ENABLE_RADIO_BUTTON, OnMsgEnableRadioButton)\r\n      MESSAGE_HANDLER(TDM_SET_PROGRESS_BAR_POS, OnMsgSetProgressBarPos)\r\n      MESSAGE_HANDLER(TDM_SET_PROGRESS_BAR_RANGE, OnMsgSetProgressBarRange)\r\n      MESSAGE_HANDLER(TDM_SET_PROGRESS_BAR_STATE, OnMsgSetProgressBarState)\r\n      MESSAGE_HANDLER(TDM_SET_PROGRESS_BAR_MARQUEE, OnMsgSetProgressBarMarquee)\r\n      MESSAGE_HANDLER(TDM_SET_MARQUEE_PROGRESS_BAR, OnMsgSetMarqueeProgressBar)\r\n      MESSAGE_HANDLER(TDM_SET_ELEMENT_TEXT, OnMsgSetElementText)\r\n      MESSAGE_HANDLER(TDM_UPDATE_ELEMENT_TEXT, OnMsgUpdateElementText)\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERICON, STN_CLICKED, OnMsgExpandoClick);\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERTEXT, STN_CLICKED, OnMsgExpandoClick);\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERHIDDENBUTTON, STN_CLICKED, OnMsgExpandoClick);\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERICON, STN_DBLCLK, OnMsgExpandoClick);\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERTEXT, STN_DBLCLK, OnMsgExpandoClick);\r\n      COMMAND_HANDLER(IDC_TASKDLG_EXPANDERHIDDENBUTTON, STN_DBLCLK, OnMsgExpandoClick);\r\n      COMMAND_ID_HANDLER(IDC_TASKDLG_VERIFYBUTTON, OnMsgVerificationClick);\r\n      COMMAND_RANGE_HANDLER(1, 64, OnMsgCommonButtonClick)\r\n      COMMAND_RANGE_HANDLER(IDC_TASKDLG_CUSTOMBUTTON_FIRST, IDC_TASKDLG_CUSTOMBUTTON_LAST, OnMsgCustomButtonClick)\r\n      COMMAND_RANGE_HANDLER(IDC_TASKDLG_RADIOBUTTON_FIRST, IDC_TASKDLG_RADIOBUTTON_LAST, OnMsgRadioClick)\r\n      NOTIFY_HANDLER(IDC_TASKDLG_CONTENTTEXT, NM_CLICK, OnMsgHyperlinkClicked)\r\n      NOTIFY_HANDLER(IDC_TASKDLG_FOOTERTEXT, NM_CLICK, OnMsgHyperlinkClicked)\r\n      NOTIFY_HANDLER(IDC_TASKDLG_EXTRATEXT, NM_CLICK, OnMsgHyperlinkClicked)\r\n   END_MSG_MAP()\r\n\r\n   // Operations\r\n\r\n   bool SetConfig(const TASKDIALOGCONFIG* pConfig)\r\n   {\r\n      ATLASSERT(pConfig);\r\n      m_cfg = *pConfig;\r\n      if( m_cfg.cButtons > 20 ) return false;\r\n      if( m_cfg.cRadioButtons > 20 ) return false;\r\n      if( (m_cfg.dwFlags & (TDF_USE_COMMAND_LINKS|TDF_USE_COMMAND_LINKS_NO_ICON)) != 0 && m_cfg.cButtons == 0 ) return FALSE;\r\n      if( m_cfg.pszExpandedInformation != NULL && m_cfg.pszExpandedControlText == NULL ) m_cfg.pszExpandedControlText = L\"Hide &details\";\r\n      if( m_cfg.pszExpandedInformation != NULL && m_cfg.pszCollapsedControlText == NULL ) m_cfg.pszCollapsedControlText= L\"Show &details\";\r\n      return true;\r\n   }\r\n\r\n   bool IsNavigated() const\r\n   {\r\n      return m_bNavigated;\r\n   }\r\n\r\n   void GetDialogResult(int* pnButton, int* pnRadioButton, BOOL* pfVerificationFlagChecked) const\r\n   {\r\n      ATLASSERT(!::IsWindow(m_hWnd));\r\n      if( pnButton != NULL ) *pnButton = m_iButtonResult;\r\n      if( pnRadioButton != NULL ) *pnRadioButton = m_iRadioResult;\r\n      if( pfVerificationFlagChecked != NULL ) *pfVerificationFlagChecked = m_bVerificationResult;\r\n   }\r\n\r\n   BOOL NavigatePage(TASKDIALOGCONFIG* pTaskConfig)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      if( !SetConfig(pTaskConfig) ) return FALSE;\r\n      m_bNavigated = true;\r\n      // BUG: Unlike the real Task Dialog we destroy the window and recreate\r\n      //      a new dialog. There will be flicker and possible reposition of window.\r\n      return EndDialog(IDCANCEL);\r\n   }\r\n\r\n   BOOL ClickButton(UINT uID)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      CButton ctrl = GetDlgItem(uID);\r\n      for( UINT i = 0; i < m_cfg.cButtons; i++ ) {\r\n         if( m_cfg.pButtons[i].nButtonID == (int) uID ) ctrl = GetDlgItem(IDC_TASKDLG_CUSTOMBUTTON_FIRST + i);\r\n      }\r\n      if( !ctrl.IsWindow() ) return FALSE;\r\n      ctrl.Click();\r\n      return TRUE;\r\n   }\r\n\r\n   BOOL EnableButton(UINT uID, BOOL bEnable)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      CButton ctrl = GetDlgItem(uID);\r\n      for( UINT i = 0; i < m_cfg.cButtons; i++ ) {\r\n         if( m_cfg.pButtons[i].nButtonID == (int) uID ) ctrl = GetDlgItem(IDC_TASKDLG_CUSTOMBUTTON_FIRST + i);\r\n      }\r\n      if( !ctrl.IsWindow() ) return FALSE;\r\n      return ctrl.EnableWindow(bEnable);\r\n   }\r\n\r\n   BOOL ClickRadioButton(UINT uID)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      CButton ctrl;\r\n      for( UINT i = 0; i < m_cfg.cRadioButtons; i++ ) {\r\n         if( m_cfg.pRadioButtons[i].nButtonID == (int) uID ) ctrl = GetDlgItem(IDC_TASKDLG_RADIOBUTTON_FIRST + i);\r\n      }\r\n      if( !ctrl.IsWindow() ) return FALSE;\r\n      ctrl.Click();\r\n      return TRUE;\r\n   }\r\n\r\n   BOOL EnableRadioButton(UINT uID, BOOL bEnable)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      CButton ctrl;\r\n      for( UINT i = 0; i < m_cfg.cRadioButtons; i++ ) {\r\n         if( m_cfg.pRadioButtons[i].nButtonID == (int) uID ) ctrl = GetDlgItem(IDC_TASKDLG_RADIOBUTTON_FIRST + i);\r\n      }\r\n      if( !ctrl.IsWindow() ) return FALSE;\r\n      return ctrl.EnableWindow(bEnable);\r\n   }\r\n\r\n   BOOL ClickVerification(BOOL bChecked, BOOL bTakeFocus)\r\n   {\r\n      BOOL bRes = CheckDlgButton(IDC_TASKDLG_VERIFYBUTTON, bChecked ? BST_CHECKED : BST_UNCHECKED);\r\n      if( bRes && bTakeFocus ) CWindow(GetDlgItem(IDC_TASKDLG_VERIFYBUTTON)).SetFocus();\r\n      return bRes;\r\n   }\r\n\r\n   BOOL SetElementText(TASKDIALOG_ELEMENTS Element, LPCWSTR pstrText)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      UINT uID = 0;\r\n      switch( Element) {\r\n      case TDE_CONTENT:              uID = IDC_TASKDLG_CONTENTTEXT; break;\r\n      case TDE_MAIN_INSTRUCTION:     uID = IDC_TASKDLG_INSTRUCTIONSTEXT; break;\r\n      case TDE_EXPANDED_INFORMATION: uID = IDC_TASKDLG_EXTRATEXT; break;\r\n      case TDE_FOOTER:               uID = IDC_TASKDLG_FOOTERTEXT; break;\r\n      }\r\n      if( uID == 0 ) return FALSE;\r\n      LPTSTR pstrBuffer = (LPTSTR) malloc(MAX_TEXT_LENGTH * sizeof(TCHAR));\r\n      if( pstrBuffer == NULL ) return FALSE;\r\n      _LoadString(pstrText, pstrBuffer, MAX_TEXT_LENGTH);\r\n      SetDlgItemText(uID, pstrBuffer);\r\n      free(pstrBuffer);\r\n      // BUG: We need to reconstruct the dialog to make space for the new text\r\n      return TRUE;\r\n   }\r\n\r\n   BOOL UpdateElementText(TASKDIALOG_ELEMENTS Element, LPCWSTR pstrText)\r\n   {\r\n      return SetElementText(Element, pstrText);\r\n   }\r\n\r\n   BOOL UpdateIcon(TASKDIALOG_ICON_ELEMENTS Element, LPCWSTR pstrIcon)\r\n   {\r\n      ATLASSERT(::IsWindow(m_hWnd));\r\n      UINT nCtlId = 0;\r\n      switch( Element ) {\r\n      case TDIE_ICON_FOOTER:  \r\n         nCtlId = IDC_TASKDLG_FOOTERICON; \r\n         m_iconMain = _LoadIcon(pstrIcon, m_Metrics.cxyLargeIcon); \r\n         break;\r\n      case TDIE_ICON_MAIN:    \r\n         nCtlId = IDC_TASKDLG_INSTRUCTIONSICON; \r\n         m_iconFooter = _LoadIcon(pstrIcon, m_Metrics.cxySmallIcon); \r\n         break;\r\n      default: \r\n         return FALSE;\r\n      }\r\n      CWindow wnd = GetDlgItem(nCtlId);\r\n      if( !wnd.IsWindow() ) return FALSE;\r\n      return wnd.Invalidate();\r\n   }\r\n\r\n   BOOL SetProgressBarPos(UINT nPos)\r\n   {\r\n      ATLASSERT(::IsWindow(GetDlgItem(IDC_TASKDLG_PROGRESSBAR)));\r\n      return SendDlgItemMessage(IDC_TASKDLG_PROGRESSBAR, PBM_SETPOS, nPos) != 0;\r\n   }\r\n\r\n   BOOL SetProgressBarRange(UINT nMin, UINT nMax)\r\n   {\r\n      ATLASSERT(::IsWindow(GetDlgItem(IDC_TASKDLG_PROGRESSBAR)));\r\n      return SendDlgItemMessage(IDC_TASKDLG_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(nMin, nMax)) != 0;\r\n   }\r\n\r\n   BOOL SetProgressBarState(int nNewState)\r\n   {\r\n      // Not sure why these assertions were here; marquee works fine on XP\r\n      //ATLASSERT(RunTimeHelper::IsVista());\r\n      ATLASSERT(::IsWindow(GetDlgItem(IDC_TASKDLG_PROGRESSBAR)));\r\n#ifndef PBM_SETSTATE\r\n      const UINT PBM_SETSTATE = WM_USER + 16;\r\n#endif // PBM_SETMARQUEE\r\n      return SendDlgItemMessage(IDC_TASKDLG_PROGRESSBAR, PBM_SETSTATE, (WPARAM) nNewState, 0) != 0;\r\n   }\r\n\r\n   BOOL SetProgressBarMarquee(int iMarquee, UINT uTimer)\r\n   {\r\n      // Not sure why these assertions were here; marquee works fine on XP\r\n      //ATLASSERT(RunTimeHelper::IsVista());\r\n      ATLASSERT(::IsWindow(GetDlgItem(IDC_TASKDLG_PROGRESSBAR)));\r\n#ifndef PBM_SETMARQUEE\r\n      const UINT PBM_SETMARQUEE = WM_USER + 10;\r\n#endif // PBS_MARQUEE\r\n      CWindow wnd = GetDlgItem(IDC_TASKDLG_PROGRESSBAR);\r\n      if( !wnd.IsWindow() ) return FALSE;\r\n      return wnd.SendMessage(PBM_SETMARQUEE, (WPARAM) iMarquee, uTimer) != 0;\r\n   }\r\n\r\n   void SetMarqueeProgressBar(BOOL bMarquee)\r\n   {\r\n      // Not sure why these assertions were here; marquee works fine on XP\r\n      //ATLASSERT(RunTimeHelper::IsVista());\r\n      ATLASSERT(::IsWindow(GetDlgItem(IDC_TASKDLG_PROGRESSBAR)));\r\n#ifndef PBS_MARQUEE\r\n      const DWORD PBS_MARQUEE = 0x08;\r\n#endif // PBS_MARQUEE\r\n      CWindow wnd = GetDlgItem(IDC_TASKDLG_PROGRESSBAR);\r\n      if( !wnd.IsWindow() ) return;\r\n      wnd.ModifyStyle(bMarquee ? 0 : PBS_MARQUEE, bMarquee ? PBS_MARQUEE : 0);\r\n      SetProgressBarMarquee(1, 30);\r\n   }\r\n\r\n   // Default TaskDialog callback implementation\r\n\r\n   static HRESULT CALLBACK TaskDialogCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData)\r\n   {\r\n      ATLASSERT(lpRefData!=0);\r\n      T* pT = (T*) lpRefData;\r\n      ATLASSERT(hwnd==pT->m_hWnd); hwnd;\r\n      BOOL bRet = FALSE;\r\n      switch( msg ) {\r\n      case TDN_DIALOG_CONSTRUCTED:\r\n         pT->OnDialogConstructed();\r\n         break;\r\n      case TDN_CREATED:\r\n         pT->OnCreated();\r\n         break;\r\n      case TDN_BUTTON_CLICKED:\r\n         bRet = pT->OnButtonClicked((int) wParam);\r\n         break;\r\n      case TDN_RADIO_BUTTON_CLICKED:\r\n         pT->OnRadioButtonClicked((int) wParam);\r\n         break;\r\n      case TDN_HYPERLINK_CLICKED:\r\n         pT->OnHyperlinkClicked((LPCWSTR) lParam);\r\n         break;\r\n      case TDN_EXPANDO_BUTTON_CLICKED:\r\n         pT->OnExpandoButtonClicked(wParam != 0);\r\n         break;\r\n      case TDN_VERIFICATION_CLICKED:\r\n         pT->OnVerificationClicked(wParam != 0);\r\n         break;\r\n      case TDN_HELP:\r\n         pT->OnHelp();\r\n         break;\r\n      case TDN_TIMER:\r\n         bRet = pT->OnTimer((DWORD) wParam);\r\n         break;\r\n      case TDN_NAVIGATED:\r\n         pT->OnNavigated();\r\n         break;\r\n      case TDN_DESTROYED:\r\n         pT->OnDestroyed();\r\n         break;\r\n      default:\r\n         ATLTRACE2(atlTraceUI, 0, _T(\"Unknown notification received in CTask98DialogImpl::TaskDialogCallback\\n\"));\r\n         break;\r\n      }\r\n      return (HRESULT) bRet;\r\n   }\r\n\r\n   // Overrideables - notification handlers\r\n\r\n   void OnDialogConstructed()\r\n   {\r\n   }\r\n\r\n   void OnCreated()\r\n   {\r\n   }\r\n\r\n   BOOL OnButtonClicked(int /*nButton*/)\r\n   {\r\n      return FALSE;   // don't prevent dialog to close\r\n   }\r\n\r\n   void OnRadioButtonClicked(int /*nRadioButton*/)\r\n   {\r\n   }\r\n\r\n   void OnHyperlinkClicked(LPCWSTR /*pszHREF*/)\r\n   {\r\n   }\r\n\r\n   void OnExpandoButtonClicked(bool /*bExpanded*/)\r\n   {\r\n   }\r\n\r\n   void OnVerificationClicked(bool /*bChecked*/)\r\n   {\r\n   }\r\n\r\n   void OnHelp()\r\n   {\r\n   }\r\n\r\n   BOOL OnTimer(DWORD /*dwTickCount*/)\r\n   {\r\n      return FALSE;   // don't reset counter\r\n   }\r\n\r\n   void OnNavigated()\r\n   {\r\n   }\r\n\r\n   void OnDestroyed()\r\n   {\r\n   }\r\n\r\n   // Data Members\r\n\r\n   TASKDIALOGCONFIG m_cfg;           // Copy of configuration\r\n   bool m_bNavigated;                // Dialog was navigated?\r\n   int m_iButtonResult;              // Button result\r\n   int m_iRadioResult;               // Radio-button result\r\n   BOOL m_bVerificationResult;       // Verification button result\r\n   DWORD m_dwTick;                   // Start tick when dialog was shown\r\n   UINT m_nDefCtlId;                 // Default dialog button\r\n   UINT m_nHoverId;                  // Currently hover Command Link button ID\r\n   SIZE m_sizeDialog;                // Recommended dialog size (in pixels)\r\n   CFont m_fontTitle;                // Title font\r\n   CFont m_fontText;                 // Text font\r\n   CBrush m_brWhite;                 // Brush for white (top) background\r\n   CBrush m_brGrey;                  // Brush for grey (bottom) background\r\n   CIcon m_iconMain;                 // Icon in Main Instructions text\r\n   CIcon m_iconFooter;               // Icon in Footer text\r\n   CIcon m_iconArrowNormal;          // Command Link arrow\r\n   CIcon m_iconArrowHot;             // Command Link arrow (hot)\r\n   CIcon m_iconChevronLess;          // Chevron icon (less)\r\n   CIcon m_iconChevronMore;          // Chevron icon (more)\r\n   bool m_bCreated;                  // Dialog fully initialized yet?\r\n   bool m_bExpanded;                 // Expando-area is expanded?\r\n   bool m_bHasCustomLinks;           // Has custom Command Link buttons?\r\n\r\n   struct {\r\n      SIZE sizeDialogPadding;\r\n      SIZE sizeButtonPadding;\r\n      SIZE sizeLinkPadding;\r\n      SIZE sizeRadioButton;\r\n      INT cxRadioIndent;\r\n      INT cxButtonGap;\r\n      INT cxButtonsDivider;\r\n      INT cxySmallIcon;\r\n      INT cxyLargeIcon;\r\n      INT cxyArrowIcon;\r\n      INT cxyExpanderIcon;\r\n      INT cxLargeIconGap;\r\n      INT cxSmallIconGap;\r\n      INT cyInstructionsGap;\r\n      INT cyContentGap;\r\n      INT cyRadioGap;\r\n      INT cyExpanderGap;\r\n      INT cyButtonLineGap;\r\n      INT cyProgressBar;\r\n      COLORREF clrTitleText;\r\n      COLORREF clrDividerDark;\r\n      COLORREF clrDividerLight;\r\n      COLORREF clrButtonDivider;\r\n      COLORREF clrBkTop;\r\n      COLORREF clrBkBottom;\r\n      COLORREF clrCmdLinkSelect;\r\n      LONG iButtonLinePos;\r\n      LONG iFooterLinePos;\r\n      LONG iExpandedLinePos;\r\n      SIZE sizeButtons;\r\n      INT cxMinButton;\r\n      INT cxMinCommandLink;\r\n      INT cxMaxVerification;\r\n      INT cxBestMainInstruction;\r\n      INT cxBestContent;\r\n      INT cxBestRadioButton;\r\n      INT cxBestCommandLink;\r\n      INT cxBestProgressBar;\r\n   } m_Metrics;\r\n\r\n   // Construction\r\n\r\n   void DoInitTemplate() \r\n   {\r\n      m_Template.Reset();\r\n\r\n      ::InitCommonControls();\r\n\r\n      bool bIsCommCtrl6 = RunTimeHelper::IsCommCtrl6();\r\n      bool bIsVista = RunTimeHelper::IsVista();\r\n\r\n      CWindowDC dc = HWND_DESKTOP;\r\n      if( !m_fontTitle.IsNull() ) m_fontTitle.DeleteObject();\r\n      if( !m_fontText.IsNull() ) m_fontText.DeleteObject();\r\n      CLogFont lfMsgBox;\r\n      lfMsgBox.SetMessageBoxFont();\r\n      m_fontText.CreateFontIndirect(&lfMsgBox);\r\n      CLogFont lfTitle = lfMsgBox;\r\n      lfTitle.MakeLarger(bIsCommCtrl6 ? 4 : 2);\r\n      lfTitle.MakeBolder(bIsCommCtrl6 ? 0 : 1);\r\n      m_fontTitle.CreateFontIndirect(&lfTitle);\r\n\r\n      InitDialogBaseUnits(m_fontText, m_cfg.hwndParent);\r\n\r\n      SIZE baseUnit = GetDialogBaseUnits();\r\n      m_Metrics.sizeDialogPadding.cx = baseUnit.cx * 3 / 2;\r\n      m_Metrics.sizeDialogPadding.cy = baseUnit.cy * 2 / 3;\r\n      m_Metrics.sizeButtonPadding.cx = baseUnit.cx * 3;\r\n      m_Metrics.sizeButtonPadding.cy = baseUnit.cy / 4;\r\n      m_Metrics.sizeLinkPadding.cx = baseUnit.cx * 2;\r\n      m_Metrics.sizeLinkPadding.cy = baseUnit.cy / 2;\r\n      m_Metrics.sizeRadioButton.cx = ::GetSystemMetrics(SM_CXMENUCHECK) + 6;\r\n      m_Metrics.sizeRadioButton.cy = ::GetSystemMetrics(SM_CYMENUCHECK);\r\n      m_Metrics.cxRadioIndent = baseUnit.cx * 3 / 2;\r\n      m_Metrics.cxButtonGap = baseUnit.cx * 1;\r\n      m_Metrics.cxButtonsDivider = 40;\r\n      m_Metrics.cxySmallIcon = ::GetSystemMetrics(SM_CYSMICON);\r\n      m_Metrics.cxyLargeIcon = ::GetSystemMetrics(SM_CYICON);\r\n      m_Metrics.cxyArrowIcon = 20;\r\n      m_Metrics.cxyExpanderIcon = 20;\r\n      m_Metrics.cxLargeIconGap = baseUnit.cx * 3 / 2;\r\n      m_Metrics.cxSmallIconGap = baseUnit.cx * 3 / 2;\r\n      m_Metrics.cyInstructionsGap = baseUnit.cy * 3 / 4;\r\n      m_Metrics.cyContentGap = baseUnit.cy * 3 / 2;\r\n      m_Metrics.cyRadioGap = baseUnit.cy / 2;\r\n      m_Metrics.cyExpanderGap = baseUnit.cy / 3;\r\n      m_Metrics.cyButtonLineGap = baseUnit.cy * 2 / 3;\r\n      m_Metrics.cyProgressBar = baseUnit.cy * 1;\r\n      m_Metrics.iButtonLinePos = 0;\r\n      m_Metrics.iFooterLinePos = 0;\r\n      m_Metrics.iExpandedLinePos = 0;\r\n      m_Metrics.sizeButtons.cx = 0;\r\n      m_Metrics.sizeButtons.cy = 0;\r\n      m_Metrics.cxMinButton = baseUnit.cx * 4;  // +8 DLU\r\n      m_Metrics.cxMinCommandLink = baseUnit.cx * 60;\r\n      m_Metrics.cxMaxVerification = baseUnit.cx * 40;\r\n      m_Metrics.cxBestMainInstruction = baseUnit.cx * 60;\r\n      m_Metrics.cxBestContent = baseUnit.cx * 50;\r\n      m_Metrics.cxBestCommandLink = baseUnit.cx * 70;\r\n      m_Metrics.cxBestProgressBar = baseUnit.cx * 45;\r\n      m_Metrics.cxBestRadioButton = baseUnit.cx * 60;\r\n      m_Metrics.clrTitleText = bIsCommCtrl6 ? RGB(0,51,153) : ::GetSysColor(COLOR_BTNTEXT);\r\n      m_Metrics.clrCmdLinkSelect = bIsCommCtrl6 ? RGB(140,232,255) : RGB(140,140,140);\r\n      m_Metrics.clrBkTop = ::GetSysColor(bIsVista ? COLOR_WINDOW : COLOR_BTNFACE);\r\n      m_Metrics.clrBkBottom = ::GetSysColor(COLOR_BTNFACE);\r\n      m_Metrics.clrDividerDark = ::GetSysColor(COLOR_BTNSHADOW);\r\n      m_Metrics.clrDividerLight = ::GetSysColor(COLOR_BTNHIGHLIGHT);\r\n      m_Metrics.clrButtonDivider = ::GetSysColor(bIsCommCtrl6 ? COLOR_BTNSHADOW : COLOR_BTNFACE);\r\n\r\n      if( !m_brWhite.IsNull() ) m_brWhite.DeleteObject();\r\n      if( !m_brGrey.IsNull() ) m_brGrey.DeleteObject();\r\n      m_brWhite.CreateSolidBrush(m_Metrics.clrBkTop);\r\n      m_brGrey.CreateSolidBrush(m_Metrics.clrBkBottom);\r\n\r\n      if( m_cfg.dwCommonButtons == 0 && m_cfg.cButtons == 0 ) m_cfg.dwCommonButtons = TDCBF_OK_BUTTON;\r\n\r\n      // Determine size of dialog. First try to determine the optimal width of\r\n      // the dialog. Then calculate the height of the dialog based on that width.\r\n      m_sizeDialog = _LayoutControls(WIDTH_PROBE, false);\r\n      const LONG CX_MIN_DIALOG = baseUnit.cx * 60;\r\n      const LONG CX_MAX_DIALOG = baseUnit.cx * 150;\r\n      if( m_sizeDialog.cx < CX_MIN_DIALOG ) m_sizeDialog.cx = CX_MIN_DIALOG;\r\n      if( m_sizeDialog.cx > CX_MAX_DIALOG ) m_sizeDialog.cx = CX_MAX_DIALOG;\r\n      if( m_cfg.cxWidth > 0 ) m_sizeDialog.cx = MapDialogUnitsX(m_cfg.cxWidth);\r\n      LONG cxWidth = m_sizeDialog.cx;\r\n      m_sizeDialog = _LayoutControls(cxWidth, false);\r\n      m_sizeDialog.cx = cxWidth;\r\n\r\n      if( (m_cfg.dwCommonButtons & TDCBF_CANCEL_BUTTON) != 0 ) m_cfg.dwFlags |= TDF_ALLOW_DIALOG_CANCELLATION;\r\n\r\n      DWORD dwHelpID = 0;\r\n      DWORD dwExStyle = 0;\r\n      DWORD dwStyle = WS_POPUP | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | DS_MODALFRAME | DS_ABSALIGN;\r\n      if( (m_cfg.dwFlags & TDF_CAN_BE_MINIMIZED) != 0 ) dwStyle |= WS_MINIMIZEBOX;\r\n      if( (m_cfg.dwFlags & TDF_ALLOW_DIALOG_CANCELLATION) != 0 ) dwStyle |= WS_SYSMENU;      \r\n      if( (m_cfg.dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) == 0 ) dwStyle |= DS_CENTER;\r\n#ifndef WS_EX_LAYOUTRTL\r\n      const UINT WS_EX_LAYOUTRTL = 0x00400000L;\r\n#endif // WS_EX_LAYOUTRTL\r\n      if( (m_cfg.dwFlags & TDF_RTL_LAYOUT) != 0 ) dwExStyle |= WS_EX_LAYOUTRTL | WS_EX_RIGHT;\r\n\r\n      TCHAR szCaption[120] = { 0 };\r\n      _LoadString(m_cfg.pszWindowTitle, szCaption, sizeof(szCaption) / sizeof(TCHAR));\r\n\r\n      WORD lfHeight = (WORD) lfMsgBox.lfHeight;\r\n      if( lfMsgBox.lfHeight < 0 ) lfHeight = (WORD) (-MulDiv(72, lfMsgBox.lfHeight, GetDeviceCaps(dc, LOGPIXELSY)));\r\n\r\n      SIZE dluDialog = MapDialogPixels(m_sizeDialog);\r\n      RECT rc = _CenterDialog(dluDialog);\r\n      m_Template.Create(true, szCaption, \r\n         (short) rc.left, (short) rc.top, (short) dluDialog.cx, (short) dluDialog.cy, \r\n         dwStyle, dwExStyle, \r\n         lfMsgBox.lfFaceName, lfHeight, (WORD) lfMsgBox.lfWeight, lfMsgBox.lfItalic, lfMsgBox.lfCharSet, \r\n         dwHelpID);\r\n   }\r\n\r\n   void DoInitControls() \r\n   {\r\n      _LayoutControls(m_sizeDialog.cx, true);\r\n   }\r\n\r\n   // Message handler\r\n\r\n   LRESULT OnWmInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      // Not accepting callbacks at this moment...\r\n      m_bCreated = false;\r\n      _Reset();\r\n      // Font assignment\r\n      SendMessageToDescendants(WM_SETFONT, (WPARAM) (HFONT) m_fontText);\r\n      SendDlgItemMessage(IDC_TASKDLG_INSTRUCTIONSTEXT, WM_SETFONT, (WPARAM) (HFONT) m_fontTitle);\r\n      // Create a timer?\r\n      if( m_bHasCustomLinks ) SetTimer(TIMERID_HOVERLINK, 100);\r\n      if( (m_cfg.dwFlags & TDF_CALLBACK_TIMER) != 0 ) SetTimer(TIMERID_TIGGER, 200);\r\n      // Set verification checkmark\r\n      if( (m_cfg.dwFlags & TDF_VERIFICATION_FLAG_CHECKED) != 0 ) CheckDlgButton(IDC_TASKDLG_VERIFYBUTTON, BST_CHECKED);\r\n      // Set Marquee Progress Bar\r\n      if( (m_cfg.dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) != 0 ) SetMarqueeProgressBar(TRUE);\r\n      // Set default radio button (may be first button)\r\n      if( (m_cfg.dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON) == 0 && m_cfg.cRadioButtons > 0 ) {\r\n         CButton ctrl;\r\n         for( UINT n = 0; !ctrl.IsWindow() && n < m_cfg.cRadioButtons; n++ ) {\r\n            if( m_cfg.nDefaultRadioButton == m_cfg.pRadioButtons[n].nButtonID ) ctrl = GetDlgItem(IDC_TASKDLG_RADIOBUTTON_FIRST + n);\r\n         }\r\n         if( !ctrl.IsWindow() ) ctrl = GetDlgItem(IDC_TASKDLG_RADIOBUTTON_FIRST);\r\n         if( ctrl.IsWindow() ) ctrl.Click();\r\n      }\r\n      // Clear arrow image on lines?\r\n      if( !m_bHasCustomLinks && (m_cfg.dwFlags & TDF_USE_COMMAND_LINKS_NO_ICON) != 0 ) {\r\n         for( UINT n = 0; n < m_cfg.cButtons; n++ ) {\r\n            CButton ctrl = GetDlgItem(IDC_TASKDLG_CUSTOMBUTTON_FIRST + n);\r\n            BUTTON_IMAGELIST bil = { (HIMAGELIST) -1, 0 };\r\n            ctrl.SendMessage(BCM_SETIMAGELIST, 0, (LPARAM) &bil);\r\n         }\r\n      }\r\n      // Set expansion level\r\n      if( m_cfg.pszExpandedInformation != NULL && (m_cfg.dwFlags & TDF_EXPANDED_BY_DEFAULT) == 0 ) {\r\n         SendMessage(WM_COMMAND, MAKEWPARAM(IDC_TASKDLG_EXPANDERICON, STN_CLICKED));\r\n      }\r\n      // Set default dialog button\r\n      if( m_nDefCtlId > 0 && m_nDefCtlId != (UINT) -1 ) {\r\n         SendMessage(DM_SETDEFID, m_nDefCtlId);\r\n         CWindow(GetDlgItem(m_nDefCtlId)).SetFocus();\r\n      }\r\n      // Get icons...\r\n      m_iconMain = (HICON) ((m_cfg.dwFlags & TDF_USE_HICON_MAIN) != 0 ? m_cfg.hMainIcon : _LoadIcon(m_cfg.pszMainIcon, m_Metrics.cxyLargeIcon));\r\n      m_iconFooter = (HICON) ((m_cfg.dwFlags & TDF_USE_HICON_FOOTER) != 0 ? m_cfg.hFooterIcon : _LoadIcon(m_cfg.pszFooterIcon, m_Metrics.cxySmallIcon));\r\n      m_iconArrowHot = _LoadIcon(TaskDlgArrowHot_ico, TaskDlgArrowHot_ico_len, 20);\r\n      m_iconArrowNormal = _LoadIcon(TaskDlgArrowNormal_ico, TaskDlgArrowNormal_ico_len, 20);\r\n      m_iconChevronLess = _LoadIcon(TaskDlgChevronLess_ico, TaskDlgChevronLess_ico_len, 20);\r\n      m_iconChevronMore = _LoadIcon(TaskDlgChevronMore_ico, TaskDlgChevronMore_ico_len, 20);\r\n      if( !m_iconMain.IsNull() && GetParent() == NULL ) SetIcon(m_iconMain, FALSE);\r\n      // Play that funky music...\r\n      _PlaySound();\r\n      // Ready to accept user input.\r\n      // Send inital callback messages.\r\n      m_bCreated = true;\r\n      _DoCallback(TDN_DIALOG_CONSTRUCTED);\r\n      _DoCallback(m_bNavigated ? TDN_NAVIGATED : TDN_CREATED);\r\n      m_bNavigated = false;\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnWmDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)\r\n   {\r\n      KillTimer(TIMERID_TIGGER);\r\n      KillTimer(TIMERID_HOVERLINK);\r\n      _DoCallback(TDN_DESTROYED);\r\n      m_bVerificationResult = IsDlgButtonChecked(IDC_TASKDLG_VERIFYBUTTON);\r\n      bHandled = FALSE;\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnWmHelp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      _DoCallback(TDN_HELP);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnWmTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)\r\n   {\r\n      if( wParam == TIMERID_TIGGER ) {\r\n         BOOL bReset = (BOOL) _DoCallback(TDN_TIMER, ::GetTickCount() - m_dwTick);\r\n         if( bReset ) m_dwTick = ::GetTickCount();\r\n      }\r\n      if( wParam == TIMERID_HOVERLINK ) {\r\n         POINT pt = { 0 };\r\n         ::GetCursorPos(&pt);\r\n         ScreenToClient(&pt);\r\n         UINT nHoverId = 0;\r\n         HWND hWnd = ::ChildWindowFromPoint(m_hWnd, pt);\r\n         if( hWnd != NULL ) {\r\n            UINT nCtlId = ::GetDlgCtrlID(hWnd);\r\n            if( nCtlId >= IDC_TASKDLG_CUSTOMBUTTON_FIRST && nCtlId <= IDC_TASKDLG_CUSTOMBUTTON_LAST ) nHoverId = nCtlId;\r\n         }\r\n         if( m_nHoverId != nHoverId ) {\r\n            if( m_nHoverId != 0 ) CWindow(GetDlgItem(m_nHoverId)).Invalidate();\r\n            m_nHoverId = nHoverId;\r\n            if( m_nHoverId != 0 ) CWindow(GetDlgItem(m_nHoverId)).Invalidate();\r\n         }\r\n      }\r\n      bHandled = FALSE;\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnWmEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      CClientDC dc = m_hWnd;\r\n      RECT rcClient = { 0 };\r\n      GetClientRect(&rcClient);\r\n      RECT rcTop = { 0, 0, rcClient.right, m_Metrics.iButtonLinePos };\r\n      RECT rcBottom = { 0, m_Metrics.iButtonLinePos, rcClient.right, rcClient.bottom };\r\n      dc.FillSolidRect(&rcTop, m_Metrics.clrBkTop);\r\n      dc.FillSolidRect(&rcBottom, m_Metrics.clrBkBottom);\r\n      RECT rcButtonLine = { 0, rcBottom.top, rcClient.right, rcBottom.top + 1 };\r\n      dc.FillSolidRect(&rcButtonLine, m_Metrics.clrButtonDivider);\r\n      if( m_Metrics.iFooterLinePos > 0 ) {\r\n         RECT rcFooterLine1 = { 0, m_Metrics.iFooterLinePos, rcClient.right, m_Metrics.iFooterLinePos + 1 };\r\n         dc.FillSolidRect(&rcFooterLine1, m_Metrics.clrDividerDark);\r\n         RECT rcFooterLine2 = { 0, m_Metrics.iFooterLinePos + 1, rcClient.right, m_Metrics.iFooterLinePos + 2 };\r\n         dc.FillSolidRect(&rcFooterLine2, m_Metrics.clrDividerLight);\r\n      }\r\n      if( m_Metrics.iExpandedLinePos > 0 ) {\r\n         RECT rcExpandoLine1 = { 0, m_Metrics.iExpandedLinePos, rcClient.right, m_Metrics.iExpandedLinePos + 1 };\r\n         dc.FillSolidRect(&rcExpandoLine1, m_Metrics.clrDividerDark);\r\n         RECT rcExpandoLine2 = { 0, m_Metrics.iExpandedLinePos + 1, rcClient.right, m_Metrics.iExpandedLinePos + 2 };\r\n         dc.FillSolidRect(&rcExpandoLine2, m_Metrics.clrDividerLight);\r\n      }\r\n      return 1;\r\n   }\r\n\r\n   LRESULT OnWmCtlColor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      LRESULT lRes = DefWindowProc();\r\n      UINT nCtlID = ::GetDlgCtrlID((HWND) lParam);\r\n      CDCHandle dc = (HDC) wParam;\r\n      switch( nCtlID ) {\r\n      case IDC_TASKDLG_INSTRUCTIONSICON:\r\n         dc.SetBkMode(TRANSPARENT);\r\n         dc.SetBkColor(m_Metrics.clrBkTop);\r\n         lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n         break;\r\n      case IDC_TASKDLG_INSTRUCTIONSTEXT:\r\n         dc.SetTextColor(m_Metrics.clrTitleText);\r\n         dc.SetBkMode(TRANSPARENT);\r\n         dc.SetBkColor(m_Metrics.clrBkTop);\r\n         lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n         break;\r\n      case IDC_TASKDLG_CONTENTTEXT:\r\n         dc.SetBkMode(TRANSPARENT);\r\n         dc.SetBkColor(m_Metrics.clrBkTop);\r\n         lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n         break;\r\n      case IDC_TASKDLG_FOOTERICON:\r\n      case IDC_TASKDLG_FOOTERTEXT:\r\n      case IDC_TASKDLG_EXPANDERICON:\r\n      case IDC_TASKDLG_EXPANDERTEXT:\r\n      case IDC_TASKDLG_VERIFYBUTTON:\r\n         dc.SetBkMode(TRANSPARENT);\r\n         dc.SetBkColor(m_Metrics.clrBkBottom);\r\n         lRes = (LRESULT) (HBRUSH) m_brGrey;\r\n         break;\r\n      case IDC_TASKDLG_OK:\r\n      case IDC_TASKDLG_NO:\r\n      case IDC_TASKDLG_YES:\r\n      case IDC_TASKDLG_RETRY:\r\n      case IDC_TASKDLG_CLOSE:\r\n      case IDC_TASKDLG_CANCEL:\r\n         dc.SetBkMode(TRANSPARENT);\r\n         dc.SetBkColor(m_Metrics.clrBkBottom);\r\n         lRes = (LRESULT) (HBRUSH) m_brGrey;\r\n         break;\r\n      default:\r\n         {\r\n            COLORREF clrBack;\r\n            if( nCtlID >= IDC_TASKDLG_RADIOBUTTON_FIRST && nCtlID <= IDC_TASKDLG_RADIOBUTTON_LAST ) {\r\n               clrBack = m_Metrics.clrBkTop;\r\n               lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n            }\r\n            else if( nCtlID >= IDC_TASKDLG_CUSTOMBUTTON_FIRST && nCtlID <= IDC_TASKDLG_CUSTOMBUTTON_LAST && (m_cfg.dwFlags & (TDF_USE_COMMAND_LINKS|TDF_USE_COMMAND_LINKS_NO_ICON)) != 0 ) {\r\n               clrBack = m_Metrics.clrBkTop;\r\n               lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n            }\r\n            else if( nCtlID == IDC_TASKDLG_EXTRATEXT && (m_cfg.dwFlags & TDF_EXPAND_FOOTER_AREA) == 0 ) {\r\n               clrBack = m_Metrics.clrBkTop;\r\n               lRes = (LRESULT) (HBRUSH) m_brWhite;\r\n            }\r\n            else {\r\n               clrBack = m_Metrics.clrBkBottom;\r\n               lRes = (LRESULT) (HBRUSH) m_brGrey;\r\n            }\r\n            dc.SetBkMode(TRANSPARENT);\r\n            dc.SetBkColor(clrBack);\r\n         }\r\n      }\r\n      return lRes;\r\n   }\r\n\r\n   LRESULT OnSysCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)\r\n   {\r\n      if( wParam == SC_CLOSE ) {\r\n         if( (m_cfg.dwFlags & TDF_ALLOW_DIALOG_CANCELLATION) == 0 ) return 0;         \r\n      }\r\n      bHandled = FALSE;\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnWmDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)\r\n   {\r\n      LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam;\r\n      if( m_bHasCustomLinks && lpDIS->CtlID >= IDC_TASKDLG_CUSTOMBUTTON_FIRST && lpDIS->CtlID <= IDC_TASKDLG_CUSTOMBUTTON_LAST ) {\r\n         _CustomDrawCommandLink(lpDIS);\r\n         return 0;\r\n      }\r\n      if( lpDIS->CtlID == IDC_TASKDLG_INSTRUCTIONSICON ) {\r\n         _CustomDrawIcon(lpDIS, m_Metrics.clrBkTop, m_iconMain, m_Metrics.cxyLargeIcon);\r\n         return 0;\r\n      }\r\n      if( lpDIS->CtlID == IDC_TASKDLG_FOOTERICON ) {\r\n         _CustomDrawIcon(lpDIS, m_Metrics.clrBkBottom, m_iconFooter, m_Metrics.cxySmallIcon);\r\n         return 0;\r\n      }\r\n      if( lpDIS->CtlID == IDC_TASKDLG_EXPANDERICON ) {\r\n         _CustomDrawIcon(lpDIS, m_Metrics.clrBkBottom, m_bExpanded ? m_iconChevronLess : m_iconChevronMore, m_Metrics.cxyExpanderIcon);\r\n         return 0;\r\n      }\r\n      bHandled = FALSE;\r\n      return 0;\r\n   }\r\n\r\n   // TaskDialog API messages\r\n\r\n   LRESULT OnMsgClickButton(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      ClickButton(wParam);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgClickRadioButton(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      ClickRadioButton(wParam);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgClickVerification(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      ClickVerification((BOOL) wParam, (BOOL) lParam);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgEnableButton(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) EnableButton(wParam, (BOOL) lParam);\r\n   }\r\n\r\n   LRESULT OnMsgEnableRadioButton(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) EnableRadioButton(wParam, (BOOL) lParam);\r\n   }\r\n\r\n   LRESULT OnMsgSetElementText(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) SetElementText((TASKDIALOG_ELEMENTS) wParam, (LPCWSTR) lParam);\r\n   }\r\n\r\n   LRESULT OnMsgUpdateElementText(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) UpdateElementText((TASKDIALOG_ELEMENTS) wParam, (LPCWSTR) lParam);\r\n   }\r\n\r\n   LRESULT OnMsgSetProgressBarPos(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      SetProgressBarPos(wParam);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgSetProgressBarRange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      SetProgressBarRange(LOWORD(lParam), HIWORD(lParam));\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgSetMarqueeProgressBar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      SetMarqueeProgressBar((BOOL) wParam);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgSetProgressBarState(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) SetProgressBarState((int) wParam);\r\n   }\r\n\r\n   LRESULT OnMsgSetProgressBarMarquee(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r\n   {\r\n      return (LRESULT) SetProgressBarMarquee(wParam, lParam);\r\n   }\r\n\r\n   // Command message handlers\r\n\r\n   LRESULT OnMsgCommonButtonClick(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      UINT nBtnID = wID;\r\n      if( _DoCallback(TDN_BUTTON_CLICKED, nBtnID) == S_FALSE ) return 0;\r\n      m_iButtonResult = (int) nBtnID;\r\n      EndDialog(wID);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgCustomButtonClick(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      UINT nBtnID = m_cfg.pButtons[wID - IDC_TASKDLG_CUSTOMBUTTON_FIRST].nButtonID;\r\n      if( _DoCallback(TDN_BUTTON_CLICKED, nBtnID) == S_FALSE ) return 0;\r\n      m_iButtonResult = (int) nBtnID;\r\n      EndDialog(wID);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgExpandoClick(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      m_bExpanded = !m_bExpanded;\r\n      _DoCallback(TDN_EXPANDO_BUTTON_CLICKED, m_bExpanded);\r\n      _DoExpandCollapse();\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgVerificationClick(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      _DoCallback(TDN_VERIFICATION_CLICKED, IsDlgButtonChecked(IDC_TASKDLG_VERIFYBUTTON) == BST_CHECKED);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgRadioClick(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      m_iRadioResult = (int) m_cfg.pRadioButtons[wID - IDC_TASKDLG_RADIOBUTTON_FIRST].nButtonID;\r\n      _DoCallback(TDN_RADIO_BUTTON_CLICKED, (WPARAM) m_iRadioResult);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnMsgHyperlinkClicked(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)\r\n   {\r\n      NMLINK* pLink = (NMLINK*) pnmh;\r\n      _DoCallback(TDN_HYPERLINK_CLICKED, 0, (LPARAM) static_cast<LPCWSTR>(pLink->item.szID[0] != '\\0' ? pLink->item.szID : pLink->item.szUrl));\r\n      return 0;\r\n   }\r\n\r\n   // Implementation\r\n\r\n   void _Reset()\r\n   {\r\n      m_iRadioResult = 0;\r\n      m_iButtonResult = IDCANCEL;\r\n      m_bExpanded = true;\r\n      m_nHoverId = 0;\r\n      m_dwTick = ::GetTickCount();\r\n   }\r\n\r\n   SIZE _LayoutControls(int cxMaxDialog, bool bCreateControls)\r\n   {\r\n      SIZE sizeEmpty = { 0 };\r\n      LPTSTR pstrBuffer = (LPTSTR) malloc(MAX_TEXT_LENGTH * sizeof(TCHAR));\r\n      if( pstrBuffer == NULL ) return sizeEmpty;\r\n\r\n      LONG cxDialog, cxExpander, cxMaxText;\r\n      SIZE sizeTitle, sizeText, sizeTemp;\r\n      LONG cyExpander;\r\n      DWORD dwStyle, dwExStyle;\r\n      UINT n;\r\n\r\n      bool bIsCommCtrl6 = RunTimeHelper::IsCommCtrl6();\r\n      bool bIsVista = RunTimeHelper::IsVista();\r\n\r\n      LONG ypos = m_Metrics.sizeDialogPadding.cy;\r\n\r\n      cxDialog = 0;\r\n      m_nDefCtlId = 0;\r\n\r\n      // Main icon\r\n\r\n      LONG xpos = m_Metrics.sizeDialogPadding.cx;\r\n\r\n      LONG cyIcon = 0;\r\n      if( m_cfg.pszMainIcon != NULL || (m_cfg.dwFlags & TDF_USE_HICON_MAIN) != 0 ) {\r\n         RECT rcIcon = { xpos, ypos, xpos + m_Metrics.cxyLargeIcon, ypos + m_Metrics.cxyLargeIcon };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_OWNERDRAW;\r\n            dwExStyle = 0;\r\n            _AddControl(CStatic::GetWndClassName(), IDC_TASKDLG_INSTRUCTIONSICON, rcIcon, dwStyle, dwExStyle, _T(\"\"));\r\n         }\r\n         xpos += (rcIcon.right - rcIcon.left) + m_Metrics.cxLargeIconGap;\r\n         cyIcon = m_Metrics.cxyLargeIcon;\r\n      }\r\n\r\n      // Main Instructions text\r\n\r\n      if( m_cfg.pszMainInstruction != NULL ) {\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         if( cxMaxDialog == WIDTH_PROBE ) cxMaxText = m_Metrics.cxBestMainInstruction;\r\n         sizeText = _GetTextSize(m_cfg.pszMainInstruction, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK | DT_NOPREFIX, m_fontTitle, cxMaxText);\r\n         if( sizeText.cy < m_Metrics.cxyLargeIcon ) sizeText.cy = m_Metrics.cxyLargeIcon;\r\n         RECT rcItem = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;\r\n            dwExStyle = 0;\r\n            _AddControl(CStatic::GetWndClassName(), IDC_TASKDLG_INSTRUCTIONSTEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.bottom - rcItem.top < cyIcon ) rcItem.bottom = rcItem.top + cyIcon;\r\n         ypos += (rcItem.bottom - rcItem.top) + m_Metrics.cyInstructionsGap;\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n      }\r\n\r\n      // Content text\r\n\r\n      if( m_cfg.pszContent != NULL ) {\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         if( cxMaxDialog == WIDTH_PROBE ) cxMaxText = m_Metrics.cxBestContent;\r\n         sizeText = _GetTextSizeHREF(m_cfg.pszContent, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK | DT_NOPREFIX, m_fontText, cxMaxText);\r\n         if( cxMaxDialog == WIDTH_PROBE && sizeText.cx > cxMaxText * 2 ) sizeText.cx = m_Metrics.cxBestContent;\r\n         RECT rcItem = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;\r\n            dwExStyle = 0;\r\n            LPCTSTR pstrClassName = bIsCommCtrl6 ? CLinkCtrl::GetWndClassName() : CStatic::GetWndClassName();\r\n            if( !bIsCommCtrl6 ) _RemoveHREF(pstrBuffer);\r\n            _AddControl(pstrClassName, IDC_TASKDLG_CONTENTTEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( cyIcon > 0 && m_cfg.pszMainInstruction == NULL && rcItem.bottom - rcItem.top < cyIcon ) rcItem.bottom = rcItem.top + cyIcon;\r\n         ypos += (rcItem.bottom - rcItem.top);\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n      }\r\n\r\n      ypos += (m_Metrics.cyContentGap / 2);\r\n\r\n      // Expando in Content area\r\n\r\n      if( m_cfg.pszExpandedInformation != NULL && ((m_cfg.dwFlags & TDF_EXPAND_FOOTER_AREA) == 0) ) {\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         sizeText = _GetTextSizeHREF(m_cfg.pszExpandedInformation, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK | DT_NOPREFIX, m_fontText, cxMaxText);\r\n         RECT rcItem = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;\r\n            dwExStyle = 0;\r\n            LPCTSTR pstrClassName = bIsCommCtrl6 ? CLinkCtrl::GetWndClassName() : CStatic::GetWndClassName();\r\n            if( !bIsCommCtrl6 ) _RemoveHREF(pstrBuffer);\r\n            _AddControl(pstrClassName, IDC_TASKDLG_EXTRATEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n         ypos += (rcItem.bottom - rcItem.top) + m_Metrics.cyButtonLineGap;\r\n         _AlignDLU(ypos, HTBOTTOM);\r\n      }\r\n\r\n      // Progress Bar\r\n\r\n      if( (m_cfg.dwFlags & (TDF_SHOW_PROGRESS_BAR|TDF_SHOW_MARQUEE_PROGRESS_BAR)) != 0 ) {\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         if( cxMaxDialog == WIDTH_PROBE ) cxMaxText = m_Metrics.cxBestProgressBar;\r\n         RECT rcItem = { xpos, ypos, xpos + cxMaxText, ypos + m_Metrics.cyProgressBar };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | PBS_SMOOTH;\r\n            dwExStyle = 0;\r\n            _AddControl(CProgressBarCtrl::GetWndClassName(), IDC_TASKDLG_PROGRESSBAR, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n         ypos += (rcItem.bottom - rcItem.top) + m_Metrics.cyInstructionsGap;\r\n      }\r\n\r\n      // Radio buttons\r\n\r\n      xpos += m_Metrics.cxRadioIndent;\r\n\r\n      if( m_cfg.cRadioButtons > 0 ) {\r\n         for( n = 0; n < m_cfg.cRadioButtons; n++ ) {\r\n            cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx - m_Metrics.sizeRadioButton.cx;\r\n            if( cxMaxDialog == WIDTH_PROBE ) cxMaxText = m_Metrics.cxBestRadioButton;\r\n            sizeText = _GetTextSize(m_cfg.pRadioButtons[n].pszButtonText, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, cxMaxText);\r\n            sizeText.cx += m_Metrics.sizeRadioButton.cx;\r\n            if( sizeText.cy < m_Metrics.sizeRadioButton.cy ) sizeText.cy = m_Metrics.sizeRadioButton.cy;\r\n            RECT rcButton = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n            if( bCreateControls ) {\r\n               dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTORADIOBUTTON | BS_TOP | BS_MULTILINE;\r\n               dwExStyle = 0;\r\n               if( n == 0 ) dwStyle |= WS_GROUP;\r\n               _AddControl(CButton::GetWndClassName(), (WORD) (IDC_TASKDLG_RADIOBUTTON_FIRST + n), rcButton, dwStyle, dwExStyle, pstrBuffer);\r\n            }\r\n            if( rcButton.right > cxDialog ) cxDialog = rcButton.right;\r\n            ypos += sizeText.cy + m_Metrics.cyRadioGap;\r\n         }\r\n         ypos += (m_Metrics.cyContentGap / 2) - m_Metrics.cyRadioGap;\r\n      }\r\n\r\n      // Command Links\r\n\r\n      m_bHasCustomLinks = false;\r\n      if( m_cfg.cButtons > 0 && (m_cfg.dwFlags & (TDF_USE_COMMAND_LINKS|TDF_USE_COMMAND_LINKS_NO_ICON)) != 0 ) {\r\n         _AlignDLU(ypos, HTBOTTOM);\r\n         if( m_cfg.cRadioButtons > 0 ) ypos += (m_Metrics.cyContentGap / 2);\r\n         for( n = 0; n < m_cfg.cButtons; n++ ) {\r\n            _LoadString(m_cfg.pButtons[n].pszButtonText, pstrBuffer, MAX_TEXT_LENGTH);\r\n            LPCTSTR pstrTitle = NULL, pstrText = NULL;\r\n            SIZE_T cchTitle = 0, cchText = 0;\r\n            _SplitCommandText(pstrBuffer, pstrTitle, cchTitle, pstrText, cchText);\r\n            LONG cxPadding = (m_Metrics.sizeLinkPadding.cx * 2);\r\n            if( (m_cfg.dwFlags & TDF_USE_COMMAND_LINKS_NO_ICON) == 0) cxPadding += m_Metrics.cxyArrowIcon + m_Metrics.cxSmallIconGap;\r\n            cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx - cxPadding;\r\n            if( cxMaxDialog == WIDTH_PROBE ) cxMaxText = m_Metrics.cxBestCommandLink - cxPadding;\r\n            sizeTitle = _GetTextSize(pstrTitle, cchTitle, DT_WORDBREAK, m_fontTitle, cxMaxText);\r\n            sizeText = _GetTextSize(pstrText, cchText, DT_NOPREFIX | DT_WORDBREAK, m_fontText, cxMaxText);\r\n            if( sizeTitle.cx > sizeText.cx ) sizeText.cx = sizeTitle.cx;\r\n            sizeText.cx += cxPadding;\r\n            if( sizeText.cx < m_Metrics.cxMinCommandLink ) sizeText.cx = m_Metrics.cxMinCommandLink;\r\n            RECT rcButton = { xpos, ypos, cxMaxDialog - m_Metrics.sizeDialogPadding.cx, ypos + sizeTitle.cy + sizeText.cy + (m_Metrics.sizeLinkPadding.cy * 2) };\r\n            _AlignDLU(rcButton.bottom, HTBOTTOM);\r\n            if( bCreateControls ) {\r\n               dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_TOP | BS_MULTILINE;\r\n               dwExStyle = 0;\r\n               if( n == 0 ) dwStyle |= WS_GROUP;\r\n               if( bIsVista ) {\r\n#ifndef BS_COMMANDLINK\r\n                  const DWORD BS_COMMANDLINK = 0x0000000E;\r\n                  const DWORD BS_DEFCOMMANDLINK = 0x0000000F;\r\n#endif // BS_COMMANDLINK\r\n                  if( m_cfg.pButtons[n].nButtonID == m_cfg.nDefaultButton ) {\r\n                     dwStyle |= BS_DEFCOMMANDLINK;\r\n                     m_nDefCtlId = (UINT) -1;\r\n                  }\r\n                  else {\r\n                     dwStyle |= BS_COMMANDLINK;\r\n                  }\r\n               }\r\n               else {\r\n                  m_bHasCustomLinks = true;\r\n                  if( m_cfg.pButtons[n].nButtonID == m_cfg.nDefaultButton ) m_nDefCtlId = IDC_TASKDLG_CUSTOMBUTTON_FIRST + n;\r\n                  dwStyle |= BS_OWNERDRAW;\r\n               }\r\n               _AddControl(CButton::GetWndClassName(), (WORD) (IDC_TASKDLG_CUSTOMBUTTON_FIRST + n), rcButton, dwStyle, dwExStyle, pstrBuffer);\r\n            }\r\n            if( xpos + sizeText.cx > cxDialog ) cxDialog = xpos + sizeText.cx;\r\n            ypos += (rcButton.bottom - rcButton.top);\r\n         }\r\n         ypos += (m_Metrics.cyContentGap / 2);\r\n      }\r\n\r\n      // Position the 'fold' - the line between white and grey areas\r\n\r\n      m_Metrics.iButtonLinePos = ypos;\r\n\r\n      if( m_cfg.pszExpandedInformation != NULL ||\r\n          m_cfg.pszVerificationText != NULL ||\r\n          m_cfg.cButtons > 0 && (m_cfg.dwFlags & (TDF_USE_COMMAND_LINKS|TDF_USE_COMMAND_LINKS_NO_ICON)) == 0  ||\r\n          m_cfg.dwCommonButtons != 0 ||\r\n          m_cfg.pszFooter != NULL )\r\n          // only add padding if something actually comes below the fold\r\n      {\r\n          ypos += m_Metrics.cyButtonLineGap;\r\n      }\r\n\r\n      // Expander area\r\n\r\n      xpos = m_Metrics.sizeDialogPadding.cx;\r\n      cyExpander = 0;\r\n      cxExpander = 0;\r\n      if( m_cfg.pszExpandedInformation != NULL ) {\r\n         RECT rcIcon = { xpos, ypos, xpos + m_Metrics.cxyExpanderIcon, ypos + m_Metrics.cxyExpanderIcon };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_OWNERDRAW | SS_NOTIFY;\r\n            dwExStyle = 0;         \r\n            _AddControl(CStatic::GetWndClassName(), IDC_TASKDLG_EXPANDERICON, rcIcon, dwStyle, dwExStyle, _T(\"\"));\r\n         }\r\n         LONG xoffset = m_Metrics.cxyExpanderIcon + m_Metrics.cxSmallIconGap;\r\n         sizeTemp = _GetTextSize(m_cfg.pszCollapsedControlText, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, m_Metrics.cxMaxVerification);\r\n         sizeText = _GetTextSize(m_cfg.pszExpandedControlText, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, m_Metrics.cxMaxVerification);\r\n         if( sizeTemp.cx > sizeText.cx ) sizeText.cx = sizeTemp.cx;\r\n         if( sizeTemp.cy > sizeText.cy ) sizeText.cy = sizeTemp.cy;\r\n         sizeText.cx += xoffset;\r\n         LONG yoffset = 0;\r\n         if( sizeText.cy < m_Metrics.cxyExpanderIcon ) yoffset = (m_Metrics.cxyExpanderIcon / 2) - (sizeText.cy / 2);\r\n         RECT rcItem = { xpos + xoffset, ypos + yoffset, xpos + xoffset + sizeText.cx, ypos + yoffset + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOTIFY;\r\n            dwExStyle = 0;\r\n            _AddControl(CStatic::GetWndClassName(), IDC_TASKDLG_EXPANDERTEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.right > cxExpander ) cxExpander = rcItem.right;\r\n         cyExpander += (rcItem.bottom - rcItem.top) + (yoffset * 2);\r\n         if( m_cfg.pszVerificationText != NULL ) cyExpander += m_Metrics.cyExpanderGap;\r\n         // Add hidden button which we use to catch accelerator\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD  | WS_TABSTOP;\r\n            dwExStyle = 0;\r\n            RECT rcZero = { 0 };\r\n            _AddControl(CButton::GetWndClassName(), IDC_TASKDLG_EXPANDERHIDDENBUTTON, rcZero, dwStyle, dwExStyle, _T(\"\"));\r\n         }\r\n      }\r\n\r\n      // Verification text\r\n\r\n      xpos = m_Metrics.sizeDialogPadding.cx;\r\n      if( m_cfg.pszVerificationText != NULL ) {\r\n         sizeText = _GetTextSize(m_cfg.pszVerificationText, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, m_Metrics.cxMaxVerification);\r\n         sizeText.cx += m_Metrics.sizeRadioButton.cx;\r\n         if( sizeText.cy < m_Metrics.sizeRadioButton.cy ) sizeText.cy = m_Metrics.sizeRadioButton.cy;\r\n         if( sizeText.cy < m_Metrics.sizeButtons.cy && cyExpander == 0 ) cyExpander = (m_Metrics.sizeButtons.cy / 2) - (sizeText.cy / 2);\r\n         const LONG cxIndent = 2;\r\n         RECT rcButton = { xpos + cxIndent, ypos + cyExpander, xpos + cxIndent + sizeText.cx, ypos + cyExpander + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX | BS_TOP | BS_LEFT | BS_MULTILINE;\r\n            dwExStyle = 0;\r\n            _AddControl(CButton::GetWndClassName(), IDC_TASKDLG_VERIFYBUTTON, rcButton, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcButton.right > cxExpander ) cxExpander = rcButton.right;\r\n         cyExpander += (rcButton.bottom - rcButton.top);\r\n      }\r\n\r\n      // Buttons\r\n\r\n      xpos = cxExpander + m_Metrics.cxButtonsDivider;     \r\n      if( m_Metrics.sizeButtons.cx > 0 ) xpos += cxMaxDialog - m_Metrics.sizeDialogPadding.cx - xpos - m_Metrics.sizeButtons.cx;\r\n\r\n      m_Metrics.sizeButtons.cx = 0;\r\n      m_Metrics.sizeButtons.cy = 0;\r\n\r\n      if( m_cfg.cButtons > 0 && (m_cfg.dwFlags & (TDF_USE_COMMAND_LINKS|TDF_USE_COMMAND_LINKS_NO_ICON)) == 0 ) {\r\n         for( n = 0; n < m_cfg.cButtons; n++ ) {\r\n            sizeText = _GetTextSize(m_cfg.pButtons[n].pszButtonText, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, 9999);\r\n            if( sizeText.cx < m_Metrics.cxMinButton ) sizeText.cx = m_Metrics.cxMinButton;\r\n            RECT rcButton = { xpos, ypos, xpos + sizeText.cx + (m_Metrics.sizeButtonPadding.cx * 2), ypos + sizeText.cy + (m_Metrics.sizeButtonPadding.cy * 2) };\r\n            _AlignDLU(rcButton.right, HTRIGHT);\r\n            if( bCreateControls ) {\r\n               dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_MULTILINE;\r\n               dwExStyle = 0;\r\n               _AddControl(CButton::GetWndClassName(), (WORD) (IDC_TASKDLG_CUSTOMBUTTON_FIRST + n), rcButton, dwStyle, dwExStyle, pstrBuffer);\r\n            }\r\n            xpos = rcButton.right + m_Metrics.cxButtonGap;\r\n            m_Metrics.sizeButtons.cx += (rcButton.right - rcButton.left) + m_Metrics.cxButtonGap;\r\n            if( m_nDefCtlId == 0 ) m_nDefCtlId = IDC_TASKDLG_CUSTOMBUTTON_FIRST + n;\r\n            if( m_cfg.nDefaultButton == m_cfg.pButtons[n].nButtonID ) m_nDefCtlId = IDC_TASKDLG_CUSTOMBUTTON_FIRST + n;\r\n            if( rcButton.bottom - rcButton.top > m_Metrics.sizeButtons.cy ) m_Metrics.sizeButtons.cy = (rcButton.bottom - rcButton.top);\r\n         }\r\n      }\r\n\r\n      if( m_cfg.dwCommonButtons != 0 ) {\r\n         int buttons[] = { \r\n            TDCBF_OK_BUTTON,     IDOK,     USER32_IDS_OK,\r\n            TDCBF_YES_BUTTON,    IDYES,    USER32_IDS_YES,\r\n            TDCBF_NO_BUTTON,     IDNO,     USER32_IDS_NO,\r\n            TDCBF_RETRY_BUTTON,  IDRETRY,  USER32_IDS_RETRY,\r\n            TDCBF_CANCEL_BUTTON, IDCANCEL, USER32_IDS_CANCEL,\r\n            TDCBF_CLOSE_BUTTON,  IDCLOSE,  USER32_IDS_CLOSE,\r\n         };\r\n         for( n = 0; n < sizeof(buttons) / sizeof(buttons[0]); n += 3 ) {\r\n            if( (m_cfg.dwCommonButtons & buttons[n]) == 0 ) continue;\r\n            sizeText = _GetTextSize(buttons[n + 2], pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK, m_fontText, 9999);\r\n            if( sizeText.cx < m_Metrics.cxMinButton ) sizeText.cx = m_Metrics.cxMinButton;\r\n            RECT rcButton = { xpos, ypos, xpos + sizeText.cx + (m_Metrics.sizeButtonPadding.cx * 2), ypos + sizeText.cy + (m_Metrics.sizeButtonPadding.cy * 2) };\r\n            _AlignDLU(rcButton.right, HTRIGHT);\r\n            if( bCreateControls ) {\r\n               dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_MULTILINE;\r\n               dwExStyle = 0;\r\n               _AddControl(CButton::GetWndClassName(), (WORD) buttons[n + 1], rcButton, dwStyle, dwExStyle, pstrBuffer);\r\n            }\r\n            xpos = rcButton.right + m_Metrics.cxButtonGap;\r\n            m_Metrics.sizeButtons.cx += (rcButton.right - rcButton.left) + m_Metrics.cxButtonGap;\r\n            if( m_nDefCtlId == 0 ) m_nDefCtlId = (UINT) buttons[n + 1];\r\n            if( m_cfg.nDefaultButton == buttons[n + 1] ) m_nDefCtlId = (UINT) buttons[n + 1];\r\n            if( rcButton.bottom - rcButton.top > m_Metrics.sizeButtons.cy ) m_Metrics.sizeButtons.cy = (rcButton.bottom - rcButton.top);\r\n         }\r\n      }\r\n\r\n      if( m_Metrics.sizeButtons.cx > 0 ) {\r\n         m_Metrics.sizeButtons.cx -= m_Metrics.cxButtonGap;\r\n         xpos -= m_Metrics.cxButtonGap;\r\n      }\r\n      if( xpos > cxDialog ) cxDialog = xpos;\r\n\r\n      if( cyExpander < m_Metrics.sizeButtons.cy ) cyExpander = m_Metrics.sizeButtons.cy;\r\n      ypos += cyExpander;\r\n\r\n      // Footer text\r\n\r\n      xpos = m_Metrics.sizeDialogPadding.cx;\r\n      if( m_cfg.pszFooter != NULL ) {\r\n         ypos += m_Metrics.cyButtonLineGap;\r\n         m_Metrics.iFooterLinePos = ypos;\r\n         ypos += m_Metrics.cyButtonLineGap;\r\n         if( m_cfg.pszFooterIcon != NULL || (m_cfg.dwFlags & TDF_USE_HICON_FOOTER) != 0 ) {\r\n            RECT rcItem = { xpos, ypos, xpos + m_Metrics.cxySmallIcon, ypos + m_Metrics.cxySmallIcon };\r\n            if( bCreateControls ) {\r\n               dwStyle = WS_CHILD | WS_VISIBLE | SS_OWNERDRAW;\r\n               dwExStyle = 0;         \r\n               _AddControl(CStatic::GetWndClassName(), IDC_TASKDLG_FOOTERICON, rcItem, dwStyle, dwExStyle, _T(\"\"));\r\n            }\r\n            xpos += (rcItem.right - rcItem.left) + m_Metrics.cxSmallIconGap;\r\n         }\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         sizeText = _GetTextSizeHREF(m_cfg.pszFooter, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK | DT_NOPREFIX, m_fontText, cxMaxText);\r\n         if( sizeText.cy < m_Metrics.cxySmallIcon ) sizeText.cy = m_Metrics.cxySmallIcon;\r\n         RECT rcItem = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;\r\n            dwExStyle = 0;\r\n            LPCTSTR pstrClassName = bIsCommCtrl6 ? CLinkCtrl::GetWndClassName() : CStatic::GetWndClassName();\r\n            if( !bIsCommCtrl6 ) _RemoveHREF(pstrBuffer);\r\n            _AddControl(pstrClassName, IDC_TASKDLG_FOOTERTEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n         ypos += (rcItem.bottom - rcItem.top);\r\n      }\r\n\r\n      // Expando in Footer area\r\n\r\n      xpos = m_Metrics.sizeDialogPadding.cx;\r\n      if( m_cfg.pszExpandedInformation != NULL && ((m_cfg.dwFlags & TDF_EXPAND_FOOTER_AREA) != 0) ) {\r\n         ypos += m_Metrics.cyButtonLineGap;\r\n         m_Metrics.iExpandedLinePos = ypos;\r\n         ypos += m_Metrics.cyButtonLineGap;\r\n         cxMaxText = cxMaxDialog - xpos - m_Metrics.sizeDialogPadding.cx;\r\n         sizeText = _GetTextSizeHREF(m_cfg.pszExpandedInformation, pstrBuffer, MAX_TEXT_LENGTH, DT_WORDBREAK | DT_NOPREFIX, m_fontText, cxMaxText);\r\n         RECT rcItem = { xpos, ypos, xpos + sizeText.cx, ypos + sizeText.cy };\r\n         if( bCreateControls ) {\r\n            dwStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;\r\n            dwExStyle = 0;\r\n            LPCTSTR pstrClassName = bIsCommCtrl6 ? CLinkCtrl::GetWndClassName() : CStatic::GetWndClassName();\r\n            if( !bIsCommCtrl6 ) _RemoveHREF(pstrBuffer);\r\n            _AddControl(pstrClassName, IDC_TASKDLG_EXTRATEXT, rcItem, dwStyle, dwExStyle, pstrBuffer);\r\n         }\r\n         if( rcItem.right > cxDialog ) cxDialog = rcItem.right;\r\n         ypos += (rcItem.bottom - rcItem.top);\r\n      }\r\n\r\n      cxDialog += m_Metrics.sizeDialogPadding.cx;\r\n      \r\n      // only add padding if anything was actually added below the fold\r\n      if(ypos > m_Metrics.iButtonLinePos)\r\n      {\r\n          ypos += m_Metrics.sizeDialogPadding.cy;\r\n      }\r\n\r\n      free(pstrBuffer);\r\n\r\n      SIZE sizeDialog = { cxDialog, ypos };\r\n      return sizeDialog;\r\n   }\r\n\r\n   void _CustomDrawIcon(LPDRAWITEMSTRUCT lpDIS, COLORREF clrBack, HICON hIcon, int cxyIcon)\r\n   {\r\n      CDCHandle dc = lpDIS->hDC;\r\n      RECT rc = lpDIS->rcItem;\r\n      dc.FillSolidRect(&rc, clrBack);\r\n      dc.DrawIconEx(rc.left, rc.top, hIcon, cxyIcon, cxyIcon);\r\n   }\r\n\r\n   void _CustomDrawCommandLink(LPDRAWITEMSTRUCT lpDIS)\r\n   {\r\n      CButton ctrl = lpDIS->hwndItem;\r\n      CDCHandle dc = lpDIS->hDC;\r\n      RECT rc = lpDIS->rcItem;\r\n      HBRUSH hOldBrush = dc.GetCurrentBrush();\r\n      HFONT hOldFont = dc.GetCurrentFont();\r\n      HPEN hOldPen = dc.GetCurrentPen();\r\n\r\n      COLORREF clrBorder = CLR_INVALID;\r\n      if( lpDIS->CtlID == m_nHoverId ) {\r\n         clrBorder = ::GetSysColor(COLOR_BTNSHADOW);\r\n      }\r\n      else {\r\n         if( (lpDIS->itemAction & ODA_DRAWENTIRE ) != 0 )\r\n            dc.FillSolidRect(&rc, m_Metrics.clrBkTop);\r\n\r\n         if( m_nDefCtlId == lpDIS->CtlID ) clrBorder = m_Metrics.clrCmdLinkSelect;\r\n         else if( (lpDIS->itemState & ODS_FOCUS) != 0 ) clrBorder = ::GetSysColor(COLOR_HIGHLIGHT);\r\n      }\r\n      if( (lpDIS->itemState & ODS_SELECTED) != 0 ) clrBorder = ::GetSysColor(COLOR_3DDKSHADOW);\r\n\r\n      if( clrBorder != CLR_INVALID ) {\r\n         POINT ptArc = { 6, 6 };\r\n         CPen penBorder;\r\n         penBorder.CreatePen(PS_SOLID, 1, clrBorder);\r\n         dc.SelectPen(penBorder);\r\n         dc.SelectBrush((HBRUSH) ::GetStockObject(HOLLOW_BRUSH));\r\n         dc.RoundRect(&rc, ptArc);\r\n      }\r\n\r\n      ::InflateRect(&rc, -m_Metrics.sizeLinkPadding.cx, -m_Metrics.sizeLinkPadding.cy);\r\n\r\n      if( (m_cfg.dwFlags & TDF_USE_COMMAND_LINKS_NO_ICON) == 0 ) {\r\n         dc.DrawIconEx(rc.left, rc.top + 2, lpDIS->CtlID == m_nHoverId ? m_iconArrowHot : m_iconArrowNormal, 20, 20);\r\n         rc.left += m_Metrics.cxyArrowIcon + m_Metrics.cxSmallIconGap;\r\n      }\r\n\r\n      LONG cx = (rc.right - rc.left);\r\n      TCHAR szWindowText[300] = { 0 };\r\n      ctrl.GetWindowText(szWindowText, sizeof(szWindowText) / sizeof(TCHAR));\r\n      LPCTSTR pstrTitle = NULL, pstrText = NULL;\r\n      SIZE_T cchTitle = 0, cchText = 0;\r\n      _SplitCommandText(szWindowText, pstrTitle, cchTitle, pstrText, cchText);\r\n      SIZE sizeTitle = _GetTextSize(pstrTitle, cchTitle, DT_WORDBREAK, m_fontTitle, cx);\r\n\r\n      RECT rcTitle = { rc.left, rc.top, rc.right, rc.bottom };\r\n      dc.SetTextColor(m_Metrics.clrTitleText);\r\n      dc.SetBkMode(TRANSPARENT);\r\n      dc.SelectFont(m_fontTitle);\r\n      dc.DrawText(pstrTitle, cchTitle, &rcTitle, DT_WORDBREAK);\r\n      RECT rcText = { rc.left, rc.top + sizeTitle.cy + 1, rc.right, rc.bottom };\r\n      dc.SelectFont(m_fontText);\r\n      dc.DrawText(pstrText, cchText, &rcText, DT_NOPREFIX | DT_WORDBREAK);\r\n\r\n      dc.SelectPen(hOldPen);\r\n      dc.SelectFont(hOldFont);\r\n      dc.SelectBrush(hOldBrush);\r\n   }\r\n\r\n   void _PlaySound()\r\n   {\r\n      if( m_cfg.pszMainIcon == TD_ERROR_ICON ) ::MessageBeep(MB_ICONHAND);\r\n      if( m_cfg.pszMainIcon == TD_WARNING_ICON ) ::MessageBeep(MB_ICONEXCLAMATION);\r\n      if( m_cfg.pszMainIcon == TD_INFORMATION_ICON ) ::MessageBeep(MB_ICONASTERISK);\r\n   }\r\n\r\n   void _RemoveHREF(LPTSTR pstr) const\r\n   {\r\n      if( (m_cfg.dwFlags & TDF_ENABLE_HYPERLINKS) == 0 ) return;\r\n      LPTSTR p = pstr;\r\n      while( *p != '\\0' ) {\r\n         if( *p == '<' ) {\r\n            ATLTRACE(_T(\"Warning: Will remove hyperlinks from TaskDialog; no support on this platform\"));\r\n            while( *p != '\\0' && *p != '>' ) p = ::CharNext(p);\r\n            if( *p == '>' ) p = ::CharNext(p);\r\n         }\r\n         else {\r\n            *pstr = *p;\r\n#ifdef _MBCS\r\n            if( ::IsDBCSLeadByte(*p) ) pstr[1] = p[1];\r\n#endif // _MBCS\r\n            p = ::CharNext(p);\r\n            pstr = ::CharNext(pstr);\r\n         }\r\n      }\r\n      *pstr = '\\0';\r\n   }\r\n\r\n   RECT _CenterDialog(SIZE dluDialog) const\r\n   {\r\n      RECT rcArea = { 0 };\r\n      if( ::IsWindow(m_cfg.hwndParent) && (m_cfg.dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) != 0 ) {\r\n         ::GetWindowRect(m_cfg.hwndParent, &rcArea);\r\n      }\r\n      else {\r\n         ::SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);\r\n      }\r\n      rcArea = MapDialogPixels(rcArea);\r\n      RECT rc = { 0 };\r\n      rc.left = rcArea.left + ((rcArea.right - rcArea.left) / 2 - dluDialog.cx / 2);\r\n      rc.top = rcArea.top + ((rcArea.bottom - rcArea.top) / 2 - dluDialog.cy / 2);\r\n      rc.right = rc.left + dluDialog.cx;\r\n      rc.bottom = rc.top + dluDialog.cy;\r\n      return rc;\r\n   }\r\n\r\n   HRESULT _DoCallback(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0)\r\n   {\r\n      if( !m_bCreated ) return S_OK;\r\n      if( m_cfg.pfCallback == NULL ) return S_OK;\r\n      return m_cfg.pfCallback(m_hWnd, uMsg, wParam, lParam, m_cfg.lpCallbackData);\r\n   }\r\n\r\n   void _DoExpandCollapse()\r\n   {\r\n      if( (m_cfg.dwFlags & TDF_EXPAND_FOOTER_AREA) == 0 )\r\n      {\r\n         CWindow wndText = GetDlgItem(IDC_TASKDLG_EXTRATEXT);\r\n         RECT rcText = { 0 };\r\n         wndText.GetWindowRect(&rcText);\r\n         int cy = (rcText.bottom - rcText.top) + m_Metrics.cyButtonLineGap;\r\n         if( !m_bExpanded ) cy = -cy;\r\n         HWND hWndFirst;\r\n         CWindow wndChild = hWndFirst = GetWindow(GW_CHILD);\r\n         while( wndChild != NULL ) {\r\n            RECT rcWin = { 0 };\r\n            wndChild.GetWindowRect(&rcWin);\r\n            if( wndChild != wndText && rcWin.top >= rcText.top ) {\r\n               ::OffsetRect(&rcWin, 0, cy);\r\n               ::MapWindowPoints(HWND_DESKTOP, m_hWnd, (LPPOINT) &rcWin, 2);\r\n               wndChild.MoveWindow(&rcWin);\r\n            }\r\n            wndChild = wndChild.GetWindow(GW_HWNDNEXT);\r\n            if( wndChild == hWndFirst ) break;\r\n         }\r\n         wndText.ShowWindow(m_bExpanded ? SW_SHOWNOACTIVATE: SW_HIDE);\r\n         if( m_Metrics.iButtonLinePos != 0 ) m_Metrics.iButtonLinePos += cy;\r\n         if( m_Metrics.iFooterLinePos != 0 ) m_Metrics.iFooterLinePos += cy;\r\n         ResizeClient(-1, m_bExpanded ? m_sizeDialog.cy : m_sizeDialog.cy + cy);\r\n      }\r\n      else\r\n      {\r\n         ResizeClient(-1, m_bExpanded ? m_sizeDialog.cy : m_Metrics.iExpandedLinePos - 1);\r\n      }\r\n\r\n      // Change text in Expander label\r\n      LPTSTR pstrBuffer = (LPTSTR) malloc(MAX_TEXT_LENGTH * sizeof(TCHAR));\r\n      if( pstrBuffer == NULL ) return;\r\n      _LoadString(m_bExpanded ? m_cfg.pszExpandedControlText : m_cfg.pszCollapsedControlText, pstrBuffer, MAX_TEXT_LENGTH);\r\n      SetDlgItemText(IDC_TASKDLG_EXPANDERTEXT, pstrBuffer);\r\n      free(pstrBuffer);\r\n\r\n      // Make sure we redraw stuff\r\n      CWindow(GetDlgItem(IDC_TASKDLG_EXPANDERICON)).Invalidate();\r\n      CWindow(GetDlgItem(IDC_TASKDLG_EXPANDERTEXT)).Invalidate();\r\n      Invalidate();\r\n   }\r\n\r\n   void _AddControl(ATL::_U_STRINGorID ClassName, WORD wId, RECT rc, DWORD dwStyle, DWORD dwExStyle, ATL::_U_STRINGorID Text)\r\n   {\r\n      rc = MapDialogPixels(rc);\r\n      m_Template.AddControl(ClassName, wId, (short) rc.left, (short) rc.top, (short) (rc.right - rc.left), (short) (rc.bottom - rc.top), dwStyle, dwExStyle, Text);\r\n   }\r\n\r\n   HICON _LoadIcon(LPCWSTR pstr, int cxy) const\r\n   {\r\n      USES_CONVERSION;\r\n      if( pstr == NULL ) return NULL;\r\n      // Determine icon identifier and resource dll\r\n      HINSTANCE hInst = m_cfg.hInstance;\r\n      if( pstr == TD_ERROR_ICON ) pstr = MAKEINTRESOURCEW(IDI_ERROR), hInst = NULL;\r\n#if _WIN32_WINNT >= 0x0600\r\n      else if( pstr == TD_SHIELD_ICON ) pstr = MAKEINTRESOURCEW(IDI_SHIELD), hInst = NULL;\r\n#endif // _WIN_WINNT\r\n      else if( pstr == TD_WARNING_ICON ) pstr = MAKEINTRESOURCEW(IDI_EXCLAMATION), hInst = NULL;\r\n      else if( pstr == TD_INFORMATION_ICON ) pstr = MAKEINTRESOURCEW(IDI_ASTERISK), hInst = NULL;\r\n      // Load icon...\r\n      UINT fuLoad = LR_DEFAULTCOLOR | LR_LOADTRANSPARENT;\r\n      if( hInst == NULL ) fuLoad |= LR_SHARED;\r\n      LPCTSTR pstrIcon = IS_INTRESOURCE(pstr) ? (LPCTSTR) pstr : W2CT(pstr);\r\n      HICON hIcon = (HICON) ::LoadImage(hInst, pstrIcon, IMAGE_ICON, cxy, cxy, fuLoad);\r\n      if( hIcon == NULL ) hIcon = (HICON) ::LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, cxy, cxy, LR_DEFAULTCOLOR | LR_SHARED | LR_LOADTRANSPARENT);\r\n      return hIcon;\r\n   }\r\n\r\n   /**\r\n    * Icon offset in GRPICONDIRENTRY structure.\r\n    */\r\n   SIZE_T _InMemoryOffset(WORD i) const { return 6 + (i * 0x0e); }\r\n\r\n   /**\r\n    * Icon offset in ICONDIRENTRY structure.\r\n    */\r\n   SIZE_T _InFileOffset(WORD i) const { return 6 + (i * 0x10); }\r\n\r\n   /**\r\n    * Load icon from raw .ico file bytes.\r\n    *\r\n    * @warning  This does no error checking and will cause a General\r\n    *           protection fault if the data passed in is not valid.\r\n    *\r\n    * @bug  Won't work if icon file data > 64k.\r\n    *\r\n    * @see http://www.ragestorm.net/blogs/?p=12\r\n    */\r\n   HICON _LoadIcon(const BYTE bytes[], SIZE_T size, int cxy) const\r\n   {\r\n       HICON icon = NULL;\r\n\r\n       BYTE* grpicondirentry =\r\n           static_cast<BYTE*>(::HeapAlloc(::GetProcessHeap(), 0, size));\r\n\r\n       if (grpicondirentry)\r\n       {\r\n           ::CopyMemory(grpicondirentry, bytes, size);\r\n\r\n           // Convert incoming ICONDIRENTRY to GRPICONDIRENTRY\r\n           WORD count = bytes[4];\r\n           for (WORD i = 0; i < count; i++)\r\n           {\r\n               ::CopyMemory(\r\n                   grpicondirentry + _InMemoryOffset(i),\r\n                   bytes + _InFileOffset(i), 0x0e);\r\n           }\r\n\r\n           // Create HICON\r\n           int offset = ::LookupIconIdFromDirectoryEx(\r\n               grpicondirentry, TRUE, cxy, cxy, LR_DEFAULTCOLOR);\r\n           if (offset > 0)\r\n           {\r\n               icon = ::CreateIconFromResourceEx(\r\n                   grpicondirentry + offset, 0, TRUE, 0x00030000, cxy, cxy,\r\n                   LR_DEFAULTCOLOR);\r\n           }\r\n\r\n           ::HeapFree(::GetProcessHeap(), 0, grpicondirentry);\r\n       }\r\n\r\n       return icon;\r\n   }\r\n\r\n   void _LoadString(LPCWSTR pstr, LPTSTR pszBuffer, SIZE_T cchMax) const\r\n   {\r\n      USES_CONVERSION;\r\n      ::ZeroMemory(pszBuffer, cchMax * sizeof(TCHAR));\r\n      if( pstr == NULL ) return;\r\n      if( IS_INTRESOURCE(pstr) ) ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(pstr), pszBuffer, cchMax);\r\n      else ::lstrcpyn(pszBuffer, W2CT(pstr), cchMax);\r\n   }\r\n\r\n   /**\r\n    * Variation of _LoadString that loads a resource from user32.dll.\r\n    */\r\n   void _LoadString(int id, LPTSTR pszBuffer, SIZE_T cchMax) const\r\n   {\r\n      USES_CONVERSION;\r\n      ::ZeroMemory(pszBuffer, cchMax * sizeof(TCHAR));\r\n      ::LoadString(::GetModuleHandle(_T(\"user32.dll\")), id, pszBuffer, cchMax);\r\n   }\r\n\r\n   void _SplitCommandText(LPTSTR pstrBuffer, LPCTSTR& pstrTitle, SIZE_T& cchTitle, LPCTSTR& pstrText, SIZE_T& cchText) const\r\n   {\r\n      LPTSTR pstrSel = pstrBuffer;\r\n      while( *pstrSel != '\\0' && *pstrSel != '\\n' ) pstrSel = ::CharNext(pstrSel);\r\n      if( *pstrSel == '\\0' ) {\r\n         pstrTitle = pstrBuffer;\r\n         pstrText = NULL;\r\n         cchTitle = lstrlen(pstrTitle);\r\n         cchText = 0;\r\n      }\r\n      else {\r\n         pstrTitle = pstrBuffer;\r\n         pstrText = pstrSel + 1;\r\n         cchText = lstrlen(pstrText);\r\n         cchTitle = lstrlen(pstrBuffer) - cchText - 1;\r\n      }\r\n   }\r\n\r\n   void _AlignDLU(LONG& pos, int iType) const\r\n   {\r\n      switch( iType ) {\r\n      case HTLEFT:   pos -= pos % GetDialogBaseUnits().cx; break;\r\n      case HTRIGHT:  pos += GetDialogBaseUnits().cx - (pos % GetDialogBaseUnits().cx); break;\r\n      case HTTOP:    pos -= pos % GetDialogBaseUnits().cy; break;\r\n      case HTBOTTOM: pos += GetDialogBaseUnits().cy - (pos % GetDialogBaseUnits().cy); break;\r\n      }\r\n   }\r\n\r\n   SIZE _GetTextSize(LPCWSTR pstr, LPTSTR pszBuffer, SIZE_T cchMax, UINT uStyle, HFONT hFont, int cxMax) const\r\n   {\r\n      _LoadString(pstr, pszBuffer, cchMax);\r\n      return _GetTextSize(pszBuffer, (UINT) -1, uStyle, hFont, cxMax);\r\n   }\r\n\r\n   /**\r\n    * Variation of _GetTextSize() that loads string from user32.dll resources.\r\n    */\r\n   SIZE _GetTextSize(int id, LPTSTR pszBuffer, SIZE_T cchMax, UINT uStyle, HFONT hFont, int cxMax) const\r\n   {\r\n      _LoadString(id, pszBuffer, cchMax);\r\n      return _GetTextSize(pszBuffer, (UINT) -1, uStyle, hFont, cxMax);\r\n   }\r\n\r\n   SIZE _GetTextSizeHREF(LPCWSTR pstr, LPTSTR pszBuffer, SIZE_T cchMax, UINT uStyle, HFONT hFont, int cxMax) const\r\n   {\r\n      _LoadString(pstr, pszBuffer, cchMax);\r\n      _RemoveHREF(pszBuffer);\r\n      SIZE sizeText = _GetTextSize(pszBuffer, (UINT) -1, uStyle, hFont, cxMax);\r\n      _LoadString(pstr, pszBuffer, cchMax);\r\n      return sizeText;\r\n   }\r\n\r\n   SIZE _GetTextSize(LPCTSTR pstr, SIZE_T cchMax, UINT uStyle, HFONT hFont, int cxMax) const\r\n   {\r\n      CClientDC dc = m_cfg.hwndParent;\r\n      SIZE sizeText = { 0 };\r\n      if( pstr == NULL ) return sizeText;\r\n      if( pstr[0] == '\\0' ) return sizeText;\r\n      HFONT hOldFont = dc.SelectFont(hFont);\r\n      RECT rc = { 0, 0, cxMax, 9999 };\r\n      if( cchMax == MAX_TEXT_LENGTH ) cchMax = (SIZE_T) -1;\r\n      dc.DrawText(pstr, cchMax, &rc, DT_CALCRECT | uStyle);\r\n      dc.SelectFont(hOldFont);\r\n      SIZE size = { rc.right - rc.left + 1, rc.bottom - rc.top + 1 };\r\n      return size;\r\n   }\r\n};\r\n\r\nclass CTask98Dialog : public CTask98DialogImpl<CTask98Dialog>\r\n{\r\n};\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////\r\n// TaskDialog 98 helpers\r\n//\r\n\r\ninline HRESULT Task98DialogIndirect(const TASKDIALOGCONFIG* pTaskConfig, int* pnButton, int* pnRadioButton, BOOL* pfVerificationFlagChecked)\r\n{\r\n   ATLASSERT(pTaskConfig);\r\n   ATLASSERT(pTaskConfig->cbSize==sizeof(TASKDIALOGCONFIG));\r\n   if( pnButton != NULL ) *pnButton = 0;\r\n   CTask98Dialog dlg;\r\n   if( !dlg.SetConfig(pTaskConfig) ) return E_FAIL;\r\n   do { dlg.DoModal(pTaskConfig->hwndParent); } while( dlg.IsNavigated() );\r\n   dlg.GetDialogResult(pnButton, pnRadioButton, pfVerificationFlagChecked);\r\n   return S_OK;\r\n}\r\n\r\ninline HRESULT Task98Dialog(HWND hwndParent, HINSTANCE hInstance, LPCTSTR pszWindowTitle, LPCTSTR pszMainInstruction, LPCTSTR pszContent, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons, LPCTSTR pszIcon, int* pnButton)\r\n{\r\n   USES_CONVERSION;\r\n   TASKDIALOGCONFIG cfg = { 0 };\r\n   cfg.cbSize = sizeof(cfg);\r\n   cfg.hInstance = hInstance;\r\n   cfg.hwndParent = hwndParent;\r\n   cfg.dwCommonButtons = dwCommonButtons;\r\n   cfg.pszWindowTitle = IS_INTRESOURCE(pszWindowTitle) ? (LPCWSTR) pszWindowTitle : T2CW(pszWindowTitle);\r\n   cfg.pszMainInstruction = IS_INTRESOURCE(pszMainInstruction) ? (LPCWSTR) pszMainInstruction : T2CW(pszMainInstruction);\r\n   cfg.pszContent = IS_INTRESOURCE(pszContent) ? (LPCWSTR) pszContent : T2CW(pszContent);\r\n   cfg.pszMainIcon = IS_INTRESOURCE(pszIcon) ? (LPCWSTR) pszIcon : T2CW(pszIcon);\r\n   return Task98DialogIndirect(&cfg, pnButton, NULL, NULL);\r\n}\r\n\r\n\r\n#endif // !defined(AFX_TASKDIALOG_H__20073232_5AA0_1E88_3A6D_0080AD509054__INCLUDED_)\r\n\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialogTest.cpp",
    "content": "// TaskDialogTest.cpp : main source file for TaskDialogTest.exe\r\n//\r\n\r\n#include \"stdafx.h\"\r\n\r\n#include <atlframe.h>\r\n#include <atlctrls.h>\r\n#include <atldlgs.h>\r\n#include <atlctrlw.h>\r\n\r\n#include \"resource.h\"\r\n\r\n#include \"maindlg.h\"\r\n\r\nCAppModule _Module;\r\n\r\n\r\nint WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpstrCmdLine*/, int /*nCmdShow*/)\r\n{\r\n   HRESULT hRes = ::CoInitialize(NULL);\r\n   ATLASSERT(SUCCEEDED(hRes));\r\n\r\n   ::DefWindowProc(NULL, 0, 0, 0L);\r\n\r\n   AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);\r\n\r\n   hRes = _Module.Init(NULL, hInstance);\r\n   ATLASSERT(SUCCEEDED(hRes));\r\n\r\n   int nRet = 0;\r\n   {\r\n      CMainDlg dlgMain;\r\n      nRet = dlgMain.DoModal();\r\n   }\r\n\r\n   _Module.Term();\r\n   ::CoUninitialize();\r\n\r\n   return nRet;\r\n}\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialogTest.dsp",
    "content": "# Microsoft Developer Studio Project File - Name=\"TaskDialogTest\" - Package Owner=<4>\r\n# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n# ** DO NOT EDIT **\r\n\r\n# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n\r\nCFG=TaskDialogTest - Win32 Debug\r\n!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n!MESSAGE use the Export Makefile command and run\r\n!MESSAGE \r\n!MESSAGE NMAKE /f \"TaskDialogTest.mak\".\r\n!MESSAGE \r\n!MESSAGE You can specify a configuration when running NMAKE\r\n!MESSAGE by defining the macro CFG on the command line. For example:\r\n!MESSAGE \r\n!MESSAGE NMAKE /f \"TaskDialogTest.mak\" CFG=\"TaskDialogTest - Win32 Debug\"\r\n!MESSAGE \r\n!MESSAGE Possible choices for configuration are:\r\n!MESSAGE \r\n!MESSAGE \"TaskDialogTest - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n!MESSAGE \"TaskDialogTest - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n!MESSAGE \r\n\r\n# Begin Project\r\n# PROP AllowPerConfigDependencies 0\r\n# PROP Scc_ProjName \"\"\r\n# PROP Scc_LocalPath \"\"\r\nCPP=cl.exe\r\nMTL=midl.exe\r\nRSC=rc.exe\r\n\r\n!IF  \"$(CFG)\" == \"TaskDialogTest - Win32 Release\"\r\n\r\n# PROP BASE Use_MFC 0\r\n# PROP BASE Use_Debug_Libraries 0\r\n# PROP BASE Output_Dir \"Release\"\r\n# PROP BASE Intermediate_Dir \"Release\"\r\n# PROP BASE Target_Dir \"\"\r\n# PROP Use_MFC 0\r\n# PROP Use_Debug_Libraries 0\r\n# PROP Output_Dir \"Release\"\r\n# PROP Intermediate_Dir \"C:\\Temp\"\r\n# PROP Ignore_Export_Lib 0\r\n# PROP Target_Dir \"\"\r\n# ADD BASE CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /Yu\"stdafx.h\" /FD /c\r\n# ADD CPP /nologo /MT /W4 /O1 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"STRICT\" /D \"_ATL_MIN_CRT\" /Yu\"stdafx.h\" /FD /c\r\n# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n# ADD BASE RSC /l 0x406 /d \"NDEBUG\"\r\n# ADD RSC /l 0x406 /d \"NDEBUG\"\r\nBSC32=bscmake.exe\r\n# ADD BASE BSC32 /nologo\r\n# ADD BSC32 /nologo\r\nLINK32=link.exe\r\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r\n# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r\n\r\n!ELSEIF  \"$(CFG)\" == \"TaskDialogTest - Win32 Debug\"\r\n\r\n# PROP BASE Use_MFC 0\r\n# PROP BASE Use_Debug_Libraries 1\r\n# PROP BASE Output_Dir \"Debug\"\r\n# PROP BASE Intermediate_Dir \"Debug\"\r\n# PROP BASE Target_Dir \"\"\r\n# PROP Use_MFC 0\r\n# PROP Use_Debug_Libraries 1\r\n# PROP Output_Dir \"Debug\"\r\n# PROP Intermediate_Dir \"C:\\Temp\"\r\n# PROP Target_Dir \"\"\r\n# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /Yu\"stdafx.h\" /FD /GZ /c\r\n# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"STRICT\" /FR /Yu\"stdafx.h\" /FD /GZ /c\r\n# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n# ADD BASE RSC /l 0x406 /d \"_DEBUG\"\r\n# ADD RSC /l 0x406 /d \"_DEBUG\"\r\nBSC32=bscmake.exe\r\n# ADD BASE BSC32 /nologo\r\n# ADD BSC32 /nologo\r\nLINK32=link.exe\r\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r\n# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r\n\r\n!ENDIF \r\n\r\n# Begin Target\r\n\r\n# Name \"TaskDialogTest - Win32 Release\"\r\n# Name \"TaskDialogTest - Win32 Debug\"\r\n# Begin Group \"Source Files\"\r\n\r\n# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n# Begin Source File\r\n\r\nSOURCE=.\\stdafx.cpp\r\n# ADD CPP /Yc\"stdafx.h\"\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\TaskDialogTest.cpp\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\TaskDialogTest.rc\r\n# End Source File\r\n# End Group\r\n# Begin Group \"Header Files\"\r\n\r\n# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n# Begin Source File\r\n\r\nSOURCE=.\\maindlg.h\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\resource.h\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\stdafx.h\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\TaskDialog.h\r\n# End Source File\r\n# End Group\r\n# Begin Group \"Resource Files\"\r\n\r\n# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDialogTest.exe.manifest\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDialogTest.ico\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDlgArrowHot.ico\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDlgArrowNormal.ico\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDlgChevronLess.ico\r\n# End Source File\r\n# Begin Source File\r\n\r\nSOURCE=.\\res\\TaskDlgChevronMore.ico\r\n# End Source File\r\n# End Group\r\n# End Target\r\n# End Project\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialogTest.dsw",
    "content": "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n\r\n###############################################################################\r\n\r\nProject: \"TaskDialogTest\"=.\\TaskDialogTest.dsp - Package Owner=<4>\r\n\r\nPackage=<5>\r\n{{{\r\n}}}\r\n\r\nPackage=<4>\r\n{{{\r\n}}}\r\n\r\n###############################################################################\r\n\r\nGlobal:\r\n\r\nPackage=<5>\r\n{{{\r\n}}}\r\n\r\nPackage=<3>\r\n{{{\r\n}}}\r\n\r\n###############################################################################\r\n\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialogTest.h",
    "content": "// TaskDialogTest.h\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/TaskDialogTest.rc",
    "content": "//Microsoft Developer Studio generated resource script.\r\n//\r\n#include \"resource.h\"\r\n\r\n#define APSTUDIO_READONLY_SYMBOLS\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 2 resource.\r\n//\r\n#include \"atlres.h\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#undef APSTUDIO_READONLY_SYMBOLS\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n// English (U.S.) resources\r\n\r\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n#ifdef _WIN32\r\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r\n#pragma code_page(1252)\r\n#endif //_WIN32\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Icon\r\n//\r\n\r\n// Icon with lowest ID value placed first to ensure application icon\r\n// remains consistent on all systems.\r\nIDR_MAINFRAME           ICON    DISCARDABLE     \"res\\\\TaskDialogTest.ico\"\r\n\r\n#ifdef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// TEXTINCLUDE\r\n//\r\n\r\n1 TEXTINCLUDE DISCARDABLE \r\nBEGIN\r\n    \"resource.h\\0\"\r\nEND\r\n\r\n2 TEXTINCLUDE DISCARDABLE \r\nBEGIN\r\n    \"#include \"\"atlres.h\"\"\\r\\n\"\r\n    \"\\0\"\r\nEND\r\n\r\n3 TEXTINCLUDE DISCARDABLE \r\nBEGIN\r\n    \"CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST \"\"res\\\\\\\\TaskDialogTest.exe.manifest\"\"\\r\\n\"\r\n    \"\\0\"\r\nEND\r\n\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Dialog\r\n//\r\n\r\nIDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 187, 102\r\nSTYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r\nCAPTION \"About\"\r\nFONT 8, \"MS Sans Serif\"\r\nBEGIN\r\n    DEFPUSHBUTTON   \"OK\",IDOK,130,81,50,14\r\n    CTEXT           \"TaskDialogTest Application v1.0\\n\\n(c) Copyright 2007\",\r\n                    IDC_STATIC,25,57,78,32\r\n    ICON            IDR_MAINFRAME,IDC_STATIC,55,26,18,20\r\n    GROUPBOX        \"\",IDC_STATIC,7,7,115,88\r\nEND\r\n\r\nIDD_MAINDLG DIALOG DISCARDABLE  0, 0, 353, 288\r\nSTYLE WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU\r\nCAPTION \"TaskDialogTest\"\r\nFONT 8, \"MS Sans Serif\"\r\nBEGIN\r\n    DEFPUSHBUTTON   \"Close\",IDOK,296,7,50,14\r\n    PUSHBUTTON      \"&About...\",ID_APP_ABOUT,296,25,50,14\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #001\",IDC_BUTTON1,14,17,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #002\",IDC_BUTTON2,14,36,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #003\",IDC_BUTTON3,13,55,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #004\",IDC_BUTTON4,13,74,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #005\",IDC_BUTTON5,13,93,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #006\",IDC_BUTTON6,13,112,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #007\",IDC_BUTTON7,13,131,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #008\",IDC_BUTTON8,13,150,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #009\",IDC_BUTTON9,13,169,180,\r\n                    15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #010\",IDC_BUTTON10,13,189,\r\n                    180,15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #011\",IDC_BUTTON11,13,209,\r\n                    180,15\r\n    PUSHBUTTON      \"Test Standard Task Dialog  #012\",IDC_BUTTON12,13,229,\r\n                    180,15\r\nEND\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// DESIGNINFO\r\n//\r\n\r\n#ifdef APSTUDIO_INVOKED\r\nGUIDELINES DESIGNINFO DISCARDABLE \r\nBEGIN\r\n    IDD_ABOUTBOX, DIALOG\r\n    BEGIN\r\n        LEFTMARGIN, 7\r\n        RIGHTMARGIN, 180\r\n        TOPMARGIN, 7\r\n        BOTTOMMARGIN, 95\r\n    END\r\n\r\n    IDD_MAINDLG, DIALOG\r\n    BEGIN\r\n        LEFTMARGIN, 7\r\n        RIGHTMARGIN, 346\r\n        TOPMARGIN, 7\r\n        BOTTOMMARGIN, 281\r\n    END\r\nEND\r\n#endif    // APSTUDIO_INVOKED\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Accelerator\r\n//\r\n\r\nIDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE \r\nBEGIN\r\n    \"N\",            ID_FILE_NEW,            VIRTKEY, CONTROL\r\n    \"O\",            ID_FILE_OPEN,           VIRTKEY, CONTROL\r\n    \"S\",            ID_FILE_SAVE,           VIRTKEY, CONTROL\r\n    \"P\",            ID_FILE_PRINT,          VIRTKEY, CONTROL\r\n    \"Z\",            ID_EDIT_UNDO,           VIRTKEY, CONTROL\r\n    \"X\",            ID_EDIT_CUT,            VIRTKEY, CONTROL\r\n    \"C\",            ID_EDIT_COPY,           VIRTKEY, CONTROL\r\n    \"V\",            ID_EDIT_PASTE,          VIRTKEY, CONTROL\r\n    VK_BACK,        ID_EDIT_UNDO,           VIRTKEY, ALT\r\n    VK_DELETE,      ID_EDIT_CUT,            VIRTKEY, SHIFT\r\n    VK_INSERT,      ID_EDIT_COPY,           VIRTKEY, CONTROL\r\n    VK_INSERT,      ID_EDIT_PASTE,          VIRTKEY, SHIFT\r\n    VK_F6,          ID_NEXT_PANE,           VIRTKEY \r\n    VK_F6,          ID_PREV_PANE,           VIRTKEY, SHIFT\r\nEND\r\n\r\n\r\n#ifndef _MAC\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Version\r\n//\r\n\r\nVS_VERSION_INFO VERSIONINFO\r\n FILEVERSION 1,0,0,1\r\n PRODUCTVERSION 1,0,0,1\r\n FILEFLAGSMASK 0x3fL\r\n#ifdef _DEBUG\r\n FILEFLAGS 0x1L\r\n#else\r\n FILEFLAGS 0x0L\r\n#endif\r\n FILEOS 0x4L\r\n FILETYPE 0x2L\r\n FILESUBTYPE 0x0L\r\nBEGIN\r\n    BLOCK \"StringFileInfo\"\r\n    BEGIN\r\n        BLOCK \"040904B0\"\r\n        BEGIN\r\n            VALUE \"CompanyName\", \"\\0\"\r\n            VALUE \"FileDescription\", \"TaskDialogTest Module\\0\"\r\n            VALUE \"FileVersion\", \"1, 0, 0, 1\\0\"\r\n            VALUE \"InternalName\", \"TaskDialogTest\\0\"\r\n            VALUE \"LegalCopyright\", \"Copyright 2007\\0\"\r\n            VALUE \"OriginalFilename\", \"TaskDialogTest.exe\\0\"\r\n            VALUE \"ProductName\", \"TaskDialogTest Module\\0\"\r\n            VALUE \"ProductVersion\", \"1, 0, 0, 1\\0\"\r\n        END\r\n    END\r\n    BLOCK \"VarFileInfo\"\r\n    BEGIN\r\n        VALUE \"Translation\", 0x409, 1200\r\n    END\r\nEND\r\n\r\n#endif    // !_MAC\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// String Table\r\n//\r\n\r\nSTRINGTABLE PRELOAD DISCARDABLE \r\nBEGIN\r\n    IDR_MAINFRAME           \"TaskDialogTest\"\r\n    IDS_TASKDLG_CANCEL      \"Cancel\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_FILE_NEW             \"Create a new document\\nNew\"\r\n    ID_FILE_OPEN            \"Open an existing document\\nOpen\"\r\n    ID_FILE_CLOSE           \"Close the active document\\nClose\"\r\n    ID_FILE_SAVE            \"Save the active document\\nSave\"\r\n    ID_FILE_SAVE_AS         \"Save the active document with a new name\\nSave As\"\r\n    ID_FILE_PAGE_SETUP      \"Change the printing options\\nPage Setup\"\r\n    ID_FILE_PRINT_SETUP     \"Change the printer and printing options\\nPrint Setup\"\r\n    ID_FILE_PRINT           \"Print the active document\\nPrint\"\r\n    ID_FILE_PRINT_PREVIEW   \"Display full pages\\nPrint Preview\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_APP_ABOUT            \"Display program information, version number and copyright\\nAbout\"\r\n    ID_APP_EXIT             \"Quit the application; prompts to save documents\\nExit\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_NEXT_PANE            \"Switch to the next window pane\\nNext Pane\"\r\n    ID_PREV_PANE            \"Switch back to the previous window pane\\nPrevious Pane\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_WINDOW_NEW           \"Open another window for the active document\\nNew Window\"\r\n    ID_WINDOW_ARRANGE       \"Arrange icons at the bottom of the window\\nArrange Icons\"\r\n    ID_WINDOW_CASCADE       \"Arrange windows so they overlap\\nCascade Windows\"\r\n    ID_WINDOW_TILE_HORZ     \"Arrange windows as non-overlapping tiles\\nTile Windows\"\r\n    ID_WINDOW_TILE_VERT     \"Arrange windows as non-overlapping tiles\\nTile Windows\"\r\n    ID_WINDOW_SPLIT         \"Split the active window into panes\\nSplit\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_EDIT_CLEAR           \"Erase the selection\\nErase\"\r\n    ID_EDIT_CLEAR_ALL       \"Erase everything\\nErase All\"\r\n    ID_EDIT_COPY            \"Copy the selection and put it on the Clipboard\\nCopy\"\r\n    ID_EDIT_CUT             \"Cut the selection and put it on the Clipboard\\nCut\"\r\n    ID_EDIT_FIND            \"Find the specified text\\nFind\"\r\n    ID_EDIT_PASTE           \"Insert Clipboard contents\\nPaste\"\r\n    ID_EDIT_REPEAT          \"Repeat the last action\\nRepeat\"\r\n    ID_EDIT_REPLACE         \"Replace specific text with different text\\nReplace\"\r\n    ID_EDIT_SELECT_ALL      \"Select the entire document\\nSelect All\"\r\n    ID_EDIT_UNDO            \"Undo the last action\\nUndo\"\r\n    ID_EDIT_REDO            \"Redo the previously undone action\\nRedo\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ID_VIEW_TOOLBAR         \"Show or hide the toolbar\\nToggle ToolBar\"\r\n    ID_VIEW_STATUS_BAR      \"Show or hide the status bar\\nToggle StatusBar\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ATL_IDS_SCSIZE          \"Change the window size\"\r\n    ATL_IDS_SCMOVE          \"Change the window position\"\r\n    ATL_IDS_SCMINIMIZE      \"Reduce the window to an icon\"\r\n    ATL_IDS_SCMAXIMIZE      \"Enlarge the window to full size\"\r\n    ATL_IDS_SCNEXTWINDOW    \"Switch to the next document window\"\r\n    ATL_IDS_SCPREVWINDOW    \"Switch to the previous document window\"\r\n    ATL_IDS_SCCLOSE         \"Close the active window and prompts to save the documents\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ATL_IDS_SCRESTORE       \"Restore the window to normal size\"\r\n    ATL_IDS_SCTASKLIST      \"Activate Task List\"\r\n    ATL_IDS_MDICHILD        \"Activate this window\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ATL_IDS_IDLEMESSAGE     \"Ready\"\r\nEND\r\n\r\nSTRINGTABLE DISCARDABLE \r\nBEGIN\r\n    ATL_IDS_MRU_FILE        \"Open this document\"\r\nEND\r\n\r\n#endif    // English (U.S.) resources\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n\r\n#ifndef APSTUDIO_INVOKED\r\n/////////////////////////////////////////////////////////////////////////////\r\n//\r\n// Generated from the TEXTINCLUDE 3 resource.\r\n//\r\nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST \"res\\\\TaskDialogTest.exe.manifest\"\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n#endif    // not APSTUDIO_INVOKED\r\n\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/icons.h",
    "content": "static const unsigned char TaskDlgArrowHot_ico[] = {\n  0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00,\n  0x08, 0x00, 0x08, 0x06, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x14, 0x14,\n  0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xb8, 0x06, 0x00, 0x00, 0x2e, 0x06,\n  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e,\n  0x2e, 0x00, 0x34, 0x34, 0x34, 0x00, 0x35, 0x35, 0x35, 0x00, 0x3c, 0x3c,\n  0x3c, 0x00, 0x3d, 0x3d, 0x3d, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x66, 0x66,\n  0x66, 0x00, 0x6a, 0x6a, 0x6a, 0x00, 0x6e, 0x6e, 0x6e, 0x00, 0x00, 0x88,\n  0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xb2,\n  0x00, 0x00, 0x13, 0xb8, 0x13, 0x00, 0x20, 0x90, 0x20, 0x00, 0x20, 0xbd,\n  0x20, 0x00, 0x23, 0xbd, 0x23, 0x00, 0x38, 0xb7, 0x38, 0x00, 0x00, 0xc0,\n  0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x0a, 0xde,\n  0x0a, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x2b, 0xd6,\n  0x2b, 0x00, 0x24, 0xee, 0x24, 0x00, 0x43, 0xb1, 0x43, 0x00, 0x5b, 0xad,\n  0x5b, 0x00, 0x54, 0xb0, 0x54, 0x00, 0x54, 0xb9, 0x54, 0x00, 0x69, 0xb8,\n  0x69, 0x00, 0x72, 0xb8, 0x72, 0x00, 0x4b, 0xca, 0x4b, 0x00, 0x54, 0xc3,\n  0x54, 0x00, 0x54, 0xcc, 0x54, 0x00, 0x5a, 0xfc, 0x5a, 0x00, 0x69, 0xe1,\n  0x69, 0x00, 0x62, 0xff, 0x62, 0x00, 0x8b, 0x8b, 0x8b, 0x00, 0x9b, 0x9b,\n  0x9b, 0x00, 0xa7, 0xa7, 0xa7, 0x00, 0xab, 0xab, 0xab, 0x00, 0xae, 0xae,\n  0xae, 0x00, 0xbd, 0xbd, 0xbd, 0x00, 0x83, 0xe5, 0x83, 0x00, 0x88, 0xe7,\n  0x88, 0x00, 0x88, 0xee, 0x88, 0x00, 0x8e, 0xf5, 0x8e, 0x00, 0x8e, 0xfb,\n  0x8e, 0x00, 0x9c, 0xff, 0x9c, 0x00, 0x9e, 0xff, 0x9e, 0x00, 0xaf, 0xeb,\n  0xaf, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xce, 0xce,\n  0xce, 0x00, 0xd3, 0xd3, 0xd3, 0x00, 0xd8, 0xd8, 0xd8, 0x00, 0xde, 0xde,\n  0xde, 0x00, 0xd0, 0xf3, 0xd0, 0x00, 0xd1, 0xf9, 0xd1, 0x00, 0xe3, 0xe3,\n  0xe3, 0x00, 0xe5, 0xe5, 0xe5, 0x00, 0xe8, 0xe8, 0xe8, 0x00, 0xe9, 0xe9,\n  0xe9, 0x00, 0xea, 0xea, 0xea, 0x00, 0xec, 0xec, 0xec, 0x00, 0xed, 0xed,\n  0xed, 0x00, 0xe7, 0xf8, 0xe7, 0x00, 0xf2, 0xf2, 0xf2, 0x00, 0xf1, 0xf5,\n  0xf1, 0x00, 0xf4, 0xf4, 0xf4, 0x00, 0xf4, 0xf6, 0xf4, 0x00, 0xf6, 0xf6,\n  0xf6, 0x00, 0xf2, 0xfa, 0xf2, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf9, 0xf9,\n  0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x42, 0x3d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x32, 0x26, 0x32,\n  0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x31, 0x42, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x44, 0x1a, 0x17, 0x17, 0x30, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x3a, 0x38, 0x42, 0x3c, 0x16,\n  0x15, 0x15, 0x2f, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x4a, 0x2d, 0x25, 0x25, 0x25, 0x25, 0x25, 0x19, 0x14, 0x14, 0x14, 0x2e,\n  0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x13, 0x13, 0x13,\n  0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x34, 0x4c, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x4c, 0x21, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e,\n  0x0d, 0x0d, 0x0d, 0x23, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x4c, 0x4c, 0x4c, 0x47, 0x4c, 0x4a, 0x12, 0x0c, 0x0c, 0x22, 0x47,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x47, 0x1b, 0x0b, 0x0b, 0x1e, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x1f, 0x0a, 0x0a,\n  0x1d, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x47, 0x20, 0x0f, 0x1c, 0x4a, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x47, 0x4c, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xfe, 0x3f, 0xf0, 0x00, 0xfc, 0x1f,\n  0xf0, 0x00, 0xfc, 0x0f, 0xf0, 0x00, 0xfe, 0x07, 0xf0, 0x00, 0xe0, 0x03,\n  0xf0, 0x00, 0xc0, 0x01, 0xf0, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xc0, 0x01,\n  0xf0, 0x00, 0xe0, 0x03, 0xf0, 0x00, 0xfe, 0x07, 0xf0, 0x00, 0xfc, 0x0f,\n  0xf0, 0x00, 0xfc, 0x1f, 0xf0, 0x00, 0xfe, 0x3f, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00,\n  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xfa, 0xfa, 0xfa, 0x3d, 0xf6, 0xf6,\n  0xf6, 0x3a, 0xf6, 0xf6, 0xf6, 0x20, 0xf8, 0xf8, 0xf8, 0x00, 0xfd, 0xfd,\n  0xfd, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xf9, 0xf9,\n  0xf9, 0x26, 0xed, 0xed, 0xed, 0x81, 0xdf, 0xdf, 0xdf, 0x9a, 0xda, 0xda,\n  0xda, 0x86, 0xdf, 0xdf, 0xdf, 0x4a, 0xef, 0xef, 0xef, 0x40, 0xfc, 0xfc,\n  0xfc, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd,\n  0xfd, 0x17, 0xef, 0xef, 0xef, 0x35, 0xe6, 0xe6, 0xe6, 0x85, 0xcf, 0xcf,\n  0xcf, 0xc5, 0xc6, 0xc6, 0xc6, 0xdf, 0xc4, 0xc4, 0xc4, 0xd8, 0xc7, 0xc7,\n  0xc7, 0xbe, 0xd5, 0xd5, 0xd5, 0x83, 0xed, 0xed, 0xed, 0x23, 0xfc, 0xfc,\n  0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb, 0xeb, 0x35, 0xe5, 0xe5,\n  0xe5, 0x91, 0xef, 0xef, 0xef, 0xcf, 0xe6, 0xe6, 0xe6, 0xf1, 0xdf, 0xdf,\n  0xdf, 0xfc, 0xc3, 0xc3, 0xc3, 0xfb, 0xbf, 0xbf, 0xbf, 0xf2, 0xc4, 0xc4,\n  0xc4, 0xc5, 0xd4, 0xd4, 0xd4, 0x65, 0xed, 0xed, 0xed, 0x0d, 0xfc, 0xfc,\n  0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0xf7, 0xf7,\n  0xf7, 0x23, 0xe2, 0xe2, 0xe2, 0x83, 0xf1, 0xf1, 0xf1, 0xce, 0x9e, 0xff,\n  0x9e, 0xf5, 0x62, 0xff, 0x62, 0xfe, 0x9c, 0xff, 0x9c, 0xff, 0xed, 0xed,\n  0xed, 0xff, 0xc2, 0xc2, 0xc2, 0xfe, 0xbf, 0xbf, 0xbf, 0xef, 0xc4, 0xc4,\n  0xc4, 0xb3, 0xd4, 0xd4, 0xd4, 0x56, 0xed, 0xed, 0xed, 0x0e, 0xfb, 0xfb,\n  0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe,\n  0xfe, 0x00, 0xf7, 0xf7, 0xf7, 0x25, 0xe7, 0xe7, 0xe7, 0x7f, 0xca, 0xca,\n  0xca, 0xc5, 0xeb, 0xeb, 0xeb, 0xf1, 0x5a, 0xfc, 0x5a, 0xfe, 0x00, 0xf9,\n  0x00, 0xff, 0x00, 0xf9, 0x00, 0xff, 0x8e, 0xfb, 0x8e, 0xff, 0xec, 0xec,\n  0xec, 0xff, 0xc2, 0xc2, 0xc2, 0xfd, 0xbf, 0xbf, 0xbf, 0xeb, 0xc4, 0xc4,\n  0xc4, 0xae, 0xd4, 0xd4, 0xd4, 0x57, 0xed, 0xed, 0xed, 0x10, 0xfb, 0xfb,\n  0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0xed, 0xed, 0xed, 0x27, 0xe1, 0xe1,\n  0xe1, 0x81, 0xc9, 0xc9, 0xc9, 0xc4, 0xb3, 0xb3, 0xb3, 0xee, 0xbb, 0xbb,\n  0xbb, 0xfd, 0xe9, 0xfa, 0xe9, 0xff, 0x24, 0xee, 0x24, 0xff, 0x00, 0xeb,\n  0x00, 0xff, 0x00, 0xeb, 0x00, 0xff, 0x8e, 0xf5, 0x8e, 0xff, 0xec, 0xec,\n  0xec, 0xff, 0xc3, 0xc3, 0xc3, 0xfd, 0xbf, 0xbf, 0xbf, 0xeb, 0xc4, 0xc4,\n  0xc4, 0xb1, 0xd5, 0xd5, 0xd5, 0x65, 0xef, 0xef, 0xef, 0x1e, 0xfd, 0xfd,\n  0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xe9, 0xe9,\n  0xe9, 0x29, 0xe6, 0xe6, 0xe6, 0x83, 0xeb, 0xeb, 0xeb, 0xc4, 0xe0, 0xe0,\n  0xe0, 0xee, 0xdb, 0xdb, 0xdb, 0xfd, 0xd4, 0xd4, 0xd4, 0xff, 0xed, 0xed,\n  0xed, 0xff, 0xd1, 0xf9, 0xd1, 0xff, 0x0a, 0xde, 0x0a, 0xff, 0x00, 0xdd,\n  0x00, 0xff, 0x00, 0xdd, 0x00, 0xff, 0x88, 0xee, 0x88, 0xff, 0xef, 0xef,\n  0xef, 0xff, 0xc2, 0xc2, 0xc2, 0xfd, 0xc0, 0xc0, 0xc0, 0xec, 0xc8, 0xc8,\n  0xc8, 0xbc, 0xe3, 0xe3, 0xe3, 0x7f, 0xf9, 0xf9, 0xf9, 0x42, 0x00, 0x00,\n  0x00, 0x00, 0xf5, 0xf5, 0xf5, 0x40, 0xe0, 0xe0, 0xe0, 0x84, 0xf6, 0xf8,\n  0xf6, 0xc4, 0x83, 0xe5, 0x83, 0xee, 0x69, 0xe1, 0x69, 0xfd, 0x69, 0xe1,\n  0x69, 0xff, 0x69, 0xe1, 0x69, 0xff, 0x69, 0xe1, 0x69, 0xff, 0x69, 0xe1,\n  0x69, 0xff, 0x2b, 0xd6, 0x2b, 0xff, 0x00, 0xce, 0x00, 0xff, 0x00, 0xce,\n  0x00, 0xff, 0x00, 0xce, 0x00, 0xff, 0x88, 0xe7, 0x88, 0xff, 0xf0, 0xf0,\n  0xf0, 0xff, 0xc6, 0xc6, 0xc6, 0xfa, 0xcf, 0xcf, 0xcf, 0xdb, 0xe9, 0xe9,\n  0xe9, 0xa2, 0xfa, 0xfa, 0xfa, 0x4d, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xfb,\n  0xfb, 0x42, 0xe3, 0xe3, 0xe3, 0xa9, 0xd0, 0xf3, 0xd0, 0xe7, 0x00, 0xc0,\n  0x00, 0xfd, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0,\n  0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0,\n  0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0,\n  0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0xaf, 0xeb, 0xaf, 0xfd, 0xe1, 0xe1,\n  0xe1, 0xe8, 0xe3, 0xe3, 0xe3, 0xb1, 0xf6, 0xf6, 0xf6, 0x72, 0xfd, 0xfd,\n  0xfd, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xf4, 0xf4, 0x42, 0xdd, 0xdd,\n  0xdd, 0xa9, 0xfa, 0xfa, 0xfa, 0xea, 0x4b, 0xca, 0x4b, 0xfe, 0x23, 0xbd,\n  0x23, 0xff, 0x23, 0xbd, 0x23, 0xff, 0x23, 0xbd, 0x23, 0xff, 0x23, 0xbd,\n  0x23, 0xff, 0x20, 0xbd, 0x20, 0xff, 0x13, 0xb8, 0x13, 0xff, 0x00, 0xb2,\n  0x00, 0xff, 0x00, 0xb2, 0x00, 0xff, 0x00, 0xb2, 0x00, 0xff, 0x54, 0xcc,\n  0x54, 0xff, 0xf5, 0xf8, 0xf5, 0xf4, 0xd9, 0xd9, 0xd9, 0xba, 0xe8, 0xe8,\n  0xe8, 0x5d, 0xfc, 0xfc, 0xfc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x3d, 0xe5, 0xe5, 0xe5, 0x88, 0xec, 0xec,\n  0xec, 0xcc, 0xfa, 0xfa, 0xfa, 0xe9, 0xfa, 0xfa, 0xfa, 0xf1, 0xf9, 0xf9,\n  0xf9, 0xf9, 0xf7, 0xf7, 0xf7, 0xfe, 0xfb, 0xfb, 0xfb, 0xff, 0xf2, 0xfa,\n  0xf2, 0xff, 0x38, 0xb7, 0x38, 0xff, 0x00, 0xa4, 0x00, 0xff, 0x00, 0xa4,\n  0x00, 0xff, 0x54, 0xc3, 0x54, 0xfe, 0xf5, 0xf7, 0xf5, 0xf9, 0xd7, 0xd7,\n  0xd7, 0xd7, 0xdf, 0xdf, 0xdf, 0x7f, 0xf7, 0xf7, 0xf7, 0x15, 0xfe, 0xfe,\n  0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xf5, 0xf5, 0xf5, 0x3b, 0xe6, 0xe6, 0xe6, 0x87, 0xdf, 0xdf,\n  0xdf, 0x9b, 0xd5, 0xd5, 0xd5, 0xaf, 0xc7, 0xc7, 0xc7, 0xd4, 0xcb, 0xcb,\n  0xcb, 0xf4, 0xf2, 0xf6, 0xf2, 0xfe, 0x43, 0xb1, 0x43, 0xff, 0x00, 0x96,\n  0x00, 0xff, 0x00, 0x96, 0x00, 0xff, 0x54, 0xb9, 0x54, 0xfd, 0xf5, 0xf7,\n  0xf5, 0xf2, 0xd7, 0xd7, 0xd7, 0xd6, 0xdf, 0xdf, 0xdf, 0x9c, 0xf6, 0xf6,\n  0xf6, 0x46, 0xfe, 0xfe, 0xfe, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x3e, 0xfd, 0xfd, 0xfd, 0x1d, 0xfb, 0xfb,\n  0xfb, 0x37, 0xe3, 0xe3, 0xe3, 0x90, 0xf5, 0xf5, 0xf5, 0xdc, 0x69, 0xb8,\n  0x69, 0xfb, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfe, 0x54, 0xb0,\n  0x54, 0xfa, 0xf5, 0xf7, 0xf5, 0xec, 0xd7, 0xd7, 0xd7, 0xc9, 0xdf, 0xdf,\n  0xdf, 0x90, 0xf6, 0xf6, 0xf6, 0x3f, 0xfe, 0xfe, 0xfe, 0x13, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0xe5, 0xe5,\n  0xe5, 0x48, 0xf5, 0xf5, 0xf5, 0xc5, 0x72, 0xb8, 0x72, 0xf7, 0x20, 0x90,\n  0x20, 0xfe, 0x5b, 0xad, 0x5b, 0xf5, 0xf7, 0xf8, 0xf7, 0xdd, 0xdf, 0xdf,\n  0xdf, 0xba, 0xe1, 0xe1, 0xe1, 0x89, 0xf6, 0xf6, 0xf6, 0x2b, 0xfe, 0xfe,\n  0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x16, 0xe8, 0xe8, 0xe8, 0x58, 0xe7, 0xe7,\n  0xe7, 0xa9, 0xf6, 0xf6, 0xf6, 0xdd, 0xfa, 0xfa, 0xfa, 0xe7, 0xfa, 0xfa,\n  0xfa, 0xd0, 0xe7, 0xe7, 0xe7, 0x9a, 0xe5, 0xe5, 0xe5, 0x50, 0xf8, 0xf8,\n  0xf8, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfa, 0xfa, 0xfa, 0x25, 0xe8, 0xe8, 0xe8, 0x71, 0xe3, 0xe3,\n  0xe3, 0x8c, 0xd9, 0xd9, 0xd9, 0x94, 0xe4, 0xe4, 0xe4, 0x8c, 0xe9, 0xe9,\n  0xe9, 0x41, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x19, 0xfd, 0xfd,\n  0xfd, 0x25, 0xfd, 0xfd, 0xfd, 0x31, 0xfe, 0xfe, 0xfe, 0x31, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xbf, 0xf0, 0x00, 0xfe, 0x07,\n  0xf0, 0x00, 0xfc, 0x03, 0xf0, 0x00, 0xf8, 0x01, 0xf0, 0x00, 0xf8, 0x00,\n  0xf0, 0x00, 0xe0, 0x00, 0x70, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x70, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xf0, 0x01, 0xf0, 0x00, 0xf0, 0x07,\n  0xf0, 0x00, 0xf8, 0x0f, 0xf0, 0x00, 0xfe, 0x1f, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00\n};\nstatic const unsigned int TaskDlgArrowHot_ico_len = 3302;\nstatic const unsigned char TaskDlgArrowNormal_ico[] = {\n  0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00,\n  0x08, 0x00, 0x08, 0x06, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x14, 0x14,\n  0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xb8, 0x06, 0x00, 0x00, 0x2e, 0x06,\n  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30,\n  0x30, 0x00, 0x35, 0x35, 0x35, 0x00, 0x36, 0x36, 0x36, 0x00, 0x3d, 0x3d,\n  0x3d, 0x00, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x61,\n  0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x20, 0x58, 0x20, 0x00, 0x40, 0x40,\n  0x40, 0x00, 0x67, 0x67, 0x67, 0x00, 0x68, 0x68, 0x68, 0x00, 0x6e, 0x6e,\n  0x6e, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x13, 0x96, 0x13, 0x00, 0x00, 0xa1,\n  0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x20, 0x9c, 0x20, 0x00, 0x23, 0x9d,\n  0x23, 0x00, 0x38, 0x94, 0x38, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x0a, 0xce,\n  0x0a, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x2b, 0xc2,\n  0x2b, 0x00, 0x24, 0xe6, 0x24, 0x00, 0x43, 0x8a, 0x43, 0x00, 0x54, 0x89,\n  0x54, 0x00, 0x5b, 0x84, 0x5b, 0x00, 0x54, 0x97, 0x54, 0x00, 0x4b, 0xaf,\n  0x4b, 0x00, 0x54, 0xa5, 0x54, 0x00, 0x54, 0xb3, 0x54, 0x00, 0x69, 0x95,\n  0x69, 0x00, 0x72, 0x95, 0x72, 0x00, 0x5a, 0xfa, 0x5a, 0x00, 0x69, 0xd2,\n  0x69, 0x00, 0x62, 0xff, 0x62, 0x00, 0x89, 0x89, 0x89, 0x00, 0x8b, 0x8b,\n  0x8b, 0x00, 0x9a, 0x9a, 0x9a, 0x00, 0xa9, 0xa9, 0xa9, 0x00, 0xab, 0xab,\n  0xab, 0x00, 0xad, 0xad, 0xad, 0x00, 0xbd, 0xbd, 0xbd, 0x00, 0xbe, 0xbe,\n  0xbe, 0x00, 0x83, 0xd9, 0x83, 0x00, 0x88, 0xdb, 0x88, 0x00, 0x88, 0xe5,\n  0x88, 0x00, 0x8e, 0xf0, 0x8e, 0x00, 0x8e, 0xf9, 0x8e, 0x00, 0x9c, 0xff,\n  0x9c, 0x00, 0x9e, 0xff, 0x9e, 0x00, 0xaf, 0xe1, 0xaf, 0x00, 0xc5, 0xc5,\n  0xc5, 0x00, 0xcc, 0xcc, 0xcc, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xd5, 0xd5,\n  0xd5, 0x00, 0xd8, 0xd8, 0xd8, 0x00, 0xdb, 0xdb, 0xdb, 0x00, 0xd0, 0xee,\n  0xd0, 0x00, 0xd1, 0xf6, 0xd1, 0x00, 0xe2, 0xe2, 0xe2, 0x00, 0xe5, 0xe5,\n  0xe5, 0x00, 0xe8, 0xe8, 0xe8, 0x00, 0xe9, 0xe9, 0xe9, 0x00, 0xeb, 0xeb,\n  0xeb, 0x00, 0xec, 0xec, 0xec, 0x00, 0xed, 0xed, 0xed, 0x00, 0xe8, 0xf9,\n  0xe8, 0x00, 0xf2, 0xf2, 0xf2, 0x00, 0xf2, 0xf4, 0xf2, 0x00, 0xf3, 0xf5,\n  0xf3, 0x00, 0xf3, 0xf6, 0xf3, 0x00, 0xf4, 0xf4, 0xf4, 0x00, 0xf5, 0xf5,\n  0xf5, 0x00, 0xf4, 0xf6, 0xf4, 0x00, 0xf5, 0xf6, 0xf5, 0x00, 0xf2, 0xf8,\n  0xf2, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x44, 0x40, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x34, 0x26, 0x34,\n  0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x44, 0x24, 0x18, 0x18, 0x33, 0x44, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x46, 0x1a, 0x17, 0x17, 0x32, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x3c, 0x3a, 0x44, 0x3e, 0x16,\n  0x15, 0x15, 0x31, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x4f, 0x2f, 0x25, 0x25, 0x25, 0x25, 0x25, 0x19, 0x11, 0x11, 0x11, 0x30,\n  0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x10, 0x10, 0x10,\n  0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x36, 0x40, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x51, 0x1f, 0x13, 0x13, 0x13, 0x13, 0x13, 0x0f,\n  0x0e, 0x0e, 0x0e, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x51, 0x51, 0x51, 0x4b, 0x51, 0x4f, 0x14, 0x08, 0x08, 0x20, 0x4b,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x4b, 0x1b, 0x07, 0x07, 0x1e, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x22, 0x06, 0x06,\n  0x1c, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x4b, 0x23, 0x09, 0x1d, 0x4b, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x4b, 0x51, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xfe, 0x3f, 0xf0, 0x00, 0xfc, 0x1f,\n  0xf0, 0x00, 0xfc, 0x0f, 0xf0, 0x00, 0xfe, 0x07, 0xf0, 0x00, 0xe0, 0x03,\n  0xf0, 0x00, 0xc0, 0x01, 0xf0, 0x00, 0xc0, 0x00, 0xf0, 0x00, 0xc0, 0x01,\n  0xf0, 0x00, 0xe0, 0x03, 0xf0, 0x00, 0xfe, 0x07, 0xf0, 0x00, 0xfc, 0x0f,\n  0xf0, 0x00, 0xfc, 0x1f, 0xf0, 0x00, 0xfe, 0x3f, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00,\n  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xfb, 0xfb, 0xfb, 0x00, 0xf7, 0xf7,\n  0xf7, 0x29, 0xf6, 0xf6, 0xf6, 0x00, 0xf8, 0xf8, 0xf8, 0x23, 0xfd, 0xfd,\n  0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x32, 0xfa, 0xfa,\n  0xfa, 0x3b, 0xed, 0xed, 0xed, 0x44, 0xdf, 0xdf, 0xdf, 0x73, 0xdb, 0xdb,\n  0xdb, 0x54, 0xe0, 0xe0, 0xe0, 0x60, 0xf0, 0xf0, 0xf0, 0x18, 0xfc, 0xfc,\n  0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd,\n  0xfd, 0x00, 0xef, 0xef, 0xef, 0x3d, 0xe6, 0xe6, 0xe6, 0x93, 0xcf, 0xcf,\n  0xcf, 0xb6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc7, 0xc7,\n  0xc7, 0xae, 0xd5, 0xd5, 0xd5, 0x71, 0xee, 0xee, 0xee, 0x26, 0xfc, 0xfc,\n  0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xec, 0xec, 0x21, 0xe5, 0xe5,\n  0xe5, 0x84, 0xee, 0xee, 0xee, 0xce, 0xe6, 0xe6, 0xe6, 0xef, 0xdf, 0xdf,\n  0xdf, 0xf6, 0xc4, 0xc4, 0xc4, 0xf6, 0xc0, 0xc0, 0xc0, 0xec, 0xc5, 0xc5,\n  0xc5, 0xca, 0xd5, 0xd5, 0xd5, 0x8b, 0xed, 0xed, 0xed, 0x45, 0xfc, 0xfc,\n  0xfc, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0xf7, 0xf7,\n  0xf7, 0x10, 0xe2, 0xe2, 0xe2, 0x6d, 0xf1, 0xf1, 0xf1, 0xc0, 0x9e, 0xff,\n  0x9e, 0xf1, 0x62, 0xff, 0x62, 0xfe, 0x9c, 0xff, 0x9c, 0xff, 0xed, 0xed,\n  0xed, 0xff, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xf8, 0xc5, 0xc5,\n  0xc5, 0xd6, 0xd5, 0xd5, 0xd5, 0x86, 0xed, 0xed, 0xed, 0x19, 0xfc, 0xfc,\n  0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe,\n  0xfe, 0x00, 0xf8, 0xf8, 0xf8, 0x0f, 0xe8, 0xe8, 0xe8, 0x5c, 0xcb, 0xcb,\n  0xcb, 0xaf, 0xeb, 0xeb, 0xeb, 0xea, 0x5a, 0xfa, 0x5a, 0xfd, 0x00, 0xf6,\n  0x00, 0xff, 0x00, 0xf6, 0x00, 0xff, 0x8e, 0xf9, 0x8e, 0xff, 0xec, 0xec,\n  0xec, 0xff, 0xc3, 0xc3, 0xc3, 0xff, 0xc0, 0xc0, 0xc0, 0xf5, 0xc5, 0xc5,\n  0xc5, 0xbd, 0xd5, 0xd5, 0xd5, 0x5d, 0xed, 0xed, 0xed, 0x10, 0xfc, 0xfc,\n  0xfc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0xed, 0xed, 0xed, 0x1d, 0xe1, 0xe1,\n  0xe1, 0x6c, 0xca, 0xca, 0xca, 0xb0, 0xb3, 0xb3, 0xb3, 0xe5, 0xbc, 0xbc,\n  0xbc, 0xfc, 0xea, 0xfb, 0xea, 0xff, 0x24, 0xe6, 0x24, 0xff, 0x00, 0xe1,\n  0x00, 0xff, 0x00, 0xe1, 0x00, 0xff, 0x8e, 0xf0, 0x8e, 0xff, 0xed, 0xed,\n  0xed, 0xff, 0xc4, 0xc4, 0xc4, 0xfd, 0xc0, 0xc0, 0xc0, 0xe6, 0xc5, 0xc5,\n  0xc5, 0xa1, 0xd5, 0xd5, 0xd5, 0x36, 0xef, 0xef, 0xef, 0x00, 0xfd, 0xfd,\n  0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xe9, 0xe9,\n  0xe9, 0x19, 0xe6, 0xe6, 0xe6, 0x71, 0xe9, 0xe9, 0xe9, 0xbb, 0xdf, 0xdf,\n  0xdf, 0xea, 0xda, 0xda, 0xda, 0xfc, 0xd5, 0xd5, 0xd5, 0xff, 0xee, 0xee,\n  0xee, 0xff, 0xd1, 0xf6, 0xd1, 0xff, 0x0a, 0xce, 0x0a, 0xff, 0x00, 0xcc,\n  0x00, 0xff, 0x00, 0xcc, 0x00, 0xff, 0x88, 0xe5, 0x88, 0xff, 0xef, 0xef,\n  0xef, 0xff, 0xc3, 0xc3, 0xc3, 0xf9, 0xc1, 0xc1, 0xc1, 0xd7, 0xc9, 0xc9,\n  0xc9, 0x87, 0xe3, 0xe3, 0xe3, 0x2c, 0xfa, 0xfa, 0xfa, 0x10, 0x00, 0x00,\n  0x00, 0x00, 0xf5, 0xf5, 0xf5, 0x13, 0xe1, 0xe1, 0xe1, 0x61, 0xf7, 0xf9,\n  0xf7, 0xb8, 0x83, 0xd9, 0x83, 0xed, 0x69, 0xd2, 0x69, 0xfd, 0x69, 0xd2,\n  0x69, 0xff, 0x69, 0xd2, 0x69, 0xff, 0x69, 0xd2, 0x69, 0xff, 0x69, 0xd2,\n  0x69, 0xff, 0x2b, 0xc2, 0x2b, 0xff, 0x00, 0xb7, 0x00, 0xff, 0x00, 0xb7,\n  0x00, 0xff, 0x00, 0xb7, 0x00, 0xff, 0x88, 0xdb, 0x88, 0xff, 0xf0, 0xf0,\n  0xf0, 0xfe, 0xc7, 0xc7, 0xc7, 0xef, 0xd0, 0xd0, 0xd0, 0xb1, 0xe9, 0xe9,\n  0xe9, 0x5c, 0xfa, 0xfa, 0xfa, 0x16, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xfb,\n  0xfb, 0x1d, 0xe3, 0xe3, 0xe3, 0x80, 0xd0, 0xee, 0xd0, 0xde, 0x00, 0xa1,\n  0x00, 0xfd, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1,\n  0x00, 0xff, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1,\n  0x00, 0xff, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1, 0x00, 0xff, 0x00, 0xa1,\n  0x00, 0xff, 0x00, 0xa1, 0x00, 0xfe, 0xaf, 0xe1, 0xaf, 0xf9, 0xe1, 0xe1,\n  0xe1, 0xe1, 0xe3, 0xe3, 0xe3, 0xa1, 0xf6, 0xf6, 0xf6, 0x36, 0xfd, 0xfd,\n  0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xf4, 0xf4, 0x14, 0xde, 0xde,\n  0xde, 0x87, 0xfa, 0xfa, 0xfa, 0xe4, 0x4b, 0xaf, 0x4b, 0xfe, 0x23, 0x9d,\n  0x23, 0xff, 0x23, 0x9d, 0x23, 0xff, 0x23, 0x9d, 0x23, 0xff, 0x23, 0x9d,\n  0x23, 0xff, 0x20, 0x9c, 0x20, 0xff, 0x13, 0x96, 0x13, 0xff, 0x00, 0x8c,\n  0x00, 0xff, 0x00, 0x8c, 0x00, 0xff, 0x00, 0x8c, 0x00, 0xfe, 0x54, 0xb3,\n  0x54, 0xf4, 0xf4, 0xf7, 0xf4, 0xd5, 0xd9, 0xd9, 0xd9, 0xa4, 0xe8, 0xe8,\n  0xe8, 0x68, 0xfd, 0xfd, 0xfd, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x25, 0xe6, 0xe6, 0xe6, 0x78, 0xed, 0xed,\n  0xed, 0xbd, 0xfa, 0xfa, 0xfa, 0xe5, 0xfa, 0xfa, 0xfa, 0xf3, 0xf9, 0xf9,\n  0xf9, 0xfa, 0xf7, 0xf7, 0xf7, 0xfe, 0xfb, 0xfb, 0xfb, 0xff, 0xf2, 0xf8,\n  0xf2, 0xff, 0x38, 0x94, 0x38, 0xff, 0x00, 0x77, 0x00, 0xff, 0x00, 0x77,\n  0x00, 0xff, 0x54, 0xa5, 0x54, 0xf8, 0xf4, 0xf6, 0xf4, 0xd1, 0xd7, 0xd7,\n  0xd7, 0x87, 0xe0, 0xe0, 0xe0, 0x37, 0xf7, 0xf7, 0xf7, 0x18, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xf5, 0xf5, 0xf5, 0x28, 0xe7, 0xe7, 0xe7, 0x5c, 0xe0, 0xe0,\n  0xe0, 0x8e, 0xd5, 0xd5, 0xd5, 0xb5, 0xc8, 0xc8, 0xc8, 0xd5, 0xca, 0xca,\n  0xca, 0xf1, 0xf3, 0xf5, 0xf3, 0xfd, 0x43, 0x8a, 0x43, 0xff, 0x00, 0x61,\n  0x00, 0xff, 0x00, 0x61, 0x00, 0xff, 0x54, 0x97, 0x54, 0xfc, 0xf4, 0xf6,\n  0xf4, 0xdf, 0xd7, 0xd7, 0xd7, 0x95, 0xe0, 0xe0, 0xe0, 0x30, 0xf7, 0xf7,\n  0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,\n  0xff, 0x00, 0xfe, 0xfe, 0xfe, 0x0b, 0xfd, 0xfd, 0xfd, 0x22, 0xfb, 0xfb,\n  0xfb, 0x39, 0xe3, 0xe3, 0xe3, 0x87, 0xf5, 0xf5, 0xf5, 0xc9, 0x69, 0x95,\n  0x69, 0xf2, 0x00, 0x4c, 0x00, 0xfe, 0x00, 0x4c, 0x00, 0xff, 0x54, 0x89,\n  0x54, 0xfd, 0xf5, 0xf7, 0xf5, 0xe9, 0xd7, 0xd7, 0xd7, 0xa8, 0xe0, 0xe0,\n  0xe0, 0x4f, 0xf7, 0xf7, 0xf7, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0xe5, 0xe5,\n  0xe5, 0x24, 0xf5, 0xf5, 0xf5, 0x90, 0x72, 0x95, 0x72, 0xdc, 0x20, 0x58,\n  0x20, 0xf7, 0x5b, 0x84, 0x5b, 0xf6, 0xf6, 0xf7, 0xf6, 0xe5, 0xdf, 0xdf,\n  0xdf, 0xb1, 0xe2, 0xe2, 0xe2, 0x5f, 0xf7, 0xf7, 0xf7, 0x0b, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x0a, 0xe9, 0xe9, 0xe9, 0x00, 0xe8, 0xe8,\n  0xe8, 0x4c, 0xf6, 0xf6, 0xf6, 0xb8, 0xfa, 0xfa, 0xfa, 0xcf, 0xf8, 0xf8,\n  0xf8, 0xbe, 0xe7, 0xe7, 0xe7, 0x99, 0xe6, 0xe6, 0xe6, 0x5d, 0xf8, 0xf8,\n  0xf8, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfa, 0xfa, 0xfa, 0x0f, 0xe9, 0xe9, 0xe9, 0x4c, 0xe4, 0xe4,\n  0xe4, 0x74, 0xd8, 0xd8, 0xd8, 0x70, 0xe3, 0xe3, 0xe3, 0x40, 0xe9, 0xe9,\n  0xe9, 0x40, 0xf9, 0xf9, 0xf9, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x16, 0xfd, 0xfd, 0xfd, 0x22, 0xfd, 0xfd,\n  0xfd, 0x12, 0xfd, 0xfd, 0xfd, 0x00, 0xfe, 0xfe, 0xfe, 0x0b, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xfe, 0x07,\n  0xf0, 0x00, 0xfc, 0x03, 0xf0, 0x00, 0xf8, 0x01, 0xf0, 0x00, 0xf8, 0x00,\n  0xf0, 0x00, 0xe0, 0x00, 0x70, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0x00,\n  0xf0, 0x00, 0x80, 0x01, 0xf0, 0x00, 0xf0, 0x03, 0xf0, 0x00, 0xf0, 0x07,\n  0xf0, 0x00, 0xf8, 0x0f, 0xf0, 0x00, 0xfc, 0x1f, 0xf0, 0x00, 0xff, 0xff,\n  0xf0, 0x00\n};\nstatic const unsigned int TaskDlgArrowNormal_ico_len = 3302;\nstatic const unsigned char TaskDlgChevronLess_ico[] = {\n  0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00,\n  0x08, 0x00, 0x08, 0x06, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x14, 0x14,\n  0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xb8, 0x06, 0x00, 0x00, 0x2e, 0x06,\n  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x2a,\n  0x2a, 0x00, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x45, 0x45,\n  0x45, 0x00, 0x46, 0x46, 0x46, 0x00, 0x48, 0x48, 0x48, 0x00, 0x50, 0x50,\n  0x50, 0x00, 0x51, 0x51, 0x51, 0x00, 0x55, 0x55, 0x55, 0x00, 0x58, 0x58,\n  0x58, 0x00, 0x5a, 0x5a, 0x5a, 0x00, 0x5b, 0x5b, 0x5b, 0x00, 0x5c, 0x5c,\n  0x5c, 0x00, 0x5e, 0x5e, 0x5e, 0x00, 0x61, 0x61, 0x61, 0x00, 0x62, 0x60,\n  0x60, 0x00, 0x64, 0x64, 0x64, 0x00, 0x65, 0x65, 0x65, 0x00, 0x68, 0x68,\n  0x68, 0x00, 0x69, 0x69, 0x69, 0x00, 0x6a, 0x6a, 0x6a, 0x00, 0x6b, 0x6b,\n  0x6b, 0x00, 0x6c, 0x6c, 0x6c, 0x00, 0x6e, 0x6e, 0x6e, 0x00, 0x6f, 0x6f,\n  0x6f, 0x00, 0x70, 0x70, 0x70, 0x00, 0x71, 0x71, 0x71, 0x00, 0x72, 0x72,\n  0x72, 0x00, 0x73, 0x73, 0x73, 0x00, 0x74, 0x74, 0x74, 0x00, 0x75, 0x75,\n  0x75, 0x00, 0x77, 0x77, 0x77, 0x00, 0x78, 0x78, 0x78, 0x00, 0x79, 0x79,\n  0x79, 0x00, 0x7c, 0x7c, 0x7d, 0x00, 0x7d, 0x7d, 0x7d, 0x00, 0x7e, 0x7e,\n  0x7e, 0x00, 0x7f, 0x7f, 0x7f, 0x00, 0x81, 0x81, 0x81, 0x00, 0x82, 0x82,\n  0x82, 0x00, 0x90, 0x90, 0x90, 0x00, 0x95, 0x95, 0x95, 0x00, 0x96, 0x96,\n  0x97, 0x00, 0x97, 0x97, 0x97, 0x00, 0x97, 0x97, 0x98, 0x00, 0x98, 0x98,\n  0x98, 0x00, 0x99, 0x9a, 0x9a, 0x00, 0x9a, 0x9a, 0x9a, 0x00, 0x9b, 0x9c,\n  0x9d, 0x00, 0x9e, 0x9f, 0x9f, 0x00, 0xa0, 0xa1, 0xa1, 0x00, 0xa1, 0xa1,\n  0xa2, 0x00, 0xa1, 0xa2, 0xa2, 0x00, 0xa3, 0xa5, 0xa5, 0x00, 0xa4, 0xa4,\n  0xa5, 0x00, 0xa7, 0xa8, 0xa9, 0x00, 0xa9, 0xa9, 0xaa, 0x00, 0xa9, 0xaa,\n  0xaa, 0x00, 0xb0, 0xb3, 0xb4, 0x00, 0xb2, 0xb5, 0xb6, 0x00, 0xb3, 0xb5,\n  0xb6, 0x00, 0xb7, 0xb7, 0xb8, 0x00, 0xb8, 0xb9, 0xb9, 0x00, 0xba, 0xbb,\n  0xbc, 0x00, 0xbc, 0xbd, 0xbe, 0x00, 0xbd, 0xbe, 0xc0, 0x00, 0xbd, 0xc0,\n  0xc2, 0x00, 0xbe, 0xc0, 0xc1, 0x00, 0xbf, 0xc2, 0xc4, 0x00, 0xc0, 0xc1,\n  0xc1, 0x00, 0xc8, 0xc9, 0xcb, 0x00, 0xc9, 0xc9, 0xc9, 0x00, 0xc8, 0xca,\n  0xca, 0x00, 0xca, 0xca, 0xca, 0x00, 0xca, 0xcb, 0xcc, 0x00, 0xc9, 0xcc,\n  0xce, 0x00, 0xcb, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x00, 0xc9, 0xcd,\n  0xd0, 0x00, 0xca, 0xcd, 0xd0, 0x00, 0xca, 0xce, 0xd0, 0x00, 0xcd, 0xce,\n  0xd0, 0x00, 0xcf, 0xcf, 0xd0, 0x00, 0xce, 0xd0, 0xd1, 0x00, 0xcf, 0xd0,\n  0xd1, 0x00, 0xd0, 0xd1, 0xd2, 0x00, 0xd3, 0xd3, 0xd3, 0x00, 0xd0, 0xd2,\n  0xd4, 0x00, 0xd1, 0xd2, 0xd4, 0x00, 0xd2, 0xd5, 0xd5, 0x00, 0xd4, 0xd4,\n  0xd4, 0x00, 0xd2, 0xd6, 0xda, 0x00, 0xd5, 0xd6, 0xd8, 0x00, 0xd6, 0xd8,\n  0xd9, 0x00, 0xd7, 0xd8, 0xd9, 0x00, 0xd4, 0xd9, 0xdd, 0x00, 0xd6, 0xdb,\n  0xde, 0x00, 0xd7, 0xda, 0xdd, 0x00, 0xd9, 0xda, 0xdb, 0x00, 0xd9, 0xdc,\n  0xde, 0x00, 0xdd, 0xdd, 0xdd, 0x00, 0xde, 0xdf, 0xde, 0x00, 0xd9, 0xde,\n  0xe1, 0x00, 0xdb, 0xe0, 0xe3, 0x00, 0xde, 0xe2, 0xe5, 0x00, 0xe0, 0xe0,\n  0xe0, 0x00, 0xe2, 0xe2, 0xe2, 0x00, 0xe0, 0xe2, 0xe4, 0x00, 0xe0, 0xe4,\n  0xe7, 0x00, 0xe2, 0xe4, 0xe7, 0x00, 0xe2, 0xe6, 0xe7, 0x00, 0xe4, 0xe6,\n  0xe6, 0x00, 0xe4, 0xe7, 0xe9, 0x00, 0xe5, 0xe7, 0xe8, 0x00, 0xe6, 0xe9,\n  0xeb, 0x00, 0xe7, 0xe9, 0xea, 0x00, 0xe7, 0xea, 0xeb, 0x00, 0xe7, 0xe9,\n  0xec, 0x00, 0xe7, 0xea, 0xec, 0x00, 0xe8, 0xe9, 0xe9, 0x00, 0xe9, 0xeb,\n  0xeb, 0x00, 0xe9, 0xec, 0xed, 0x00, 0xea, 0xec, 0xee, 0x00, 0xeb, 0xec,\n  0xee, 0x00, 0xeb, 0xee, 0xef, 0x00, 0xec, 0xed, 0xee, 0x00, 0xec, 0xee,\n  0xee, 0x00, 0xed, 0xee, 0xef, 0x00, 0xec, 0xee, 0xf0, 0x00, 0xed, 0xef,\n  0xf0, 0x00, 0xee, 0xf0, 0xf2, 0x00, 0xef, 0xf1, 0xf2, 0x00, 0xf1, 0xf3,\n  0xf3, 0x00, 0xf1, 0xf3, 0xf4, 0x00, 0xf2, 0xf3, 0xf4, 0x00, 0xf2, 0xf4,\n  0xf5, 0x00, 0xf3, 0xf4, 0xf5, 0x00, 0xf4, 0xf5, 0xf5, 0x00, 0xf5, 0xf5,\n  0xf7, 0x00, 0xf6, 0xf6, 0xf6, 0x00, 0xf5, 0xf7, 0xf8, 0x00, 0xf8, 0xf8,\n  0xf9, 0x00, 0xfa, 0xfb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x0d, 0x11, 0x17, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x23, 0x31, 0x4e, 0x5b, 0x5b,\n  0x5b, 0x4e, 0x31, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x17, 0x2a, 0x4e, 0x5b, 0x3e, 0x3a, 0x37, 0x3a, 0x40, 0x5b, 0x4e,\n  0x2a, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x37, 0x5b, 0x40,\n  0x3a, 0x37, 0x37, 0x37, 0x37, 0x37, 0x3a, 0x40, 0x5b, 0x37, 0x1e, 0x00,\n  0x00, 0x00, 0x00, 0x11, 0x2b, 0x5b, 0x42, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\n  0x3d, 0x3d, 0x3d, 0x3d, 0x45, 0x5b, 0x31, 0x00, 0x00, 0x00, 0x00, 0x23,\n  0x50, 0x4e, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,\n  0x45, 0x50, 0x4e, 0x28, 0x00, 0x00, 0x00, 0x31, 0x65, 0x50, 0x70, 0x7c,\n  0x81, 0x70, 0x50, 0x50, 0x50, 0x70, 0x7c, 0x81, 0x70, 0x50, 0x65, 0x31,\n  0x00, 0x00, 0x17, 0x4e, 0x61, 0x5c, 0x89, 0x09, 0x09, 0x81, 0x77, 0x5c,\n  0x77, 0x89, 0x09, 0x09, 0x81, 0x5c, 0x61, 0x45, 0x1e, 0x00, 0x17, 0x5c,\n  0x61, 0x61, 0x84, 0x09, 0x09, 0x09, 0x89, 0x77, 0x84, 0x09, 0x09, 0x09,\n  0x89, 0x61, 0x61, 0x58, 0x1e, 0x00, 0x17, 0x61, 0x61, 0x61, 0x7c, 0x89,\n  0x09, 0x09, 0x09, 0x84, 0x09, 0x09, 0x09, 0x89, 0x7c, 0x61, 0x61, 0x5b,\n  0x17, 0x00, 0x17, 0x61, 0x69, 0x68, 0x69, 0x7c, 0x7c, 0x09, 0x09, 0x09,\n  0x09, 0x09, 0x89, 0x7c, 0x69, 0x68, 0x69, 0x5b, 0x17, 0x00, 0x1e, 0x4e,\n  0x70, 0x69, 0x70, 0x70, 0x81, 0x8d, 0x09, 0x09, 0x09, 0x89, 0x81, 0x70,\n  0x70, 0x69, 0x70, 0x45, 0x1e, 0x00, 0x1e, 0x35, 0x7c, 0x77, 0x77, 0x77,\n  0x77, 0x89, 0x89, 0x09, 0x8d, 0x89, 0x77, 0x77, 0x77, 0x77, 0x77, 0x2a,\n  0x00, 0x00, 0x00, 0x28, 0x65, 0x7c, 0x81, 0x81, 0x7c, 0x81, 0x89, 0x8f,\n  0x89, 0x81, 0x7c, 0x81, 0x81, 0x7c, 0x58, 0x23, 0x00, 0x00, 0x00, 0x1e,\n  0x31, 0x7c, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8d, 0x89, 0x89, 0x89, 0x89,\n  0x89, 0x70, 0x2a, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x23, 0x37, 0x7c, 0x89,\n  0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x7c, 0x31, 0x23, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x2a, 0x65, 0x89, 0x8f, 0x8f, 0x8f,\n  0x8f, 0x8f, 0x89, 0x61, 0x2a, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x1e, 0x28, 0x31, 0x4e, 0x70, 0x70, 0x65, 0x4e, 0x2a, 0x28,\n  0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x1e, 0x23, 0x1e, 0x1e, 0x1e, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xfe, 0x0f, 0xf0, 0x00, 0xf0, 0x03,\n  0xf0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xc0, 0x00, 0x70, 0x00, 0x80, 0x00,\n  0x70, 0x00, 0x80, 0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x70, 0x00, 0xe0, 0x00,\n  0xf0, 0x00, 0xf0, 0x01, 0xf0, 0x00, 0xfc, 0x0f, 0xf0, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00,\n  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89,\n  0x89, 0x07, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0x07, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0x2a, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89,\n  0x89, 0xac, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89,\n  0x89, 0x42, 0x89, 0x89, 0x89, 0x18, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89,\n  0x89, 0x18, 0x89, 0x89, 0x89, 0x42, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0xac, 0x89, 0x89, 0x89, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89,\n  0x89, 0x1f, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0xb9, 0x89, 0x89,\n  0x89, 0x18, 0xd4, 0xd4, 0xd4, 0xff, 0xb7, 0xb7, 0xb8, 0xff, 0xa9, 0xaa,\n  0xaa, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa9, 0xaa, 0xaa, 0xff, 0xba, 0xbb,\n  0xbc, 0xff, 0xd4, 0xd4, 0xd4, 0xff, 0x89, 0x89, 0x89, 0x18, 0x89, 0x89,\n  0x89, 0xb9, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0x1f, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x90, 0xd4, 0xd4, 0xd4, 0xff, 0xba, 0xbb, 0xbc, 0xff, 0xa9, 0xaa,\n  0xaa, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa4, 0xa4,\n  0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa9, 0xaa,\n  0xaa, 0xff, 0xba, 0xbb, 0xbc, 0xff, 0xd4, 0xd4, 0xd4, 0xff, 0x89, 0x89,\n  0x89, 0x90, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89,\n  0x89, 0xac, 0x89, 0x89, 0x89, 0xb9, 0xd4, 0xd4, 0xd4, 0xff, 0xbd, 0xbe,\n  0xc0, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5,\n  0xb6, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5,\n  0xb6, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb3, 0xb5,\n  0xb6, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xd4, 0xd4, 0xd4, 0xff, 0x89, 0x89,\n  0x89, 0xb9, 0x89, 0x89, 0x89, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0x2a, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0x18, 0xcc, 0xcc, 0xcc, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xca, 0xcd, 0xd0, 0xff, 0x89, 0x89, 0x89, 0x18, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0xa4, 0xdd, 0xdd, 0xdd, 0xff, 0xca, 0xcd,\n  0xd0, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xca, 0xcd, 0xd0, 0xff, 0xca, 0xcd,\n  0xd0, 0xff, 0xca, 0xcd, 0xd0, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xeb, 0xec,\n  0xee, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xca, 0xcd,\n  0xd0, 0xff, 0xdd, 0xdd, 0xdd, 0xff, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89,\n  0x89, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x42, 0xd6, 0xdb, 0xde, 0xff, 0xd2, 0xd6, 0xda, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xd2, 0xd6, 0xda, 0xff, 0xe7, 0xea,\n  0xec, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xd2, 0xd6, 0xda, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0x89, 0x89, 0x89, 0x42, 0x89, 0x89, 0x89, 0xf7, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0x18, 0xd6, 0xdb,\n  0xde, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0x89, 0x89,\n  0x89, 0x18, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0x07, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0x0e, 0xd6, 0xdb, 0xde, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xef, 0xf1,\n  0xf2, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0x18, 0xde, 0xe2, 0xe5, 0xff, 0xdb, 0xe0, 0xe3, 0xff, 0xde, 0xe2,\n  0xe5, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xeb, 0xec, 0xee, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xeb, 0xec,\n  0xee, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xdb, 0xe0, 0xe3, 0xff, 0xde, 0xe2,\n  0xe5, 0xff, 0x89, 0x89, 0x89, 0x18, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0x07, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0x42, 0xe4, 0xe6,\n  0xe6, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xe4, 0xe6,\n  0xe6, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xf5, 0xf7, 0xf8, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0x55, 0x55, 0x55, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0xe4, 0xe6,\n  0xe6, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0x89, 0x89,\n  0x89, 0x42, 0x89, 0x89, 0x89, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0xa4, 0xeb, 0xec, 0xee, 0xff, 0xe7, 0xea,\n  0xec, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xe7, 0xea,\n  0xec, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0x55, 0x55,\n  0x55, 0xff, 0xf5, 0xf7, 0xf8, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xe7, 0xea,\n  0xec, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xe7, 0xea,\n  0xec, 0xff, 0xe7, 0xea, 0xec, 0xff, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89,\n  0x89, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0x2a, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0x18, 0xeb, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xeb, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xeb, 0xec, 0xee, 0xff, 0x89, 0x89,\n  0x89, 0x18, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0x2a, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0xac, 0x89, 0x89,\n  0x89, 0xb9, 0xeb, 0xec, 0xee, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf5, 0xf7, 0xf8, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0x89, 0x89, 0x89, 0xb9, 0x89, 0x89,\n  0x89, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x90, 0xeb, 0xec, 0xee, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xeb, 0xec, 0xee, 0xff, 0x89, 0x89,\n  0x89, 0x90, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0x1f, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0xb9, 0x89, 0x89, 0x89, 0x18, 0xf3, 0xf4, 0xf5, 0xff, 0xfa, 0xfb,\n  0xfb, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xfa, 0xfb,\n  0xfb, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0x89, 0x89,\n  0x89, 0x18, 0x89, 0x89, 0x89, 0xb9, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0xac, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89, 0x89, 0x42, 0x89, 0x89,\n  0x89, 0x18, 0x89, 0x89, 0x89, 0x0e, 0x89, 0x89, 0x89, 0x18, 0x89, 0x89,\n  0x89, 0x42, 0x89, 0x89, 0x89, 0xa4, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0xac, 0x89, 0x89, 0x89, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0x89, 0x2a, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89,\n  0x89, 0xff, 0x89, 0x89, 0x89, 0xff, 0x89, 0x89, 0x89, 0xf7, 0x89, 0x89,\n  0x89, 0x9a, 0x89, 0x89, 0x89, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xf0, 0x00, 0xf8, 0x03,\n  0xf0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xc0, 0x00, 0x70, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0xc0, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x03,\n  0xf0, 0x00\n};\nstatic const unsigned int TaskDlgChevronLess_ico_len = 3302;\nstatic const unsigned char TaskDlgChevronMore_ico[] = {\n  0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00,\n  0x08, 0x00, 0x08, 0x06, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x14, 0x14,\n  0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xb8, 0x06, 0x00, 0x00, 0x2e, 0x06,\n  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x2a,\n  0x2a, 0x00, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x45, 0x45,\n  0x45, 0x00, 0x46, 0x46, 0x46, 0x00, 0x48, 0x48, 0x48, 0x00, 0x50, 0x50,\n  0x50, 0x00, 0x51, 0x51, 0x51, 0x00, 0x55, 0x55, 0x55, 0x00, 0x58, 0x58,\n  0x58, 0x00, 0x5a, 0x5a, 0x5a, 0x00, 0x5b, 0x5b, 0x5b, 0x00, 0x5c, 0x5c,\n  0x5c, 0x00, 0x5e, 0x5e, 0x5e, 0x00, 0x61, 0x61, 0x61, 0x00, 0x62, 0x60,\n  0x60, 0x00, 0x64, 0x64, 0x64, 0x00, 0x65, 0x65, 0x65, 0x00, 0x68, 0x68,\n  0x68, 0x00, 0x69, 0x69, 0x69, 0x00, 0x6a, 0x6a, 0x6a, 0x00, 0x6b, 0x6b,\n  0x6b, 0x00, 0x6c, 0x6c, 0x6c, 0x00, 0x6e, 0x6e, 0x6e, 0x00, 0x6f, 0x6f,\n  0x6f, 0x00, 0x70, 0x70, 0x70, 0x00, 0x71, 0x71, 0x71, 0x00, 0x72, 0x72,\n  0x72, 0x00, 0x73, 0x73, 0x73, 0x00, 0x74, 0x74, 0x74, 0x00, 0x75, 0x75,\n  0x75, 0x00, 0x77, 0x77, 0x77, 0x00, 0x78, 0x78, 0x78, 0x00, 0x79, 0x79,\n  0x79, 0x00, 0x7c, 0x7c, 0x7d, 0x00, 0x7d, 0x7d, 0x7d, 0x00, 0x7e, 0x7e,\n  0x7e, 0x00, 0x7f, 0x7f, 0x7f, 0x00, 0x81, 0x81, 0x81, 0x00, 0x82, 0x82,\n  0x82, 0x00, 0x90, 0x90, 0x90, 0x00, 0x95, 0x95, 0x95, 0x00, 0x96, 0x96,\n  0x97, 0x00, 0x97, 0x97, 0x97, 0x00, 0x97, 0x97, 0x98, 0x00, 0x98, 0x98,\n  0x98, 0x00, 0x99, 0x9a, 0x9a, 0x00, 0x9a, 0x9a, 0x9a, 0x00, 0x9b, 0x9c,\n  0x9d, 0x00, 0x9e, 0x9f, 0x9f, 0x00, 0xa0, 0xa1, 0xa1, 0x00, 0xa1, 0xa1,\n  0xa2, 0x00, 0xa1, 0xa2, 0xa2, 0x00, 0xa3, 0xa5, 0xa5, 0x00, 0xa4, 0xa4,\n  0xa5, 0x00, 0xa7, 0xa8, 0xa9, 0x00, 0xa9, 0xa9, 0xaa, 0x00, 0xa9, 0xaa,\n  0xaa, 0x00, 0xb0, 0xb3, 0xb4, 0x00, 0xb2, 0xb5, 0xb6, 0x00, 0xb3, 0xb5,\n  0xb6, 0x00, 0xb7, 0xb7, 0xb8, 0x00, 0xb8, 0xb9, 0xb9, 0x00, 0xba, 0xbb,\n  0xbc, 0x00, 0xbc, 0xbd, 0xbe, 0x00, 0xbd, 0xbe, 0xc0, 0x00, 0xbd, 0xc0,\n  0xc2, 0x00, 0xbe, 0xc0, 0xc1, 0x00, 0xbf, 0xc2, 0xc4, 0x00, 0xc0, 0xc1,\n  0xc1, 0x00, 0xc6, 0xc8, 0xc9, 0x00, 0xc8, 0xc9, 0xcb, 0x00, 0xc9, 0xc9,\n  0xc9, 0x00, 0xc8, 0xca, 0xca, 0x00, 0xca, 0xca, 0xca, 0x00, 0xca, 0xcb,\n  0xcc, 0x00, 0xc9, 0xcc, 0xce, 0x00, 0xcb, 0xcc, 0xcc, 0x00, 0xcc, 0xcc,\n  0xcc, 0x00, 0xc9, 0xcd, 0xd0, 0x00, 0xca, 0xcd, 0xd0, 0x00, 0xca, 0xce,\n  0xd0, 0x00, 0xcd, 0xce, 0xd0, 0x00, 0xcf, 0xcf, 0xd0, 0x00, 0xce, 0xd0,\n  0xd1, 0x00, 0xcf, 0xd0, 0xd1, 0x00, 0xd0, 0xd1, 0xd2, 0x00, 0xd3, 0xd3,\n  0xd3, 0x00, 0xd0, 0xd2, 0xd4, 0x00, 0xd1, 0xd2, 0xd4, 0x00, 0xd2, 0xd5,\n  0xd5, 0x00, 0xd4, 0xd4, 0xd4, 0x00, 0xd2, 0xd6, 0xda, 0x00, 0xd5, 0xd6,\n  0xd8, 0x00, 0xd6, 0xd8, 0xd9, 0x00, 0xd7, 0xd8, 0xd9, 0x00, 0xd4, 0xd8,\n  0xdc, 0x00, 0xd6, 0xdb, 0xde, 0x00, 0xd7, 0xda, 0xdd, 0x00, 0xd9, 0xda,\n  0xdb, 0x00, 0xd9, 0xdc, 0xde, 0x00, 0xdc, 0xdd, 0xdf, 0x00, 0xdd, 0xdd,\n  0xdd, 0x00, 0xde, 0xdf, 0xde, 0x00, 0xd9, 0xde, 0xe1, 0x00, 0xdb, 0xe0,\n  0xe3, 0x00, 0xde, 0xe2, 0xe5, 0x00, 0xe0, 0xe0, 0xe0, 0x00, 0xe2, 0xe2,\n  0xe2, 0x00, 0xe0, 0xe2, 0xe4, 0x00, 0xe2, 0xe4, 0xe7, 0x00, 0xe2, 0xe6,\n  0xe7, 0x00, 0xe4, 0xe6, 0xe6, 0x00, 0xe4, 0xe7, 0xe9, 0x00, 0xe5, 0xe7,\n  0xe9, 0x00, 0xe7, 0xea, 0xeb, 0x00, 0xe7, 0xea, 0xec, 0x00, 0xe8, 0xe9,\n  0xe9, 0x00, 0xe9, 0xeb, 0xeb, 0x00, 0xea, 0xea, 0xeb, 0x00, 0xe8, 0xeb,\n  0xed, 0x00, 0xe9, 0xec, 0xed, 0x00, 0xea, 0xec, 0xee, 0x00, 0xeb, 0xec,\n  0xee, 0x00, 0xec, 0xed, 0xee, 0x00, 0xec, 0xee, 0xee, 0x00, 0xed, 0xee,\n  0xef, 0x00, 0xec, 0xee, 0xf0, 0x00, 0xed, 0xef, 0xf1, 0x00, 0xee, 0xf0,\n  0xf2, 0x00, 0xef, 0xf1, 0xf2, 0x00, 0xf0, 0xf2, 0xf3, 0x00, 0xf1, 0xf3,\n  0xf3, 0x00, 0xf1, 0xf3, 0xf4, 0x00, 0xf2, 0xf3, 0xf5, 0x00, 0xf2, 0xf4,\n  0xf5, 0x00, 0xf3, 0xf4, 0xf5, 0x00, 0xf4, 0xf5, 0xf5, 0x00, 0xf6, 0xf6,\n  0xf6, 0x00, 0xf5, 0xf7, 0xf8, 0x00, 0xf8, 0xf9, 0xf9, 0x00, 0xfa, 0xfb,\n  0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x0d, 0x12, 0x15, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x20, 0x2e, 0x4b, 0x5c, 0x58,\n  0x5c, 0x49, 0x2e, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x13, 0x2c, 0x4e, 0x56, 0x3e, 0x39, 0x37, 0x3a, 0x3f, 0x54, 0x49,\n  0x2c, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x35, 0x59, 0x40,\n  0x38, 0x37, 0x37, 0x36, 0x37, 0x37, 0x38, 0x41, 0x56, 0x33, 0x1b, 0x00,\n  0x00, 0x00, 0x00, 0x12, 0x2d, 0x5b, 0x42, 0x3d, 0x3b, 0x3c, 0x3c, 0x47,\n  0x3c, 0x3c, 0x3b, 0x3d, 0x44, 0x56, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x23,\n  0x53, 0x4d, 0x45, 0x45, 0x43, 0x45, 0x66, 0x78, 0x66, 0x45, 0x43, 0x45,\n  0x45, 0x51, 0x48, 0x28, 0x00, 0x00, 0x00, 0x31, 0x65, 0x52, 0x50, 0x50,\n  0x50, 0x6e, 0x7f, 0x07, 0x7f, 0x6e, 0x50, 0x50, 0x50, 0x52, 0x63, 0x2e,\n  0x00, 0x00, 0x15, 0x4a, 0x63, 0x5d, 0x61, 0x61, 0x73, 0x87, 0x07, 0x07,\n  0x07, 0x81, 0x73, 0x61, 0x61, 0x5d, 0x63, 0x46, 0x1c, 0x00, 0x16, 0x5e,\n  0x62, 0x61, 0x62, 0x79, 0x80, 0x07, 0x07, 0x07, 0x07, 0x07, 0x80, 0x79,\n  0x62, 0x61, 0x62, 0x55, 0x1b, 0x00, 0x17, 0x5f, 0x62, 0x62, 0x75, 0x85,\n  0x07, 0x07, 0x07, 0x85, 0x07, 0x07, 0x07, 0x85, 0x75, 0x62, 0x62, 0x5a,\n  0x17, 0x00, 0x18, 0x60, 0x6a, 0x69, 0x84, 0x07, 0x07, 0x07, 0x84, 0x7a,\n  0x84, 0x07, 0x07, 0x07, 0x84, 0x69, 0x6a, 0x57, 0x18, 0x00, 0x19, 0x4c,\n  0x70, 0x6b, 0x8c, 0x07, 0x07, 0x8c, 0x82, 0x6b, 0x82, 0x8c, 0x07, 0x07,\n  0x88, 0x6b, 0x6f, 0x44, 0x1f, 0x00, 0x1a, 0x32, 0x74, 0x72, 0x89, 0x8b,\n  0x83, 0x83, 0x72, 0x72, 0x72, 0x83, 0x89, 0x89, 0x83, 0x72, 0x73, 0x2a,\n  0x00, 0x00, 0x00, 0x27, 0x64, 0x7e, 0x80, 0x80, 0x7b, 0x80, 0x80, 0x7b,\n  0x80, 0x80, 0x7b, 0x80, 0x80, 0x7c, 0x56, 0x26, 0x00, 0x00, 0x00, 0x1c,\n  0x2f, 0x77, 0x86, 0x86, 0x83, 0x86, 0x86, 0x83, 0x86, 0x86, 0x83, 0x86,\n  0x86, 0x71, 0x29, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x25, 0x34, 0x7d, 0x8a,\n  0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x76, 0x2c, 0x24, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x2b, 0x67, 0x8b, 0x8e, 0x8e, 0x8d,\n  0x8e, 0x8e, 0x8a, 0x60, 0x29, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x1c, 0x27, 0x30, 0x4f, 0x6c, 0x6d, 0x68, 0x49, 0x2a, 0x27,\n  0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x1e, 0x21, 0x1e, 0x1f, 0x1c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xfe, 0x0f, 0xf0, 0x00, 0xf0, 0x03,\n  0xf0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xc0, 0x00, 0x70, 0x00, 0x80, 0x00,\n  0x70, 0x00, 0x80, 0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x70, 0x00, 0xe0, 0x00,\n  0xf0, 0x00, 0xf0, 0x01, 0xf0, 0x00, 0xfc, 0x0f, 0xf0, 0x00, 0x28, 0x00,\n  0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00,\n  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,\n  0x80, 0x07, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0x07, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x2a, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80,\n  0x80, 0xac, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80,\n  0x80, 0x42, 0x80, 0x80, 0x80, 0x18, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80,\n  0x80, 0x18, 0x80, 0x80, 0x80, 0x42, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0xac, 0x80, 0x80, 0x80, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,\n  0x80, 0x1f, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0xb9, 0x80, 0x80,\n  0x80, 0x18, 0xcf, 0xd0, 0xd1, 0xff, 0xb7, 0xb7, 0xb8, 0xff, 0xa9, 0xa9,\n  0xaa, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa9, 0xaa, 0xaa, 0xff, 0xb8, 0xb9,\n  0xb9, 0xff, 0xcf, 0xcf, 0xd0, 0xff, 0x80, 0x80, 0x80, 0x18, 0x80, 0x80,\n  0x80, 0xb9, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0x1f, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x90, 0xd0, 0xd2, 0xd4, 0xff, 0xba, 0xbb, 0xbc, 0xff, 0xa7, 0xa8,\n  0xa9, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa3, 0xa5,\n  0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa4, 0xa4, 0xa5, 0xff, 0xa7, 0xa8,\n  0xa9, 0xff, 0xbc, 0xbd, 0xbe, 0xff, 0xcf, 0xd0, 0xd1, 0xff, 0x80, 0x80,\n  0x80, 0x90, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,\n  0x80, 0xac, 0x80, 0x80, 0x80, 0xb9, 0xd2, 0xd5, 0xd5, 0xff, 0xbd, 0xbe,\n  0xc0, 0xff, 0xb3, 0xb5, 0xb6, 0xff, 0xb0, 0xb3, 0xb4, 0xff, 0xb2, 0xb5,\n  0xb6, 0xff, 0xb2, 0xb5, 0xb6, 0xff, 0xc6, 0xc8, 0xc9, 0xff, 0xb2, 0xb5,\n  0xb6, 0xff, 0xb2, 0xb5, 0xb6, 0xff, 0xb0, 0xb3, 0xb4, 0xff, 0xb3, 0xb5,\n  0xb6, 0xff, 0xbe, 0xc0, 0xc1, 0xff, 0xcf, 0xd0, 0xd1, 0xff, 0x80, 0x80,\n  0x80, 0xb9, 0x80, 0x80, 0x80, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0x2a, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0x18, 0xc9, 0xcc, 0xce, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xbd, 0xc0, 0xc2, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xdc, 0xdd,\n  0xdf, 0xff, 0xea, 0xea, 0xeb, 0xff, 0xdc, 0xdd, 0xdf, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xbd, 0xc0, 0xc2, 0xff, 0xbf, 0xc2, 0xc4, 0xff, 0xbf, 0xc2,\n  0xc4, 0xff, 0xca, 0xcd, 0xd0, 0xff, 0x80, 0x80, 0x80, 0x18, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0xa4, 0xd9, 0xdc, 0xde, 0xff, 0xca, 0xce,\n  0xd0, 0xff, 0xc9, 0xcd, 0xd0, 0xff, 0xc9, 0xcd, 0xd0, 0xff, 0xc9, 0xcd,\n  0xd0, 0xff, 0xe0, 0xe2, 0xe4, 0xff, 0xed, 0xee, 0xef, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xed, 0xee, 0xef, 0xff, 0xe0, 0xe2, 0xe4, 0xff, 0xc9, 0xcd,\n  0xd0, 0xff, 0xc9, 0xcd, 0xd0, 0xff, 0xc9, 0xcd, 0xd0, 0xff, 0xca, 0xce,\n  0xd0, 0xff, 0xd7, 0xda, 0xdd, 0xff, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80,\n  0x80, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x42, 0xd7, 0xda, 0xdd, 0xff, 0xd2, 0xd6, 0xda, 0xff, 0xd4, 0xd8,\n  0xdc, 0xff, 0xd4, 0xd8, 0xdc, 0xff, 0xe5, 0xe7, 0xe9, 0xff, 0xf2, 0xf3,\n  0xf5, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xed, 0xef, 0xf1, 0xff, 0xe5, 0xe7, 0xe9, 0xff, 0xd4, 0xd8,\n  0xdc, 0xff, 0xd4, 0xd8, 0xdc, 0xff, 0xd2, 0xd6, 0xda, 0xff, 0xd7, 0xda,\n  0xdd, 0xff, 0x80, 0x80, 0x80, 0x42, 0x80, 0x80, 0x80, 0xf7, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0x18, 0xd6, 0xdb,\n  0xde, 0xff, 0xd4, 0xd8, 0xdc, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0xe8, 0xeb,\n  0xed, 0xff, 0xec, 0xee, 0xf0, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xe8, 0xeb, 0xed, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0xd4, 0xd8, 0xdc, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0x80, 0x80,\n  0x80, 0x18, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0x07, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0x0e, 0xd6, 0xdb, 0xde, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xf1, 0xf3, 0xf3, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0xf1, 0xf3,\n  0xf3, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xf1, 0xf3, 0xf3, 0xff, 0xe7, 0xea, 0xec, 0xff, 0xd6, 0xdb,\n  0xde, 0xff, 0xd6, 0xdb, 0xde, 0xff, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0x18, 0xdb, 0xe0, 0xe3, 0xff, 0xd9, 0xde, 0xe1, 0xff, 0xf0, 0xf2,\n  0xf3, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xf0, 0xf2, 0xf3, 0xff, 0xe9, 0xec, 0xed, 0xff, 0xf0, 0xf2,\n  0xf3, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0xf0, 0xf2, 0xf3, 0xff, 0xd9, 0xde, 0xe1, 0xff, 0xdb, 0xe0,\n  0xe3, 0xff, 0x80, 0x80, 0x80, 0x18, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0x07, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0x42, 0xe2, 0xe6,\n  0xe7, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xf5, 0xf7, 0xf8, 0xff, 0x50, 0x50,\n  0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0xf5, 0xf7, 0xf8, 0xff, 0xee, 0xf0,\n  0xf2, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xee, 0xf0, 0xf2, 0xff, 0xf5, 0xf7,\n  0xf8, 0xff, 0x50, 0x50, 0x50, 0xff, 0x50, 0x50, 0x50, 0xff, 0xf2, 0xf4,\n  0xf5, 0xff, 0xde, 0xe2, 0xe5, 0xff, 0xe2, 0xe4, 0xe7, 0xff, 0x80, 0x80,\n  0x80, 0x42, 0x80, 0x80, 0x80, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0xa4, 0xe7, 0xea, 0xeb, 0xff, 0xe4, 0xe7,\n  0xe9, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xf6, 0xf6, 0xf6, 0xff, 0xef, 0xf1,\n  0xf2, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xe4, 0xe7, 0xe9, 0xff, 0xe4, 0xe7,\n  0xe9, 0xff, 0xe4, 0xe7, 0xe9, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xf3, 0xf4,\n  0xf5, 0xff, 0xf3, 0xf4, 0xf5, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xe4, 0xe7,\n  0xe9, 0xff, 0xe5, 0xe7, 0xe9, 0xff, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80,\n  0x80, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x2a, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0x18, 0xec, 0xee, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xea, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xea, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xea, 0xec, 0xee, 0xff, 0xec, 0xee,\n  0xf0, 0xff, 0xec, 0xee, 0xf0, 0xff, 0xeb, 0xec, 0xee, 0xff, 0x80, 0x80,\n  0x80, 0x18, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0x2a, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xac, 0x80, 0x80,\n  0x80, 0xb9, 0xe9, 0xeb, 0xeb, 0xff, 0xf1, 0xf3, 0xf4, 0xff, 0xf1, 0xf3,\n  0xf4, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xf1, 0xf3, 0xf4, 0xff, 0xf1, 0xf3,\n  0xf4, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xf1, 0xf3, 0xf4, 0xff, 0xf1, 0xf3,\n  0xf4, 0xff, 0xef, 0xf1, 0xf2, 0xff, 0xf1, 0xf3, 0xf4, 0xff, 0xf1, 0xf3,\n  0xf4, 0xff, 0xe4, 0xe6, 0xe6, 0xff, 0x80, 0x80, 0x80, 0xb9, 0x80, 0x80,\n  0x80, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x90, 0xec, 0xed, 0xee, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xf4, 0xf5,\n  0xf5, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xf4, 0xf5,\n  0xf5, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xf4, 0xf5,\n  0xf5, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0xe8, 0xe9, 0xe9, 0xff, 0x80, 0x80,\n  0x80, 0x90, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0x0e, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0x1f, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0xb9, 0x80, 0x80, 0x80, 0x18, 0xf6, 0xf6, 0xf6, 0xff, 0xfa, 0xfb,\n  0xfb, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xf8, 0xf9, 0xf9, 0xff, 0xfa, 0xfb,\n  0xfb, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0xf4, 0xf5, 0xf5, 0xff, 0x80, 0x80,\n  0x80, 0x18, 0x80, 0x80, 0x80, 0xb9, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0xac, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80, 0x80, 0x42, 0x80, 0x80,\n  0x80, 0x18, 0x80, 0x80, 0x80, 0x0e, 0x80, 0x80, 0x80, 0x18, 0x80, 0x80,\n  0x80, 0x42, 0x80, 0x80, 0x80, 0xa4, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0xac, 0x80, 0x80, 0x80, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x2a, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80,\n  0x80, 0xff, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xf7, 0x80, 0x80,\n  0x80, 0x9a, 0x80, 0x80, 0x80, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xf0, 0x00, 0xf8, 0x03,\n  0xf0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xc0, 0x00, 0x70, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n  0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x30, 0x00, 0x80, 0x00,\n  0x30, 0x00, 0xc0, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x03,\n  0xf0, 0x00\n};\nstatic const unsigned int TaskDlgChevronMore_ico_len = 3302;\n"
  },
  {
    "path": "thirdparty/taskdialog98/maindlg.h",
    "content": "// maindlg.h : interface of the CMainDlg class\r\n//\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n#if !defined(AFX_MAINDLG_H__DA2FA2FB_17F8_4507_9DAE_D279641E8337__INCLUDED_)\r\n#define AFX_MAINDLG_H__DA2FA2FB_17F8_4507_9DAE_D279641E8337__INCLUDED_\r\n\r\n#if _MSC_VER >= 1000\r\n#pragma once\r\n#endif // _MSC_VER >= 1000\r\n\r\n\r\n#if _WIN32_WINNT < 0x0600\r\n\r\ninline int AtlTaskDialog(HWND hWndParent, \r\n                         ATL::_U_STRINGorID WindowTitle, ATL::_U_STRINGorID MainInstructionText, ATL::_U_STRINGorID ContentText, \r\n                         TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons = 0U, ATL::_U_STRINGorID Icon = (LPCTSTR)NULL)\r\n{\r\n   int nRet = -1;\r\n   typedef HRESULT (STDAPICALLTYPE *PFN_TaskDialog)(HWND hwndParent, HINSTANCE hInstance, PCWSTR pszWindowTitle, PCWSTR pszMainInstruction, PCWSTR pszContent, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons, PCWSTR pszIcon, int* pnButton);\r\n   HMODULE m_hCommCtrlDLL = ::LoadLibrary(_T(\"comctl32.dll\"));\r\n   if(m_hCommCtrlDLL != NULL)\r\n   {\r\n      PFN_TaskDialog pfnTaskDialog = (PFN_TaskDialog)::GetProcAddress(m_hCommCtrlDLL, \"TaskDialog\");\r\n      if(pfnTaskDialog != NULL)\r\n      {\r\n         USES_CONVERSION;\r\n         HRESULT hRet = pfnTaskDialog(hWndParent, ModuleHelper::GetResourceInstance(), \r\n            IS_INTRESOURCE(WindowTitle.m_lpstr) ? (LPCWSTR) WindowTitle.m_lpstr : T2CW(WindowTitle.m_lpstr), \r\n            IS_INTRESOURCE(MainInstructionText.m_lpstr) ? (LPCWSTR) MainInstructionText.m_lpstr : T2CW(MainInstructionText.m_lpstr), \r\n            IS_INTRESOURCE(ContentText.m_lpstr) ?  (LPCWSTR) ContentText.m_lpstr : T2CW(ContentText.m_lpstr), \r\n            dwCommonButtons, \r\n            IS_INTRESOURCE(Icon.m_lpstr) ? (LPCWSTR) Icon.m_lpstr : T2CW(Icon.m_lpstr),\r\n            &nRet);\r\n         ATLVERIFY(SUCCEEDED(hRet));\r\n      }\r\n\r\n      ::FreeLibrary(m_hCommCtrlDLL);\r\n   }\r\n   return nRet;\r\n}\r\n\r\n#endif // _WIN32_WINNT < 0x0600\r\n\r\ninline int AtlTaskDialogIndirect(TASKDIALOGCONFIG* pTask, int* pnButton = NULL, int* pnRadioButton = NULL, BOOL* pfVerificationFlagChecked = NULL)\r\n{\r\n   // This allows apps to run on older versions of Windows\r\n   typedef HRESULT (STDAPICALLTYPE *PFN_TaskDialogIndirect)(const TASKDIALOGCONFIG* pTaskConfig, int* pnButton, int* pnRadioButton, BOOL* pfVerificationFlagChecked);\r\n\r\n   HRESULT hRet = E_UNEXPECTED;\r\n   HMODULE m_hCommCtrlDLL = ::LoadLibrary(_T(\"comctl32.dll\"));\r\n   if(m_hCommCtrlDLL != NULL)\r\n   {\r\n      PFN_TaskDialogIndirect pfnTaskDialogIndirect = (PFN_TaskDialogIndirect)::GetProcAddress(m_hCommCtrlDLL, \"TaskDialogIndirect\");\r\n      if(pfnTaskDialogIndirect != NULL)\r\n         hRet = pfnTaskDialogIndirect(pTask, pnButton, pnRadioButton, pfVerificationFlagChecked);\r\n\r\n      ::FreeLibrary(m_hCommCtrlDLL);\r\n   }\r\n   return hRet;\r\n}\r\n\r\n\r\n\r\nclass CMainDlg : public CDialogImpl<CMainDlg>\r\n{\r\npublic:\r\n   enum { IDD = IDD_MAINDLG };\r\n\r\n   BEGIN_MSG_MAP(CMainDlg)\r\n      MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)\r\n      COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)\r\n      COMMAND_ID_HANDLER(IDOK, OnClose)\r\n      COMMAND_ID_HANDLER(IDCANCEL, OnClose)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON1, OnTest1)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON2, OnTest2)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON3, OnTest3)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON4, OnTest4)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON5, OnTest5)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON6, OnTest6)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON7, OnTest7)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON8, OnTest8)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON9, OnTest9)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON10, OnTest10)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON11, OnTest11)\r\n      COMMAND_ID_HANDLER(IDC_BUTTON12, OnTest12)\r\n   END_MSG_MAP()\r\n\r\n   LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r\n   {\r\n      CenterWindow();\r\n\r\n      HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), \r\n         IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);\r\n      SetIcon(hIcon, TRUE);\r\n      HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), \r\n         IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r\n      SetIcon(hIconSmall, FALSE);\r\n\r\n      return TRUE;\r\n   }\r\n\r\n   LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      CSimpleDialog<IDD_ABOUTBOX, FALSE> dlg;\r\n      dlg.DoModal();\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnClose(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      EndDialog(wID);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest1(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      LPCTSTR pstrWindowTitle = _T(\"Window Title\");\r\n      LPCTSTR pstrInstructionsText = _T(\"Test Case 1\");\r\n      LPCTSTR pstrContentText1 = _T(\"This is Bjarke's Task Dialog\");\r\n      LPCTSTR pstrContentText2 = _T(\"This is the Windows Vista Task Dialog\");\r\n      int iRes = 0;\r\n      Task98Dialog(m_hWnd, _Module.GetResourceInstance(), pstrWindowTitle,pstrInstructionsText, pstrContentText1, TDCBF_YES_BUTTON | TDCBF_OK_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON, MAKEINTRESOURCE(IDR_MAINFRAME), &iRes);\r\n      AtlTaskDialog(m_hWnd, pstrWindowTitle, pstrInstructionsText, pstrContentText2, TDCBF_YES_BUTTON | TDCBF_OK_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON, MAKEINTRESOURCE(IDR_MAINFRAME));\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest2(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      LPCTSTR pstrWindowTitle = _T(\"Window Title\");\r\n      LPCTSTR pstrInstructionsText = _T(\"Click on the button below\");\r\n      LPCTSTR pstrContentText = _T(\"Choose a button. Do the right thing and read this multi-line entry. Can there be any more?? I don't know. Maybe there is.\");\r\n      int iRes = 0;\r\n      Task98Dialog(m_hWnd, _Module.GetResourceInstance(), pstrWindowTitle,pstrInstructionsText, pstrContentText, TDCBF_OK_BUTTON, MAKEINTRESOURCE(IDR_MAINFRAME), &iRes);\r\n      AtlTaskDialog(m_hWnd, pstrWindowTitle, pstrInstructionsText, pstrContentText, TDCBF_OK_BUTTON, MAKEINTRESOURCE(IDR_MAINFRAME));\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest3(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = MAKEINTRESOURCEW(IDR_MAINFRAME);\r\n      cfg.pszContent = L\"This is the contents\";\r\n      cfg.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_OK_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button #1\" },\r\n         { 101, L\"Button #2\\nText Below\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = 101;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }   \r\n\r\n   LRESULT OnTest4(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_WARNING_ICON;\r\n      cfg.pszMainInstruction = L\"This is a test\";\r\n      cfg.pszContent = MAKEINTRESOURCEW(IDS_TASKDLG_CANCEL);\r\n      cfg.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_OK_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button #1\" },\r\n         { 101, L\"Button #2\\nText Below\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = 101;\r\n      TASKDIALOG_BUTTON radios[] = {\r\n         { 200, L\"Radio #1\" },\r\n         { 201, L\"Radio #2\\nText Below\" },\r\n         { 202, L\"Radio #3\" },\r\n      };\r\n      cfg.pRadioButtons = radios;\r\n      cfg.nDefaultRadioButton = 202;\r\n      cfg.cRadioButtons = 3;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }   \r\n\r\n   LRESULT OnTest5(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_ERROR_ICON;\r\n      cfg.pszMainInstruction = L\"This is another test\\nThere are 3 lines\\nof instruction text here.\";\r\n      cfg.pszContent = L\"This is the contents of yet another test. Testing the verifaction checkbox below.\";\r\n      cfg.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_OK_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button #1\" },\r\n         { 101, L\"Button #2\\nText Below\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = IDNO;\r\n      cfg.pszVerificationText = L\"Verifcation text. This is a very long text, so maybe it will wrap.\";\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }   \r\n\r\n   LRESULT OnTest6(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_ERROR_ICON;\r\n      cfg.pszMainInstruction = L\"This is another test\";\r\n      cfg.pszContent = L\"This is the contents of yet another test. Testing the verifaction checkbox below.\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button #1\" },\r\n         { 101, L\"Button #2\\nText Below\" },\r\n         { 102, L\"Button #3\\nThis is a longer line of text which nothing really interesting in it. Lets see how long it can be.\\nLine 2\" },\r\n         { 103, L\"Button #4\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 4;\r\n      cfg.nDefaultButton = 101;\r\n      cfg.pszVerificationText = L\"Verifcation text.\";\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_USE_COMMAND_LINKS;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest7(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_ERROR_ICON;\r\n      cfg.pszMainInstruction = L\"This is another test. The Main Instruction label can also be rather long and span multiple lines.\";\r\n      cfg.pszContent = L\"This is the contents of yet another long label. Testing the verifaction checkbox below. This line is longer than the others.\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button #1\" },\r\n         { 101, L\"Button #2\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = 101;\r\n      TASKDIALOG_BUTTON radios[] = {\r\n         { 200, L\"Radio #1\" },\r\n         { 201, L\"Radio #2\\nText Below\" },\r\n         { 202, L\"Radio #3. This is a rather long radio button text label which will span multiple lines.\" },\r\n      };\r\n      cfg.pRadioButtons = radios;\r\n      cfg.nDefaultRadioButton = 202;\r\n      cfg.cRadioButtons = 3;\r\n      cfg.pszVerificationText = L\"Verifcation text.\";\r\n      cfg.pszFooter = L\"Footer Text\";\r\n      cfg.pszFooterIcon = MAKEINTRESOURCEW(IDR_MAINFRAME);\r\n      cfg.pszExpandedControlText = NULL;\r\n      cfg.pszCollapsedControlText = NULL;\r\n      cfg.pszExpandedInformation = L\"Expanded information text here...\";\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_USE_COMMAND_LINKS;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }   \r\n\r\n   LRESULT OnTest8(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_ERROR_ICON;\r\n      cfg.pszMainInstruction = L\"This is another test\";\r\n      cfg.pszContent = L\"This is the contents of yet another test with a <a href=\\\"link1\\\">link</a>.\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Button Label for Control #1\" },\r\n         { 101, L\"Button Label for Control #2\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = 101;\r\n      TASKDIALOG_BUTTON radios[] = {\r\n         { 200, L\"Radio #1\\nText Below Radio button #1\" },\r\n         { 201, L\"Radio #2\\nText Below Radio button #2\" },\r\n         { 202, L\"Radio #3\\nText Below Radio button #3\" },\r\n      };\r\n      cfg.pRadioButtons = radios;\r\n      cfg.cRadioButtons = 3;\r\n      cfg.pszVerificationText = L\"Verifcation text. This is a long text with\\ntwo lines.\";\r\n      cfg.pszFooter = L\"Footer Text with a <a href=\\\"link1\\\">link</a>.\";\r\n      //cfg.hFooterIcon = ::LoadIcon(NULL, MAKEINTRESOURCE(IDI_WINLOGO));\r\n      cfg.hFooterIcon = (HICON) ::LoadImage(NULL, MAKEINTRESOURCE(IDI_ASTERISK), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT | LR_SHARED);\r\n      cfg.pszExpandedControlText = L\"Collapse Control Text\\nWith an extra line. Wohoo.\";\r\n      cfg.pszCollapsedControlText = L\"Expand Control Text\";\r\n      cfg.pszExpandedInformation = L\"Expanded information text here with a <a id=\\\"link1\\\">link</a>.\";\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_USE_COMMAND_LINKS_NO_ICON | TDF_EXPAND_FOOTER_AREA | TDF_USE_HICON_FOOTER | TDF_ENABLE_HYPERLINKS;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }\r\n\r\n   static HRESULT CALLBACK TaskDialogCallback9(HWND hWnd, UINT msg, WPARAM wParam, LPARAM /*lParam*/, LONG_PTR /*lpRefData*/)\r\n   {\r\n      switch( msg ) {\r\n      case TDN_TIMER:\r\n         ::SendMessage(hWnd, TDM_SET_PROGRESS_BAR_POS, wParam / 30, 0L);\r\n         if( wParam / 30 >= 100 ) ::SendMessage(hWnd, TDM_CLICK_BUTTON, IDOK, 0L);\r\n         break;\r\n      }\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest9(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = TD_ERROR_ICON;\r\n      cfg.pszMainInstruction = L\"This is Progress Bar test\";\r\n      cfg.pszContent = L\"This is the content text above the Progress Bar.\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      cfg.nDefaultButton = IDOK;\r\n      cfg.pfCallback = TaskDialogCallback9;\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SHOW_PROGRESS_BAR | TDF_CALLBACK_TIMER;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }\r\n\r\n   static HRESULT CALLBACK TaskDialogCallback10(HWND hWnd, UINT msg, WPARAM wParam, LPARAM /*lParam*/, LONG_PTR /*lpRefData*/)\r\n   {\r\n      switch( msg ) {\r\n      case TDN_DIALOG_CONSTRUCTED:\r\n         ::SendMessage(hWnd, TDM_ENABLE_BUTTON, 101, 0L);\r\n         ::SendMessage(hWnd, TDM_ENABLE_BUTTON, IDCANCEL, 0L);\r\n         ::SendMessage(hWnd, TDM_ENABLE_RADIO_BUTTON, 201, 0L);\r\n         ::SendMessage(hWnd, TDM_SET_MARQUEE_PROGRESS_BAR, 1, 0L);\r\n         ::SendMessage(hWnd, TDM_SET_PROGRESS_BAR_MARQUEE, 1, 30L);\r\n         break;\r\n      case TDN_BUTTON_CLICKED:\r\n         if( wParam == 100 ) {\r\n            ::SendMessage(hWnd, TDM_CLICK_RADIO_BUTTON, 202, 0L);\r\n            return S_FALSE;\r\n         }\r\n         return S_OK;\r\n      }\r\n      return 0;\r\n   }\r\n\r\n   LRESULT OnTest10(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hwndParent = m_hWnd;\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.hMainIcon = ::LoadIcon(NULL, MAKEINTRESOURCE(IDI_WINLOGO));\r\n      cfg.pszMainInstruction = L\"This is Progress Bar test\";\r\n      cfg.pszContent = L\"This is the content text above the Progress Bar.\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      cfg.pfCallback = TaskDialogCallback10;\r\n      TASKDIALOG_BUTTON buttons[] = {\r\n         { 100, L\"Not clickable\" },\r\n         { 101, L\"Disabled\" },\r\n      };\r\n      cfg.pButtons = buttons;\r\n      cfg.cButtons = 2;\r\n      cfg.nDefaultButton = 101;\r\n      TASKDIALOG_BUTTON radios[] = {\r\n         { 200, L\"Radio #1\\nText Below Radio button #1. This button as a very very long text line which should wrap the text to several lines I hope. It will test the sizing of the dialog.\" },\r\n         { 201, L\"Radio #2\\nText Below Radio button #2\" },\r\n         { 202, L\"Radio #3\\nText Below Radio button #3. This is another long line which will wrap to the second line only.\" },\r\n      };\r\n      cfg.pRadioButtons = radios;\r\n      cfg.cRadioButtons = 3;\r\n      cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SHOW_PROGRESS_BAR | TDF_USE_HICON_MAIN;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }  \r\n\r\n   class CTask98Dialog11 : public CTask98DialogImpl<CTask98Dialog11>\r\n   {\r\n   public:\r\n      int DoModal(HWND hWnd = NULL)\r\n      {\r\n         m_cfg.hwndParent = hWnd;\r\n         m_cfg.pszWindowTitle = L\"Window Title\";\r\n         m_cfg.hMainIcon = ::LoadIcon(NULL, MAKEINTRESOURCE(IDI_WINLOGO));\r\n         m_cfg.pszMainInstruction = L\"This is Progress Bar test\";\r\n         m_cfg.pszContent = L\"This is the content text above the Progress Bar.\";\r\n         m_cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n         m_cfg.nDefaultButton = IDOK;\r\n         TASKDIALOG_BUTTON buttons[] = {\r\n            { 100, L\"Not clickable\" },\r\n            { 101, L\"Disabled\" },\r\n         };\r\n         m_cfg.pButtons = buttons;\r\n         m_cfg.cButtons = 2;\r\n         m_cfg.nDefaultButton = 101;\r\n         TASKDIALOG_BUTTON radios[] = {\r\n            { 200, L\"Radio #1\\nText Below Radio button #1.\" },\r\n            { 201, L\"Radio #2\\nText Below Radio button #2.\" },\r\n            { 202, L\"Radio #3\\nText Below Radio button #3.\" },\r\n         };\r\n         m_cfg.pRadioButtons = radios;\r\n         m_cfg.cRadioButtons = 3;\r\n         m_cfg.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SHOW_PROGRESS_BAR | TDF_USE_HICON_MAIN | TDF_CALLBACK_TIMER;\r\n         return CTask98DialogImpl<CTask98Dialog11>::DoModal(hWnd);\r\n      }\r\n\r\n      void OnCreated()\r\n      {\r\n         EnableButton(IDCANCEL, FALSE);\r\n         EnableButton(101, FALSE);\r\n         EnableRadioButton(201, FALSE);\r\n         ClickRadioButton(202);\r\n      }\r\n      BOOL OnTimer(UINT wTime)\r\n      {\r\n         SetProgressBarPos(wTime / 30);\r\n         return FALSE;\r\n      }\r\n      BOOL OnButtonClicked(int nID)\r\n      {\r\n         if( nID == 100 ) return TRUE;\r\n         return FALSE;\r\n      }\r\n   };\r\n\r\n   LRESULT OnTest11(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      CTask98Dialog11 dlg;\r\n      dlg.DoModal();\r\n      return 0;\r\n   }  \r\n\r\n   LRESULT OnTest12(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r\n   {\r\n      TASKDIALOGCONFIG cfg = { 0 };\r\n      cfg.cbSize = sizeof(cfg);\r\n      cfg.hInstance = _Module.GetResourceInstance();\r\n      cfg.pszWindowTitle = L\"Window Title\";\r\n      cfg.pszMainIcon = MAKEINTRESOURCEW(IDR_MAINFRAME);\r\n      cfg.pszContent = L\"This is the contents.\\nhttp://www.viksoe.dk/code/testing_a_really.long.url.html?with=argument&that=makes&it&even=1&longer_than_the&screen=no&anditjustkeepgoingandgoing\\nLine3\\nLine4\\nLine5\";\r\n      cfg.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;\r\n      int iRes, iRadio, iVerify;\r\n      Task98DialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      AtlTaskDialogIndirect(&cfg, &iRes, &iRadio, &iVerify);\r\n      return 0;\r\n   }   \r\n};\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////\r\n\r\n//{{AFX_INSERT_LOCATION}}\r\n// Microsoft Visual C++ will insert additional declarations immediately before the previous line.\r\n\r\n#endif // !defined(AFX_MAINDLG_H__DA2FA2FB_17F8_4507_9DAE_D279641E8337__INCLUDED_)\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/res/TaskDialogTest.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\r\n<assemblyIdentity \r\n\tversion=\"1.0.0.0\" \r\n\tprocessorArchitecture=\"x86\" \r\n\tname=\"TaskDialogTest\" \r\n\ttype=\"win32\" \r\n/> \r\n<description>\r\n\tTaskDialogTest Application\r\n</description>\r\n<dependency>\r\n\t<dependentAssembly>\r\n\t\t<assemblyIdentity \r\n\t\t\ttype=\"win32\" \r\n\t\t\tname=\"Microsoft.Windows.Common-Controls\" \r\n\t\t\tversion=\"6.0.0.0\" \r\n\t\t\tprocessorArchitecture=\"x86\" \r\n\t\t\tpublicKeyToken=\"6595b64144ccf1df\" \r\n\t\t\tlanguage=\"*\" \r\n\t\t/>\r\n\t</dependentAssembly>\r\n</dependency>\r\n</assembly>\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/res/make_icons_include.bat",
    "content": "xxd -i TaskDlgArrowHot.ico >> icons.h\r\nxxd -i TaskDlgArrowNormal.ico >> icons.h\r\nxxd -i TaskDlgChevronLess.ico >> icons.h\r\nxxd -i TaskDlgChevronMore.ico >> icons.h"
  },
  {
    "path": "thirdparty/taskdialog98/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\r\n// Microsoft Developer Studio generated include file.\r\n// Used by TaskDialogTest.rc\r\n//\r\n#define IDD_ABOUTBOX                    100\r\n#define IDR_MAINFRAME                   128\r\n#define IDD_MAINDLG                     129\r\n#define IDS_TASKDLG_CANCEL              133\r\n#define IDC_BUTTON1                     1000\r\n#define IDC_BUTTON2                     1001\r\n#define IDC_BUTTON3                     1002\r\n#define IDC_BUTTON4                     1003\r\n#define IDC_BUTTON5                     1004\r\n#define IDC_BUTTON6                     1005\r\n#define IDC_BUTTON7                     1006\r\n#define IDC_BUTTON8                     1007\r\n#define IDC_BUTTON9                     1008\r\n#define IDC_BUTTON10                    1009\r\n#define IDC_BUTTON11                    1010\r\n#define IDC_BUTTON12                    1011\r\n\r\n// Next default values for new objects\r\n// \r\n#ifdef APSTUDIO_INVOKED\r\n#ifndef APSTUDIO_READONLY_SYMBOLS\r\n#define _APS_NEXT_RESOURCE_VALUE        205\r\n#define _APS_NEXT_COMMAND_VALUE         32772\r\n#define _APS_NEXT_CONTROL_VALUE         1001\r\n#define _APS_NEXT_SYMED_VALUE           101\r\n#endif\r\n#endif\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes\r\n//   TaskDialogTest.pch will be the pre-compiled header\r\n//   stdafx.obj will contain the pre-compiled type information\r\n\r\n#include \"stdafx.h\"\r\n\r\n#if (_ATL_VER < 0x0700)\r\n#include <atlimpl.cpp>\r\n#endif //(_ATL_VER < 0x0700)\r\n"
  },
  {
    "path": "thirdparty/taskdialog98/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\r\n//  or project specific include files that are used frequently, but\r\n//      are changed infrequently\r\n//\r\n\r\n#if !defined(AFX_STDAFX_H__6C883435_4806_4F6B_B9EF_95D748291576__INCLUDED_)\r\n#define AFX_STDAFX_H__6C883435_4806_4F6B_B9EF_95D748291576__INCLUDED_\r\n\r\n// Change these values to use different versions\r\n#define WINVER          0x0400\r\n#define _WIN32_WINNT    0x0400\r\n#define _WIN32_IE       0x0500\r\n#define _RICHEDIT_VER   0x0100\r\n#define _CRT_SECURE_NO_WARNINGS\r\n#define _CRT_NON_CONFORMING_SWPRINTFS\r\n\r\n#include <atlbase.h>\r\n#include <atlapp.h>\r\n\r\nextern CAppModule _Module;\r\n\r\n#include <atlwin.h>\r\n#include <atlctrls.h>\r\n#include <atldlgs.h>\r\n\r\n#include \"TaskDialog.h\"\r\n\r\n\r\n//{{AFX_INSERT_LOCATION}}\r\n// Microsoft Visual C++ will insert additional declarations immediately before the previous line.\r\n\r\n#endif // !defined(AFX_STDAFX_H__6C883435_4806_4F6B_B9EF_95D748291576__INCLUDED_)\r\n"
  }
]