pax_global_header00006660000000000000000000000064125040610310014502gustar00rootroot0000000000000052 comment=646b06563f8ddfa877823e573b651639e91df802 mu-0.9.12/000077500000000000000000000000001250406103100122145ustar00rootroot00000000000000mu-0.9.12/.gitignore000066400000000000000000000015061250406103100142060ustar00rootroot00000000000000www/mu mug mu mug2 .desktop *html .deps .libs autom4te* Makefile Makefile.in INSTALL aclocal.m4 config.* configure install-sh depcomp libtool ltmain.sh m4/* missing nohup.out vgdump stamp-h1 GPATH GRTAGS GSYMS GTAGS *.lo *.o *.la *.x *.go *.gz *.bz2 \#* *.aux *.cp *.fn *.info *.ky *.log *.pg *.toc *.tp *.vr *.elc elisp-comp elc-stamp dummy.cc msg2pdf /contrib/gmime-test /emacs/TODO test-mu-cmd test-mu-cmd-cfind test-mu-contacts test-mu-container test-mu-date test-mu-flags test-mu-maildir test-mu-msg test-mu-msg-fields test-mu-query test-mu-runtime test-mu-store test-mu-str test-mu-threads test-mu-util mu4e-meta.el mu4e.pdf texinfo.tex texi.texi *.tex *.pdf /www/auto/* configure.lineno /test.xml /mu4e/mdate-sh /mu4e/mu4e-about.el /mu4e/stamp-vti /mu4e/version.texi /guile/tests/test-mu-guile /lib/doxyfile /version.texi /compile mu-0.9.12/.travis.yml000066400000000000000000000013661250406103100143330ustar00rootroot00000000000000language: c compiler: - gcc env: global: - BUILD_PKGS="libtool autoconf automake texinfo" - BUILD_LIBS="libgmime-2.6-dev libxapian-dev guile-2.0-dev libwebkitgtk-dev" - TEST_PKGS="pmccabe" matrix: - EVM_EMACS=emacs-24.1-bin - EVM_EMACS=emacs-24.2-bin - EVM_EMACS=emacs-24.3-bin before_install: - git submodule update --init --recursive - sudo apt-get update -qq - sudo apt-get install -qq ${BUILD_PKGS} ${BUILD_LIBS} ${TEST_PKGS} install: - sudo mkdir /usr/local/evm - sudo chown $(id -u):$(id -g) /usr/local/evm - curl -fsSkL https://raw.github.com/rejeep/evm/master/go | bash - export PATH="$HOME/.evm/bin:$PATH" - evm install $EVM_EMACS --use script: - autoreconf -i - ./configure - make - make check mu-0.9.12/AUTHORS000066400000000000000000000000531250406103100132620ustar00rootroot00000000000000Dirk-Jan C. Binnema mu-0.9.12/COPYING000066400000000000000000001045131250406103100132530ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mu-0.9.12/ChangeLog000066400000000000000000000000561250406103100137670ustar00rootroot00000000000000Please use the 'git log' to see the ChangeLog mu-0.9.12/HACKING000066400000000000000000000104751250406103100132120ustar00rootroot00000000000000* HACKING Here are some guidelines for hacking on the 'mu' source code. This is a fairly long list -- this is not meant to discourage anyone from working on mu; I think most of the rules are common sense anyway, and some of the more stylistic-aesthetic rules are clearly visible in current source code, so as long as any new code 'fits in', it should go a long way in satisfying them. I should add some notes for the Lisp/Scheme code as well... ** Coding style For consistency and, more important, to keep things understandable, mu attempts to follow the following rules: 1. Basic code layout is like in the Linux kernel coding style. Keep the '{' on the same line as the statement, except for functions. Tabs for indentation, space for aligment; use 8-char tabs. 2. Lines should not exceed 80 characters (C) or 100 characters (C++) 3. Functions should not exceed 35 lines (with few exceptions). You can easily check if any functions violate this rule with 'make line35', which lists all functions with more than 35 non-comment lines. 4. Source files should not exceed 1000 lines 5. A function's cyclomatic complexity should not exceed 10 (there could be rare exceptions, see the toplevel Makefile.am). You can test the cyclomatic complexity with the pmccabe tool; if you installed that, you can use 'make cc10' to list all functions that violate this rule; there should be none. 6. Filenames have their components separated with dashes (e.g, 'mu-log.h'), and start with 'mu-' where appropriate. 7. Global functions have the prefix based on their module, e.g., mu-foo.h declares a function of 'mu_foo_bar (int a);', mu-foo.c implements this. 8. Non-global functions *don't* have the module prefix, and are declared static. 9. Functions have their return type on a separate line before the function name, so: int foo (const char *bar) { .... } There is no non-aesthetic reason for this. 10. In C code, variable-declarations are at the beginning of a block; in principle, C++ follows that same guideline, unless for heavy yet uncertain initializations following RAII. In C code, the declaration does *not* initialize the variable. This will give the compiler a chance to warn us if the variable is not initialized in a certain code path. 11. Returned strings of type char* must be freed by the caller; if they are not to be freed, 'const char*' should be used instead 12. Functions calls have a space between function name and arguments, unless there are none, so: foo (12, 3); and bar(); after a comma, a space should follow. 13. Functions that do not take arguments are explicitly declared as f(void) and not f(). Reason: f() means that the arguments are /unspecified/ (in C) 14. C-code should use /* comments */, not // commments ** Logging For logging, mu uses the GLib logging functions/macros as listed below, except when logging may not have been initialized. The logging system redirects most logging to the log file (typically, ~/.mu/mu.log). g_warning, g_message and g_critical are shown to the user, except when running with --quiet, in which case g_message is *not* shown. - g_message is for non-error messages the user will see (unless running with --quiet) - g_warning is for problems the user may be able to do something about (and they are written on stderr) - g_critical is for mu bugs, serious, internal problems (g_return_if_fail and friends use this). (and they are written on stderr) - don't use g_error If you just want to log something in the log file without writing to screen, use MU_LOG_WRITE, as defined in mu-util.h ** Compiling from git For hacking, you're strongly advised to use the latest git version. Compilation from git should be straightforward, if you have the right tools (automake, autoconf and friends) installed -- but if you do development, you'd probably have those. Anyhow, to compile straight from git: $ git clone https://github.com/djcb/mu $ cd mu $ autoreconf -i $ ./configure $ make # Local Variables: # mode: org; org-startup-folded: nofold # End: mu-0.9.12/Makefile.am000066400000000000000000000045751250406103100142630ustar00rootroot00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk if BUILD_GUILE guile=guile else guile= endif if BUILD_MU4E mu4e=mu4e else mu4e= endif SUBDIRS=m4 man lib $(guile) mu $(mu4e) contrib toys ACLOCAL_AMFLAGS=-I m4 # so we can say 'make test' check: test cleanupnote cleanupnote: @echo -e "\nNote: you can remove the mu-test- dir in your tempdir" @echo "after 'make check' has finished." tags: gtags # this warns about function that have a cyclomatic complexity of > 10, # which is a sign that it needs some refactoring. requires the pmccabe # tool. If all is fine, it outputs nothing cc10: @$(PMCCABE) `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v tests \ | sort -nr | awk '($$1 > 10)' # this warns about functions that are over 35 non-comment lines long, which is a # sign that they need some refactoring. requires the pmccabe tool. if # all is fine, it outputs nothing # note, some functions are exempted from this rule. line35: @$(PMCCABE) -c `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v config_options_group_find \ | grep -v SCM_DEFINE \ | grep -v tests \ | awk '($$5 > 35)' # get all todo/fixme messages fixme: @grep -i 'FIXME\|TODO' `find src -type f` # check whether we can run make distcheck from the repo version gitcheck: cd `mktemp -d`; \ git clone git://github.com/djcb/mu.git ; \ cd mu; \ autoreconf -i ; \ ./configure ; \ make distcheck EXTRA_DIST= \ TODO \ HACKING \ gtest.mk \ NEWS \ autogen.sh mu-0.9.12/NEWS000066400000000000000000000362371250406103100127260ustar00rootroot00000000000000#+STARTUP:showall * NEWS (user visible changes) ** Development *** mu - truncate /all/ terms the go beyond xapian's max term length - lowercase the domain-part of email addresses in mu cfind (and mu4e), if the domain is in ascii - give messages without msgids fake-message-ids; this fixes the problem where such messages were not found in --include-related queries - cleanup of the query parser *** mu4e - update the emacs <-> backend protocol; documented in the mu-server man page ** Release 0.9.9.5 *** mu - allow 'contact:' as a shortcut in queries for 'from:foo OR to:foo OR cc:foo OR bcc:foo', and 'recip:' as a shortcut for 'to:foo OR cc:foo OR bcc:foo' - support getting related messages (--include-related), which includes messages that may not match the query, but that are in the same threads as messages that were - support "list:"/"v:" for matching mailing list names, and the "v" format-field to show them. E.g 'mu find list:emacs-orgmode.gnu.org' *** mu4e - scroll down in message view takes you to next message (but see `mu4e-view-scroll-to-next') - support 'human dates', that is, show the time for today's messages, and the date for older messages in the headers view - replace `mu4e-user-mail-address-regexp' and `mu4e-my-mail-addresses' with `mu4e-user-mail-address-list' - support tags (i.e.., X-Keywords and friends) in the headers-view, and the message view. Thanks to Abdó Roig-Maranges. New field ":tags". - automatically update the headers buffer when new messages are found during indexing; set `mu4e-headers-auto-update' to nil to disable this. - update mail/index with M-x mu4e-update-mail-and-index; which everywhere in mu4e is available with key C-S-u. Use prefix argument to run in background. - add function `mu4e-update-index' to only update the index - add 'friendly-names' for mailing lists, so they should up nicely in the headers view *** guile - add 'mu script' command to run mu script, for example to do statistics on your message corpus. See the mu-script man-page. *** mug - ported to gtk+ 3; remove gtk+ 2.x code * Old news :PROPERTIES: :VISIBILITY: folded :END: ** Release 0.9.9 <2012-10-14> *** mu4e - view: address can be toggled long/short, compose message - sanitize opening urls (mouse-1, and not too eager) - tooltips for header labels, flags - add sort buttons to header-labels - support signing / decryption of messages - improve address-autocompletion (e.g., ensure it's case-insensitive) - much faster when there are many maildirs - improved line wrapping - better handle attached messages - improved URL-matching - improved messages to user (mu4e-(warn|error|message)) - add refiling functionality - support fancy non-ascii in the UI - dynamic folders (i.e.., allow mu4e-(sent|draft|trash|refile)-folder) to be a function - dynamic attachment download folder (can be a function now) - much improved manual *** mu - remove --summary (use --summary-len instead) - add --after for mu find, to limit to messages after T - add new command `mu verify', to verify signatures - fix iso-2022-jp decoding (and other 7-bit clean non-ascii) - add support for X-keywords - performance improvements for threaded display (~ 25% for 23K msgs) - mu improved user-help (and the 'mu help' command) - toys/mug2 replaces toys/mug *** mu-guile - automated tests - add mu:timestamp, mu:count - handle db reopenings in the background ** Release 0.9.8.5 <2012-07-01> *** mu4e - auto-completion of e-mail addresses - inline display of images (see `mu4e-view-show-images'), uses imagemagick if available - interactively change number of headers / columns for showing headers with C-+ and C-- in headers, view mode - support flagging message - navigate to previous/next queries like a web browser (with , ) - narrow search results with '/' - next/previous take a prefix arg now, to move to the nth previous/next message - allow for writing rich-text messages with org-mode - enable marking messages as Flagged - custom marker functions (see manual) - better "dwim" handling of buffer switching / killing - deferred marking of message (i.e.., mark now, decide what to mark for later) - enable changing of sort order, display of threads - clearer marks for marked messages - fix sorting by subject (disregarding Re:, Fwd: etc.) - much faster handling when there are many maildirs (speedbar) - handle mailto: links - improved, extended documentation *** mu - support .noupdate files (parallel to .noindex, dir is ignored unless we're doing a --rebuild). - append all inline text parts, when getting the text body - respect custom maildir flags - correctly handle the case where g_utf8_strdown (str) > len (str) - make gtk, guile, webkit dependency optional, even if they are installed ** Release 0.9.8.4 <2012-05-08> *** mu4e - much faster header buffers - split view mode (headers, view); see `mu4e-split-view'. - add search history for queries - ability to open attachments with arbitrary programs, pipe through shell commands or open in the current emacs - quote names in recipient addresses - mu4e-get-maildirs works now for recursive maildirs as well - define arbitrary operations for headers/messages/attachments using the actions system -- see the chapter 'Actions' in the manual - allow mu4e to be uses as the default emacs mailer (`mu4e-user-agent') - mark headers based on a regexp, `mu4e-mark-matches', or '%' - mark threads, sub-threads (mu4e-hdrs-mark-thread, mu4e-hdrs-mark-subthread, or 'T', 't') - add msg2pdf toy - easy logging (using `mu4e-toggle-logging') - improve mu4e-speedbar for use in headers/view - use the message-mode FCC system for saving messages to the sent-messages folder - fix: off-by-one in number of matches shown *** general - fix for opening files with non-ascii names - much improved support for searching non-Latin (Cyrillic etc.) languages we can now match 'Тесла' or 'Аркона' without problems - smarter escaping (fixes issues with finding message ids) - fixes for queries with brackets - allow --summary-len for the length of message summaries - numerous other small fixes ** Release 0.9.8.3 <2012-04-06> *NOTE*: existing mu/mu4e are recommended to run `mu index --rebuild' after installation. *** mu4e - allow for searching by editing bookmarks (`mu4e-search-bookmark-edit-first') (keybinding 'B') - make it configurable what to do with sent messages (see `mu4e-sent-messages-behavior') - speedbar support (initial patch by Antono V) - better handling of drafts: - don't save too early - more descriptive buffer names (based on Subject, if any) - don't put "--text-follows-this-line--" markers in files - automatically include signatures, if set - add user-settable variables mu4e-view-wrap-lines and mu4e-view-hide-cited, which determine the initial way a message is displayed - improved documentation *** general - much improved searching for GMail folders (i.e. maildir:/ matching); this requires a 'mu index --rebuild' - correctly handle utf-8 messages, even if they don't specify this explicitly - fix compiler warnings for newer/older gcc and clang/clang++ - fix unit tests (and some code) for Ubuntu 10.04 and FreeBSD9 - fix warnings for compilation with GTK+ 3.2 and recent glib (g_set_error) - fix mu_msg_move_to_maildir for top-level messages - fix in maildir scanning - plug some memleaks ** Release 0.9.8.2 <2012-03-11> *** mu4e: - make mail updating non-blocking - allow for automatic periodic update ('mu4e-update-interval') - allow for external triggering of update - make behavior when leaving the headers buffer customizable, ie. ask/apply/ignore ('mu4e-headers-leave-behaviour') *** general - fix output for some non-UTF8 locales - open ('play') file names with spaces - don't show unnecessary errors for --format=links - make build warning-free for clang/clang++ - allow for slightly older autotools - fix unit tests for some hidden assumptions (locale, dir structure etc.) - some documentation updates / clarifications ** Release 0.9.8.1 <2012-02-18 Sat> *** mu - show only leaf/rfc822 MIME-parts *** mu4e - allow for shell commands with arguments in `mu4e-get-mail-command'. - support marking messages as 'read' and 'unread' - show the current query in the the mode-line (`global-mode-string'). - don't repeat 'Re:' / 'Fwd:' - colorize cited message parts - better handling of text-based, embedded message attachments - for text-bodies, concatenate all text/plain parts - make filladapt dep optional - documentation improvements ** Release 0.9.8 <2012-01-31> - '--descending' has been renamed into '--reverse' - search for attachment MIME-type using 'mime:' or 'y:' - search for text in text-attachments using 'embed:' or 'e:' - searching for attachment file names now uses 'file:' (was: 'attach:') - experimental emacs-based mail client -- "mu4e" - added more unit tests - improved guile binding - no special binary is needed anymore, it's installable are works with the normal guile system; code has been substantially improved. still 'experimental' ** Release 0.9.7 <2011-09-03 Sat> - don't enforce UTF-8 output, use locale (fixes issue #11) - add mail threading to mu-find (using -t/--threads) (sorta fixes issue #13) - add header line to --format=mutt-ab (mu cfind), (fixes issue #42) - terminate mu view results with a form-feed marker (use --terminate) (fixes issue #41) - search X-Label: tags (fixes issue #40) - added toys/muile, the mu guile shells, which allows for message stats etc. - fix date handling (timezones) ** Release 0.9.6 <2011-05-28 Sat> - FreeBSD build fix - fix matching for mu cfind to be as expected - fix mu-contacts for broken names/emails - clear the contacts-cache too when doing a --rebuild - wildcard searches ('*') for fields (except for path/maildir) - search for attachment file names (with 'a:'/'attach:') -- also works with wildcards - remove --xquery completely; use --output=xquery instead - fix progress info in 'mu index' - display the references for a message using the 'r' character (xmu find) - remove --summary-len/-k, instead use --summary for mu view and mu find, and - support colorized output for some sub-commands (view, cfind and extract). Disabled by default, use --color to enable, or set env MU_COLORS to non-empty - update documentation, added more examples ** Release 0.9.5 <2011-04-25 Mon> - bug fix for infinite loop in Maildir detection - minor fixes in tests, small optimizations ** Release 0.9.4 <2011-04-12 Tue> - add the 'cfind' command, to search/export contact information - add 'flag:unread' as a synonym for 'flag:new OR NOT flag:unseen' - updated documentation ** Release 0.9.3 <2011-02-13 Sun> - don't warn about missing files with --quiet ** Release 0.9.2 <2011-02-02 Wed> - stricter checking of options; and options must now *follow* the sub-command (if any); so, something like: 'mu index --maildir=/foo/bar' - output searches as plain text (default), XML, JSON or s-expressions using --format=plain|xml|json|sexp. For example: 'mu find foobar --output=json'. These format options are experimental (except for 'plain') - the --xquery option should now be used as --format=xquery, for output symlinks, use --format=links. This is a change in the options. - search output can include the message size using the 'z' shortcut - match message size ranges (i.e.. size:500k..2M) - fix: honor the --overwrite (or lack thereof) parameter - support folder names with special characters (@, ' ', '.' and so on) - better check for already-running mu index - when --maildir= is not provided for mu index, default to the last one - add --max-msg-size, to specify a new maximum message size - move the 'mug' UI to toys/mug; no longer installable - better support for Solaris builds, Gentoo. ** Release 0.9.1 <2010-12-05 Sun> - Add missing icon for mug - Fix unit tests (Issue #30) - Fix Fedora 14 build (broken GTK+ 3) (Issue #31) ** Release 0.9 <2010-12-04 Sat> - you can now search for the message priority ('prio:high', 'prio:low', 'prio:normal') - you can now search for message flags, e.g. 'flag:attach' for messages with attachment, or 'flag:encrypted' for encrypted messages - you can search for time-intervals, e.g. 'date:2010-11-26..2010-11-29' for messages in that range. See the mu-find(1) and mu-easy(1) man-pages for details and examples. - you can store bookmarked queries in ~/.mu/bookmarks - the 'flags' parameter has been renamed in 'flag' - add a simple graphical UI for searching, called 'mug' - fix --clearlinks for file systems without entry->d_type (fixes issue #28) - make matching case-insensitive and accent-insensitive (accent-insensitive for characters in Unicode Blocks 'Latin-1 Supplement' and 'Latin Extended-A') - more extensive pre-processing is done to make searching for email-addresses and message-ids less likely to not work (issue #21) - updated the man-pages - experimental support for Fedora 14, which uses GMime 2.5.x (fixes issue #29) ** Release 0.8 <2010-10-30 Sat> - There's now 'mu extract' for getting information about MIME-parts (attachments) and extracting them - Queries are now internally converted to lowercase; this solves some of the false-negative issues - All mu sub-commands now have their own man-page - 'mu find' now takes a --summary-len= argument to print a summary of up-to-n lines of the message - Same for 'mu view'; the summary replaces the full body - Setting the mu home dir now goes with -m, --muhome - --log-stderr, --reindex, --rebuild, --autoupgrade, --nocleanup, --mode, --linksdir, --clearlinks lost their single char version ** Release 0.7 <2010-02-27 Sat> - Database format changed - Automatic database scheme version check, notifies users when an upgrade is needed - 'mu view', to view mail message files - Support for >10K matches - Support for unattended upgrades - that is, the database can automatically by upgraded (--autoupgrade). Also, the log file is automatically cleaned when it gets too big (unless you use --nocleanup) - Search for a certain Maildir using the maildir:,m: search prefixes. For example, you can find all messages located in ~/Maildir/foo/bar/cur/msg ~/Maildir/foo/bar/new/msg and with m:/foo/bar this replace the search for path/p in 0.6 - Fixes for reported issues () - A test suite with a growing number of unit tests ** Release 0.6 <2010-01-23 Sat> - First new release of mu since 2008 - No longer depends on sqlite # Local Variables: # mode: org; org-startup-folded: nil # End: mu-0.9.12/README000066400000000000000000000037161250406103100131030ustar00rootroot00000000000000 README ====== Welcome to mu! --------------- Given the enormous amounts of e-mail many people gather and the importance of e-mail message in our work-flows, it's essential to quickly deal with all that mail - in particular, to instantly find that one important e-mail you need right now. [mu] is a tool for dealing with e-mail messages stored in the Maildir-format. =mu='s purpose in life is to help you to quickly find the messages you need; in addition, it allows you to view messages, extract attachments, create new maildirs, and so on. See the [mu cheatsheet] for some examples. =mu= is fully documented. After indexing your messages into a [Xapian]-database, you can search them using a custom query language. You can use various message fields or words in the body text to find the right messages. Built on top of the mu, are some extensions (included in this package): - mu-for-emacs ([mu4e]): a full-featured e-mail client that runs inside emacs - [mu-guile]: bindings for the Guile/Scheme programming language (version 2.0 and later) - a toy GTK+-interface called 'mug' (in the 'toys/' subdir) =mu= is written in C and a bit of C++ (to interface with Xapian), with =mu4e= written in [Emacs-Lisp] and =mu-guile= in a mix of C and Scheme. Note, =mu= is available in Debian/Ubuntu under the name =maildir-utils=; apparently because they don't like short name. It's also possible to confuse that name with the [GNU Mailutils] project (which is totally unrelated) - but now you have been warned. [mu]: http://www.djcbsoftware.nl/code/mu [mu cheatsheet]: http://www.djcbsoftware.nl/code/mu/cheatsheet.html [Xapian]: http://www.xapian.org [mu4e]: http://www.djcbsoftware.nl/code/mu/mu4e.html [mu-guile]: http://www.djcbsoftware.nl/code/mu/mu-guile.html [Emacs-Lisp]: http://en.wikipedia.org/wiki/Emacs-Lisp [GNU Mailutils]: http://mailutils.org/ mu-0.9.12/README.org000066400000000000000000000034401250406103100136630ustar00rootroot00000000000000* Welcome to mu! Given the enormous amounts of e-mail many people gather and the importance of e-mail message in our work-flows, it's essential to quickly deal with all that mail - in particular, to instantly find that one important e-mail you need right now. [[http://www.djcbsoftware.nl/code/mu][mu]] is a tool for dealing with e-mail messages stored in the Maildir-format. =mu='s purpose in life is to help you to quickly find the messages you need; in addition, it allows you to view messages, extract attachments, create new maildirs, and so on. See the [[http://www.djcbsoftware.nl/code/mu/cheatsheet.html][mu cheatsheet]] for some examples. =mu= is fully documented. After indexing your messages into a [[http://www.xapian.org][Xapian]]-database, you can search them using a custom query language. You can use various message fields or words in the body text to find the right messages. Built on top of the mu, are some extensions (included in this package): - mu-for-emacs ([[http://www.djcbsoftware.nl/code/mu/mu4e.html][mu4e]]): a full-featured e-mail client that runs inside emacs - [[http://www.djcbsoftware.nl/code/mu/mu-guile.html][mu-guile]]: bindings for the Guile/Scheme programming language (version 2.0 and later) - a toy GTK+-interface called 'mug' (in the 'toys/' subdir) =mu= is written in C and a bit of C++ (to interface with Xapian), with =mu4e= written in [[http://en.wikipedia.org/wiki/Emacs-Lisp][Emacs-Lisp]] and =mu-guile= in a mix of C and Scheme. Note, =mu= is available in Debian/Ubuntu under the name =maildir-utils=; apparently because they don't like short name. It's also possible to confuse that name with the [[http://mailutils.org/][GNU Mailutils]] project (which is totally unrelated) - but now you have been warned. mu-0.9.12/TODO000066400000000000000000000140501250406103100127040ustar00rootroot00000000000000#+STARTUP: showall * TODO (fixes, ideas, etc.) ** Future stuff *** mu - put threading information in the database, and enable getting the complete threads when searching - refactor fill_database function in test cases - don't show duplicate e-mails (i.e.. for Gmail); check the message-id *** mu-guile - move contact export to separate scm - fix logging *** mu4e - special-case replying to messages sent by self - identities (see Jacek's 'mu4e: From field in replies' mail) ==> [ workaround available, using mu4e-pre-compose-hook, dynamic folders ] - new-mail warning ==> [ workaround available, using mu4e-index-updated-hook ] - custom header fields in headers-view, message-view - show maildirs as a tree, not a list in speed bar - review emacs menus - re-factor / separate window/buffer management - enable keeping message view buffers around - better naming for draft/view buffers - header updating interferes with marks (when updating for 'mark as read', when reading a marked message) - set/unset flag editing command - handling of database upgrades - restore point after rerunning a search - make the mu4e-bookmarks format similar to the other ones - refresh current query after update? - fix mu4e-mark-set to work from the view buffer as well - open links to mails through headers-mode somehow (i.e.., mu4e-view-message-with-msgid) - improve mouse interaction (i.e., cursor vs point) - show counts of messages in searches (in main view) - show flush only if there's something to flush (and # of flushables) - fix unsafe temp-file handling - make copy paste name/address in mu4e-view possible * Done (0.9.9.x) - mu4e: scroll down –> go to next message - mu: add contact: as a shortcut for matching from/to/cc/bcc: - guile integration - statistics - 'human' dates in the headers view - :tags in headers, message view * Done :PROPERTIES: :VISIBILITY: folded :END: ** Done (0.9.9) - make contacts in the view clickable (toggle long/short display, compose message) - opening urls is too eager (now use M-RET for opening url at point, not just RET, which conflicted with using RET for scrolling) - document quoting of queries - use mu-error - tooltips in header labels - tooltip for flags field - remove --summary option (for mu find, mu view); use --summary-len instead - add sort buttons to header labels (and do the sorting) - cleanup mu-cmd-find - implement --after for mu find, to only show message files changed after a certain time (mtime) - add mu:timestamp for guile (referring to the message file's mtime) - guile automated tests - add 'mu verify' - automated tests - handle verbose/quiet/normal output 'mu verify' - check gmime 2.4 does not break - hook up mu4e with 'mu verify' - add 'help' command - refactor mu-msg-part - move widgets/ into toys/mug2, remove toys/mug/, rename toys/mug2 -> toys/mug - add guile mu:count - don't show GPG/PKCS7 sigs as attachments - fix address completion (quote names) - add support for X-Keywords (in addition to X-Label) - guile: add stats test cases - fixed iso-2022-jp (japanese) decoding - make address completion case-insensitive - recognize '*' in urls - handle exception 'The revision being read has been discarded - you should call Xapian::Database::reopen() and retry the operation' - handle passwords from get-mail shell command - support fancy (non-ascii) chars for header flags, thread prefix strings - improve performance of getting the list of maildirs - fix setting wrapped/hide state in viewer - fix ' realpath() failed for...' stuff - allow for fancy chars (> ascii), make it configurable (mu4e-use-fancy-chars) - don't user `error' for user-errors - better echo-area reporting - improve help feedback for user (command line) - handling of encrypted messages - improved checked for gmime-2.6 crypto funcs - handling of command line options / help - fix / add support for :size - mu4e~view-wrap-lines (use visual-line-mode? see Jacek's mu4e~view-wrap-lines mail) - better help - threading optimizations - actions for /all/ headers, actions for /all/ attachment - handle attached messages with attachments ** Done (0.8.9.5) - make next/prev header respect prefix argument (Jacek's patch) - make search results a stack (well, multiple stacks) - optionally keep cc with user's email - enable setting/unsetting 'Flagged' on messages - allow narrowing of search results - interactive split-view control (Jacek) - view images inline - *FIX* slow maildirs when there are many - *FIX* ignore unrecognized maildir flag letters - *FIX*: reply-to does not make it to the frontend - *FIX* wrong buffer deleted after sending (see '(non mu) buffer is killed') - rich text composing (with org-mode) - let message-mode deal with burying/killing compose buffers - *FIX* add runtime check for imagemagick - *FIX* no error note if target message already exists (when moving) - sorting + show / hide threads - *FIX* having multiple header views visible - *FIX* fix for strings where len (g_utf8_strdown (str)) > len (str) - make sure marks correspond to the *current* message in message view (see https://github.com/djcb/mu/issues/26) - *FIX* don't remove unknown message flags when moving - make guile/gtk/webkit dependency optional - improve fringe marks (see https://github.com/djcb/mu/issues/21) - mark message, decide what to do with them later (i.e.. 'deferred marking') - custom predicate functions for marking - make mu4e buffer killing less aggressive (i.e.., DWIM) - about mu4e - hide some headers when composing - fix sorting subjects with ':' (but not 'Re:' or 'Fwd:') - strip signature from original when replying - make refresh after changing sort, threads the default - contact completion (see Jacek's 'mu4e: using' mail) - *FIX* emacs23 mailto: handling - *FIX* message interference - *FIX* emacs23.2+ auto-completion # Local Variables: # mode: org # End: mu-0.9.12/autogen.sh000077500000000000000000000000541250406103100142140ustar00rootroot00000000000000#!/bin/sh echo "please use 'autoreconf -i'" mu-0.9.12/c.cfg000066400000000000000000002125511250406103100131250ustar00rootroot00000000000000# Uncrustify 0.60 # # General options # # The type of line endings newlines = lf # auto/lf/crlf/cr # The original size of tabs in the input input_tab_size = 8 # number # The size of tabs in the output (only used if align_with_tabs=true) output_tab_size = 8 # number # The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) string_escape_char = 92 # number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # number # Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. # If true (default), 'assert(x<0 && y>=3)' will be broken. # Improvements to template detection may make this option obsolete. tok_split_gte = false # false/true # Control what to do with the UTF-8 BOM (recommend 'remove') utf8_bom = ignore # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 utf8_byte = false # false/true # Force the output encoding to UTF-8 utf8_force = false # false/true # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. indent_columns = 8 # number # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level indent_continue = 0 # number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs to brace level, align with spaces # 2=indent and align with tabs, using spaces when not on a tabstop indent_with_tabs = 1 # number # Comments that are not a brace level are indented with tabs on a tabstop. # Requires indent_with_tabs=2. If false, will use spaces. indent_cmt_with_tabs = false # false/true # Whether to indent strings broken by '\' so that they line up indent_align_string = false # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True indent_xml_string = 0 # number # Spaces to indent '{' from level indent_brace = 0 # number # Whether braces are indented to the body level indent_braces = false # false/true # Disabled indenting function braces if indent_braces is true indent_braces_no_func = false # false/true # Disabled indenting class braces if indent_braces is true indent_braces_no_class = false # false/true # Disabled indenting struct braces if indent_braces is true indent_braces_no_struct = false # false/true # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Whether the 'namespace' body is indented indent_namespace = false # false/true # The number of spaces to indent a namespace block indent_namespace_level = 0 # number # If the body of the namespace is longer than this number, it won't be indented. # Requires indent_namespace=true. Default=0 (no limit) indent_namespace_limit = 0 # number # Whether the 'extern "C"' body is indented indent_extern = false # false/true # Whether the 'class' body is indented indent_class = false # false/true # Whether to indent the stuff after a leading class colon indent_class_colon = false # false/true # Virtual indent from the ':' for member initializers. Default is 2 indent_ctor_init_leading = 2 # number # Additional indenting for constructor initializer list indent_ctor_init = 0 # number # False=treat 'else\nif' as 'else if' for indenting purposes # True=indent the 'if' one level indent_else_if = false # false/true # Amount to indent variable declarations after a open brace. neg=relative, pos=absolute indent_var_def_blk = 0 # number # Indent continued variable declarations instead of aligning. indent_var_def_cont = false # false/true # True: force indentation of function definition to start in column 1 # False: use the default behavior indent_func_def_force_col1 = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren indent_func_call_param = false # false/true # Same as indent_func_call_param, but for function defs indent_func_def_param = false # false/true # Same as indent_func_call_param, but for function protos indent_func_proto_param = false # false/true # Same as indent_func_call_param, but for class declarations indent_func_class_param = false # false/true # Same as indent_func_call_param, but for class variable constructors indent_func_ctor_var_param = false # false/true # Same as indent_func_call_param, but for templates indent_template_param = false # false/true # Double the indent for indent_func_xxx_param options indent_func_param_double = false # false/true # Indentation column for standalone 'const' function decl/proto qualifier indent_func_const = 0 # number # Indentation column for standalone 'throw' function decl/proto qualifier indent_func_throw = 0 # number # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 0 # number # Spaces to indent single line ('//') comments on lines before code indent_sing_line_comments = 0 # number # If set, will indent trailing single line ('//') comments relative # to the code instead of trying to keep the same absolute column indent_relative_single_line_comments = false # false/true # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 0 # number # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. indent_case_brace = 0 # number # Whether to indent comments found in first column indent_col1_comment = false # false/true # How to indent goto labels # >0 : absolute column where 1 is the leftmost column # <=0 : subtract from brace indent indent_label = 1 # number # Same as indent_label, but for access specifiers that are followed by a colon indent_access_spec = 1 # number # Indent the code after an access specifier by one level. # If set, this option forces 'indent_access_spec=0' indent_access_spec_body = false # false/true # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 1 # number # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren indent_bool_paren = false # false/true # If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones indent_first_bool_expr = true # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies indent_preserve_sql = false # false/true # Align continued statements at the '='. Default=True # If FALSE or the '=' is followed by a newline, the next line is indent one tab. indent_align_assign = true # false/true # Indent OC blocks at brace level instead of usual rules. indent_oc_block = false # false/true # Indent OC blocks in a message relative to the parameter name. # 0=use indent_oc_block rules, 1+=spaces to indent indent_oc_block_msg = 0 # number # Minimum indent for subsequent parameters indent_oc_msg_colon = 0 # number # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc sp_arith = force # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc sp_assign = force # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign sp_cpp_lambda_assign = ignore # ignore/add/remove/force # Add or remove space after the capture specification in C++11 lambda. sp_cpp_lambda_paren = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype sp_assign_default = ignore # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. sp_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. sp_after_assign = ignore # ignore/add/remove/force # Add or remove space around assignment '=' in enum sp_enum_assign = force # ignore/add/remove/force # Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. sp_enum_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. sp_enum_after_assign = ignore # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. Default=Add sp_pp_concat = add # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. sp_pp_stringify = ignore # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||' sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')' sp_inside_paren = ignore # ignore/add/remove/force # Add or remove space between nested parens sp_paren_paren = ignore # ignore/add/remove/force # Whether to balance spaces inside nested parens sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{' sp_paren_brace = force # ignore/add/remove/force # Add or remove space before pointer star '*' sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a variable name # If set to 'ignore', sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = remove # ignore/add/remove/force # Add or remove space between pointer stars '*' sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = remove # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a func proto/def. sp_after_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open paren (function types). sp_ptr_star_paren = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a func proto/def. sp_before_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' sp_before_byref = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a variable name # If set to 'ignore', sp_before_byref is used instead. sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = ignore # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a func proto/def. sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a func proto/def. sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space between type and word. Default=Force sp_after_type = force # ignore/add/remove/force # Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space in 'template <' vs 'template<'. # If set to ignore, sp_before_angle is used. sp_template_angle = ignore # ignore/add/remove/force # Add or remove space before '<>' sp_before_angle = ignore # ignore/add/remove/force # Add or remove space inside '<' and '>' sp_inside_angle = ignore # ignore/add/remove/force # Add or remove space after '<>' sp_after_angle = ignore # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List();' sp_angle_paren = ignore # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' sp_angle_word = ignore # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add sp_angle_shift = add # ignore/add/remove/force # Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False # sp_angle_shift cannot remove the space without this option. sp_permit_cpp11_shift = false # false/true # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')' sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space before if-condition ')'. Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force # Add or remove space before if-condition '('. Overrides sp_inside_sparen. sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' sp_after_sparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'invariant' and '(' in the D language. sp_invariant_paren = ignore # ignore/add/remove/force # Add or remove space after the ')' in 'invariant (C) c' in the D language. sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while' sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. Default=Remove sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statement. sp_before_semi_for_empty = force # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. Default=Add sp_after_semi = add # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. Default=Force sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). sp_after_semi_for_empty = force # ignore/add/remove/force # Add or remove space before '[' (except '[]') sp_before_square = ignore # ignore/add/remove/force # Add or remove space before '[]' sp_before_squares = ignore # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']' sp_inside_square = ignore # ignore/add/remove/force # Add or remove space after ',' sp_after_comma = force # ignore/add/remove/force # Add or remove space before ',' sp_before_comma = remove # ignore/add/remove/force # Add or remove space between an open paren and comma: '(,' vs '( ,' sp_paren_comma = force # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a non-punctuator sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space after class ':' sp_after_class_colon = ignore # ignore/add/remove/force # Add or remove space before class ':' sp_before_class_colon = ignore # ignore/add/remove/force # Add or remove space before case ':'. Default=Remove sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren, as in 'operator ++(' sp_after_operator_sym = ignore # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' sp_after_cast = ignore # ignore/add/remove/force # Add or remove spaces inside cast parens sp_inside_paren_cast = ignore # ignore/add/remove/force # Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' sp_cpp_cast_paren = ignore # ignore/add/remove/force # Add or remove space between 'sizeof' and '(' sp_sizeof_paren = ignore # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn) sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}' sp_inside_braces_enum = force # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}' sp_inside_braces_struct = ignore # ignore/add/remove/force # Add or remove space inside '{' and '}' sp_inside_braces = force # ignore/add/remove/force # Add or remove space inside '{}' sp_inside_braces_empty = remove # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration sp_func_proto_paren = force # ignore/add/remove/force # Add or remove space between function name and '(' on function definition sp_func_def_paren = force # ignore/add/remove/force # Add or remove space inside empty function '()' sp_inside_fparens = ignore # ignore/add/remove/force # Add or remove space inside function '(' and ')' sp_inside_fparen = ignore # ignore/add/remove/force # Add or remove space inside the first parens in the function type: 'void (*x)(...)' sp_inside_tparen = ignore # ignore/add/remove/force # Add or remove between the parens in the function type: 'void (*x)(...)' sp_after_tparen_close = ignore # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of function sp_fparen_brace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls sp_func_call_paren = force # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without parameters. # If set to 'ignore' (the default), sp_func_call_paren is used. sp_func_call_paren_empty = force # ignore/add/remove/force # Add or remove space between the user function name and '(' on function calls # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. sp_func_call_user_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren sp_func_class_paren = ignore # ignore/add/remove/force # Add or remove space between 'return' and '(' sp_return_paren = ignore # ignore/add/remove/force # Add or remove space between '__attribute__' and '(' sp_attribute_paren = ignore # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)' sp_defined_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)' sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }' # If set to ignore, sp_before_sparen is used. sp_catch_paren = ignore # ignore/add/remove/force # Add or remove space between 'version' and '(' in 'version (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_version_paren = ignore # ignore/add/remove/force # Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between macro and value sp_macro = force # ignore/add/remove/force # Add or remove space between macro function ')' and value sp_macro_func = force # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line sp_brace_typedef = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line sp_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line sp_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space before the '::' operator sp_before_dc = ignore # ignore/add/remove/force # Add or remove space after the '::' operator sp_after_dc = ignore # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) operator. Default=Remove sp_not = remove # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. Default=Remove sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. Default=Remove # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. Default=Remove sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. Default=Remove # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. Default=Add sp_before_nl_cont = force # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' sp_after_oc_scope = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f:(int) x;' vs '-(int) f: (int) x;' sp_after_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '-(int) f: (int) x;' vs '-(int) f : (int) x;' sp_before_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_after_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_before_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '[object setValue:1];' vs '[object setValue: 1];' sp_after_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '[object setValue:1];' vs '[object setValue :1];' sp_before_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int)f: (int) x;' vs '-(int)f: (int)x;' sp_after_oc_type = ignore # ignore/add/remove/force # Add or remove space after the first (type) in message specs # '-(int) f:(int)x;' vs '-(int)f:(int)x;' sp_after_oc_return_type = ignore # ignore/add/remove/force # Add or remove space between '@selector' and '(' # '@selector(msgName)' vs '@selector (msgName)' # Also applies to @protocol() constructs sp_after_oc_at_sel = ignore # ignore/add/remove/force # Add or remove space between '@selector(x)' and the following word # '@selector(foo) a:' vs '@selector(foo)a:' sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space inside '@selector' parens # '@selector(foo)' vs '@selector( foo )' # Also applies to @protocol() constructs sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space before a block pointer caret # '^int (int arg){...}' vs. ' ^int (int arg){...}' sp_before_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space after a block pointer caret # '^int (int arg){...}' vs. '^ int (int arg){...}' sp_after_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space between the receiver and selector in a message. # '[receiver selector ...]' sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # Add or remove space after @property. sp_after_oc_property = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f' sp_cond_colon = force # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f' sp_cond_question = force # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. sp_case_label = force # ignore/add/remove/force # Control the space around the D '..' operator. sp_range = ignore # ignore/add/remove/force # Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) sp_after_for_colon = ignore # ignore/add/remove/force # Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) sp_before_for_colon = ignore # ignore/add/remove/force # Control the spacing in 'extern (C)' (D) sp_extern_paren = ignore # ignore/add/remove/force # Control the space after the opening of a C++ comment '// A' vs '//A' sp_cmt_cpp_start = force # ignore/add/remove/force # Controls the spaces between #else or #endif and a trailing comment sp_endif_cmt = ignore # ignore/add/remove/force # Controls the spaces after 'new', 'delete', and 'delete[]' sp_after_new = ignore # ignore/add/remove/force # Controls the spaces before a trailing or embedded comment sp_before_tr_emb_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment sp_num_before_tr_emb_cmt = 0 # number # Control space between a Java annotation and the open paren. sp_annotation_paren = ignore # ignore/add/remove/force # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs align_keep_tabs = false # false/true # Whether to use tabs for aligning align_with_tabs = false # false/true # Whether to bump out to the next tab when aligning align_on_tabstop = false # false/true # Whether to left-align numbers align_number_left = false # false/true # Align variable definitions in prototypes and functions align_func_params = true # false/true # Align parameters in single-line functions that have the same name. # The function names must already be aligned with each other. align_same_func_call_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 1 # number # How to align the star in variable definitions. # 0=Part of the type 'void * foo;' # 1=Part of the variable 'void *foo;' # 2=Dangling 'void *foo;' align_var_def_star_style = 1 # number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 0 # number # The gap for aligning variable definitions align_var_def_gap = 0 # number # Whether to align the colon in struct bit fields align_var_def_colon = false # false/true # Whether to align any attribute after the variable name align_var_def_attribute = false # false/true # Whether to align inline struct/enum/union variable definitions align_var_def_inline = false # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 1 # number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 0 # number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 1 # number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # number # The span for aligning struct/union (0=don't align) align_var_struct_span = 1 # number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # number # The gap for aligning struct/union member definitions align_var_struct_gap = 0 # number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 1 # number # The minimum space between the type and the synonym of a typedef align_typedef_gap = 0 # number # The span for aligning single-line typedefs (0=don't align) align_typedef_span = 1 # number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typedef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 0 # number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typedef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 1 # number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment align_right_cmt_mix = false # false/true # If a trailing comment is more than this number of columns away from the text it follows, # it will qualify for being aligned. This has to be > 0 to do anything. align_right_cmt_gap = 1 # number # Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) align_right_cmt_at_col = 0 # number # The span for aligning function prototypes (0=don't align) align_func_proto_span = 0 # number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # number # Align function protos on the 'operator' keyword instead of what follows align_on_operator = false # false/true # Whether to mix aligning prototype and variable declarations. # If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = false # false/true # Align single-line functions with function prototypes, uses align_func_proto_span align_single_line_func = false # false/true # Aligning the open brace of single-line functions. # Requires align_single_line_func=true, uses align_func_proto_span align_single_line_brace = false # false/true # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # number # The span for aligning ObjC msg spec (0=don't align) align_oc_msg_spec_span = 0 # number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = true # false/true # # Align macro functions and variables together align_pp_define_together = true # false/true # The minimum space between label and value of a preprocessor define align_pp_define_gap = 0 # number # The span for aligning on '#define' bodies (0=don't align) align_pp_define_span = 1 # number # Align lines that start with '<<' with previous '<<'. Default=true align_left_shift = true # false/true # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) align_oc_msg_colon_span = 0 # number # If true, always align with the first parameter, even if it is too short. align_oc_msg_colon_first = false # false/true # Aligning parameters in an Obj-C '+' or '-' declaration on the ':' align_oc_decl_colon = false # false/true # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}' nl_collapse_empty_body = false # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' nl_assign_leave_one_liners = false # false/true # Don't split one-line braced statements inside a class xx { } body nl_class_leave_one_liners = false # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = false # false/true # Don't split one-line get or set functions nl_getset_leave_one_liners = false # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = false # false/true # Don't split one-line if/else statements - 'if(a) b++;' nl_if_leave_one_liners = false # false/true # Don't split one-line OC messages nl_oc_msg_leave_one_liner = false # false/true # Add or remove newlines at the start of the file nl_start_of_file = ignore # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' nl_start_of_file_min = 0 # number # Add or remove newline at the end of the file nl_end_of_file = force # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') nl_end_of_file_min = 1 # number # Add or remove newline between '=' and '{' nl_assign_brace = force # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only) nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' nl_after_square_assign = ignore # ignore/add/remove/force # The number of blank lines after a block of variable definitions at the top of a function body # 0 = No change (default) nl_func_var_def_blk = 0 # number # The number of newlines before a block of typedefs # 0 = No change (default) nl_typedef_blk_start = 0 # number # The number of newlines after a block of typedefs # 0 = No change (default) nl_typedef_blk_end = 0 # number # The maximum consecutive newlines within a block of typedefs # 0 = No change (default) nl_typedef_blk_in = 0 # number # The number of newlines before a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_start = 0 # number # The number of newlines after a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_end = 0 # number # The maximum consecutive newlines within a block of variable definitions # 0 = No change (default) nl_var_def_blk_in = 0 # number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { } nl_fcall_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and '{' nl_enum_brace = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{' nl_struct_brace = ignore # ignore/add/remove/force # Add or remove newline between 'union' and '{' nl_union_brace = ignore # ignore/add/remove/force # Add or remove newline between 'if' and '{' nl_if_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'else' nl_brace_else = ignore # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead nl_elseif_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and '{' nl_else_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and 'if' nl_else_if = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'finally' nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{' nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{' nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{' nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{' nl_for_brace = ignore # ignore/add/remove/force # Add or remove newline between 'catch' and '{' nl_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch' nl_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{' nl_while_brace = ignore # ignore/add/remove/force # Add or remove newline between 'scope (x)' and '{' (D) nl_scope_brace = ignore # ignore/add/remove/force # Add or remove newline between 'unittest' and '{' (D) nl_unittest_brace = ignore # ignore/add/remove/force # Add or remove newline between 'version (x)' and '{' (D) nl_version_brace = ignore # ignore/add/remove/force # Add or remove newline between 'using' and '{' nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. # Due to general newline/brace handling, REMOVE may not work. nl_brace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'do' and '{' nl_do_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement nl_brace_while = ignore # ignore/add/remove/force # Add or remove newline between 'switch' and '{' nl_switch_brace = ignore # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = true # false/true # Whether to put a newline before 'case' statement nl_before_case = false # false/true # Add or remove newline between ')' and 'throw' nl_before_throw = ignore # ignore/add/remove/force # Whether to put a newline after 'case' statement nl_after_case = false # false/true # Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. nl_case_colon_brace = ignore # ignore/add/remove/force # Newline between namespace and { nl_namespace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'template<>' and whatever follows. nl_template_class = ignore # ignore/add/remove/force # Add or remove newline between 'class' and '{' nl_class_brace = ignore # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization nl_class_init_args = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a function definition nl_func_type_name = force # ignore/add/remove/force # Add or remove newline between return type and function name inside a class {} # Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between function scope and name in a definition # Controls the newline after '::' in 'void A::f() { }' nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' nl_func_paren = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the definition nl_func_def_paren = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration nl_func_decl_start = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function definition nl_func_def_start = ignore # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. nl_func_def_start_single = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function declaration nl_func_decl_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function definition nl_func_def_args = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function declaration nl_func_decl_end = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function definition nl_func_def_end = ignore # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. nl_func_def_end_single = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function declaration. nl_func_decl_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function definition. nl_func_def_empty = ignore # ignore/add/remove/force # Whether to put each OC message parameter on a separate line # See nl_oc_msg_leave_one_liner nl_oc_msg_args = false # false/true # Add or remove newline between function signature and '{' nl_fdef_brace = force # ignore/add/remove/force # Add or remove a newline between the return keyword and return expression. nl_return_expr = ignore # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements nl_after_semicolon = false # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = false # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # false/true # Whether to put a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open_empty = false # false/true # Whether to put a newline after a brace close. # Does not apply if followed by a necessary ';'. nl_after_brace_close = false # false/true # Whether to put a newline after a virtual brace close. # Would add a newline before return in: 'if (foo) a++; return;' nl_after_vbrace_close = false # false/true # Control the newline between the close brace and 'b' in: 'struct { int a; } b;' # Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros nl_define_macro = false # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true # Add or remove blank line before 'if' nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement nl_after_if = ignore # ignore/add/remove/force # Add or remove blank line before 'for' nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while' nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch' nl_before_switch = force # ignore/add/remove/force # Add or remove blank line after 'switch' statement nl_after_switch = force # ignore/add/remove/force # Add or remove blank line before 'do' nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement nl_after_do = ignore # ignore/add/remove/force # Whether to double-space commented-entries in struct/enum nl_ds_struct_enum_cmt = false # false/true # Whether to double-space before the close brace of a struct/union/enum # (lower priority than 'eat_blanks_before_close_brace)' nl_ds_struct_enum_close_brace = false # false/true # Add or remove a newline around a class colon. # Related to pos_class_colon, nl_class_init_args, and pos_comma. nl_class_colon = ignore # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' nl_create_while_one_liner = false # false/true # # Positioning options # # The position of arithmetic operators in wrapped expressions pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of assignment in wrapped expressions. # Do not affect '=' followed by '{' pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of boolean operators in wrapped expressions pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of comparison operators in wrapped expressions pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of conditional (b ? t : f) operators in wrapped expressions pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in wrapped expressions pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in the constructor initialization list pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of colons between constructor and member initialization pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # # Line Splitting options # # Try to limit code width to N number of columns code_width = 80 # number # Whether to fully split long 'for' statements at semi-colons ls_for_split_full = false # false/true # Whether to fully split long function protos/calls at commas ls_func_split_full = false # false/true # Whether to split lines as close to code_width as possible and ignore some groupings ls_code_width = false # false/true # # Blank line options # # The maximum consecutive newlines nl_max = 0 # number # The number of newlines after a function prototype, if followed by another function prototype nl_after_func_proto = 0 # number # The number of newlines after a function prototype, if not followed by another function prototype nl_after_func_proto_group = 0 # number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 0 # number # The number of newlines after '}' of a multi-line function body in a class declaration nl_after_func_body_class = 0 # number # The number of newlines after '}' of a single line function body nl_after_func_body_one_liner = 0 # number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 0 # number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # number # Whether to force a newline after a multi-line comment. nl_after_multiline_comment = false # false/true # The number of newlines after '}' or ';' of a struct/enum/union definition nl_after_struct = 0 # number # The number of newlines after '}' or ';' of a class definition nl_after_class = 0 # number # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 0 # number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # 0 = No change. nl_after_access_spec = 0 # number # The number of newlines between a function def and the function comment. # 0 = No change. nl_comment_func_def = 0 # number # The number of newlines after a try-catch-finally block that isn't followed by a brace close. # 0 = No change. nl_after_try_catch_finally = 0 # number # The number of newlines before and after a property, indexer or event decl. # 0 = No change. nl_around_cs_property = 0 # number # The number of newlines between the get/set/add/remove handlers in C#. # 0 = No change. nl_between_get_set = 0 # number # Add or remove newline between C# property and the '{' nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{' eat_blanks_after_open_brace = false # false/true # Whether to remove blank lines before '}' eat_blanks_before_close_brace = false # false/true # How aggressively to remove extra newlines not in preproc. # 0: No change # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config nl_remove_extra_newlines = 0 # number # Whether to put a blank line before 'return' statements, unless after an open brace. nl_before_return = true # false/true # Whether to put a blank line after 'return' statements, unless followed by a close brace. nl_after_return = false # false/true # Whether to put a newline after a Java annotation statement. # Only affects annotations that are after a newline. nl_after_annotation = ignore # ignore/add/remove/force # Controls the newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement mod_full_brace_do = ignore # ignore/add/remove/force # Add or remove braces on single-line 'for' statement mod_full_brace_for = ignore # ignore/add/remove/force # Add or remove braces on single-line function definitions. (Pawn) mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. mod_full_brace_if = ignore # ignore/add/remove/force # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. mod_full_brace_if_chain = false # false/true # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # number # Add or remove braces on single-line 'while' statement mod_full_brace_while = ignore # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement mod_full_brace_using = ignore # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement mod_paren_on_return = remove # ignore/add/remove/force # Whether to change optional semicolons to real semicolons mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools mod_full_paren_if_bool = false # false/true # Whether to remove superfluous semicolons mod_remove_extra_semicolon = false # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 0 # number # If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after # the #endif, a comment will be added. mod_add_long_ifdef_endif_comment = 1 # number # If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after # the #else, a comment will be added. mod_add_long_ifdef_else_comment = 1 # number # If TRUE, will sort consecutive single-line 'import' statements [Java, D] mod_sort_import = false # false/true # If TRUE, will sort consecutive single-line 'using' statements [C#] mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. mod_move_case_break = true # false/true # Will add or remove the braces around a fully braced case statement. # Will only remove the braces if there are no variable declarations in the block. mod_case_brace = ignore # ignore/add/remove/force # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = true # false/true # # Comment modifications # # Try to wrap comments at cmt_width columns cmt_width = 80 # number # Set the comment reflow mode (default: 0) # 0: no reflowing (apart from the line wrapping due to cmt_width) # 1: no touching at all # 2: full reflow cmt_reflow_mode = 0 # number # If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. # Default is true. cmt_indent_multi = true # false/true # Whether to group c-comments that look like they are in a block cmt_c_group = false # false/true # Whether to put an empty '/*' on the first line of the combined c-comment cmt_c_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined c-comment cmt_c_nl_end = true # false/true # Whether to group cpp-comments that look like they are in a block cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment cmt_cpp_nl_end = true # false/true # Whether to change cpp-comments into c-comments cmt_cpp_to_c = true # false/true # Whether to put a star on subsequent comment lines cmt_star_cont = true # false/true # The number of spaces to insert at the start of subsequent comment lines cmt_sp_before_star_cont = 0 # number # The number of spaces to insert after the star on subsequent comment lines cmt_sp_after_star_cont = 0 # number # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length. Default=True cmt_multi_check_last = true # false/true # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_footer = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. # Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. # Will substitute $(class) with the class name. cmt_insert_class_header = "" # string # The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment. # Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. cmt_insert_oc_msg_header = "" # string # If a preprocessor is encountered when stepping backwards from a function name, then # this option decides whether the comment should be inserted. # Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. cmt_insert_before_preproc = false # false/true # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 pp_indent = ignore # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. pp_indent_count = 1 # number # Add or remove space after # based on pp_level of #if blocks pp_space = ignore # ignore/add/remove/force # Sets the number of spaces added with pp_space pp_space_count = 0 # number # The indent for #region and #endregion in C# and '#pragma region' in C/C++ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion pp_region_indent_code = false # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif when not at file-level pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = false # false/true # You can force a token to be a type with the 'type' option. # Example: # type myfoo1 myfoo2 # # You can create custom macro-based indentation using macro-open, # macro-else and macro-close. # Example: # macro-open BEGIN_TEMPLATE_MESSAGE_MAP # macro-open BEGIN_MESSAGE_MAP # macro-close END_MESSAGE_MAP # # You can assign any keyword to any type with the set option. # set func_call_user _ N_ # # The full syntax description of all custom definition config entries # is shown below: # # define custom tokens as: # - embed whitespace in token using '' escape character, or # put token in quotes # - these: ' " and ` are recognized as quote delimiters # # type token1 token2 token3 ... # ^ optionally specify multiple tokens on a single line # define def_token output_token # ^ output_token is optional, then NULL is assumed # macro-open token # macro-close token # macro-else token # set id token1 token2 ... # ^ optionally specify multiple tokens on a single line # ^ id is one of the names in token_enum.h sans the CT_ prefix, # e.g. PP_PRAGMA # # all tokens are separated by any mix of ',' commas, '=' equal signs # and whitespace (space, tab) # mu-0.9.12/configure.ac000066400000000000000000000271161250406103100145110ustar00rootroot00000000000000## Copyright (C) 2008-2014 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. AC_INIT([mu],[0.9.11],[https://github.com/djcb/mu/issues],[mu]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([mu/mu.cc]) # libtoolize wants to put some stuff in here; if you have an old # autotools/libtool setup. you can try to comment this out AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE # silent build if we have a new enough automake m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AS_IF([test x$prefix = xNONE],[prefix=/usr/local]) AC_SUBST(prefix) # AC_PROG_CXX *before* AC_PROG_CC, otherwise configure won't error out # when a c++ compiler is not found. Weird, huh? AC_PROG_CXX AC_PROG_CC AC_PROG_CC_STDC AC_PROG_INSTALL AC_HEADER_STDC # for now, use AM_PROG_LIBTOOL, as we don't want to require # a too new setup for autotools/libtool AM_PROG_LIBTOOL #LT_INIT([disable-shared]) AC_PROG_AWK AC_CHECK_PROG(SORT,sort,sort) AC_CHECK_HEADERS([wordexp.h]) # use the 64-bit versions AC_SYS_LARGEFILE # check for makeinfo AC_CHECK_PROG(have_makeinfo,makeinfo,yes,no) AM_CONDITIONAL(HAVE_MAKEINFO,test "x$have_makeinfo" = "xyes") # we need emacs for byte-compiling mu4e build_mu4e=no AC_ARG_ENABLE([mu4e], AS_HELP_STRING([--disable-mu4e],[Disable building mu4e])) AS_IF([test "x$enable_mu4e" != "xno"], [ AM_PATH_LISPDIR AS_IF([test "x$lispdir" != "xno"], [ emacs_version="`$EMACS --version | head -1`" lispdir="${lispdir}/mu4e/" ]) AS_CASE([$emacs_version],[*23*|*24*|*25*],[build_mu4e=yes], [AC_WARN([emacs is too old to build mu4e (need emacs >= 23.x)])]) ]) AM_CONDITIONAL(BUILD_MU4E, test "x$build_mu4e" = "xyes") # we need some special tricks for filesystems that don't have d_type; # e.g. Solaris. See mu-maildir.c. Explicitly disabling it is for # testing purposes only AC_ARG_ENABLE([dirent-d-type], AC_HELP_STRING([--disable-dirent-d-type], [Don't use dirent->d_type, even if you have it]), [], [AC_STRUCT_DIRENT_D_TYPE] ) AS_IF([test "x$ac_cv_member_struct_dirent_d_type" != "xyes"], [use_dirent_d_type="no"], [use_dirent_d_type="yes"]) # support for d_ino (inode) in struct dirent is optional; if it's # available we can sort direntries by inode and access them in that # order; this is much faster on some file systems (such as extfs3). # Explicity disabling it is for testing purposes only. AC_ARG_ENABLE([dirent-d-ino], AC_HELP_STRING([--disable-dirent-d-ino], [Don't use dirent->d_ino, even if you have it]), [], [AC_STRUCT_DIRENT_D_INO] ) AS_IF([test "x$ac_cv_member_struct_dirent_d_ino" != "xyes"], [use_dirent_d_ino="no"], [use_dirent_d_ino="yes"]) # we need these AC_CHECK_FUNCS([memset memcpy realpath setlocale strerror getpass]) AC_CHECK_FUNC(timegm,[],[AC_MSG_ERROR([missing required function timegm])]) # require pkg-config AC_PATH_PROG([PKG_CONFIG], [pkg-config], [no]) AS_IF([test "x$PKG_CONFIG" = "xno"],[ AC_MSG_ERROR([ *** The pkg-config script could not be found. Make sure it is *** in your path, or set the PKG_CONFIG environment variable *** to the full path to pkg-config.]) ]) # glib2? # we need 2.14 at least, because we use GRegex PKG_CHECK_MODULES(GLIB,glib-2.0 >= 2.24 gobject-2.0 gio-2.0) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) glib_version="`$PKG_CONFIG --modversion glib-2.0`" # gmime PKG_CHECK_MODULES(GMIME,gmime-2.6 >= 2.6.7) AC_SUBST(GMIME_CFLAGS) AC_SUBST(GMIME_LIBS) gmime_version="`$PKG_CONFIG --modversion gmime-2.6`" # xapian checking - we need 1.2.x at least AC_CHECK_PROG(XAPIAN_CONFIG,xapian-config,xapian-config,no) AM_CONDITIONAL(HAVE_XAPIAN,test "x$XAPIAN_CONFIG" != "xno") AS_IF([test "x$XAPIAN_CONFIG" = "xno"],[ AC_MSG_ERROR([ *** xapian could not be found; please install it *** e.g., in debian/ubuntu the package would be 'libxapian-dev' *** If you compiled it yourself, you should ensure that xapian-config *** is in your PATH.])], [xapian_version=$($XAPIAN_CONFIG --version | sed -e 's/.* //')]) AS_CASE([$xapian_version], [1.[[2-9]].[[0-9]]*], [], [AC_MSG_ERROR([*** xapian version >= 1.2 needed, but version $xapian_version found.])]) XAPIAN_CXXFLAGS="`$XAPIAN_CONFIG --cxxflags`" XAPIAN_LIBS="`$XAPIAN_CONFIG --libs`" have_xapian="yes" AC_SUBST(XAPIAN_CXXFLAGS) AC_SUBST(XAPIAN_LIBS) ############################################################################### # we set the set the version of the MuStore (Xapian database) layout # here; it will become part of the db name, so we can automatically # recreate the database when we have incompatible changes. # # note that MU_STORE_SCHEMA_VERSION does not necessarily follow MU # versioning, as we hopefully don't have updates for each version; # also, this has nothing to do with Xapian's software version AC_DEFINE(MU_STORE_SCHEMA_VERSION,["9.9"], ['Schema' version of the database]) ############################################################################### ############################################################################### # we need GTK+3 for some of the graphical tools # use --without-gtk to disable it AC_ARG_ENABLE([gtk],AS_HELP_STRING([--disable-gtk],[Disable GTK+])) AS_IF([test "x$enable_gtk" != "xno"],[ PKG_CHECK_MODULES(GTK,gtk+-3.0,[have_gtk=yes],[have_gtk=no]) AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) gtk_version="`$PKG_CONFIG --modversion gtk+-3.0`"]) AM_CONDITIONAL(HAVE_GTK,[test "x$have_gtk" = "xyes"]) # webkit? needed for the fancy web widget # use --disable-webkit to disable it, even if you have it AC_ARG_ENABLE([webkit],AS_HELP_STRING([--disable-webkit],[Disable webkit])) AS_IF([test "x$enable_webkit" != "xno"],[ PKG_CHECK_MODULES(WEBKIT,webkitgtk-3.0 >= 1.8.0,[have_webkit=yes],[have_webkit=no]) AS_IF([test "x$have_webkit" = "xyes"],[ webkit_version="`$PKG_CONFIG --modversion webkitgtk-3.0`"]) AC_SUBST(WEBKIT_CFLAGS) AC_SUBST(WEBKIT_LIBS) ]) AM_CONDITIONAL(HAVE_WEBKIT, [test "x$have_webkit" = "xyes"]) AM_CONDITIONAL(BUILD_GUI,[test "x$have_webkit" = "xyes" -a "x$have_gtk" = "xyes"]) ############################################################################### ############################################################################### # check for guile 2.0 & guile-snarf build_guile=no AC_ARG_ENABLE([guile], AS_HELP_STRING([--disable-guile],[Disable guile])) AS_IF([test "x$enable_guile" != "xno"], [ PKG_CHECK_MODULES(GUILE,guile-2.0,[build_guile=yes],[build_guile=no]) AC_SUBST(GUILE_CFLAGS) AC_SUBST(GUILE_LIBS) GUILE_EXECDIR="`${PKG_CONFIG} guile-2.0 --variable=exec_prefix`/bin" AS_IF([test "x$build_guile" = "xyes"],[ AC_PATH_PROG(GUILE_BINARY, [guile], [], [$GUILE_EXECDIR]) AS_IF([test "x$GUILE_BINARY" != "x"], [AC_DEFINE_UNQUOTED([GUILE_BINARY],["$GUILE_BINARY"],[Path to the guile binary])], [AC_MSG_WARN([cannot find guile-snarf])])]) AS_IF([test "x$build_guile" = "xyes"],[ AC_PATH_PROG(GUILE_SNARF, [guile-snarf], [], [$GUILE_EXECDIR]) AS_IF([test "x$GUILE_SNARF" != "x"], [AC_DEFINE_UNQUOTED([GUILE_SNARF],["$GUILE_SNARF"],[Path to guile-snarf]) GUILE_SITEDIR="`${PKG_CONFIG} guile-2.0 --variable=sitedir`"], [AC_MSG_WARN([cannot find guile-snarf])])]) AC_SUBST(GUILE_SITEDIR)]) AS_IF([test "x$build_guile" = "xyes" -a "x$GUILE_SNARF" != "x"], [guile_version="`${PKG_CONFIG} --modversion guile-2.0`" AC_DEFINE_UNQUOTED([BUILD_GUILE],1,[Whether we build with guile 2.0 support])]) AM_CONDITIONAL(BUILD_GUILE, [test "x$build_guile" = "xyes"]) ############################################################################### ############################################################################### # check for makeinfo AC_CHECK_PROG(have_makeinfo,makeinfo,yes,no) AM_CONDITIONAL(HAVE_MAKEINFO, [test "x$have_makeinfo" = "xyes"]) ############################################################################### ############################################################################### # check for pmccabe AC_PATH_PROG([PMCCABE],[pmccabe],[no]) AS_IF([test "x$PMCCABE" = "xno"],[ have_pmccabe="no" AC_MSG_WARN([ *** Developers: you do not seem to have the pmccabe tool installed. *** Please install it if you want to run the automated code checks]) ],[have_pmccabe="yes"]) ############################################################################### AC_CONFIG_FILES([ Makefile mu/Makefile mu/tests/Makefile lib/Makefile lib/doxyfile lib/tests/Makefile mu4e/Makefile mu4e/mu4e-meta.el mu4e/texi.texi guile/Makefile guile/texi.texi guile/mu/Makefile guile/examples/Makefile guile/tests/Makefile guile/scripts/Makefile toys/Makefile toys/mug/Makefile toys/msg2pdf/Makefile man/Makefile m4/Makefile contrib/Makefile ]) AC_OUTPUT echo echo "mu configuration is complete." echo "------------------------------------------------" if test -e ~/.mu/xapian-0.6; then echo "Note: since version 0.7, the Xapian database is no longer stored as " echo "~/.mu/xapian-0.6 but instead simply as ~/.mu/xapian. You can " echo "remove the old /xapian-0.6 directory to save some disk space" fi echo "mu version : $VERSION" echo echo "Xapian version : $xapian_version" echo "GLib version : $glib_version" echo "GMime version : $gmime_version" AM_COND_IF([BUILD_GUI],[ echo "GTK+ version : $gtk_version" echo "Webkit version : $webkit_version" ]) AM_COND_IF([BUILD_GUILE],[ echo "Guile version : $guile_version" ]) if test "x$build_mu4e" = "xyes"; then echo "Emacs version : $emacs_version" fi echo echo "Have wordexp : $ac_cv_header_wordexp_h" echo "Build mu4e emacs frontend : $build_mu4e" AM_COND_IF([BUILD_GUI],[ echo "Build 'mug' toy-ui (gtk+/webkit) : yes"],[ echo "Build 'mug' toy-ui (gtk+/webkit) : no" ]) echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe" echo echo "Have direntry->d_ino : $use_dirent_d_ino" echo "Have direntry->d_type : $use_dirent_d_type" echo "------------------------------------------------" echo # # Warnings / notes # # makeinfo if test "x$have_makeinfo" != "xyes"; then echo "* You do not seem to have the makeinfo program; if you are building from git" echo " you need that to create documentation for guile and emacs. It is in the" echo " texinfo package in debian/ubuntu (ie., apt-get install texinfo)" echo fi # gui if test "x$buildgui" = "xyes"; then echo "* The demo UI will be built in toys/mug" echo fi # wordexp if test "x$ac_cv_header_wordexp_h" != "xyes"; then echo "* Your system does not seem to have the 'wordexp' function." echo " This means that you cannot use shell-like expansion in options and " echo " some other places. So, for example, instead of" echo " --maildir=~/Maildir" echo " you should use the complete path, something like:" echo " --maildir=/home/user/Maildir" fi echo echo "Now, type 'make' to build mu (or 'make check' to run the unit tests, too)" echo mu-0.9.12/contrib/000077500000000000000000000000001250406103100136545ustar00rootroot00000000000000mu-0.9.12/contrib/Makefile.am000066400000000000000000000021721250406103100157120ustar00rootroot00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk AM_CPPFLAGS=$(GMIME_CFLAGS) $(GLIB_CFLAGS) -I${prefix}/include AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement -pedantic noinst_PROGRAMS=gmime-test gmime_test_SOURCES=gmime-test.c gmime_test_LDADD=$(GMIME_LIBS) $(GLIB_LIBS) EXTRA_DIST= \ mu-completion.zsh \ mu-sexp-convert \ mu.spec mu-0.9.12/contrib/gmime-test.c000066400000000000000000000110551250406103100160750ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ /* gmime-test; compile with: gcc -o gmime-test gmime-test.c -Wall -O0 -ggdb3 \ `pkg-config --cflags --libs gmime-2.4` */ #include #include #include #include #include static gchar* get_recip (GMimeMessage *msg, GMimeRecipientType rtype) { char *recep; InternetAddressList *receps; receps = g_mime_message_get_recipients (msg, rtype); recep = (char*)internet_address_list_to_string (receps, FALSE); if (!recep || !*recep) { g_free (recep); return NULL; } return recep; } static gchar* get_refs_str (GMimeMessage *msg) { const gchar *str; const GMimeReferences *cur; GMimeReferences *mime_refs; gchar *rv; str = g_mime_object_get_header (GMIME_OBJECT(msg), "References"); if (!str) return NULL; mime_refs = g_mime_references_decode (str); for (rv = NULL, cur = mime_refs; cur; cur = g_mime_references_get_next(cur)) { const char* msgid; msgid = g_mime_references_get_message_id (cur); rv = g_strdup_printf ("%s%s%s", rv ? rv : "", rv ? "," : "", msgid); } g_mime_references_free (mime_refs); return rv; } static void print_date (GMimeMessage *msg) { time_t t; int tz; char buf[64]; size_t len; struct tm *t_m; g_mime_message_get_date (msg, &t, &tz); t_m = localtime (&t); len = strftime (buf, sizeof(buf) - 1, "%c", t_m); if (len > 0) g_print ("Date : %s (%s%04d)\n", buf,tz < 0 ? "-" : "+", tz); } static gboolean test_message (GMimeMessage *msg) { gchar *val; const gchar *str; g_print ("From : %s\n", g_mime_message_get_sender (msg)); val = get_recip (msg, GMIME_RECIPIENT_TYPE_TO); g_print ("To : %s\n", val ? val : "" ); g_free (val); val = get_recip (msg, GMIME_RECIPIENT_TYPE_CC); g_print ("Cc : %s\n", val ? val : "" ); g_free (val); val = get_recip (msg, GMIME_RECIPIENT_TYPE_BCC); g_print ("Bcc : %s\n", val ? val : "" ); g_free (val); str = g_mime_message_get_subject (msg); g_print ("Subject: %s\n", str ? str : ""); print_date (msg); str = g_mime_message_get_message_id (msg); g_print ("Msg-id : %s\n", str ? str : ""); { gchar *refsstr; refsstr = get_refs_str (msg); g_print ("Refs : %s\n", refsstr ? refsstr : ""); g_free (refsstr); } return TRUE; } static gboolean test_stream (GMimeStream *stream) { GMimeParser *parser; GMimeMessage *msg; gboolean rv; parser = NULL; msg = NULL; parser = g_mime_parser_new_with_stream (stream); if (!parser) { g_warning ("failed to create parser"); rv = FALSE; goto leave; } msg = g_mime_parser_construct_message (parser); if (!msg) { g_warning ("failed to construct message"); rv = FALSE; goto leave; } rv = test_message (msg); leave: if (parser) g_object_unref (parser); else g_object_unref (stream); if (msg) g_object_unref (msg); return rv; } static gboolean test_file (const char *path) { FILE *file; GMimeStream *stream; gboolean rv; stream = NULL; file = NULL; file = fopen (path, "r"); if (!file) { g_warning ("cannot open file '%s': %s", path, strerror(errno)); rv = FALSE; goto leave; } stream = g_mime_stream_file_new (file); if (!stream) { g_warning ("cannot open stream for '%s'", path); rv = FALSE; goto leave; } rv = test_stream (stream); /* test-stream will unref it */ leave: if (file) fclose (file); return rv; } int main (int argc, char *argv[]) { gboolean rv; if (argc != 2) { g_printerr ("usage: %s \n", argv[0]); return 1; } setlocale (LC_ALL, ""); g_mime_init(GMIME_ENABLE_RFC2047_WORKAROUNDS); rv = test_file (argv[1]); g_mime_shutdown (); return rv ? 0 : 1; } mu-0.9.12/contrib/mu-completion.zsh000066400000000000000000000063641250406103100172030ustar00rootroot00000000000000#compdef mu ## Copyright (C) 2011-2012 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # zsh completion for mu. Install this by copying/linking to this file somewhere in # your $fpath; the link/copy must have a name starting with an underscore "_" # main dispatcher function _mu() { if (( CURRENT > 2 )) ; then local cmd=${words[2]} curcontext="${curcontext%:*:*}:mu-$cmd" (( CURRENT-- )) shift words _call_function ret _mu_$cmd return ret else _mu_commands fi } _mu_commands() { local -a mu_commands mu_commands=( 'index:scan your maildirs and import their metadata in the database' 'find:search for messages in the database' 'view:display specific messages' 'cfind:search for contacts (name + email) in the database' 'extract:extract message-parts (attachments) and save or open them' 'mkdir:create maildirs' # below are not generally very useful, so let's not auto-complete them # 'add: add a message to the database.' # 'remove:remove a message from the database.' # 'server:sart the mu server' ) _describe -t command 'command' mu_commands } _mu_common_options=( '--debug[output information useful for debugging mu]' '--quiet[do not give any non-critical information]' '--nocolor[do not use colors in some of the output]' '--version[display mu version and copyright information]' '--log-stderr[log to standard error]' ) _mu_db_options=( '--muhome[use some non-default location for the mu database]:directory:_files' ) _mu_find_options=( '--fields[fields to display in the output]' '--sortfield[field to sort the output by]' '--descending[sort in descending order]' '--summary[include a summary of the message]' '--summary-len[number of lines to use for the summary]' '--bookmark[use a named bookmark]' '--output[set the kind of output for the query]' ) _mu_view_options=( '--summary[only show a summary of the message]' '--summary-len[number of lines to use for the summary]' ) _mu_view() { _arguments -s : \ $_mu_common_options \ $_mu_view_options } _mu_extract() { _files } _mu_find() { _arguments -s : \ $_mu_common_options \ $_mu_db_options \ $_mu_find_options } _mu_index() { _arguments -s : \ $_mu_db_options \ $_mu_common_options }mu _mu_cleanup() { _arguments -s : \ $_mu_db_options \ $_mu_common_options } _mu_mkdir() { _arguments -s : \ '--mode=[file mode for the new Maildir]:file mode: ' \ $_mu_common_options } _mu "$@" # Local variables: # mode: sh # End: mu-0.9.12/contrib/mu-sexp-convert000077500000000000000000000144221250406103100166610ustar00rootroot00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; Copyright (C) 2012 Dirk-Jan C. Binnema ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; ;; a little hack to convert the output of ;; mu find --format=sexp ;; and ;; mu view --format=sexp ;; into XML or JSON (use-modules (ice-9 getopt-long) (ice-9 format) (ice-9 regex)) (use-modules (sxml simple)) (define (mapconcat func lst sepa) "Apply FUNC to elements of LST, concat the result as strings separated by SEPA." (if (null? lst) "" (string-append (func (car lst)) (if (null? (cdr lst)) "" (string-append sepa (mapconcat func (cdr lst) sepa)))))) (define (property-list? obj) "Is OBJ a elisp-style property list (ie. a list of the form (:symbol1 something :symbol2 somethingelse), as in an elisp proplilst." (and (list? obj) (not (null? obj)) (symbol? (car obj)) (string= ":" (substring (symbol->string (car obj)) 0 1)))) (define (plist->pairs plist) "Convert an elisp-style property list; e.g: (:prop1 foo :prop2: bar ...) into a list of pairs ((prop1 . foo) (prop2 . bar) ...)." (if (null? plist) '() (cons (cons (substring (symbol->string (car plist)) 1) (cadr plist)) (plist->pairs (cddr plist))))) (define (string->xml str) "XML-encode STR." ;; sneakily re-using sxml->xml (call-with-output-string (lambda (port) (sxml->xml str port)))) (define (string->json str) "Convert string into a JSON-encoded string." (letrec ((convert (lambda (lst) (if (null? lst) "" (string-append (cond ((equal? (car lst) #\") "\\\"") ((equal? (car lst) #\\) "\\\\") ((equal? (car lst) #\/) "\\/") ((equal? (car lst) #\bs) "\\b") ((equal? (car lst) #\ff) "\\f") ((equal? (car lst) #\lf) "\\n") ((equal? (car lst) #\cr) "\\r") ((equal? (car lst) #\ht) "\\t") (#t (string (car lst)))) (convert (cdr lst))))))) (convert (string->list str)))) (define (etime->time_t t) "Convert elisp time object T into a time_t value." (logior (ash (car t) 16) (car (cdr t)))) (define (sexp->xml) "Convert string INPUT to XML, return the XML (string)." (letrec ((convert-xml (lambda* (expr #:optional parent) (cond ((property-list? expr) (mapconcat (lambda (pair) (format #f "\t<~a>~a\n" (car pair) (convert-xml (cdr pair) (car pair)) (car pair))) (plist->pairs expr) " ")) ((list? expr) (cond ((member parent '("from" "to" "cc" "bcc")) (mapconcat (lambda (addr) (format #f "
~a~a
" (if (string? (car addr)) (format #f "~a" (string->xml (car addr))) "") (if (string? (cdr addr)) (format #f "~a" (string->xml (cdr addr))) ""))) expr " ")) ((string= parent "parts") "") ;; for now, ignore ;; convert the crazy emacs time thingy to time_t... ((string= parent "date") (format #f "~a" (etime->time_t expr))) (#t (mapconcat (lambda (elm) (format #f "~a" (convert-xml elm))) expr "")))) ((string? expr) (string->xml expr)) ((symbol? expr) (format #f "~a" expr)) ((number? expr) (number->string expr)) (#t ".")))) (msg->xml (lambda () (let ((expr (read))) (if (not (eof-object? expr)) (string-append (format #f "\n~a\n" (convert-xml expr)) (msg->xml)) ""))))) (format #f "\n\n~a" (msg->xml)))) (define (sexp->json) "Convert string INPUT to JSON, return the JSON (string)." (letrec ((convert-json (lambda* (expr #:optional parent) (cond ((property-list? expr) (mapconcat (lambda (pair) (format #f "\n\t\"~a\": ~a" (car pair) (convert-json (cdr pair) (car pair)))) (plist->pairs expr) ", ")) ((list? expr) (cond ((member parent '("from" "to" "cc" "bcc")) (string-append "[" (mapconcat (lambda (addr) (format #f "{~a~a}" (if (string? (car addr)) (format #f "\"name\": \"~a\"," (string->json (car addr))) "") (if (string? (cdr addr)) (format #f "\"email\": \"~a\"" (string->json (cdr addr))) ""))) expr ", ") "]")) ((string= parent "parts") "[]") ;; todo ;; convert the crazy emacs time thingy to time_t... ((string= parent "date") (format #f "~a" (format #f "~a" (etime->time_t expr)))) (#t (string-append "[" (mapconcat (lambda (elm) (format #f "~a" (convert-json elm))) expr ",") "]")))) ((string? expr) (format #f "\"~a\"" (string->json expr))) ((symbol? expr) (format #f "\"~a\"" expr)) ((number? expr) (number->string expr)) (#t ".")))) (msg->json (lambda (first) (let ((expr (read))) (if (not (eof-object? expr)) (string-append (format #f "~a{~a\n}" (if first "" ",\n") (convert-json expr)) (msg->json #f)) ""))))) (format #f "[\n~a\n]" (msg->json #t)))) (define (main args) (let* ((optionspec '((format (value #t)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu-sexp-convert " "--format=\n" "reads from standard-input and prints to standard output\n")) (outformat (or (option-ref options 'format #f) (begin (display msg) (exit 1))))) (cond ((string= outformat "xml") (format #t "~a\n" (sexp->xml))) ((string= outformat "json") (format #t "~a\n" (sexp->json))) (#t (begin (display msg) (exit 1)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.12/contrib/mu.spec000066400000000000000000000066251250406103100151620ustar00rootroot00000000000000 # These refer to the release version # When 0.9.9.6 gets out, remove the global pre line %global pre pre2 %global rel 1 Summary: A lightweight email search engine for Maildirs Name: mu Version: 0.9.9.6 URL: https://github.com/djcb/mu # From Packaging:NamingGuidelines for pre-relase versions: # Release: 0.%{X}.%{alphatag} where %{X} is the release number %if %{pre} Release: 0.%{rel}.%{prerelease}%{?dist} %else Release: %{rel}%{?dist} %endif License: GPLv3 Group: Applications/Internet BuildRoot: %{_tmppath}/%{name}-%{version}-build # Source is at ssaavedra repo because djcb has not yet this version tag created Source0: http://github.com/ssaavedra/%{name}/archive/v%{version}%{?pre}.tar.gz BuildRequires: emacs-el BuildRequires: emacs BuildRequires: gmime-devel BuildRequires: guile-devel BuildRequires: xapian-core-devel BuildRequires: libuuid-devel BuildRequires: texinfo Requires: gmime Requires: guile Requires: xapian-core-libs Requires: emacs-filesystem >= %{_emacs_version} %description E-mail is the 'flow' in the work flow of many people. Consequently, one spends a lot of time searching for old e-mails, to dig up some important piece of information. With people having tens of thousands of e-mails (or more), this is becoming harder and harder. How to find that one e-mail in an ever-growing haystack? Enter mu. 'mu' is a set of command-line tools for Linux/Unix that enable you to quickly find the e-mails you are looking for, assuming that you store your e-mails in Maildirs (if you don't know what 'Maildirs' are, you are probably not using them). %package gtk Group: Applications/Internet Summary: GUI for using mu (called mug) BuildRequires: gtk3-devel BuildRequires: webkitgtk3-devel Requires: gtk3 Requires: gmime Requires: webkitgtk3 Requires: mu = %{version}-%{release} %description gtk Mug is a simple GUI for mu from version 0.9. %package guile Group: Applications/Internet Summary: Guile scripting capabilities for mu Requires: guile Requires: mu = %{version}-%{release} Requires(post): info Requires(preun): info %description guile Bindings for Guile to interact with mu. %prep %setup -n %{name}-%{version}%{?pre} -q %build autoreconf -i %configure make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} install -p -c -m 755 %{_builddir}/%{buildsubdir}/toys/mug/mug %{buildroot}%{_bindir}/mug cp -p %{_builddir}/%{buildsubdir}/mu4e/*.el %{buildroot}%{_emacs_sitelispdir}/mu4e/ rm -f %{buildroot}%{_infodir}/dir %clean rm -rf %{buildroot} %post /sbin/install-info \ --info-dir=%{_infodir} %{_infodir}/mu4e.info.gz || : %preun if [ $1 = 0 -a -f %{_infodir}/mu4e.info.gz ]; then /sbin/install-info --delete \ --info-dir=%{_infodir} %{_infodir}/mu4e.info.gz || : fi %post guile /sbin/install-info \ --info-dir=%{_infodir} %{_infodir}/mu-guile.info.gz || : %preun guile if [ $1 = 0 -a -f %{_infodir}/mu-guile.info.gz ]; then /sbin/install-info --delete \ --info-dir=%{_infodir} %{_infodir}/mu-guile.info.gz || : fi %files %defattr(-,root,root) %{_bindir}/mu %{_mandir}/man1/* %{_mandir}/man5/* %{_datadir}/mu/* %{_emacs_sitelispdir}/mu4e %{_emacs_sitelispdir}/mu4e/*.elc %{_emacs_sitelispdir}/mu4e/*.el %{_infodir}/mu4e.info.gz %files gtk %{_bindir}/mug %files guile %{_libdir}/libguile-mu.* %{_datadir}/guile/site/2.0/mu/* %{_datadir}/guile/site/2.0/mu.scm %{_infodir}/mu-guile.info.gz %changelog * Wed Feb 12 2014 Santiago Saavedra - 0.9.9.5-1 - Create first SPEC. mu-0.9.12/gtest.mk000066400000000000000000000024711250406103100136770ustar00rootroot00000000000000## Copyright (C) 2011 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by the ## Free Software Foundation; either version 3, or (at your option) any ## later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. TEST_PROGS= # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof mu-0.9.12/guile/000077500000000000000000000000001250406103100133215ustar00rootroot00000000000000mu-0.9.12/guile/Makefile.am000066400000000000000000000041411250406103100153550ustar00rootroot00000000000000## Copyright (C) 2011-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # note, we need top_builddir for snarfing with 'make distcheck' (ie., # with separate builddir) SUBDIRS= . mu scripts examples tests AM_CPPFLAGS=-I. -I${top_builddir} -I${top_srcdir}/lib ${GUILE_CFLAGS} ${GLIB_CFLAGS} # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter lib_LTLIBRARIES= \ libguile-mu.la libguile_mu_la_SOURCES= \ mu-guile.c \ mu-guile.h \ mu-guile-message.c \ mu-guile-message.h libguile_mu_la_LIBADD= \ ${top_builddir}/lib/libmu.la \ ${GUILE_LIBS} libguile_mu_la_LDFLAGS= -export-dynamic XFILES= \ mu-guile.x \ mu-guile-message.x info_TEXINFOS= \ mu-guile.texi mu_guile_TEXINFOS= \ fdl.texi BUILT_SOURCES=$(XFILES) snarfcppopts= $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $(AM_CPPFLAGS) SUFFIXES = .x .doc .c.x: $(GUILE_SNARF) -o $@ $< $(snarfcppopts) # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir=${prefix}/share/guile/site/2.0/ scm_DATA=mu.scm EXTRA_DIST=$(scm_DATA) ## Add -MG to make the .x magic work with auto-dep code. MKDEP = $(CC) -M -MG $(snarfcppopts) DISTCLEANFILES=$(XFILES) mu-0.9.12/guile/examples/000077500000000000000000000000001250406103100151375ustar00rootroot00000000000000mu-0.9.12/guile/examples/Makefile.am000066400000000000000000000016011250406103100171710ustar00rootroot00000000000000## Copyright (C) 2012-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk EXTRA_DIST= \ msg-graphs \ contacts-export \ org2mu4e \ mu-biff mu-0.9.12/guile/examples/contacts-export000077500000000000000000000052231250406103100202240ustar00rootroot00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012 Dirk-Jan C. Binnema ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (srfi srfi-1)) (use-modules (mu)) (define (sort-by-freq c1 c2) (< (mu:frequency c1) (mu:frequency c2))) (define (sort-by-newness c1 c2) (< (mu:last-seen c1) (mu:last-seen c2))) (define (main args) (let* ((optionspec '( (muhome (value #t)) (sort-by (value #t)) (revert (value #f)) (format (value #t)) (limit (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: contacts-export [--help] [--muhome=] " "--format= " "--sort-by= [--revert] [--limit=]\n")) (help (option-ref options 'help #f)) (muhome (option-ref options 'muhome #f)) (sort-by (or (option-ref options 'sort-by #f) "frequency")) (revert (option-ref options 'revert #f)) (form (or (option-ref options 'format #f) "plain")) (limit (string->number (option-ref options 'limit "1000000")))) (if help (begin (display msg) (exit 0)) (begin (setlocale LC_ALL "") (mu:initialize muhome) (let* ((sort-func (cond ((string= sort-by "frequency") sort-by-freq) ((string= sort-by "newness") sort-by-newness) (else (begin (display msg) (exit 1))))) (contacts '())) ;; make a list of all contacts (mu:for-each-contact (lambda (c) (set! contacts (cons c contacts)))) ;; should we sort it? (if sort-by (set! contacts (sort! contacts (if revert (negate sort-func) sort-func)))) ;; should we limit the number? (if (and limit (< limit (length contacts))) (set! contacts (take! contacts limit))) ;; export! (for-each (lambda (c) (format #t "~a\n" (mu:contact->string c form))) contacts)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.12/guile/examples/msg-graphs000077500000000000000000000107461250406103100171450ustar00rootroot00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (setlocale LC_ALL "") (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats) (mu plot)) ;;(use-modules (mu) (mu message) (mu stats) (mu plot)) (define (per-hour expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per hour matching ~a" expr) "Hour" "Messages" plain-text)) (define (per-day expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per weekday matching ~a" expr) "Day" "Messages" plain-text)) (define (per-month expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (mu:month-numbers->names (sort (mu:tabulate (lambda (msg) (tm:mon (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per month matching ~a" expr) "Month" "Messages" plain-text)) (define (per-year-month expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (string->number (format #f "~d~2'0d" (+ 1900 (tm:year (localtime (mu:date msg)))) (tm:mon (localtime (mu:date msg)))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year/month matching ~a" expr) "Year/Month" "Messages" plain-text)) (define (per-year expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (+ 1900 (tm:year (localtime (mu:date msg))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year matching ~a" expr) "Year" "Messages" plain-text)) (define (main args) (let* ((optionspec '( (muhome (value #t)) (what (value #t)) (text (value #f)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu-msg-stats [--help] [--text] " "[--muhome=] " "--what= [searchexpr]\n")) (help (option-ref options 'help #f)) (what (option-ref options 'what #f)) (text (option-ref options 'text #f)) (muhome (option-ref options 'muhome #f)) (restargs (option-ref options '() #f)) (expr (if restargs (string-join restargs) ""))) (if (or help (not what)) (begin (display msg) (exit (if help 0 1)))) (mu:initialize muhome) (cond ((string= what "per-hour") (per-hour expr text)) ((string= what "per-day") (per-day expr text)) ((string= what "per-month") (per-month expr text)) ((string= what "per-year-month") (per-year-month expr text)) ((string= what "per-year") (per-year expr text)) (else (begin (display msg) (exit 1)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.12/guile/examples/mu-biff000077500000000000000000000035531250406103100164200ustar00rootroot00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012 Dirk-Jan C. Binnema ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; script to list the message matching which are newer than ;; minutes ;; use it, eg. like: ;; $ mu-biff --newer-than=`date +%s --date='5 minutes ago'` "maildir:/inbox" (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (mu)) (define (main args) (let* ((optionspec '((muhome (value #t)) (newer-than (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu-biff [--help] [--muhome=]" " [--newer-than=] ")) (help (option-ref options 'help #f)) (newer-than (string->number (option-ref options 'newer-than "0"))) (muhome (option-ref options 'muhome #f)) (query (string-concatenate (option-ref options '() '())))) (if help (begin (display msg) (newline) (exit 0)) (begin (mu:initialize muhome) (mu:for-each-message (lambda (msg) (if (> (mu:timestamp msg) newer-than) (format #t "~a ~a\n" (mu:from msg) (mu:subject msg)))) query))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.12/guile/examples/org2mu4e000077500000000000000000000050041250406103100165300ustar00rootroot00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (mu)) (define (display-org-header query) "Print the header for the org-file for QUERY." (format #t "* Messages matching '~a'\n\n" query)) (define (org-mu4e-link msg) "Create a link for this message understandable by org-mu4e." (let* ((subject ;; cleanup subject (string-map (lambda (kar) (if (member kar '(#\] #\[)) #\space kar)) (or (mu:subject msg) "No subject")))) (format #f "[[mu4e:msgid:~a][~s]]" (mu:message-id msg) subject))) (define (display-org-entry msg tag) "Write an org entry for MSG." (format #t "** ~a ~a\n\t~s\n\t~s\n" (org-mu4e-link msg) (if tag (string-concatenate `(":" ,tag "::")) "") (or (mu:from msg) "?") (let ((body (mu:body-txt msg))) (if (not body) ;; get a 'summary' of the body text "" (string-map (lambda (c) (if (or (char=? c #\newline) (char=? c #\return)) #\space c)) (substring body 0 (min (string-length body) 100))))))) (define (main args) (let* ((optionspec '( (muhome (value #t)) (tag (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu4e-org [--help] [--muhome=] [--tag=] ")) (help (option-ref options 'help #f)) (tag (option-ref options 'tag #f)) (muhome (option-ref options 'muhome #f)) (query (string-concatenate (option-ref options '() '())))) (if help (begin (display msg) (exit 0)) (begin (mu:initialize muhome) (display-org-header query) (mu:for-each-message (lambda (msg) (display-org-entry msg tag)) query))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.12/guile/fdl.texi000066400000000000000000000510301250406103100147600ustar00rootroot00000000000000@c The GNU Free Documentation License. @center Version 1.2, November 2002 @c This file is intended to be included within another document, @c hence no sectioning command or @node. @display Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain @sc{ascii} without markup, Texinfo input format, La@TeX{} input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML}, PostScript or @acronym{PDF} designed for human modification. Examples of transparent image formats include @acronym{PNG}, @acronym{XCF} and @acronym{JPG}. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML}, PostScript or @acronym{PDF} produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document 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. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation 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. See @uref{http://www.gnu.org/copyleft/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. @end enumerate @page @heading ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with@dots{}Texts.'' line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: mu-0.9.12/guile/mu-guile-message.c000066400000000000000000000372501250406103100166420ustar00rootroot00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include "mu-guile.h" #include #include #include #include #include /* pseudo field, not in Xapian */ #define MU_GUILE_MSG_FIELD_ID_TIMESTAMP (MU_MSG_FIELD_ID_NUM + 1) /* some symbols */ static SCM SYMB_PRIO_LOW, SYMB_PRIO_NORMAL, SYMB_PRIO_HIGH; static SCM SYMB_FLAG_NEW, SYMB_FLAG_PASSED, SYMB_FLAG_REPLIED, SYMB_FLAG_SEEN, SYMB_FLAG_TRASHED, SYMB_FLAG_DRAFT, SYMB_FLAG_FLAGGED, SYMB_FLAG_SIGNED, SYMB_FLAG_ENCRYPTED, SYMB_FLAG_HAS_ATTACH, SYMB_FLAG_UNREAD; static SCM SYMB_CONTACT_TO, SYMB_CONTACT_CC, SYMB_CONTACT_BCC, SYMB_CONTACT_FROM; struct _MuMsgWrapper { MuMsg *_msg; gboolean _unrefme; }; typedef struct _MuMsgWrapper MuMsgWrapper; static long MSG_TAG; static gboolean mu_guile_scm_is_msg (SCM scm) { return SCM_NIMP(scm) && (long)SCM_CAR(scm) == MSG_TAG; } SCM mu_guile_msg_to_scm (MuMsg *msg) { MuMsgWrapper *msgwrap; g_return_val_if_fail (msg, SCM_UNDEFINED); msgwrap = scm_gc_malloc (sizeof (MuMsgWrapper), "msg"); msgwrap->_msg = msg; msgwrap->_unrefme = FALSE; SCM_RETURN_NEWSMOB (MSG_TAG, msgwrap); } struct _FlagData { MuFlags flags; SCM lst; }; typedef struct _FlagData FlagData; #define MU_GUILE_INITIALIZED_OR_ERROR \ do { if (!(mu_guile_initialized())) \ return mu_guile_error (FUNC_NAME, 0, \ "mu not initialized; call mu:initialize", \ SCM_UNDEFINED); \ } while (0) static void check_flag (MuFlags flag, FlagData *fdata) { SCM flag_scm; if (!(fdata->flags & flag)) return; switch (flag) { case MU_FLAG_NEW: flag_scm = SYMB_FLAG_NEW; break; case MU_FLAG_PASSED: flag_scm = SYMB_FLAG_PASSED; break; case MU_FLAG_REPLIED: flag_scm = SYMB_FLAG_REPLIED; break; case MU_FLAG_SEEN: flag_scm = SYMB_FLAG_SEEN; break; case MU_FLAG_TRASHED: flag_scm = SYMB_FLAG_TRASHED; break; case MU_FLAG_SIGNED: flag_scm = SYMB_FLAG_SIGNED; break; case MU_FLAG_DRAFT: flag_scm = SYMB_FLAG_DRAFT; break; case MU_FLAG_FLAGGED: flag_scm = SYMB_FLAG_FLAGGED; break; case MU_FLAG_ENCRYPTED: flag_scm = SYMB_FLAG_ENCRYPTED; break; case MU_FLAG_HAS_ATTACH: flag_scm = SYMB_FLAG_HAS_ATTACH; break; case MU_FLAG_UNREAD: flag_scm = SYMB_FLAG_UNREAD; break; default: flag_scm = SCM_UNDEFINED; } fdata->lst = scm_append_x (scm_list_2(fdata->lst, scm_list_1 (flag_scm))); } static SCM get_flags_scm (MuMsg *msg) { FlagData fdata; fdata.flags = mu_msg_get_flags (msg); fdata.lst = SCM_EOL; mu_flags_foreach ((MuFlagsForeachFunc)check_flag, &fdata); return fdata.lst; } static SCM get_prio_scm (MuMsg *msg) { switch (mu_msg_get_prio (msg)) { case MU_MSG_PRIO_LOW: return SYMB_PRIO_LOW; case MU_MSG_PRIO_NORMAL: return SYMB_PRIO_NORMAL; case MU_MSG_PRIO_HIGH: return SYMB_PRIO_HIGH; default: g_return_val_if_reached (SCM_UNDEFINED); } } static SCM msg_string_list_field (MuMsg *msg, MuMsgFieldId mfid) { SCM scmlst; const GSList *lst; lst = mu_msg_get_field_string_list (msg, mfid); for (scmlst = SCM_EOL; lst; lst = g_slist_next(lst)) { SCM item; item = scm_list_1 (mu_guile_scm_from_str((const char*)lst->data)); scmlst = scm_append_x (scm_list_2(scmlst, item)); } return scmlst; } static SCM get_body (MuMsg *msg, gboolean html) { SCM data; const char* body; MuMsgOptions opts; opts = MU_MSG_OPTION_NONE; if (html) body = mu_msg_get_body_html (msg, opts); else body = mu_msg_get_body_text (msg, opts); if (body) data = mu_guile_scm_from_str (body); else data = SCM_BOOL_F; /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msg); return data; } SCM_DEFINE (get_field, "mu:c:get-field", 2, 0, 0, (SCM MSG, SCM FIELD), "Get the field FIELD from message MSG.\n") #define FUNC_NAME s_get_field { MuMsgWrapper *msgwrap; MuMsgFieldId mfid; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_integer_p(FIELD), FIELD, SCM_ARG2, FUNC_NAME); mfid = scm_to_int (FIELD); SCM_ASSERT (mfid < MU_MSG_FIELD_ID_NUM || mfid == MU_GUILE_MSG_FIELD_ID_TIMESTAMP, FIELD, SCM_ARG2, FUNC_NAME); switch (mfid) { case MU_MSG_FIELD_ID_PRIO: return get_prio_scm (msgwrap->_msg); case MU_MSG_FIELD_ID_FLAGS: return get_flags_scm (msgwrap->_msg); case MU_MSG_FIELD_ID_BODY_HTML: return get_body (msgwrap->_msg, TRUE); case MU_MSG_FIELD_ID_BODY_TEXT: return get_body (msgwrap->_msg, FALSE); /* our pseudo-field; we get it from the message file */ case MU_GUILE_MSG_FIELD_ID_TIMESTAMP: return scm_from_uint ( (unsigned)mu_msg_get_timestamp(msgwrap->_msg)); default: break; } switch (mu_msg_field_type (mfid)) { case MU_MSG_FIELD_TYPE_STRING: return mu_guile_scm_from_str (mu_msg_get_field_string(msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_BYTESIZE: case MU_MSG_FIELD_TYPE_TIME_T: return scm_from_uint ( mu_msg_get_field_numeric (msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_INT: return scm_from_int ( mu_msg_get_field_numeric (msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_STRING_LIST: return msg_string_list_field (msgwrap->_msg, mfid); default: SCM_ASSERT (0, FIELD, SCM_ARG2, FUNC_NAME); } } #undef FUNC_NAME struct _EachContactData { SCM lst; MuMsgContactType ctype; }; typedef struct _EachContactData EachContactData; static void contacts_to_list (MuMsgContact *contact, EachContactData *ecdata) { SCM item; if (ecdata->ctype != MU_MSG_CONTACT_TYPE_ALL && mu_msg_contact_type (contact) != ecdata->ctype) return; item = scm_list_1 (scm_cons (mu_guile_scm_from_str(mu_msg_contact_name (contact)), mu_guile_scm_from_str(mu_msg_contact_address (contact)))); ecdata->lst = scm_append_x (scm_list_2(ecdata->lst, item)); } SCM_DEFINE (get_contacts, "mu:c:get-contacts", 2, 0, 0, (SCM MSG, SCM CONTACT_TYPE), "Get a list of contact information pairs.\n") #define FUNC_NAME s_get_contacts { MuMsgWrapper *msgwrap; EachContactData ecdata; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_symbol_p (CONTACT_TYPE) || scm_is_bool(CONTACT_TYPE), CONTACT_TYPE, SCM_ARG2, FUNC_NAME); if (CONTACT_TYPE == SCM_BOOL_F) return SCM_UNSPECIFIED; /* nothing to do */ else if (CONTACT_TYPE == SCM_BOOL_T) ecdata.ctype = MU_MSG_CONTACT_TYPE_ALL; else { if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_TO)) ecdata.ctype = MU_MSG_CONTACT_TYPE_TO; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_CC)) ecdata.ctype = MU_MSG_CONTACT_TYPE_CC; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_BCC)) ecdata.ctype = MU_MSG_CONTACT_TYPE_BCC; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_FROM)) ecdata.ctype = MU_MSG_CONTACT_TYPE_FROM; else return mu_guile_error (FUNC_NAME, 0, "invalid contact type", SCM_UNDEFINED); } ecdata.lst = SCM_EOL; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); mu_msg_contact_foreach (msgwrap->_msg, (MuMsgContactForeachFunc)contacts_to_list, &ecdata); /* explicitly close the file backend, so we won't run out of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return ecdata.lst; } #undef FUNC_NAME struct _AttInfo { SCM attlist; gboolean attachments_only; }; typedef struct _AttInfo AttInfo; static void each_part (MuMsg *msg, MuMsgPart *part, AttInfo *attinfo) { char *mime_type, *filename; SCM elm; if (!part->type) return; if (attinfo->attachments_only && !mu_msg_part_maybe_attachment (part)) return; mime_type = g_strdup_printf ("%s/%s", part->type, part->subtype); filename = mu_msg_part_get_filename (part, FALSE); elm = scm_list_5 ( /* msg */ mu_guile_scm_from_str (mu_msg_get_path(msg)), /* index */ scm_from_uint(part->index), /* filename or #f */ filename ? mu_guile_scm_from_str (filename) : SCM_BOOL_F, /* mime-type */ mime_type ? mu_guile_scm_from_str (mime_type): SCM_BOOL_F, /* size */ part->size > 0 ? scm_from_uint (part->size) : SCM_BOOL_F); g_free (mime_type); g_free (filename); attinfo->attlist = scm_cons (elm, attinfo->attlist); } SCM_DEFINE (get_parts, "mu:c:get-parts", 1, 1, 0, (SCM MSG, SCM ATTS_ONLY), "Get the list of mime-parts for MSG. If ATTS_ONLY is #t, only" "get parts that are (look like) attachments. The resulting list has " "elements which are list of the form (index name mime-type size).\n") #define FUNC_NAME s_get_parts { MuMsgWrapper *msgwrap; AttInfo attinfo; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_bool(ATTS_ONLY), ATTS_ONLY, SCM_ARG2, FUNC_NAME); attinfo.attlist = SCM_EOL; /* empty list */ attinfo.attachments_only = ATTS_ONLY == SCM_BOOL_T ? TRUE : FALSE; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); mu_msg_part_foreach (msgwrap->_msg, MU_MSG_OPTION_NONE, (MuMsgPartForeachFunc)each_part, &attinfo); /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return attinfo.attlist; } #undef FUNC_NAME SCM_DEFINE (get_header, "mu:c:get-header", 2, 0, 0, (SCM MSG, SCM HEADER), "Get an arbitary HEADER from MSG.\n") #define FUNC_NAME s_get_header { MuMsgWrapper *msgwrap; char *header; SCM val; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_string (HEADER)||HEADER==SCM_UNDEFINED, HEADER, SCM_ARG2, FUNC_NAME); msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); header = scm_to_utf8_string (HEADER); val = mu_guile_scm_from_str (mu_msg_get_header(msgwrap->_msg, header)); free (header); /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return val; } #undef FUNC_NAME static void call_func (SCM FUNC, MuMsgIter *iter, const char* func_name) { SCM msgsmob; MuMsg *msg; msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */ msgsmob = mu_guile_msg_to_scm (mu_msg_ref(msg)); scm_call_1 (FUNC, msgsmob); } static MuMsgIter* get_query_iter (MuQuery *query, const char* expr, int maxnum) { MuMsgIter *iter; GError *err; err = NULL; iter = mu_query_run (query, expr, MU_MSG_FIELD_ID_NONE, maxnum, MU_QUERY_FLAG_NONE, &err); if (!iter) { mu_guile_g_error ("", err); g_clear_error (&err); } return iter; } SCM_DEFINE (for_each_message, "mu:c:for-each-message", 3, 0, 0, (SCM FUNC, SCM EXPR, SCM MAXNUM), "Call FUNC for each msg in the message store matching EXPR. EXPR is" "either a string containing a mu search expression or a boolean; in the former " "case, limit the messages to only those matching the expression, in the " "latter case, match /all/ messages if the EXPR equals #t, and match " "none if EXPR equals #f.") #define FUNC_NAME s_for_each_message { MuMsgIter *iter; char* expr; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (scm_procedure_p (FUNC), FUNC, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_bool(EXPR) || scm_is_string (EXPR), EXPR, SCM_ARG2, FUNC_NAME); SCM_ASSERT (scm_is_integer (MAXNUM), MAXNUM, SCM_ARG3, FUNC_NAME); if (EXPR == SCM_BOOL_F) return SCM_UNSPECIFIED; /* nothing to do */ if (EXPR == SCM_BOOL_T) expr = strdup (""); /* note, "" matches *all* messages */ else expr = scm_to_utf8_string(EXPR); iter = get_query_iter (mu_guile_instance()->query, expr, scm_to_int(MAXNUM)); free (expr); if (!iter) return SCM_UNSPECIFIED; while (!mu_msg_iter_is_done(iter)) { call_func (FUNC, iter, FUNC_NAME); mu_msg_iter_next (iter); } mu_msg_iter_destroy (iter); return SCM_UNSPECIFIED; } #undef FUNC_NAME static SCM register_symbol (const char *name) { SCM scm; scm = scm_from_utf8_symbol (name); scm_c_define (name, scm); scm_c_export (name, NULL); return scm; } static void define_symbols (void) { SYMB_CONTACT_TO = register_symbol ("mu:contact:to"); SYMB_CONTACT_CC = register_symbol ("mu:contact:cc"); SYMB_CONTACT_FROM = register_symbol ("mu:contact:from"); SYMB_CONTACT_BCC = register_symbol ("mu:contact:bcc"); SYMB_PRIO_LOW = register_symbol ("mu:prio:low"); SYMB_PRIO_NORMAL = register_symbol ("mu:prio:normal"); SYMB_PRIO_HIGH = register_symbol ("mu:prio:high"); SYMB_FLAG_NEW = register_symbol ("mu:flag:new"); SYMB_FLAG_PASSED = register_symbol ("mu:flag:passed"); SYMB_FLAG_REPLIED = register_symbol ("mu:flag:replied"); SYMB_FLAG_SEEN = register_symbol ("mu:flag:seen"); SYMB_FLAG_TRASHED = register_symbol ("mu:flag:trashed"); SYMB_FLAG_DRAFT = register_symbol ("mu:flag:draft"); SYMB_FLAG_FLAGGED = register_symbol ("mu:flag:flagged"); SYMB_FLAG_SIGNED = register_symbol ("mu:flag:signed"); SYMB_FLAG_ENCRYPTED = register_symbol ("mu:flag:encrypted"); SYMB_FLAG_HAS_ATTACH = register_symbol ("mu:flag:has-attach"); SYMB_FLAG_UNREAD = register_symbol ("mu:flag:unread"); } static struct { const char* name; unsigned val; } VAR_PAIRS[] = { { "mu:field:bcc", MU_MSG_FIELD_ID_BCC }, { "mu:field:body-html", MU_MSG_FIELD_ID_BODY_HTML }, { "mu:field:body-txt", MU_MSG_FIELD_ID_BODY_TEXT }, { "mu:field:cc", MU_MSG_FIELD_ID_CC }, { "mu:field:date", MU_MSG_FIELD_ID_DATE }, { "mu:field:flags", MU_MSG_FIELD_ID_FLAGS }, { "mu:field:from", MU_MSG_FIELD_ID_FROM }, { "mu:field:maildir", MU_MSG_FIELD_ID_MAILDIR }, { "mu:field:message-id",MU_MSG_FIELD_ID_MSGID }, { "mu:field:path", MU_MSG_FIELD_ID_PATH }, { "mu:field:prio", MU_MSG_FIELD_ID_PRIO }, { "mu:field:refs", MU_MSG_FIELD_ID_REFS }, { "mu:field:size", MU_MSG_FIELD_ID_SIZE }, { "mu:field:subject", MU_MSG_FIELD_ID_SUBJECT }, { "mu:field:tags", MU_MSG_FIELD_ID_TAGS }, { "mu:field:to", MU_MSG_FIELD_ID_TO }, /* non-Xapian field: timestamp */ { "mu:field:timestamp", MU_GUILE_MSG_FIELD_ID_TIMESTAMP } }; static void define_vars (void) { unsigned u; for (u = 0; u != G_N_ELEMENTS(VAR_PAIRS); ++u) { scm_c_define (VAR_PAIRS[u].name, scm_from_uint (VAR_PAIRS[u].val)); scm_c_export (VAR_PAIRS[u].name, NULL); } } static SCM msg_mark (SCM msg_smob) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); msgwrap->_unrefme = TRUE; return SCM_UNSPECIFIED; } static size_t msg_free (SCM msg_smob) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); if (msgwrap->_unrefme) mu_msg_unref (msgwrap->_msg); return sizeof (MuMsgWrapper); } static int msg_print (SCM msg_smob, SCM port, scm_print_state * pstate) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); scm_puts ("#_msg), port); scm_puts (">", port); return 1; } void* mu_guile_message_init (void *data) { MSG_TAG = scm_make_smob_type ("msg", sizeof(MuMsgWrapper)); scm_set_smob_mark (MSG_TAG, msg_mark); scm_set_smob_free (MSG_TAG, msg_free); scm_set_smob_print (MSG_TAG, msg_print); define_vars (); define_symbols (); #ifndef SCM_MAGIC_SNARFER #include "mu-guile-message.x" #endif /*SCM_MAGIC_SNARFER*/ return NULL; } mu-0.9.12/guile/mu-guile-message.h000066400000000000000000000020321250406103100166350ustar00rootroot00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_GUILE_MESSAGE_H__ #define __MU_GUILE_MESSAGE_H__ #include G_BEGIN_DECLS /** * Initialize this mu guile module. * * @param data * * @return */ void* mu_guile_message_init (void *data); G_END_DECLS #endif /*__MU_GUILE_MESSAGE_H__*/ mu-0.9.12/guile/mu-guile.c000066400000000000000000000127741250406103100152240ustar00rootroot00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include #include #include #include "mu-guile.h" SCM mu_guile_scm_from_str (const char *str) { if (!str) return SCM_BOOL_F; else return scm_from_stringn (str, strlen(str), "UTF-8", SCM_FAILED_CONVERSION_QUESTION_MARK); } SCM mu_guile_error (const char *func_name, int status, const char *fmt, SCM args) { scm_error_scm (scm_from_locale_symbol ("MuError"), scm_from_utf8_string (func_name ? func_name : ""), scm_from_utf8_string (fmt), args, scm_list_1 (scm_from_int (status))); return SCM_UNSPECIFIED; } SCM mu_guile_g_error (const char *func_name, GError *err) { scm_error_scm (scm_from_locale_symbol ("MuError"), scm_from_utf8_string (func_name), scm_from_utf8_string (err ? err->message : "error"), SCM_UNDEFINED, SCM_UNDEFINED); return SCM_UNSPECIFIED; } /* there can be only one */ static MuGuile *_singleton = NULL; static gboolean mu_guile_init_instance (const char *muhome) { MuStore *store; MuQuery *query; GError *err; setlocale (LC_ALL, ""); if (!mu_runtime_init (muhome, "guile")) return FALSE; err = NULL; store = mu_store_new_read_only (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), &err); if (!store) goto errexit; query = mu_query_new (store, &err); mu_store_unref (store); if (!query) goto errexit; _singleton = g_new0 (MuGuile, 1); _singleton->query = query; return TRUE; errexit: mu_guile_g_error (__FUNCTION__, err); g_clear_error (&err); return FALSE; } static void mu_guile_uninit_instance (void) { g_return_if_fail (_singleton); mu_query_destroy (_singleton->query); g_free (_singleton); _singleton = NULL; mu_runtime_uninit (); } MuGuile* mu_guile_instance (void) { g_return_val_if_fail (_singleton, NULL); return _singleton; } gboolean mu_guile_initialized (void) { return _singleton != NULL; } SCM_DEFINE_PUBLIC (mu_initialize, "mu:initialize", 0, 1, 0, (SCM MUHOME), "Initialize mu - needed before you call any of the other " "functions. Optionally, you can provide MUHOME which should be an " "absolute path to your mu home directory " "-- typically, the default, ~/.mu, should be just fine.") #define FUNC_NAME s_mu_initialize { char *muhome; gboolean rv; SCM_ASSERT (scm_is_string (MUHOME) || MUHOME == SCM_BOOL_F || SCM_UNBNDP(MUHOME), MUHOME, SCM_ARG1, FUNC_NAME); if (mu_guile_initialized()) return mu_guile_error (FUNC_NAME, 0, "Already initialized", SCM_UNSPECIFIED); if (SCM_UNBNDP(MUHOME) || MUHOME == SCM_BOOL_F) muhome = NULL; else muhome = scm_to_utf8_string (MUHOME); rv = mu_guile_init_instance (muhome); free (muhome); if (!rv) return mu_guile_error (FUNC_NAME, 0, "Failed to initialize mu", SCM_UNSPECIFIED); /* cleanup when we're exiting */ atexit (mu_guile_uninit_instance); return SCM_UNSPECIFIED; } #undef FUNC_NAME SCM_DEFINE_PUBLIC (mu_initialized_p, "mu:initialized?", 0, 0, 0, (void), "Whether mu is initialized or not.\n") #define FUNC_NAME s_mu_initialized_p { return mu_guile_initialized() ? SCM_BOOL_T : SCM_BOOL_F; } #undef FUNC_NAME SCM_DEFINE (log_func, "mu:c:log", 1, 0, 1, (SCM LEVEL, SCM FRM, SCM ARGS), "log some message at LEVEL using a list of ARGS applied to FRM" "(in 'simple-format' notation).\n") #define FUNC_NAME s_log_func { gchar *output; SCM str; int level; SCM_ASSERT (scm_integer_p(LEVEL), LEVEL, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_string(FRM), FRM, SCM_ARG2, ""); SCM_VALIDATE_REST_ARGUMENT(ARGS); level = scm_to_int (LEVEL); if (level != G_LOG_LEVEL_MESSAGE && level != G_LOG_LEVEL_WARNING && level != G_LOG_LEVEL_CRITICAL) return mu_guile_error (FUNC_NAME, 0, "invalid log level", SCM_UNSPECIFIED); str = scm_simple_format (SCM_BOOL_F, FRM, ARGS); if (!scm_is_string (str)) return SCM_UNSPECIFIED; output = scm_to_utf8_string (str); g_log (G_LOG_DOMAIN, level, "%s", output); free (output); return SCM_UNSPECIFIED; } #undef FUNC_NAME static struct { const char* name; unsigned val; } VAR_PAIRS[] = { { "mu:message", G_LOG_LEVEL_MESSAGE }, { "mu:warning", G_LOG_LEVEL_WARNING }, { "mu:critical", G_LOG_LEVEL_CRITICAL } }; static void define_vars (void) { unsigned u; for (u = 0; u != G_N_ELEMENTS(VAR_PAIRS); ++u) { scm_c_define (VAR_PAIRS[u].name, scm_from_uint (VAR_PAIRS[u].val)); scm_c_export (VAR_PAIRS[u].name, NULL); } } void* mu_guile_init (void *data) { define_vars (); #ifndef SCM_MAGIC_SNARFER #include "mu-guile.x" #endif /*SCM_MAGIC_SNARFER*/ return NULL; } mu-0.9.12/guile/mu-guile.h000066400000000000000000000042021250406103100152140ustar00rootroot00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_GUILE_H__ #define __MU_GUILE_H__ #include #include G_BEGIN_DECLS struct _MuGuile { MuQuery *query; }; typedef struct _MuGuile MuGuile; /** * get the single MuGuile instance * * @return the instance or NULL in case of error */ MuGuile *mu_guile_instance (void); /** * whether mu-guile is initialized * * @return TRUE if MuGuile is Initialized, FALSE otherwise */ gboolean mu_guile_initialized (void); /** * raise a guile error (based on a GError) * * @param func_name function name * @param err the error * * @return SCM_UNSPECIFIED */ SCM mu_guile_g_error (const char *func_name, GError *err); /** * raise a guile error * * @param func_name function * @param status err code * @param fmt format string for error msg * @param args params for format string * * @return SCM_UNSPECIFIED */ SCM mu_guile_error (const char *func_name, int status, const char *fmt, SCM args); /** * convert a const char* into an SCM -- either a string or, if str == * NULL, #f. It assumes str is in UTF8 encoding, and replace * characters with '?' if needed. * * @param str a string or NULL * * @return a guile string or #f */ SCM mu_guile_scm_from_str (const char *str); /** * Initialize this mu guile module. * * @param data * * @return */ void* mu_guile_init (void *data); G_END_DECLS #endif /*__MU_GUILE_H__*/ mu-0.9.12/guile/mu-guile.texi000066400000000000000000001004021250406103100157350ustar00rootroot00000000000000\input texinfo.tex @c -*-texinfo-*- @c %**start of header @setfilename mu-guile.info @settitle mu-guile user manual @c Use proper quote and backtick for code sections in PDF output @c Cf. Texinfo manual 14.2 @set txicodequoteundirected @set txicodequotebacktick @documentencoding UTF-8 @c %**end of header @include texi.texi @copying Copyright @copyright{} 2012 Dirk-Jan C. Binnema @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License.'' @end quotation @end copying @titlepage @title @t{mu-guile} - extending @t{mu} with Guile Scheme @subtitle version @value{mu-version} @author Dirk-Jan C. Binnema @c The following two commands start the copyright page. @page @vskip 0pt plus 1filll @insertcopying @end titlepage @dircategory The Algorithmic Language Scheme @direntry * Mu-guile: (mu-guile). Guile-bindings for the mu e-mail indexer/searcher @end direntry @contents @ifnottex @node Top @top mu-guile manual @end ifnottex @iftex @node Welcome to mu-guile @unnumbered Welcome to mu-guile @end iftex Welcome to @t{mu-guile}! @t{mu} is a program for indexing and searching your e-mails. It can search your messages in many different ways, but sometimes that may not be enough. If you have very specific queries, or want do generate some statistics, you need some more power. @t{mu-guile} is made for those cases. @t{mu-guile} exposes the internals of @t{mu} and its database to the @t{guile} programming language. Guile is the @emph{GNU Ubiquitous Intelligent Language for Extensions} - a version of the @emph{Scheme} programming language and the official GNU extension language. Guile/Scheme is a member of the @emph{Lisp} family of programming languages -- like emacs-lisp, @emph{Racket}, Common Lisp. If you're not familiar with Scheme, @t{mu-guile} is an excellent opportunity to learn a bit about! Trust me, it's not very hard -- and it it's @emph{fun}! @menu * Getting started:: * Initializing mu-guile:: * Messages:: * Contacts:: * Attachments and other parts:: * Statistics:: * Plotting data:: * Writing scripts:: Appendices * Recipies:: Snippets do specific things * GNU Free Documentation License:: The license of this manual. @end menu @node Getting started @chapter Getting started @menu * Installation:: * Making sure it works:: @end menu This chapter walks you through the installation and the basic setup. @node Installation @section Installation @t{mu-guile} is part of @t{mu} - by installing the latter, the former is necessarily installed as well. At the time of writing, there are no distribution-provided packaged versions of @t{mu-guile}; so for now, you need to follow the steps below. @subsection Guile 2.x @t{mu-guile} is built automatically when @t{mu} is built, if you have @t{guile} version 2 or higher. (@t{mu} checks for this during @t{configure}). Thus, the first step is to ensure you have @t{guile} installed. On Debian/Ubuntu you can install @t{guile} 2.x using the @t{guile-2.0-dev} package (and its dependencies): @example $ sudo apt-get install guile-2.0-dev @end example At the time of writing, there are no official packages for Fedora@footnote{@url{https://bugzilla.redhat.com/show_bug.cgi?id=678238}}. If you are using Fedora or any other system that does not have packages, you need to compile @t{guile} from source@footnote{@url{http://www.gnu.org/software/guile/manual/html_node/Obtaining-and-Installing-Guile.html#Obtaining-and-Installing-Guile}}. @subsection gnuplot For creating graphs with @t{mu-guile}, you need the @t{gnuplot} program -- most likely, there is a package available for your system; for example: @example $ sudo apt-get install gnuplot @end example and in Fedora: @example $ sudo yum install gnuplot @end example @subsection mu Assuming @t{guile} 2.x is installed correctly, @t{mu} finds it during its @t{configure}-stage, and creates @t{mu-guile}. Building @t{mu} follows the normal steps -- please see the @t{mu} documentation for the details. The output of @t{./configure} should end with a little text describing the detected versions of various libraries @t{mu} depends on. In particular, it should mention the @t{guile} version, e.g. @example Guile version : 2.0.3.82-a2c66 @end example If you don't see any line referring to @t{guile}, please install it, and run @t{configure} again. After a succesfull @t{./configure}, we can make and install the package: @example $ make && sudo make install @end example @subsection mu-guile After this, @t{mu} and @t{mu-guile} are installed -- usually somewhere under @t{/usr/local}.You may need to update @t{guile}'s @code{%load-path} to find it there. You can check the current @code{%load-path} with the following: @example guile -c '(display %load-path)(newline)' @end example If necessary, you can add the @t{%load-path} by adding to your @file{~/.guile}: @lisp (set! %load-path (cons "/usr/local/share/guile/site/2.0" %load-path)) @end lisp Or, alternatively, you can set @t{GUILE_LOAD_PATH}: @example export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.0" @end example In both cases the directory should be the directory that contains the installed @t{mu.scm}; if you installed @t{mu} under a different prefix, you must change the @code{%load-path} accordingly. After this, you should be ready to go! @node Making sure it works @section Making sure it works Assuming @t{mu-guile} has been installed correctly (@ref{Installation}), and also assuming that you have already indexed your e-mail messages (if necessary, see the @t{mu-index} man-page), we are ready to start @t{mu-guile}; a session may look something like this: @cartouche @verbatim GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> @end verbatim @end cartouche @noindent Now, copy-paste the following after the prompt: @cartouche @lisp (use-modules (mu)) (mu:initialize) (for-each (lambda(msg) (format #t "Subject: ~a\n" (mu:subject msg))) (mu:message-list "hello")) @end lisp @end cartouche @noindent After pressing @key{Enter}, you should get a list of all subjects of messages that match @t{hello}: @verbatim ... Subject: RE: The Bird Serpent War Cataclysm Subject: Hello! Subject: Re: post-run tomorrow Subject: When all is lost ... @end verbatim @noindent If all this works, congratulations! @t{mu-guile} is installed now, ready to serve your every searching need! @node Initializing mu-guile @chapter Initializing mu-guile We now have installed @t{mu-guile}, and in @ref{Making sure it works} confirmed that things work by trying some simple script. In this and the following chapters, we take a closer look at programming with @t{mu-guile}. It is possible to write separate programs with @t{mu-guile}, but for now we'll do things @emph{interactively}, that is, from the Guile-prompt (``@abbr{REPL}''). As we have seen, we start our @t{mu-guile} session by starting @t{guile}: @verbatim $ guile @end verbatim @cartouche @verbatim GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> @end verbatim @end cartouche The first thing we need to do is loading the modules. All the basics are in the @t{(mu)} module, with some statistical extras in @t{(mu stats)}, and some graph plotting functionality in @t{(mu plot)}@footnote{@code{(mu plot)} requires the @t{gnuplot} program}. Let's load all of them: @verbatim scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot)) @end verbatim The first time you do this, @t{guile} will probably respond by showing some messages about compiling the modules, and then return to you with another prompt. Before we can do anything with @t{mu guile}, we need to initialize the system: @verbatim scheme@(guile-user)> (mu:initialize) @end verbatim This opens the database for reading, using the default location of @file{~/.mu}@footnote{If you keep your @t{mu} database in a non-standard place, use @code{(mu:initialize "/path/to/my/mu/")}} Now, @t{mu-guile} is ready to go. In the next chapter, we go through the modules and show what you can do with them. @node Messages @chapter Messages In this chapter, we discuss searching messages and doing things with them. @menu * Finding messages:: query for messages in the databse * Message methods:: what methods are available for messages? * Example - the longest subject:: find the messages with the longest subject @end menu @node Finding messages @section Finding messages Now we are ready to retrieve some messages from the system. There are two main procedures to do this: @itemize @item @code{(mu:message-list [])} @item @code{(mu:for-each-message [])} @end itemize @noindent The first procedure, @code{mu:message-list} returns a list of all messages matching @t{}; if you leave @t{} out, it returns @emph{all} messages. For example, to get all messages with @t{coffee} in the subject line: @verbatim scheme@(guile-user)> (mu:message-list "subject:coffee") $1 = (#< 9040640> #< 9040630> #< 9040570>) @end verbatim @noindent Apparently, we have three messages matching @t{subject:coffee}, so we get a list of three @code{} objects. Let's just use the @code{mu:subject} procedure ('method') provided by @code{} objects to retrieve the subject-field (more about methods in the next section). For your convenience, @t{guile} has saved the result of our last query in a variable called @t{$1}, so to get the subject of the first message in the list, we can do: @verbatim scheme@(guile-user)> (mu:subject (car $1)) $2 = "Re: best coffee ever!" @end verbatim @noindent The second procedure we mentioned, @code{mu:for-each-message}, executes some procedure for each message matched by the search expression (or @emph{all} messages if the search expression is omitted): @verbatim scheme@(guile-user)> (mu:for-each-message (lambda(msg) (display (mu:subject msg)) (newline)) "subject:coffee") Re: best coffee ever! best coffee ever! Coffee beans scheme@(guile-user)> @end verbatim @noindent Using @code{mu:message-list} and/or @code{mu:for-each-message}@footnote{Implementation node: @code{mu:message-list} is implemented in terms of @code{mu:for-each-message}, not the other way around. Due to the way @t{mu} works, @code{mu:for-each-message} is rather more efficient than a combination of @code{for-each} and @code{mu:message-list}} and a couple of @t{} methods, together with what Guile/Scheme provides, should allow for many interesting programs. @node Message methods @section Message methods Now that we've seen how to retrieve lists of message objects (@code{}), let's see what we can do with such an object. @code{} defines the following methods that all take a single @code{} object as a parameter. We won't go into the exact meanings for all of these procedures here - for the details about various flags / properties, please refer to the @t{mu-find} man-page. @itemize @item @code{(mu:bcc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none @item @code{(mu:body-html msg)}: : the html body of the message, or @t{#f} if there is none @item @code{(mu:body-txt msg)}: the plain-text body of the message, or @t{#f} if there is none @item @code{(mu:cc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none @item @code{(mu:date msg)}: the @t{Date} field of the message, or 0 if there is none @item @code{(mu:flags msg)}: list of message-flags for this message @item @code{(mu:from msg)}: the @t{From} field of the message, or @t{#f} if there is none @item @code{(mu:maildir msg)}: the maildir this message lives in, or @t{#f} if there is none @item @code{(mu:message-id msg)}: the @t{Message-Id} field of the message, or @t{#f} if there is none @item @code{(mu:path msg)}: the file system path for this message @item @code{(mu:priority msg)}: the priority of this message (either @t{mu:prio:low}, @t{mu:prio:normal} or @t{mu:prio:high} @item @code{(mu:references msg)}: the list of messages (message-ids) this message refers to in(mu: the @t{References:} header @item @code{(mu:size msg)}: size of the message in bytes @item @code{(mu:subject msg)}: the @t{Subject} field of the message, or @t{#f} if there is none. @item @code{(mu:tags msg)}: list of tags for this message @item @code{(mu:timestamp msg)}: the timestamp (mtime) of the message file, or #f if there is none. message file @item @code{(mu:to msg)}: the sender of the message, or @t{#f} if there is none @end itemize With these methods, we can query messages for their properties; for example: @verbatim scheme@(guile-user)> (define msg (car (mu:message-list "snow"))) scheme@(guile-user)> (mu:subject msg) $1 = "Re: Running in the snow is beautiful" scheme@(guile-user)> (mu:flags msg) $2 = (mu:flag:replied mu:flag:seen) scheme@(guile-user)> (strftime "%F" (localtime (mu:date msg))) $3 = "2011-01-15" @end verbatim There are a couple more methods: @itemize @item @code{(mu:header msg "")} returns an arbitrary message header (or @t{#f} if not found) -- e.g. @code{(header msg "User-Agent")} @item If you include the @t{mu contact} module, the @code{(mu:contacts msg [contact-type])} method (to get a list of contacts) is added. @xref{Contacts}. @item If you include the @t{mu part} module, the @code{((mu:parts msg)} and @code{(mu:attachments msg)} methods are added. @xref{Attachments and other parts}. @end itemize @node Example - the longest subject @section Example - the longest subject Now, let's write a little example -- let's find out what is the @emph{longest subject} of any e-mail messages we received in the year 2011. You can try this if you put the following in a separate file, make it executable, and run it like any program. @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (use-modules (srfi srfi-1)) (mu:initialize) ;; note: (subject msg) => #f if there is no subject (define list-of-subjects (map (lambda (msg) (or (mu:subject msg) "")) (mu:message-list "date:2011..2011"))) ;; see the mu-find manpage for the date syntax (define longest-subject (fold (lambda (subj1 subj2) (if (> (string-length subj1) (string-length subj2)) subj1 subj2)) "" list-of-subjects)) (format #t "Longest subject: ~s\n" longest-subject) @end lisp There are many other ways to solve the same problem, for example by using an iterative approach with @code{mu:for-each-message}, but it should show how one can easily write little programs to answer specific questions about your e-mail corpus. @node Contacts @chapter Contacts We can retrieve the sender and recipients of an e-mail message using methods like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These procedures return the list of recipients as a single string; however, often it is more useful to deal with recipients as separate objects. @menu * Contact procedures and objects:: * All contacts:: * Utility procedures:: * Example - mutt export:: @end menu @node Contact procedures and objects @section Contact procedures and objects Message objects (@pxref{Messages}) have a method @t{mu:contacts}: @code{(mu:contacts [])} The @t{} is a symbol, one of @code{mu:to}, @code{mu:from}, @code{mu:cc} or @code{mu:bcc}. This will then get the contact objects for the contacts of the corresponding type. If you leave out the contact-type (or specify @t{#t} for it, you will get a list of @emph{all} contact objects for the message. A contact object (@code{}) has two methods: @itemize @item @code{mu:name} returns the name of the contact, or #f if there is none @item @code{mu:email} returns the e-mail address of the contact, or #f if there is none @end itemize Let's get a list of all names and e-mail addresses in the 'To:' field, of messages matching 'book': @lisp (use-modules (mu)) (mu:initialize) (mu:for-each-message (lambda (msg) (for-each (lambda (contact) (format #t "~a => ~a\n" (or (mu:email contact) "") (or (mu:name contact) "no-name"))) (mu:contacts msg mu:field:to))) "book") @end lisp This shows what the methods do, but for many uses, it would be more useful to have each of the contacts only show up @emph{once} - for that, please refer to @xref{All contacts}. @node All contacts @section All contacts Sometimes you may want to inspect @emph{all} the different contacts in the @t{mu} database. This is useful, for instance, when exporting contacts to some external format that can then be important in an e-mail program. To enable this, there is the procedure @code{mu:for-each-contact}, defined as @code{(mu:for-each-contact procedure [search-expression])}. This will aggregate the unique contacts from @emph{all} messages matching @t{} (when it is left empty, it will match all messages in the database), and execute @t{procedure} for each of them. The @t{procedure} receives an object of the type @t{}, which is a @emph{subclass} of the @t{} class discussed in @xref{Contact procedures and objects}. @t{} objects expose the following additional methods: @itemize @item @code{(mu:frequency )}: returns the @emph{number of times} this contact occured in one of the address fields @item @code{(mu:last-seen )}: returns the @emph{most recent time} the contact was seen in one of the address fields, as a @t{time_t} value @end itemize The method assumes an e-mail address is unique for a certain contact; if a certain e-mail address occurs with different names, it uses the most recent non-empty name. @node Utility procedures @section Utility procedures To make dealing with contacts even easier, there are a number of utility procedures that can save you a bit of typing. For converting contacts to some textual form, there is @code{(mu:contact->string format)}, which takes a contact and returns a text string with the given format. Currently supported formats are @t{"org-contact}, @t{"mutt-alias"}, @t{"mutt-ab"}, @t{"wanderlust"} and @t{"plain"}. @node Example - mutt export @section Example - mutt export Let's see how we could export the addresses in the @t{mu} database to the addressbook format of the venerable @t{mutt}@footnote{@url{http://www.mutt.org/}} e-mail client. The addressbook format that @t{mutt} uses is a sequence of lines that look something like: @verbatim alias [] "<" ">" @end verbatim @t{mu guile} provides the procedure @code{(mu:contact->string format)} that we can use to do the conversion. We may want to focus on people with whom we have frequent correspondence; so we may want to limit ourselves to people we have seen at least 10 times in the last year. It is a bit hard to @emph{guess} the nick name for e-mail contacts, but @code{mu:contact->string} tries something based on the name. You can always adjust them later by hand, obviously. @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (mu:initialize) ;; Get a list of contacts that were seen at least 20 times since 2010 (define (selected-contacts) (let ((addrs '()) (start (car (mktime (car (strptime "%F" "2010-01-01"))))) (minfreq 20)) (mu:for-each-contact (lambda (contact) (if (and (mu:email contact) (>= (mu:frequency contact) minfreq) (>= (mu:last-seen contact) start)) (set! addrs (cons contact addrs))))) addrs)) (for-each (lambda (contact) (format #t "~a\n" (mu:contact->string contact "mutt-alias"))) (selected-contacts)) @end lisp This simple program could be improved in many ways; this is left as an excercise to the reader. @node Attachments and other parts @chapter Attachments and other parts To deal with @emph{attachments}, or, more in general @emph{MIME-parts}, there is the @t{mu part} module. @menu * Parts and their methods:: * Attachment example:: @end menu @node Parts and their methods @section Parts and their methods The module defines the @code{} class, and adds two methods to @code{} objects: @itemize @item @code{(mu:parts msg)} - returns a list @code{} objects, one for each MIME-parts in the message. @item @code{(mu:attachments msg)} - like @code{parts}, but only list those MIME-parts that look like proper attachments. @end itemize A @code{} object exposes a few methods to get information about the part: @itemize @item @code{(mu:name )} - returns the file name of the mime-part, or @code{#f} if there is none. @item @code{(mu:mime-type )} - returns the mime-type of the mime-part, or @code{#f} if there is none. @item @code{(mu:size )} - returns the size in bytes of the mime-part @end itemize Then, we may want to save the part to a file; this can be done using either: @itemize @item @code{(mu:save part )} - save a part to a temporary file, return the file name@footnote{the temporary filename is a predictable procedure of (user-id, msg-path, part-index)} @item @code{(mu:save-as )} - save part to file at path @end itemize @node Attachment example @section Attachment example Let's look at some small example. Let's get a list of the biggest attachments in messages about Luxemburg: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (mu:initialize) (define (all-attachments expr) "Return a list of (name . size) for all attachments in messages matching EXPR." (let ((pairs '())) (mu:for-each-message (lambda (msg) (for-each (lambda (att) ;; add (filename . size) to the list (set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs))) (mu:attachments msg))) expr) pairs)) (for-each (lambda (att) (format #t "~a: ~,1fKb\n" (car att) (exact->inexact (/ (cdr att) 1024)))) (sort (all-attachments "Luxemburg") (lambda (att1 att2) (< (cdr att1) (cdr att2))))) @end lisp As an exercise for the reader, you might want to re-rewrite the @code{all-attachments} in terms of @code{mu:message-list}, which would probably be a bit more elegant. @node Statistics @chapter Statistics @t{mu-guile} offers some convenience procedures to determine various statistics about the messages in the database. @menu * Basics:: @code{mu:count}, @code{mu:average}, ... * Tabulating values:: @code{mu:tabulate} * Most frequent values:: @code{mu:top-n-most-frequent} @end menu @node Basics @section Basics Let's look at some of the basic statistical operations available, in an interactive session: @example GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@@(guile-user)> ;; load modules, initialize mu scheme@@(guile-user)> (use-modules (mu) (mu stats)) scheme@@(guile-user)> (mu:initialize) scheme@@(guile-user)> scheme@@(guile-user)> ;; count the number of messages with 'hello' in their subject scheme@@(guile-user)> (mu:count "subject:hello") $1 = 162 scheme@@(guile-user)> ;; average the size of messages with hello in their subject scheme@@(guile-user)> (mu:average mu:size "subject:hello") $2 = 34597733/81 scheme@@(guile-user)> (exact->inexact $2) $3 = 427132.506172839 scheme@@(guile-user)> ;; calculate the correlation between message size and scheme@@(guile-user)> ;; subject length scheme@@(guile-user)> (mu:correl mu:size (lambda (msg) (string-length (mu:subject msg))) "subject:hello") $5 = -0.10804368622292 scheme@@(guile-user)> @end example @node Tabulating values @section Tabulating values @code{(mu:tabulate [])} applies @t{} to each message matching @t{} (leave empty to match @emph{all} messages), and returns a associative list (a list of pairs) with each of the different results of @t{} and their frequencies. For fields that contain lists of values (such as address-fields), each of the values in the list is added separately. @subsection Example: messages per weekday We demonstrate @code{mu:tabulate} with an example. Suppose we want to know how many messages we receive per weekday: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) ;; create a list like (("Sun" . 13) ("Mon" . 23) ...) (define weekday-table (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg))))) (lambda (a b) (< (car a) (car b)))))) (for-each (lambda (elm) (format #t "~a: ~a\n" (car elm) (cdr elm))) weekday-table) @end lisp The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the frequencies per hour -- this returns a list of pairs: @verbatim ((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993)) @end verbatim We sort these pairs by the day number, and then apply @code{mu:weekday-numbers->names}, which takes the list, and returns a list where the day numbers are replace by there abbreviated name (in the current locale). Note, there is also @code{mu:month-numbers->names}. The script then outputs these numbers in the following form: @verbatim Sun: 2278 Mon: 2993 Tue: 3184 Wed: 2833 Thu: 2800 Fri: 2339 Sat: 1856 @end verbatim Clearly, Saturday is a slow day for e-mail... @node Most frequent values @section Most frequent values In the above example, the number of values is small (the seven weekdays); however, in many cases there can be many different values (for example, all different message subjects), many of which may not be very interesting -- all we need to know is the top-10 of most frequently seen values. This is fairly easy to achieve using @code{mu:tabulate} -- to get the top-10 subjects@footnote{this requires the @code{(srfi srfi-1)}-module}, we can use something like this: @lisp (take (sort (mu:tabulate mu:subject) (lambda (a b) (> (cdr a) (cdr b)))) 10) @end lisp If this is not short enough, @t{mu-guile} offers a convenience procedure to do this: @code{mu:top-n-most-frequent}. For example, to get the top-10 people we sent mail to most often: @lisp (mu:top-n-most-frequent mu:to 10 "maildir:/sent") @end lisp Can't make it much easier than that! @node Plotting data @chapter Plotting data You can plot the results in the format produced by @code{mu:tabulate} with the @t{(mu plot)} module, an experimental module that requires the @t{gnuplot}@footnote{@url{http://www.gnuplot.info/}} program to be installed on your system. The @code{mu:plot-histogram} procedure takes the following arguments: @code{(mu:plot-histogram <x-label> <y-label> [<want-ascii>])} Here, @code{<data>} is a table of data in the format that @code{mu:tabulate} produces. @code{<title>}, @code{<x-label>} and @code{<y-lablel>} are, respectively, the title of the graph, and the labels for X- and Y-axis. Finally, if you pass @t{#t} for the final @code{<want-ascii>} parameter, a plain-text rendering of the graph will be produced; otherwise, a graphical window will be shown. An example should clarify how this works in practice; let's plot the number of message per hour: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) (define (mail-per-hour-table) (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg))))) (lambda (x y) (< (car x) (car y))))) (mu:plot-histogram (mail-per-hour-table) "Mail per hour" "Hour" "Frequency") @end lisp @cartouche @verbatim Mail per hour Frequency 1200 ++--+--+--+--+-+--+--+--+--+-+--+--+--+-+--+--+--+--+-+--+--+--+--++ |+ + + + + + + "/tmp/fileHz7D2u" using 2:xticlabels(1) ******** 1100 ++ *** +* **** * * * 1000 *+ * **** * +* * * ****** **** * ** * * 900 *+ * * ** **** * **** ** * +* * * * ** * * ********* * ** ** * * 800 *+ * **** ** * * * * ** * * ** ** * +* 700 *+ *** **** * ** * * * * ** **** * ** ** * +* * * * **** * * ** * * * * ** * **** ** ** * * 600 *+ * **** * * * * ** * * * * ** * * * ** ** * +* * * ** * * * * * ** * * * * ** * * * ** ** * * 500 *+ * ** * * * * * ** * * * * ** * * * ** ** * +* * * ** **** *** * * * ** * * * * ** * * * ** ** * * 400 *+ * ** ** **** * * * * * ** * * * * ** * * * ** ** * +* *+ *+**+**+* +*******+* +* +*+ *+**+* +*+ *+ *+**+* +*+ *+**+**+* +* 300 ******************************************************************** 0 1 2 3 4 5 6 7 8 910 11 12 1314 15 16 17 1819 20 21 22 23 Hour @end verbatim @end cartouche @node Writing scripts @chapter Writing scripts The @t{mu} program has built-in support for running guile-scripts, and comes with a number of examples. You can get a list of all scripts with the @t{mu script} command: @verbatim $ mu script Available scripts (use --verbose for details): * find-dups: find duplicate messages * msgs-count: count the number of messages matching some query * msgs-per-day: graph the number of messages per day * msgs-per-hour: graph the number of messages per hour * msgs-per-month: graph the number of messages per month * msgs-per-year: graph the number of messages per year * msgs-per-year-month: graph the number of messages per year-month @end verbatim You can then execute such a script by its name: @verbatim $ mu msgs-per-month --textonly --query=hello Messages per month matching hello 240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++ | + + + + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** | 220 ++ * * ****** | * * * * 200 ++ * * * +* | * * * * 180 ++ ****** * * * +* | * * * * * * 160 ****** * * * * * +* * * * * * * * * * ******* * * * * ****** * * 140 *+ ** * * * * * * ******** +* * ** ******* * * * * * ** ** * 120 *+ ** ** ******* * * * * ** ** +* * ** ** ** * * * ******* ** ** * 100 *+ ** ** ** * * * * ** ** ** +* * + ** + ** + ** + * + * + + * + * + ** + ** + ** + * 80 ********************************************************************** Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Month @end verbatim Please refer to the @t{mu-script} man-page for some details on writing your own scripts. @node Recipies @appendix Recipies @itemize @item Calculating the average length of subject-lines @lisp ;; the average length of all our (let ((len 0) (n 0)) (mu:for-each-message (lambda (msg) (set! len (+ len (string-length (or (mu:subject msg) "")))) (set! n (+ n 1)))) (if (= n 0) 0 (/ len n))) ;; this gives a rational, exact result; ;; use exact->inexact to get decimals ;; we we can make this short with the mu:average (with (mu stats)) (mu:average (lambda (msg) (string-length (or (mu:subject msg) "")))) @end lisp @end itemize @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texi @bye ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu.scm������������������������������������������������������������������������������0000664�0000000�0000000�00000024523�12504061031�0014454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (define-module (mu) :use-module (oop goops) :use-module (ice-9 optargs) :use-module (texinfo string-utils) :export ( ;; classes <mu:message> <mu:contact> <mu:part> ;; general ;; mu:initialize ;; mu:initialized? mu:log-warning mu:log-message mu:log-critical ;; search funcs mu:for-each-message mu:for-each-msg mu:message-list ;; message funcs mu:header ;; message accessors mu:field:bcc mu:field:body-html mu:field:body-txt mu:field:cc mu:field:date mu:field:flags mu:field:from mu:field:maildir mu:field:message-id mu:field:path mu:field:prio mu:field:refs mu:field:size mu:field:subject mu:field:tags mu:field:timestamp mu:field:to ;; contact funcs mu:name mu:email mu:contact->string ;; mu:for-each-contact ;; mu:contacts ;; ;; <mu:contact-with-stats> mu:frequency mu:last-seen ;; parts <mu:part> ;; message function mu:attachments mu:parts ;; <mu:part> methods mu:name mu:mime-type ;; size ;; mu:save ;; mu:save-as )) ;; this is needed for guile < 2.0.4 (setlocale LC_ALL "") ;; load the binary (load-extension "libguile-mu" "mu_guile_init") (load-extension "libguile-mu" "mu_guile_message_init") ;; define some dummies so we don't get errors during byte compilation (eval-when (compile) (define mu:c:get-field) (define mu:c:get-contacts) (define mu:c:for-each-message) (define mu:c:get-header) (define mu:critical) (define mu:c:log) (define mu:message) (define mu:c:log) (define mu:warning) (define mu:c:log) (define mu:get-parts)) (define (mu:log-warning frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:warning frm args)) (define (mu:log-message frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:message frm args)) (define (mu:log-critical frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:critical frm args)) (define-class <mu:message> () (msg #:init-keyword #:msg)) ;; the MuMsg-smob we're wrapping (define-syntax define-getter (syntax-rules () ((define-getter method-name field) (begin (define-method (method-name (msg <mu:message>)) (mu:c:get-field (slot-ref msg 'msg) field)) (export method-name))))) (define-getter mu:bcc mu:field:bcc) (define-getter mu:body-html mu:field:body-html) (define-getter mu:body-txt mu:field:body-txt) (define-getter mu:cc mu:field:cc) (define-getter mu:date mu:field:date) (define-getter mu:flags mu:field:flags) (define-getter mu:from mu:field:from) (define-getter mu:maildir mu:field:maildir) (define-getter mu:message-id mu:field:message-id) (define-getter mu:path mu:field:path) (define-getter mu:priority mu:field:prio) (define-getter mu:references mu:field:refs) (define-getter mu:size mu:field:size) (define-getter mu:subject mu:field:subject) (define-getter mu:tags mu:field:tags) (define-getter mu:timestamp mu:field:timestamp) (define-getter mu:to mu:field:to) (define-method (mu:header (msg <mu:message>) (hdr <string>)) "Get an arbitrary header HDR from message MSG; return #f if it does not exist." (mu:c:get-header (slot-ref msg 'msg) hdr)) (define* (mu:for-each-message func #:optional (expr #t) (maxresults -1)) "Execute function FUNC for each message that matches mu search expression EXPR. If EXPR is not provided, match /all/ messages in the store. MAXRESULTS specifies the maximum of messages to return, or -1 (the default) for no limit." (mu:c:for-each-message (lambda (msg) (func (make <mu:message> #:msg msg))) expr maxresults)) ;; backward-compatibility alias (define mu:for-each-msg mu:for-each-message) (define* (mu:message-list #:optional (expr #t) (maxresults -1)) "Return a list of all messages matching mu search expression EXPR. If EXPR is not provided, return a list of /all/ messages in the store. MAXRESULTS specifies the maximum of messages to return, or -1 (the default) for no limit." (let ((lst '())) (mu:for-each-message (lambda (m) (set! lst (append! lst (list m)))) expr maxresults) lst)) ;; contacts (define-class <mu:contact> () (name #:init-value #f #:accessor mu:name #:init-keyword #:name) (email #:init-value #f #:accessor mu:email #:init-keyword #:email)) (define-method (mu:contacts (msg <mu:message>) contact-type) "Get all contacts for MSG of the given CONTACT-TYPE. MSG is of type <mu-message>, while contact type is either `mu:contact:to', `mu:contact:cc', `mu:contact:from' or `mu:contact:bcc' to get the corresponding type of contacts, or #t to get all. Returns a list of <mu-contact> objects." (map (lambda (pair) ;; a pair (na . addr) (make <mu:contact> #:name (car pair) #:email (cdr pair))) (mu:c:get-contacts (slot-ref msg 'msg) contact-type))) (define-method (mu:contacts (msg <mu:message>)) "Get contacts of all types for message MSG as a list of <mu-contact> objects." (mu:contacts msg #t)) (define-class <mu:contact-with-stats> (<mu:contact>) (tstamp #:init-value 0 #:accessor mu:timestamp #:init-keyword #:timestamp) (last-seen #:init-value 0 #:accessor mu:last-seen) (freq #:init-value 1 #:accessor mu:frequency)) (define* (mu:for-each-contact proc #:optional (expr #t)) "Execute PROC for each contact. PROC receives a <mu-contact> instance as parameter. If EXPR is specified, only consider contacts in messages matching EXPR." (let ((c-hash (make-hash-table 4096))) (mu:for-each-message (lambda (msg) (for-each (lambda (ct) (let ((ct-ws (make <mu:contact-with-stats> #:name (mu:name ct) #:email (mu:email ct) #:timestamp (mu:date msg)))) (update-contacts-hash c-hash ct-ws))) (mu:contacts msg #t))) expr) (hash-for-each ;; c-hash now contains a map of email->contact (lambda (email ct-ws) (proc ct-ws)) c-hash))) (define-method (update-contacts-hash c-hash (nc <mu:contact-with-stats>)) "Update the contacts hash with a new and/or existing contact." ;; xc: existing-contact, nc: new contact (let ((xc (hash-ref c-hash (mu:email nc)))) (if (not xc) ;; no existing contact with this email address? (hash-set! c-hash (mu:email nc) nc) ;; store the new contact. ;; otherwise: (begin ;; 1) update the frequency for the existing contact (set! (mu:frequency xc) (1+ (mu:frequency xc))) ;; 2) update the name if the new one is not empty and its timestamp is newer ;; in that case, also update the timestamp (if (and (mu:name nc) (> (string-length (mu:name nc))) (> (mu:timestamp nc) (mu:timestamp xc))) (set! (mu:name xc) (mu:name nc)) (set! (mu:timestamp xc) (mu:timestamp nc))) ;; 3) update last-seen with timestamp, if x's timestamp is newer (if (> (mu:timestamp nc) (mu:last-seen xc)) (set! (mu:last-seen xc) (mu:timestamp nc))) ;; okay --> now xc has been updated; but it back in the hash (hash-set! c-hash (mu:email xc) xc))))) (define-method (mu:contact->string (contact <mu:contact>) (form <string>)) "Convert a contact to a string in format FORM, which is a string, either \"org-contact\", \"mutt-alias\", \"mutt-ab\", \"wanderlust\", \"quoted\" \"plain\"." (let* ((name (mu:name contact)) (email (mu:email contact)) (nick ;; simplistic nick guessing... (string-map (lambda(kar) (if (char-alphabetic? kar) kar #\_)) (string-downcase (or name email))))) (cond ((string= form "plain") (format #f "~a~a~a" (or name "") (if name " " "") email)) ((string= form "org-contact") (format #f "* ~s\n:PROPERTIES:\n:EMAIL:~a\n:NICK:~a\n:END:" (or name email) email nick)) ((string= form "wanderlust") (format #f "~a ~s ~s" nick (or name email) email)) ((string= form "mutt-alias") (format #f "alias ~a ~a <~a>" nick (or name email) email)) ((string= form "mutt-ab") (format #f "~a\t~a\t" email (or name ""))) ((string= form "quoted") (string-append "\"" (escape-special-chars (string-append (if name (format #f "\"~a\" " name) "") (format #f "<~a>" email)) "\"" #\\) "\"")) (else (error "Unsupported format"))))) ;; message parts (define-class <mu:part> () (msgpath #:init-value #f #:init-keyword #:msgpath) (index #:init-value #f #:init-keyword #:index) (name #:init-value #f #:getter mu:name #:init-keyword #:name) (mime-type #:init-value #f #:getter mu:mime-type #:init-keyword #:mime-type) (size #:init-value 0 #:getter mu:size #:init-keyword #:size)) (define-method (get-parts (msg <mu:message>) (files-only <boolean>)) "Get the part for MSG as a list of <mu:part> objects; if FILES-ONLY is #t, only get the part with file names." (map (lambda (part) (make <mu:part> #:msgpath (list-ref part 0) #:index (list-ref part 1) #:name (list-ref part 2) #:mime-type (list-ref part 3) #:size (list-ref part 4))) (mu:get-parts (slot-ref msg 'msg) files-only))) (define-method (mu:attachments (msg <mu:message>)) "Get the attachments for MSG as a list of <mu:part> objects." (get-parts msg #t)) (define-method (mu:parts (msg <mu:message>)) "Get the MIME-parts for MSG as a list of <mu-part> objects." (get-parts msg #f)) ;; (define-method (mu:save (part <mu:part>)) ;; "Save PART to a temporary file, and return the file name. If the ;; part had a filename, the temporary file's file name will be just that; ;; otherwise a name is made up." ;; (mu:save-part (slot-ref part 'msgpath) (slot-ref part 'index))) ;; (define-method (mu:save-as (part <mu:part>) (filepath <string>)) ;; "Save message-part PART to file system path PATH." ;; (copy-file (save part) filepath)) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/���������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0013742�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/Makefile.am����������������������������������������������������������������������0000664�0000000�0000000�00000001770�12504061031�0016003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir=${prefix}/share/guile/site/2.0/mu/ scm_DATA= \ stats.scm \ plot.scm \ script.scm EXTRA_DIST=$(scm_DATA) ��������mu-0.9.12/guile/mu/README���������������������������������������������������������������������������0000664�0000000�0000000�00000015023�12504061031�0014623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������* OUTDATED * * README ** What is muile? `muile' is a little experiment/toy using the equally experimental mu guile bindings, to be found in libmuguile/ in the top-level source directory. `guile'[1] is an interpreter/library for the Scheme programming language[2], specifically meant for extending other programs. It is, in fact, the official GNU language for doing so. 'muile' requires guile 2.x to get the full support. Older versions may not support e.g. the 'mu-stats.scm' things discussed below. The combination of mu + guile is called `muile', and allows you to write little Scheme-programs to query the mu-database, or inspect individual messages. It is still in an experimental stage, but useful already. ** How do I get it? The git-version and the future 0.9.7 version of mu will automatically build muile if you have guile. I've been using guile 2.x from git, but installing the 'guile-1.8-dev' package (Ubuntu/Debian) should do the trick. (I only did very minimal testing with guile 1.8 though). Then, configure mu. The configure output should tell you about whether guile was found (and where). If it's found, build mu, and toys/muile should be created, as well. ** What can I do with it? Go to toys/muile and start muile. You'll end up with a guile-shell where you can type scheme [1], it looks something like this (for guile 2.x): ,---- | scheme@(guile-user)> `---- Now, let's load a message (of course, replace with a message on your system): ,---- | scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S")) `---- This defines a variable 'msg', which holds some message on your file system. It's now easy to inspect this message: ,---- | scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S")) `---- Now, we can inspect this message a bit: ,---- | scheme@(guile-user)> (mu:msg:subject msg) | $1 = "See me in bikini :-)" | scheme@(guile-user)> (mu:msg:flags msg) | $2 = (mu:attach mu:unread) `---- and so on. Note, it's probably easiest to explore the various mu: methods using autocompletion; to enable that make sure you have ,---- | (use-modules (ice-9 readline)) | (activate-readline) `---- in your ~/.guile configuration. ** does this tool have some parameters? Yes, there is --muhome to set a non-default place for the message database (see the documentation on --muhome in the mu-find manpage). And there is --msg=<path> where you specify some particular message file; it will be available as 'mu:current-msg' in the guile (muile) environment. For example: ,---- | ./muile --msg=~/Maildir/inbox/cur/1311310172_1234:2,S | [...] | scheme@(guile-user)> mu:current-msg | $1 = #<msg /home/djcb/Maildir/inbox/cur/1311310172_1234:2,S> | scheme@(guile-user)> (mu:msg:size mu:current-msg) | $2 = 7206 `---- ** What about searching messages in the database? That's easy, too - it does require a little more scheme knowledge. For searching messages there is the mu:store:for-each function, which takes two arguments; the first argument is a function that will be called for each message found. The optional second argument is the search expression (following 'mu find' syntax); if don't provide the argument, all messages match. So how does this work in practice? Let's see I want to see the subject and sender for messages about milk: ,---- | (mu:store:for-each (lambda(msg) (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) "milk") `---- or slightly more readable: ,---- | (mu:store:for-each | (lambda(msg) | (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) | "milk") `---- As you can see, I provide an anonymous ('lambda') function which will be called for each message matching 'milk'. Admittedly, this requires a bit of Scheme-knowledge... but this time is good as any to learn this nice language. ** Can I do some statistics on my messages? Yes you can. In fact, it's pretty easy. If you load (in the muile/ directory) the file 'mu-stats.scm': ,---- | (load "mu-stats.scm") `---- you'll get a bunch of functions (with names starting with 'mu:stats') to make this very easy. Let's see, suppose I want to see how many messages I get per weekday: ,---- | scheme@(guile-user)> (mu:stats:per-weekday) | $1 = ((0 . 2255) (1 . 2788) (2 . 2868) (3 . 2599) (4 . 2629) (5 . 2287) (6 . 1851)) `---- Note, Sunday=0, Monday=1 and so on. Apparently, I get/send most of e-mail on Tuesdays, and least on Saturday. And note that mu:stats:per-weekdays takes an optional search expression argument, to limit the results to messages matching that, e.g., to only consider messages related to emacs during this year: ,---- | scheme@(guile-user)> (mu:stats:per-weekday "emacs date:2011..now") | $8 = ((0 . 54) (1 . 22) (2 . 46) (3 . 47) (4 . 39) (5 . 54) (6 . 50)) `---- There's also 'mu:stats:per-month', 'mu:stats:per-year', 'mu:stats:per-hour'. I learnt that during 3-4am I sent/receive only about a third of what I sent during 11-12pm. ** What about getting the top-10 people in the To:-field? Easy. ,---- | scheme@(guile-user)> (mu:stats:top-n-to) | $1 = ((("Abc" "myself@example.com") . 4465) (("Def" "somebodyelse@example.com") . 2114) | (and so on) `---- I've changed the names a bit to protect the innocent, but what the function does is return a list of pairs of (<name> <email>) . <frequency> descending in order of frequency. Note, 'mu:stats:top-n-to' takes two optional arguments - first the 'n' in top-n (default is 10), and seconds as search expression to limit the messages considered. There are also the functions 'mu:stats:top-n-subject' and 'mu:stats:top-n-from' which do the same, mutatis mutandis, and it's quite easy to add your own (see the mu-stats.scm for examples) ** What about showing the results in a table? Even easier. Try: ,---- | (mu:stats:table (mu:stats:top-n-to)) `---- or ,---- | (mu:stats:table (mu:stats:per-weekday)) `---- You can also export the table: ,---- | (mu:stats:export (mu:stats:per-weekday)) `---- which will create a temporary file with the results, for further processing in e.g. 'R' or 'gnuplot'. [1] http://www.gnu.org/s/guile/ [2] http://en.wikipedia.org/wiki/Scheme_(programming_language) # Local Variables: # mode: org; org-startup-folded: nil # End: �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/contact.scm����������������������������������������������������������������������0000664�0000000�0000000�00000000205�12504061031�0016076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(define-module (mu contact) :use-module(mu)) (display "(mu contact) is deprecated, please remove from (use-modules ...)") (newline) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/message.scm����������������������������������������������������������������������0000664�0000000�0000000�00000000206�12504061031�0016070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(define-module (mu message) :use-module (mu)) (display "(mu message) is deprecated, please remove from (use-modules ...)") (newline) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/part.scm�������������������������������������������������������������������������0000664�0000000�0000000�00000000200�12504061031�0015404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(define-module (mu part) :use-module (mu)) (display "(mu part) is deprecated, please remove from (use-modules ...)") (newline) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/plot.scm�������������������������������������������������������������������������0000664�0000000�0000000�00000005446�12504061031�0015435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; ;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (define-module (mu plot) :use-module (mu) :use-module (ice-9 popen) :export ( mu:plot ;; alias for mu:plot-histogram mu:plot-histogram )) (define (export-pairs pairs) "Write a temporary file with the list of PAIRS in table format, and return the file name." (let* ((datafile (tmpnam)) (output (open datafile (logior O_CREAT O_WRONLY) #O0600))) (for-each (lambda(pair) (display (format #f "~a ~a\n" (car pair) (cdr pair)) output)) pairs) (close output) datafile)) (define (find-program-in-path prog) "Find exutable program PROG in PATH; return the full path, or #f if not found." (let* ((path (parse-path (getenv "PATH"))) (progpath (search-path path prog))) (if (not progpath) #f (if (access? progpath X_OK) ;; is progpath #f)))) (define* (mu:plot-histogram data title x-label y-label #:optional (output "dumb") (extra-gnuplot-opts '())) "Plot DATA with TITLE, X-LABEL and X-LABEL using the gnuplot program. DATA is a list of cons-pairs (X . Y). OUTPUT is a string that determines the type of output that gnuplot produces, depending on the system. Which options are available depends on the particulars for the gnuplot installation, but typical examples would be \"dumb\" for text-only display, \"wxterm\" to write to a graphical window, or \"png\" to write a PNG-image to stdout. EXTRA-GNUPLOT-OPTS is a list of any additional options for gnuplot." (if (not (find-program-in-path "gnuplot")) (error "cannot find 'gnuplot' in path")) (let ((datafile (export-pairs data)) (gnuplot (open-pipe "gnuplot -p" OPEN_WRITE))) (display (string-append "reset\n" "set term " (or output "dumb") "\n" "set title \"" title "\"\n" "set xlabel \"" x-label "\"\n" "set ylabel \"" y-label "\"\n" "set boxwidth 0.9\n" (string-join extra-gnuplot-opts "\n") "plot \"" datafile "\" using 2:xticlabels(1) with boxes fs solid\n") gnuplot) (close-pipe gnuplot))) ;; backward compatibility (define mu-plot mu:plot-histogram) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/script.scm�����������������������������������������������������������������������0000664�0000000�0000000�00000004052�12504061031�0015753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (define-module (mu script) :export (mu:run-stats)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats) (mu plot)) (define (help-and-exit) "Show some help." (display (string-append "usage: script [--help] [--textonly] " "[--muhome=<muhome>] [--query=<query>") (newline)) (exit 0)) (define (mu:run-stats args func) "Run some statistics function. Interpret argument-list ARGS (like command-line arguments). Possible arguments are: --help (show some help and exit) --muhome (path to alternative mu home directory) --output (a string describing the output, e.g. \"dumb\", \"png\" \"wxt\") searchexpr (a search query) then call FUNC with args SEARCHEXPR and OUTPUT." (setlocale LC_ALL "") (let* ((optionspec '((muhome (value #t)) (query (value #t)) (output (value #f)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (query (option-ref options 'query #f)) (help (option-ref options 'help #f)) (output (option-ref options 'output #f)) (muhome (option-ref options 'muhome #f)) (restargs (option-ref options '() #f))) (if help (help-and-exit)) (mu:initialize muhome) (func (or query "") output))) ;; Local Variables: ;; mode: scheme ;; End: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/mu/stats.scm������������������������������������������������������������������������0000664�0000000�0000000�00000013404�12504061031�0015606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; ;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (define-module (mu stats) :use-module (oop goops) :use-module (mu) :use-module (srfi srfi-1) :use-module (ice-9 i18n) :use-module (ice-9 r5rs) :export ( mu:tabulate mu:top-n-most-frequent mu:count mu:average mu:stddev mu:correl mu:max mu:min mu:weekday-numbers->names mu:month-numbers->names)) (define* (mu:tabulate func #:optional (expr #t)) "Execute FUNC for each message matching EXPR, and return an alist with maps each result of FUNC to its frequency. If the result of FUNC is a list, add each of its values separately. FUNC is a function takes a <mu-message> instance as its argument. For example, to tabulate messages by weekday, one could use: (mu:tabulate (lambda(msg) (tm:wday (localtime (date msg))))), and get back a list like ((1 . 2) (2 . 5)(3 . 4)(4 . 4)(5 . 12)(6 . 7)(7. 2))." (let* ((table '()) ;; func to add a value to our table (update-table (lambda (val) (let ((old-freq (or (assoc-ref table val) 0))) (set! table (assoc-set! table val (1+ old-freq))))))) (mu:for-each-message (lambda(msg) (let ((val (func msg))) (if (list? val) (for-each update-table val) (update-table val)))) expr) table)) (define* (top-n func less n #:optional (expr #t)) "Take the results of (mu:tabulate FUNC EXPR), sort using LESS (a function taking two arguments A and B (cons cells, (VAL . FREQ)), and returns #t if A < B, #f otherwise), and then take the first N." (take (sort (mu:tabulate func expr) less) n)) (define* (mu:top-n-most-frequent func n #:optional (expr #t)) "Take the results of (mu:tabulate FUNC EXPR), and return the N items with the higest frequency." (top-n func (lambda (a b) (> (cdr a) (cdr b))) n expr)) (define* (mu:count #:optional (expr #t)) "Count the number of messages matching EXPR. If EXPR is not provided, match /all/ messages." (let ((num 0)) (mu:for-each-message (lambda (msg) (set! num (1+ num))) expr) num)) (define (average lst) "Calculate the average of a list LST of numbers, or #f if undefined." (if (null? lst) #f (/ (apply + lst) (length lst)))) (define (stddev lst) "Calculate the standard deviation of a list LST of numbers or #f if undefined." (let* ((avg (average lst)) (sosq (if avg (apply + (map (lambda (x)(* (- x avg) (- x avg))) lst))))) (if sosq (sqrt (/ sosq (length lst)))))) (define* (mu:average func #:optional (expr #t)) "Get the average value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (average (map func (mu:message-list expr)))) (define* (mu:stddev func #:optional (expr #t)) "Get the standard deviation the the values of FUNC applied to all messages matching EXPR (or #t for all). This is the 'population' stddev, not the 'sample' stddev. Returns #f if undefined." (stddev (map func (mu:message-list expr)))) (define* (mu:max func #:optional (expr #t)) "Get the maximum value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (apply max (map func (mu:message-list expr)))) (define* (mu:min func #:optional (expr #t)) "Get the minimum value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (apply min (map func (mu:message-list expr)))) (define (correl lst) "Calculate Pearson's correlation coefficient for a list LST of cons pair, where the car and cdr of the pairs are values from data set 1 and 2, respectively." (let ((n (length lst)) (sx (apply + (map car lst))) (sy (apply + (map cdr lst))) (sxy (apply + (map (lambda (cell) (* (car cell) (cdr cell))) lst))) (sxx (apply + (map (lambda (cell) (* (car cell) (car cell))) lst))) (syy (apply + (map (lambda (cell) (* (cdr cell) (cdr cell))) lst)))) (/ (- (* n sxy) (* sx sy)) (sqrt (* (- (* n sxx) (* sx sx)) (- (* n syy) (* sy sy))))))) (define* (mu:correl func1 func2 #:optional (expr #t)) "Determine Pearson's correlation coefficient between the value for functions FUNC1 and FUNC2 to all messages matching EXPR (or #t for all). Returns #f if undefined." (let ((data (map (lambda (msg) (cons (func1 msg) (func2 msg))) (mu:message-list expr)))) (if data (correl data) #f))) ;; a list of abbreviated, localized day names (define day-names (map locale-day-short (iota 7 1))) (define (mu:weekday-numbers->names table) "Convert a list of pairs with the car denoting a day number (0-6) into a list of pairs with the car replaced by the corresponding day name (abbreviated) for the current locale." (map (lambda (pair) (cons (list-ref day-names (car pair)) (cdr pair))) table)) ;; a list of abbreviated, localized month names (define month-names (map locale-month-short (iota 12 1))) (define (mu:month-numbers->names table) "Convert a list of pairs with the car denoting a month number (0-11) into a list of pairs with the car replaced by the corresponding day name (abbreviated)." (map (lambda (pair) (cons (list-ref month-names (car pair)) (cdr pair))) table)) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0015010�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/Makefile.am�����������������������������������������������������������������0000664�0000000�0000000�00000002067�12504061031�0017051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk EXTRA_DIST= \ msgs-count.scm \ msgs-per-year.scm \ msgs-per-hour.scm \ msgs-per-month.scm \ msgs-per-day.scm \ msgs-per-year-month.scm \ find-dups.scm muguiledistscriptdir = $(pkgdatadir)/scripts/ muguiledistscript_SCRIPTS = $(EXTRA_DIST) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/find-dups.scm���������������������������������������������������������������0000775�0000000�0000000�00000010046�12504061031�0017411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: find duplicate messages ;; INFO: options: ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --delete: delete all but the first one (experimental, be careful!) (use-modules (mu) (mu script) (mu stats)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format) (ice-9 rdelim)) (define (md5sum path) (let* ((port (open-pipe* OPEN_READ "md5sum" path)) (md5 (read-delimited " " port))) (close-pipe port) md5)) (define (remove-msg filepath delete) (if delete (begin (format #t "\t~a ... " (basename filepath)) (let* ((port (open-pipe* OPEN_READ "mu" "remove" filepath)) (line (read-line port)) (err (status:exit-val (close-pipe port)))) (if (and (equal? err 0) (eof-object? line)) (begin (format #t "ok\n") (delete-file filepath)) (begin (format #t "\nError while deleting:\n\t~a\n" line))))) (format #t "would\t~a\n" (basename filepath)))) (define (find-dups delete) (let ((id-table (make-hash-table 20000))) ;; fill the hash with <msgid-size> => <list of paths> (mu:for-each-message (lambda (msg) (let* ((id (format #f "~a-~d" (mu:message-id msg) (mu:size msg))) (lst (hash-ref id-table id))) (if lst (set! lst (cons (mu:path msg) lst)) (set! lst (list (mu:path msg)))) (hash-set! id-table id lst)))) ;; list all the paths with multiple elements; check the md5sum to ;; make 100%-minus-ε sure they are really the same file. (hash-for-each (lambda (id paths) (if (> (length paths) 1) (let ((hash (make-hash-table 10))) (for-each (lambda (path) (when (file-exists? path) (let* ((md5 (md5sum path)) (lst (hash-ref hash md5))) (if lst (set! lst (cons path lst)) (set! lst (list path))) (hash-set! hash md5 lst)))) paths) ;; hash now maps the md5sum to the messages... (hash-for-each (lambda (md5 mpaths) (if (> (length mpaths) 1) (begin (format #t "md5sum: ~a:\n" md5) (let ((num 1)) (for-each (lambda (path) (if (equal? num 1) (format #t "\t~a\n" (basename path)) (remove-msg path delete)) (set! num (+ 1 num))) mpaths))))) hash)))) id-table))) (define (main args) "Find duplicate messages and, potentially, delete the dups. Be careful with that! Interpret argument-list ARGS (like command-line arguments). Possible arguments are: --muhome (path to alternative mu home directory). --delete (delete all but the first one)." (setlocale LC_ALL "") (let* ((optionspec '( (muhome (value #t)) (delete (value #f)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (help (option-ref options 'help #f)) (delete (option-ref options 'delete #f)) (muhome (option-ref options 'muhome #f))) (mu:initialize muhome) (find-dups delete))) ;; Local Variables: ;; mode: scheme ;; End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-count.scm��������������������������������������������������������������0000775�0000000�0000000�00000002425�12504061031�0017621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir (use-modules (mu) (mu script) (mu stats)) (define (count expr output) "Print the total number of messages matching the query EXPR. OUTPUT is ignored." (display (mu:count expr)) (newline)) (define (main args) (mu:run-stats args count)) ;; Local Variables: ;; mode: scheme ;; End: �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-per-day.scm������������������������������������������������������������0000775�0000000�0000000�00000003340�12504061031�0020027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-day expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per weekday matching ~a" expr) "Day" "Messages" output)) (define (main args) (mu:run-stats args per-day)) ;; Local Variables: ;; mode: scheme ;; End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-per-hour.scm�����������������������������������������������������������0000775�0000000�0000000�00000003304�12504061031�0020227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-hour expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per hour matching ~a" expr) "Hour" "Messages" output)) (define (main args) (mu:run-stats args per-hour)) ;; Local Variables: ;; mode: scheme ;; End: ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-per-month.scm����������������������������������������������������������0000775�0000000�0000000�00000003341�12504061031�0020400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-month expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (mu:month-numbers->names (sort (mu:tabulate (lambda (msg) (tm:mon (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per month matching ~a" expr) "Month" "Messages" output)) (define (main args) (mu:run-stats args per-month)) ;; Local Variables: ;; mode: scheme ;; End: �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-per-year-month.scm�����������������������������������������������������0000775�0000000�0000000�00000003500�12504061031�0021333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-year-month expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (string->number (format #f "~d~2'0d" (+ 1900 (tm:year (localtime (mu:date msg)))) (tm:mon (localtime (mu:date msg)))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year/month matching ~a" expr) "Year/Month" "Messages" output)) (define (main args) (mu:run-stats args per-year-month)) ;; Local Variables: ;; mode: scheme ;; End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/scripts/msgs-per-year.scm�����������������������������������������������������������0000775�0000000�0000000�00000003320�12504061031�0020210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=<query>: limit to messages matching query ;; INFO: --muhome=<muhome>: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-year expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (+ 1900 (tm:year (localtime (mu:date msg))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year matching ~a" expr) "Year" "Messages" output)) (define (main args) (mu:run-stats args per-year)) ;; Local Variables: ;; mode: scheme ;; End: ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/tests/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0014463�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/tests/Makefile.am�������������������������������������������������������������������0000664�0000000�0000000�00000003701�12504061031�0016520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by the ## Free Software Foundation; either version 3, or (at your option) any ## later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -I ${top_srcdir}/lib/tests \ -DMU_TESTMAILDIR=\"${top_srcdir}/lib/tests/testdir\" \ -DMU_TESTMAILDIR2=\"${top_srcdir}/lib/tests/testdir2\" \ -DMU_TESTMAILDIR3=\"${top_srcdir}/lib/tests/testdir3\" \ -DMU_PROGRAM=\"${abs_top_builddir}/mu/mu\" \ -DMU_GUILE_MODULE_PATH=\"${abs_top_srcdir}/guile/\" \ -DMU_GUILE_LIBRARY_PATH=\"${abs_top_builddir}/guile/.libs\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_PROGRAMS= $(TEST_PROGS) TEST_PROGS += test-mu-guile test_mu_guile_SOURCES= test-mu-guile.c dummy.cc test_mu_guile_LDADD=${top_builddir}/lib/tests/libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES= \ dummy.cc dummy.cc: touch dummy.cc EXTRA_DIST=test-mu-guile.scm ���������������������������������������������������������������mu-0.9.12/guile/tests/test-mu-guile.c���������������������������������������������������������������0000664�0000000�0000000�00000005533�12504061031�0017336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include "../mu-query.h" #include <stdlib.h> #include <unistd.h> #include <string.h> #include "test-mu-common.h" #include "mu-store.h" /* tests for the command line interface, uses testdir2 */ static gchar* fill_database (void) { gchar *cmdline, *tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s" " --quiet", MU_PROGRAM, tmpdir, MU_TESTMAILDIR2); if (g_test_verbose()) g_print ("%s\n", cmdline); err = NULL; if (!g_spawn_command_line_sync (cmdline, NULL, NULL, NULL, &err)) { g_printerr ("Error: %s\n", err ? err->message : "?"); g_assert (0); } g_free (cmdline); return tmpdir; } static void test_something (const char *what) { char *dir, *cmdline; gint result; dir = fill_database (); cmdline = g_strdup_printf ( "LD_LIBRARY_PATH=%s %s -q -L %s -e main %s/test-mu-guile.scm " "--muhome=%s --test=%s", MU_GUILE_LIBRARY_PATH, GUILE_BINARY, MU_GUILE_MODULE_PATH, ABS_SRCDIR, dir, what); if (g_test_verbose ()) g_print ("cmdline: %s\n", cmdline); result = system (cmdline); g_assert (result == 0); g_free (dir); g_free (cmdline); } static void test_mu_guile_queries (void) { test_something ("queries"); } static void test_mu_guile_messages (void) { test_something ("message"); } static void test_mu_guile_stats (void) { test_something ("stats"); } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); if (!set_en_us_utf8_locale()) return 0; /* don't error out... */ g_test_add_func ("/guile/queries", test_mu_guile_queries); g_test_add_func ("/guile/message", test_mu_guile_messages); g_test_add_func ("/guile/stats", test_mu_guile_stats); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING| G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/tests/test-mu-guile.scm�������������������������������������������������������������0000775�0000000�0000000�00000010416�12504061031�0017675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh exec guile -e main -s $0 $@ !# ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; ;; This program is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation; either version 3, or (at your option) any ;; later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. (setlocale LC_ALL "") (use-modules (srfi srfi-1)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats)) (define (n-results-or-exit query n) "Run QUERY, and exit 1 if the number of results != N." (let ((lst (mu:message-list query))) (if (not (= (length lst) n)) (begin (simple-format (current-error-port) "Query: \"~A\"; expected ~A, got ~A\n" query n (length lst)) (exit 1))))) (define (test-queries) "Test a bunch of queries (or die trying)." (n-results-or-exit "hello" 1) (n-results-or-exit "f:john fruit" 1) (n-results-or-exit "f:soc@example.com" 1) (n-results-or-exit "t:alki@example.com" 1) (n-results-or-exit "t:alcibiades" 1) (n-results-or-exit "f:soc@example.com OR f:john" 2) (n-results-or-exit "f:soc@example.com OR f:john OR t:edmond" 3) (n-results-or-exit "t:julius" 1) (n-results-or-exit "s:dude" 1) (n-results-or-exit "t:dantès" 1) (n-results-or-exit "file:sittingbull.jpg" 1) (n-results-or-exit "file:custer.jpg" 1) (n-results-or-exit "file:custer.*" 1) (n-results-or-exit "j:sit*" 1) (n-results-or-exit "mime:image/jpeg" 1) (n-results-or-exit "mime:text/plain" 13) (n-results-or-exit "y:text*" 13) (n-results-or-exit "y:image*" 1) (n-results-or-exit "mime:message/rfc822" 2)) (define (error-exit msg . args) "Print error and exit." (let ((msg (apply format #f msg args))) (simple-format (current-error-port) "*ERROR*: ~A\n" msg) (exit 1))) (define (str-equal-or-exit got exp) "S1 == S2 or exit 1." ;; (format #t "'~A' <=> '~A'\n" s1 s2) (if (not (string= exp got)) (error-exit "Expected \"~A\", got \"~A\"\n" exp got))) (define (test-message) "Test functions for a particular message." (let ((msg (car (mu:message-list "hello")))) (str-equal-or-exit (mu:subject msg) "Fwd: rfc822") (str-equal-or-exit (mu:to msg) "martin") (str-equal-or-exit (mu:from msg) "foobar <foo@example.com>") (str-equal-or-exit (mu:header msg "X-Mailer") "Ximian Evolution 1.4.5") (if (not (equal? (mu:priority msg) mu:prio:normal)) (error-exit "Expected ~A, got ~A" (mu:priority msg) mu:prio:normal))) (let ((msg (car (mu:message-list "atoms")))) (str-equal-or-exit (mu:subject msg) "atoms") (str-equal-or-exit (mu:to msg) "Democritus <demo@example.com>") (str-equal-or-exit (mu:from msg) "\"Richard P. Feynman\" <rpf@example.com>") (str-equal-or-exit (mu:header msg "Content-transfer-encoding") "7BIT") (if (not (equal? (mu:priority msg) mu:prio:high)) (error-exit "Expected ~a, got ~a" (mu:priority msg) mu:prio:high)))) (define (num-equal-or-exit got exp) "S1 == S2 or exit 1." ;; (format #t "'~A' <=> '~A'\n" s1 s2) (if (not (= exp got)) (error-exit "Expected \"~S\", got \"~S\"\n" exp got))) (define (test-stats) "Test statistical functions." ;; average (num-equal-or-exit (mu:average mu:size) 82152/13) (num-equal-or-exit (floor (mu:stddev mu:size)) 13020.0) (num-equal-or-exit (mu:max mu:size) 46308) (num-equal-or-exit (mu:min mu:size) 111)) (define (main args) (let* ((optionspec '((muhome (value #t)) (test (value #t)))) (options (getopt-long args optionspec)) (muhome (option-ref options 'muhome #f)) (test (option-ref options 'test #f))) (mu:initialize muhome) (if test (cond ((string= test "queries") (test-queries)) ((string= test "message") (test-message)) ((string= test "stats") (test-stats)) (#t (exit 1)))))) ;; Local Variables: ;; mode: scheme ;; End: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/guile/texi.texi.in������������������������������������������������������������������������0000664�0000000�0000000�00000000117�12504061031�0015571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@c the version for mu for including in texinfo docs @set mu-version @VERSION@ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/��������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0012762�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/Makefile.am���������������������������������������������������������������������������0000664�0000000�0000000�00000004606�12504061031�0015024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # enforce compiling guile (optionally) first,then this dir first # before decending into tests/ SUBDIRS= . tests AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) $(GMIME_CFLAGS) $(GLIB_CFLAGS) $(GUILE_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter \ -Wdeclaration-after-statement -pedantic -Wno-variadic-macros AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_LTLIBRARIES= \ libmu.la libmu_la_SOURCES= \ mu-bookmarks.c \ mu-bookmarks.h \ mu-contacts.c \ mu-contacts.h \ mu-container.c \ mu-container.h \ mu-date.c \ mu-date.h \ mu-flags.h \ mu-flags.c \ mu-index.c \ mu-index.h \ mu-log.c \ mu-log.h \ mu-maildir.c \ mu-maildir.h \ mu-msg-crypto.c \ mu-msg-doc.cc \ mu-msg-doc.h \ mu-msg-fields.c \ mu-msg-fields.h \ mu-msg-file.c \ mu-msg-file.h \ mu-msg-iter.cc \ mu-msg-iter.h \ mu-msg-part.c \ mu-msg-part.h \ mu-msg-prio.c \ mu-msg-prio.h \ mu-msg-priv.h \ mu-msg-sexp.c \ mu-msg.c \ mu-msg.h \ mu-msg.h \ mu-query.cc \ mu-query.h \ mu-runtime.c \ mu-runtime.h \ mu-script.c \ mu-script.h \ mu-store.cc \ mu-store.h \ mu-store-read.cc \ mu-store-write.cc \ mu-store-priv.hh \ mu-str.c \ mu-str.h \ mu-threader.c \ mu-threader.h \ mu-util.c \ mu-util.h libmu_la_LIBADD= \ $(XAPIAN_LIBS) \ $(GMIME_LIBS) \ $(GLIB_LIBS) \ $(GUILE_LIBS) EXTRA_DIST= \ mu-msg-crypto.c \ doxyfile.in ��������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/doxyfile.in���������������������������������������������������������������������������0000664�0000000�0000000�00000015576�12504061031�0015153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Doxyfile 0.1 #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- PROJECT_NAME = mu PROJECT_NUMBER = @VERSION@ OUTPUT_DIRECTORY = apidocs OUTPUT_LANGUAGE = English EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = INTERNAL_DOCS = NO STRIP_CODE_COMMENTS = YES CASE_SENSE_NAMES = YES SHORT_NAMES = NO HIDE_SCOPE_NAMES = NO VERBATIM_HEADERS = YES SHOW_INCLUDE_FILES = YES JAVADOC_AUTOBRIEF = YES INHERIT_DOCS = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES DISTRIBUTE_GROUP_DOC = NO TAB_SIZE = 8 GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES ALIASES = ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 OPTIMIZE_OUTPUT_FOR_C = YES SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_FORMAT = WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @top_srcdir@/lib/ FILE_PATTERNS = *.c *.h RECURSIVE = YES EXCLUDE = tests # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = Makefile.* ChangeLog CHANGES CHANGES.* README \ README.* *.png AUTHORS DESIGN DESIGN.* *.desktop \ DESKTOP* COMMENTS HOWTO magic NOTES TODO THANKS # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = YES MAN_OUTPUT = man MAN_EXTENSION = .3mu MAN_LINKS = YES #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = YES #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = "G_BEGIN_DECLS=" \ "G_END_DECLS=" # "DOXYGEN_SHOULD_SKIP_THIS" \ # "DBUS_GNUC_DEPRECATED=" \ # "_DBUS_DEFINE_GLOBAL_LOCK(name)=" \ # "_DBUS_GNUC_PRINTF(from,to)=" SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO PERL_PATH = #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES TEMPLATE_RELATIONS = YES HIDE_UNDOC_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES GRAPHICAL_HIERARCHY = YES DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 640 MAX_DOT_GRAPH_HEIGHT = 1024 GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO ����������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-bookmarks.c������������������������������������������������������������������������0000664�0000000�0000000�00000006030�12504061031�0015534�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <glib.h> #include "mu-bookmarks.h" #define MU_BOOKMARK_GROUP "mu" struct _MuBookmarks { char *_bmpath; GHashTable *_hash; }; static void fill_hash (GHashTable *hash, GKeyFile *kfile) { gchar **keys, **cur; keys = g_key_file_get_keys (kfile, MU_BOOKMARK_GROUP, NULL, NULL); if (!keys) return; for (cur = keys; *cur; ++cur) { gchar *val; val = g_key_file_get_string (kfile, MU_BOOKMARK_GROUP, *cur, NULL); if (val) g_hash_table_insert (hash, *cur, val); } /* don't use g_strfreev, because we put them in the hash table; * only free the gchar** itself */ g_free (keys); } static GHashTable* create_hash_from_key_file (const gchar *bmpath) { GKeyFile *kfile; GHashTable *hash; kfile = g_key_file_new (); if (!g_key_file_load_from_file (kfile, bmpath, G_KEY_FILE_NONE, NULL)) { g_key_file_free (kfile); return NULL; } hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); fill_hash (hash, kfile); g_key_file_free (kfile); return hash; } MuBookmarks* mu_bookmarks_new (const gchar *bmpath) { MuBookmarks *bookmarks; GHashTable *hash; g_return_val_if_fail (bmpath, NULL); hash = create_hash_from_key_file (bmpath); if (!hash) return NULL; bookmarks = g_new (MuBookmarks, 1); bookmarks->_bmpath = g_strdup (bmpath); bookmarks->_hash = hash; return bookmarks; } void mu_bookmarks_destroy (MuBookmarks *bm) { if (!bm) return; g_free (bm->_bmpath); g_hash_table_destroy (bm->_hash); g_free (bm); } const gchar* mu_bookmarks_lookup (MuBookmarks *bm, const gchar *name) { g_return_val_if_fail (bm, NULL); g_return_val_if_fail (name, NULL); return g_hash_table_lookup (bm->_hash, name); } struct _BMData { MuBookmarksForeachFunc _func; gpointer _user_data; }; typedef struct _BMData BMData; static void each_bookmark (const gchar* key, const gchar *val, BMData *bmdata) { bmdata->_func (key, val, bmdata->_user_data); } void mu_bookmarks_foreach (MuBookmarks *bm, MuBookmarksForeachFunc func, gpointer user_data) { BMData bmdata; g_return_if_fail (bm); g_return_if_fail (func); bmdata._func = func; bmdata._user_data = user_data; g_hash_table_foreach (bm->_hash, (GHFunc)each_bookmark, &bmdata); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-bookmarks.h������������������������������������������������������������������������0000664�0000000�0000000�00000004445�12504061031�0015551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_BOOKMARKS_H__ #define __MU_BOOKMARKS_H__ #include <glib.h> G_BEGIN_DECLS /** * @addtogroup MuBookmarks * Functions for dealing with bookmarks * @{ */ struct _MuBookmarks; /*! \struct MuBookmarks * \brief Opaque structure representing a sequence of bookmarks */ typedef struct _MuBookmarks MuBookmarks; /** * create a new bookmarks object. when it's no longer needed, use * mu_bookmarks_destroy * * @param bmpath path to the bookmarks file * * @return a new BookMarks object, or NULL in case of error */ MuBookmarks *mu_bookmarks_new (const gchar *bmpath) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a bookmarks object * * @param bm a bookmarks object, or NULL */ void mu_bookmarks_destroy (MuBookmarks *bm); /** * get the value for some bookmark * * @param bm a valid bookmarks object * @param name name of the bookmark to retrieve * * @return the value of the bookmark or NULL in case in error, e.g. if * the bookmark was not found */ const gchar* mu_bookmarks_lookup (MuBookmarks *bm, const gchar *name); typedef void (*MuBookmarksForeachFunc) (const gchar *key, const gchar *val, gpointer user_data); /** * call a function for each bookmark * * @param bm a valid bookmarks object * @param func a callback function to be called for each bookmarks * @param user_data a user pointer passed to the callback */ void mu_bookmarks_foreach (MuBookmarks *bm, MuBookmarksForeachFunc func, gpointer user_data); /** @} */ G_END_DECLS #endif /*__MU_BOOKMARKS_H__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-contacts.c�������������������������������������������������������������������������0000664�0000000�0000000�00000026534�12504061031�0015375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <unistd.h> #include <errno.h> #include <string.h> #include <ctype.h> #include "mu-contacts.h" #include "mu-util.h" #include "mu-str.h" #define EMAIL_KEY "email" #define NAME_KEY "name" #define TSTAMP_KEY "tstamp" #define PERSONAL_KEY "personal" #define FREQ_KEY "frequency" /* note: 'personal' here means a mail where my e-mail addresses is explicitly * in one of the address fields, ie., it's not some mailing list message */ struct _ContactInfo { gchar *_name, *_email; gboolean _personal; time_t _tstamp; unsigned _freq; }; typedef struct _ContactInfo ContactInfo; static void contact_info_destroy (ContactInfo *cinfo); static ContactInfo *contact_info_new (char *email, char *name, gboolean personal, time_t tstamp, unsigned freq); struct _MuContacts { GKeyFile *_ccache; gchar *_path; GHashTable *_hash; gboolean _dirty; }; static GKeyFile* load_key_file (const char *path) { GError *err; GKeyFile *keyfile; gboolean file_exists; err = NULL; /* of course this is racy, but it's only for giving more * meaningful errors to users */ file_exists = TRUE; if (access(path, F_OK) != 0) { if (errno != ENOENT) { g_warning ("cannot open %s: %s", path, strerror(errno)); return NULL; } file_exists = FALSE; } err = NULL; keyfile = g_key_file_new (); if (file_exists && !g_key_file_load_from_file (keyfile, path, G_KEY_FILE_KEEP_COMMENTS, &err)) { g_warning ("could not load keyfile %s: %s", path, err->message); g_error_free (err); g_key_file_free (keyfile); return NULL; } return keyfile; } static gboolean get_values (GKeyFile *kfile, const gchar *group, gchar **email, gchar **name, gboolean *personal, size_t *tstamp, unsigned *freq) { GError *err; err = NULL; do { int i; *email = g_key_file_get_value (kfile, group, EMAIL_KEY, &err); if (!*email) break; *tstamp = (time_t)g_key_file_get_integer (kfile, group, TSTAMP_KEY, &err); if (err) break; *personal = g_key_file_get_boolean (kfile, group, PERSONAL_KEY, NULL); *name = g_key_file_get_value (kfile, group, NAME_KEY, NULL); i = g_key_file_get_integer (kfile, group, FREQ_KEY, NULL); *freq = (unsigned)(i >= 0) ? i : 1; return TRUE; } while (0); g_warning ("error getting value for %s: %s", group, err->message ? err->message: "error"); g_clear_error (&err); return FALSE; } static gboolean deserialize_cache (MuContacts *self) { gchar **groups; gsize i, len; groups = g_key_file_get_groups (self->_ccache, &len); for (i = 0; i != len; ++i) { ContactInfo *cinfo; char *name, *email; size_t tstamp; gboolean personal; unsigned freq; if (!get_values (self->_ccache, groups[i], &email, &name, &personal, &tstamp, &freq)) continue; /* ignore this one... */ cinfo = contact_info_new (email, name, personal, tstamp, freq); /* note, we're using the groups[i], so don't free with g_strfreev */ g_hash_table_insert (self->_hash, groups[i], cinfo); } g_free (groups); return TRUE; } static gboolean set_comment (GKeyFile *kfile) { GError *err; const char *comment = " automatically generated -- do not edit"; err = NULL; if (!g_key_file_set_comment (kfile, NULL, NULL, comment, &err)) { g_warning ("could not write comment to keyfile: %s", err->message); g_error_free (err); return FALSE; } return TRUE; } MuContacts* mu_contacts_new (const gchar *path) { MuContacts *self; g_return_val_if_fail (path, NULL); self = g_new0 (MuContacts, 1); self->_path = g_strdup (path); self->_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)contact_info_destroy); self->_ccache = load_key_file (path); if (!self->_ccache || !set_comment (self->_ccache)) { mu_contacts_destroy (self); return NULL; } deserialize_cache (self); MU_WRITE_LOG("deserialized contacts from cache %s", path); self->_dirty = FALSE; return self; } void mu_contacts_clear (MuContacts *self) { g_return_if_fail (self); if (self->_ccache) g_key_file_free (self->_ccache); g_hash_table_remove_all (self->_hash); self->_ccache = g_key_file_new (); self->_dirty = FALSE; } /* * we use the e-mail address to create a key in the GKeyFile, but we * have to mutilate a bit so that it's (a) *cough* practically-unique * and (b) valid as a GKeyFile group name (ie., valid utf8, no control * chars, no '[' or ']') */ static const char* encode_email_address (const char *addr) { static char enc[254 + 1]; /* max size for an e-mail addr */ char *cur; if (!addr) return FALSE; /* make sure chars are with {' ' .. '~'}, and not '[' ']' */ for (cur = strncpy(enc, addr, sizeof(enc)); *cur != '\0'; ++cur) if (!isalnum(*cur)) { *cur = 'A' + (*cur % ('Z' - 'A')); } else *cur = tolower(*cur); return enc; } /* downcase the domain-part of the email address, but only if it * consists of ascii (to prevent screwing up idna addresses) */ char* downcase_domain_maybe (const char *addr) { char *addr_conv, *at, *cur; addr_conv = g_strdup (addr); if (!(at = strchr (addr_conv, '@'))) { /*huh?*/ g_free (addr_conv); return NULL; } for (cur = at + 1; *cur; ++cur) { if (isascii(*cur)) *cur = g_ascii_tolower (*cur); else { /* non-ascii; return the unchanged original */ g_free (addr_conv); return g_strdup (addr); } } return addr_conv; } static void clear_str (char* str) { if (str) { mu_str_remove_ctrl_in_place (str); g_strstrip (str); } } gboolean mu_contacts_add (MuContacts *self, const char *addr, const char *name, gboolean personal, time_t tstamp) { ContactInfo *cinfo; const char *group; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (addr, FALSE); group = encode_email_address (addr); cinfo = (ContactInfo*) g_hash_table_lookup (self->_hash, group); if (!cinfo) { char *addr_dc; if (!(addr_dc = downcase_domain_maybe (addr))) return FALSE; cinfo = contact_info_new (addr_dc, name ? g_strdup(name) : NULL, personal, tstamp, 1); g_hash_table_insert (self->_hash, g_strdup(group), cinfo); } else { if (cinfo->_tstamp < tstamp) { if (!mu_str_is_empty(name)) { /* update the name to the last one used, unless it's * empty*/ g_free (cinfo->_name); cinfo->_name = g_strdup (name); if (cinfo->_name) mu_str_remove_ctrl_in_place (cinfo->_name); } cinfo->_tstamp = tstamp; } ++cinfo->_freq; } self->_dirty = TRUE; return TRUE; } struct _EachContactData { MuContactsForeachFunc _func; gpointer _user_data; GRegex *_rx; size_t _num; }; typedef struct _EachContactData EachContactData; static void /* email will never be NULL, but ci->_name may be */ each_contact (const char *group, ContactInfo *ci, EachContactData *ecdata) { if (!ci->_email) g_warning ("missing email: %u", (unsigned)ci->_tstamp); /* ignore this contact if we have a regexp, and it matches * neither email nor name (if we have a name) */ while (ecdata->_rx) { /* note, only once */ if (g_regex_match (ecdata->_rx, ci->_email, 0, NULL)) break; /* email matches? continue! */ if (!ci->_name) return; /* email did not match, no name? ignore this one */ if (g_regex_match (ecdata->_rx, ci->_name, 0, NULL)) break; /* name matches? continue! */ return; /* nothing matched, ignore this one */ } ecdata->_func (ci->_email, ci->_name, ci->_personal, ci->_tstamp, ci->_freq, ecdata->_user_data); ++ecdata->_num; } gboolean mu_contacts_foreach (MuContacts *self, MuContactsForeachFunc func, gpointer user_data, const char *pattern, size_t *num) { EachContactData ecdata; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (func, FALSE); if (pattern) { GError *err; err = NULL; ecdata._rx = g_regex_new (pattern, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0, &err); if (!ecdata._rx) { g_warning ("error in regexp '%s': %s", pattern, err->message); g_error_free (err); return FALSE; } } else ecdata._rx = NULL; ecdata._func = func; ecdata._user_data = user_data; ecdata._num = 0; g_hash_table_foreach (self->_hash, (GHFunc)each_contact, &ecdata); if (ecdata._rx) g_regex_unref (ecdata._rx); if (num) *num = ecdata._num; return TRUE; } static void each_keyval (const char *group, ContactInfo *cinfo, MuContacts *self) { /* use set value so the string do not necessarily have to be * valid utf-8 */ if (cinfo->_name) g_key_file_set_value (self->_ccache, group, NAME_KEY, cinfo->_name); g_key_file_set_value (self->_ccache, group, EMAIL_KEY, cinfo->_email); g_key_file_set_boolean (self->_ccache, group, PERSONAL_KEY, cinfo->_personal); g_key_file_set_integer (self->_ccache, group, TSTAMP_KEY, (int)cinfo->_tstamp); g_key_file_set_integer (self->_ccache, group, FREQ_KEY, (int)cinfo->_freq); } static gboolean serialize_cache (MuContacts *self) { gchar *data; gsize len; gboolean rv; g_hash_table_foreach (self->_hash, (GHFunc)each_keyval, self); /* Note: err arg is unused */ data = g_key_file_to_data (self->_ccache, &len, NULL); if (len) { GError *err; err = NULL; rv = g_file_set_contents (self->_path, data, len, &err); if (!rv) { g_warning ("failed to serialize cache to %s: %s", self->_path, err->message); g_error_free (err); } g_free (data); } else rv = TRUE; return rv; } void mu_contacts_destroy (MuContacts *self) { if (!self) return; if (self->_ccache && self->_dirty) { serialize_cache (self); MU_WRITE_LOG("serialized contacts cache %s", self->_path); } if (self->_ccache) g_key_file_free (self->_ccache); g_free (self->_path); if (self->_hash) g_hash_table_destroy (self->_hash); g_free (self); } /* note, we will *own* the name, email we get, and we'll free them in * the end... */ static ContactInfo * contact_info_new (char *email, char *name, gboolean personal, time_t tstamp, unsigned freq) { ContactInfo *cinfo; /* email should not be NULL, name can */ g_return_val_if_fail (email, NULL); cinfo = g_slice_new (ContactInfo); /* we need to clear the strings from control chars because * they could screw up the keyfile */ clear_str (email); clear_str (name); cinfo->_email = email; cinfo->_name = name; cinfo->_personal = personal; cinfo->_tstamp = tstamp; cinfo->_freq = freq; return cinfo; } static void contact_info_destroy (ContactInfo *cinfo) { if (!cinfo) return; g_free (cinfo->_email); g_free (cinfo->_name); g_slice_free (ContactInfo, cinfo); } size_t mu_contacts_count (MuContacts *self) { g_return_val_if_fail (self, 0); return g_hash_table_size (self->_hash); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-contacts.h�������������������������������������������������������������������������0000664�0000000�0000000�00000007441�12504061031�0015376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_CONTACTS_H__ #define __MU_CONTACTS_H__ #include <glib.h> #include <time.h> G_BEGIN_DECLS struct _MuContacts; typedef struct _MuContacts MuContacts; /** * create a new MuContacts object; use mu_contacts_destroy when you no longer need it * * @param ccachefile full path to the file with cached list of contacts * * @return a new MuContacts* if succeeded, NULL otherwise */ MuContacts* mu_contacts_new (const gchar *ccachefile) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * add a contacts; if there's a contact with this e-mail address * already, it will not updated unless the timestamp of this one is * higher and has a non-empty name * * @param contacts a contacts object * @param email e-mail address of the contact (not NULL) * @param name name of the contact (or NULL) * @param personal whether the contact is 'personal' (ie., my address * appears in one of the address fields) * @param tstamp timestamp for this address * * @return TRUE if succeeded, FALSE otherwise */ gboolean mu_contacts_add (MuContacts *self, const char *email, const char* name, gboolean personal, time_t tstamp); /** * destroy the Contacts object * * @param contacts a contacts object */ void mu_contacts_destroy (MuContacts *self); /** * clear all contacts from the cache * * @param self a MuContacts instance */ void mu_contacts_clear (MuContacts *self); /** * get the path for the contacts cache file * * @param contacts a contacts object * * @return the path as a constant string (don't free), or NULL in case * of error */ const gchar* mu_contacts_get_path (MuContacts *self); /** * return the number of contacts * * @param self a contacts object * * @return the number of contacts */ size_t mu_contacts_count (MuContacts *self); /** * call called for mu_contacts_foreach; returns the e-mail address, * name (which may be NULL) , whether the message is 'personal', the * timestamp for the address (when it was last seen), and the * frequency (in how many message did this contact participate) * */ typedef void (*MuContactsForeachFunc) (const char *email, const char *name, gboolean personal, time_t tstamp, unsigned freq, gpointer user_data); /** * call a function for either each contact, or each contact satisfying * a regular expression, * * @param contacts contacts object * @param func callback function to be called for each * @param user_data user data to pass to the callback * @param pattern a regular expression which matches either the e-mail * or name, to filter out contacts, or NULL to not do any filtering. * @param num receives the number of contacts found, or NULL * * @return TRUE if the function succeeded, or FALSE if the provide * regular expression was invalid (and not NULL) */ gboolean mu_contacts_foreach (MuContacts *self, MuContactsForeachFunc func, gpointer user_data, const char* pattern, size_t *num); G_END_DECLS #endif /*__MU_CONTACTS_H__*/ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-container.c������������������������������������������������������������������������0000664�0000000�0000000�00000033056�12504061031�0015536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> /* for memset */ #include <math.h> /* for log, ceil */ #include "mu-container.h" #include "mu-msg.h" #include "mu-msg-iter.h" /* * path data structure, to determine the thread paths mentioned above; * the path is filled as we're traversing the tree of MuContainers * (messages) */ struct _Path { int *_data; guint _len; }; typedef struct _Path Path; static Path* path_new (guint initial); static void path_destroy (Path *p); static void path_inc (Path *p, guint index); static gchar* path_to_string (Path *p, const char* frmt); MuContainer* mu_container_new (MuMsg *msg, guint docid, const char *msgid) { MuContainer *c; g_return_val_if_fail (!msg || docid != 0, NULL); c = g_slice_new0 (MuContainer); if (msg) c->msg = mu_msg_ref (msg); c->leader = c; c->docid = docid; c->msgid = msgid; return c; } void mu_container_destroy (MuContainer *c) { if (!c) return; if (c->msg) mu_msg_unref (c->msg); g_slice_free (MuContainer, c); } static void set_parent (MuContainer *c, MuContainer *parent) { while (c) { c->parent = parent; c = c->next; } } G_GNUC_UNUSED static gboolean check_dup (MuContainer *c, GHashTable *hash) { if (g_hash_table_lookup (hash, c)) { g_warning ("ALREADY!!"); mu_container_dump (c, TRUE); g_assert (0); } else g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); return TRUE; } G_GNUC_UNUSED static void assert_no_duplicates (MuContainer *c) { GHashTable *hash; hash = g_hash_table_new (g_direct_hash, g_direct_equal); mu_container_foreach (c, (MuContainerForeachFunc)check_dup, hash); g_hash_table_destroy (hash); } MuContainer* mu_container_append_siblings (MuContainer *c, MuContainer *sibling) { g_assert (c); g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); g_return_val_if_fail (c != sibling, NULL); /* assert_no_duplicates (c); */ set_parent (sibling, c->parent); /* find the last sibling and append; first we try our cache * 'last', otherwise we need to walk the chain. We use a * cached last as to avoid walking the chain (which is * O(n*n)) */ if (c->last) c->last->next = sibling; else { /* no 'last' cached, so walk the chain */ MuContainer *c2; for (c2 = c; c2 && c2->next; c2 = c2->next); c2->next = sibling; } /* update the cached last */ c->last = sibling->last ? sibling->last : sibling; /* assert_no_duplicates (c); */ return c; } MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling) { MuContainer *cur, *prev; g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); for (prev = NULL, cur = c; cur; cur = cur->next) { if (cur == sibling) { if (!prev) c = cur->next; else prev->next = cur->next; break; } prev = cur; } /* unset the cached last; it's not valid anymore * * TODO: we could actually do a better job updating last * rather than invalidating it. */ if (c) c->last = NULL; return c; } MuContainer* mu_container_append_children (MuContainer *c, MuContainer *child) { g_return_val_if_fail (c, NULL); g_return_val_if_fail (child, NULL); g_return_val_if_fail (c != child, NULL); /* assert_no_duplicates (c); */ set_parent (child, c); if (!c->child) c->child = child; else c->child = mu_container_append_siblings (c->child, child); /* assert_no_duplicates (c->child); */ return c; } MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child) { g_return_val_if_fail (c, NULL); g_return_val_if_fail (child, NULL); /* g_assert (!child->child); */ /* g_return_val_if_fail (!child->child, NULL); */ g_return_val_if_fail (c != child, NULL); c->child = mu_container_remove_sibling (c->child, child); return c; } typedef void (*MuContainerPathForeachFunc) (MuContainer*, gpointer, Path*); static void mu_container_path_foreach_real (MuContainer *c, guint level, Path *path, MuContainerPathForeachFunc func, gpointer user_data) { if (!c) return; path_inc (path, level); func (c, user_data, path); /* children */ mu_container_path_foreach_real (c->child, level + 1, path, func, user_data); /* siblings */ mu_container_path_foreach_real (c->next, level, path, func, user_data); } static void mu_container_path_foreach (MuContainer *c, MuContainerPathForeachFunc func, gpointer user_data) { Path *path; path = path_new (100); mu_container_path_foreach_real (c, 0, path, func, user_data); path_destroy (path); } gboolean mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, gpointer user_data) { g_return_val_if_fail (func, FALSE); if (!c) return TRUE; if (!mu_container_foreach (c->child, func, user_data)) return FALSE; /* recurse into children */ /* recurse into siblings */ if (!mu_container_foreach (c->next, func, user_data)) return FALSE; return func (c, user_data); } MuContainer* mu_container_splice_children (MuContainer *c, MuContainer *sibling) { MuContainer *children; g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); children = sibling->child; sibling->child = NULL; return mu_container_append_siblings (c, children); } MuContainer* mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child) { MuContainer *newchild; g_return_val_if_fail (parent, NULL); g_return_val_if_fail (child, NULL); g_return_val_if_fail (parent != child, NULL); newchild = child->child; child->child=NULL; return mu_container_append_children (parent, newchild); } static GSList* mu_container_to_list (MuContainer *c) { GSList *lst; for (lst = NULL; c; c = c->next) lst = g_slist_prepend (lst, c); return lst; } static gpointer list_last_data (GSList *lst) { GSList *tail; tail = g_slist_last (lst); return tail->data; } static MuContainer* mu_container_from_list (GSList *lst) { MuContainer *c, *cur, *tail; if (!lst) return NULL; tail = list_last_data (lst); for (c = cur = (MuContainer*)lst->data; cur; lst = g_slist_next(lst)) { cur->next = lst ? (MuContainer*)lst->data : NULL; cur->last = tail; cur=cur->next; } return c; } struct _SortFuncData { MuMsgFieldId mfid; gboolean descending; gpointer user_data; }; typedef struct _SortFuncData SortFuncData; static int container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) { if (a == b) return 0; else if (!a->msg) return -1; else if (!b->msg) return 1; return mu_msg_cmp (a->msg, b->msg, mfid); } static gboolean container_is_leaf (const MuContainer *c) { return c->child == NULL; } static MuContainer* container_max (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) { return container_cmp (a, b, mfid) > 0 ? a : b; } static MuContainer* find_sorted_tree_leader (MuContainer *root, SortFuncData *order) { MuContainer *last_child; if (container_is_leaf (root)) return root; if (!order->descending) last_child = root->child->last; else /* reversed order, first is last */ last_child = root->child; return container_max (root, last_child->leader, order->mfid); } static int sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) { if (data->descending) return container_cmp (b->leader, a->leader, data->mfid); else return container_cmp (a->leader, b->leader, data->mfid); } static MuContainer* container_sort_real (MuContainer *c, SortFuncData *sfdata) { GSList *lst; MuContainer *cur; if (!c) return NULL; for (cur = c; cur; cur = cur->next) { if (cur->child) cur->child = container_sort_real (cur->child, sfdata); cur->leader = find_sorted_tree_leader (cur, sfdata); } /* sort siblings */ lst = mu_container_to_list (c); lst = g_slist_sort_with_data(lst, (GCompareDataFunc)sort_func_wrapper, sfdata); c = mu_container_from_list (lst); g_slist_free (lst); return c; } MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean descending, gpointer user_data) { SortFuncData sfdata; sfdata.mfid = mfid; sfdata.descending = descending; sfdata.user_data = user_data; g_return_val_if_fail (c, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); return container_sort_real (c, &sfdata); } static gboolean unequal (MuContainer *a, MuContainer *b) { return a == b ? FALSE : TRUE; } gboolean mu_container_reachable (MuContainer *haystack, MuContainer *needle) { g_return_val_if_fail (haystack, FALSE); g_return_val_if_fail (needle, FALSE); if (!mu_container_foreach (haystack, (MuContainerForeachFunc)unequal, needle)) return TRUE; return FALSE; } static gboolean dump_container (MuContainer *c) { const gchar* subject; if (!c) { g_print ("<empty>\n"); return TRUE; } subject = (c->msg) ? mu_msg_get_subject (c->msg) : "<none>"; g_print ("[%s][%s m:%p p:%p docid:%u %s]\n",c->msgid, subject, (void*)c, (void*)c->parent, c->docid, c->msg ? mu_msg_get_path (c->msg) : ""); return TRUE; } void mu_container_dump (MuContainer *c, gboolean recursive) { g_return_if_fail (c); if (!recursive) dump_container (c); else mu_container_foreach (c, (MuContainerForeachFunc)dump_container, NULL); } static Path* path_new (guint initial) { Path *p; p = g_slice_new0 (Path); p->_data = g_new0 (int, initial); p->_len = initial; return p; } static void path_destroy (Path *p) { if (!p) return; g_free (p->_data); g_slice_free (Path, p); } static void path_inc (Path *p, guint index) { if (index + 1 >= p->_len) { p->_data = g_renew (int, p->_data, 2 * p->_len); memset (&p->_data[p->_len], 0, p->_len); p->_len *= 2; } ++p->_data[index]; p->_data[index + 1] = 0; } static gchar* path_to_string (Path *p, const char* frmt) { char *str; guint u; if (!p->_data) return NULL; for (u = 0, str = NULL; p->_data[u] != 0; ++u) { char segm[16]; snprintf (segm, sizeof(segm), frmt, p->_data[u] - 1); if (!str) str = g_strdup (segm); else { gchar *tmp; tmp = g_strdup_printf ("%s:%s", str, segm); g_free (str); str = tmp; } } return str; } static unsigned count_colons (const char *str) { unsigned num; num = 0; while (str++ && *str) if (*str == ':') ++num; return num; } static MuMsgIterThreadInfo* thread_info_new (gchar *threadpath, gboolean root, gboolean first_child, gboolean empty_parent, gboolean has_child, gboolean is_dup) { MuMsgIterThreadInfo *ti; ti = g_slice_new (MuMsgIterThreadInfo); ti->threadpath = threadpath; ti->level = count_colons (threadpath); /* hacky... */ ti->prop = MU_MSG_ITER_THREAD_PROP_NONE; ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0; ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0; ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0; ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0; ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0; return ti; } static void thread_info_destroy (MuMsgIterThreadInfo *ti) { if (ti) { g_free (ti->threadpath); g_slice_free (MuMsgIterThreadInfo, ti); } } struct _ThreadInfo { GHashTable *hash; const char *format; }; typedef struct _ThreadInfo ThreadInfo; static void add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c, char *threadpath) { gboolean is_root, first_child, empty_parent, is_dup, has_child; /* 'root' means we're a child of the dummy root-container */ is_root = (c->parent == NULL); first_child = is_root ? FALSE : (c->parent->child == c); empty_parent = is_root ? FALSE : (!c->parent->msg); is_dup = c->flags & MU_CONTAINER_FLAG_DUP; has_child = c->child ? TRUE : FALSE; g_hash_table_insert (thread_info_hash, GUINT_TO_POINTER(c->docid), thread_info_new (threadpath, is_root, first_child, empty_parent, has_child, is_dup)); } /* device a format string that is the minimum size to fit up to * matchnum matches -- returns static memory */ static const char* thread_segment_format_string (size_t matchnum) { unsigned digitnum; static char frmt[16]; /* get the number of digits needed in a hex-representation of * matchnum */ digitnum = (unsigned) (ceil (log(matchnum)/log(16))); snprintf (frmt, sizeof(frmt), "%%0%ux", digitnum); return frmt; } static gboolean add_thread_info (MuContainer *c, ThreadInfo *ti, Path *path) { gchar *pathstr; pathstr = path_to_string (path, ti->format); add_to_thread_info_hash (ti->hash, c, pathstr); return TRUE; } GHashTable* mu_container_thread_info_hash_new (MuContainer *root_set, size_t matchnum) { ThreadInfo ti; g_return_val_if_fail (root_set, NULL); g_return_val_if_fail (matchnum > 0, NULL); /* create hash docid => thread-info */ ti.hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)thread_info_destroy); ti.format = thread_segment_format_string (matchnum); mu_container_path_foreach (root_set, (MuContainerPathForeachFunc)add_thread_info, &ti); return ti.hash; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-container.h������������������������������������������������������������������������0000664�0000000�0000000�00000014704�12504061031�0015542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_CONTAINER_H__ #define __MU_CONTAINER_H__ #include <glib.h> #include <mu-msg.h> enum _MuContainerFlag { MU_CONTAINER_FLAG_NONE = 0, MU_CONTAINER_FLAG_DELETE = 1 << 0, MU_CONTAINER_FLAG_SPLICE = 1 << 1, MU_CONTAINER_FLAG_DUP = 1 << 2 }; typedef enum _MuContainerFlag MuContainerFlag; /* * MuContainer data structure, as seen in JWZs document: * http://www.jwz.org/doc/threading.html */ struct _MuContainer { struct _MuContainer *parent, *child, *next; /* note: we cache the last of the string of next->next->... * `mu_container_append_siblings' shows up high in the * profiles since it needs to walk to the end, and this give * O(n*n) behavior. * */ struct _MuContainer *last; /* Node in the subtree rooted at this node which comes first * in the descending sort order, e.g. the latest message if * sorting by date. We compare the leaders when ordering * subtrees. */ struct _MuContainer *leader; MuMsg *msg; const char *msgid; unsigned docid; MuContainerFlag flags; }; typedef struct _MuContainer MuContainer; /** * create a new Container object * * @param msg a MuMsg, or NULL; when it's NULL, docid should be 0 * @param docid a Xapian docid, or 0 * @param msgid a message id, or NULL * * @return a new Container instance, or NULL in case of error; free * with mu_container_destroy */ MuContainer* mu_container_new (MuMsg *msg, guint docid, const char* msgid); /** * free a Container object * * @param c a Container object, or NULL */ void mu_container_destroy (MuContainer *c); /** * append new child(ren) to this container; the child(ren) container's * parent pointer will point to this one * * @param c a Container instance * @param child a child * * @return the Container instance with a child added */ MuContainer* mu_container_append_children (MuContainer *c, MuContainer *child); /** * append a new sibling to this (list of) containers; all the siblings * will get the same parent that @c has * * @param c a container instance * @param sibling a sibling * * @return the container (list) with the sibling(s) appended */ MuContainer* mu_container_append_siblings (MuContainer *c, MuContainer *sibling); /** * remove a _single_ child container from a container * * @param c a container instance * @param child the child container to remove * * @return the container with the child removed; if the container did * have this child, nothing changes */ MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child); /** * remove a _single_ sibling container from a container * * @param c a container instance * @param sibling the sibling container to remove * * @return the container with the sibling removed; if the container did * have this sibling, nothing changes */ MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling); /** * promote sibling's children to be this container's siblings * * @param c a container instance * @param sibling a sibling of this container * * @return the container with the sibling's children promoted */ MuContainer* mu_container_splice_children (MuContainer *c, MuContainer *sibling); /** * promote child's children to be parent's children * * @param parent a container instance * @param child a child of this container * * @return the new container with it's children's children promoted */ MuContainer* mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child); typedef gboolean (*MuContainerForeachFunc) (MuContainer*, gpointer); /** * execute some function on all siblings an children of some container * (recursively) until all children have been visited or the callback * function returns FALSE * * @param c a container * @param func a function to call for each container * @param user_data a pointer to pass to the callback function * * @return */ gboolean mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, gpointer user_data); /** * check wither container needle is a child or sibling (recursively) * of container haystack * * @param haystack a container * @param needle a container * * @return TRUE if needle is reachable from haystack, FALSE otherwise */ gboolean mu_container_reachable (MuContainer *haystack, MuContainer *needle); /** * dump the container to stdout (for debugging) * * @param c a container * @param recursive whether to include siblings, children */ void mu_container_dump (MuContainer *c, gboolean recursive); typedef int (*MuContainerCmpFunc) (MuContainer *c1, MuContainer *c2, gpointer user_data); /** * sort the tree of MuContainers, recursively; ie. each of the list of * siblings (children) will be sorted according to @func; if the * container is empty, the first non-empty 'leftmost' child is used. * * @param c a container * @param mfid the field to sort by * @param revert if TRUE, revert the sorting order * * @param user_data a user pointer to pass to the sorting function * * @return a sorted container */ MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean revert, gpointer user_data); /** * create a hashtable with maps document-ids to information about them, * ie. Xapian docid => MuMsgIterThreadInfo * * @param root_set the containers @param matchnum the number of * matches in the list (this is needed to determine the shortest * possible collation keys ('threadpaths') for the messages * * @return a hash; free with g_hash_table_destroy */ GHashTable* mu_container_thread_info_hash_new (MuContainer *root_set, size_t matchnum); #endif /*__MU_CONTAINER_H__*/ ������������������������������������������������������������mu-0.9.12/lib/mu-date.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000014034�12504061031�0014464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012 <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include <stdlib.h> #include <ctype.h> #include "mu-util.h" #include "mu-date.h" #include "mu-str.h" const char* mu_date_str_s (const char* frm, time_t t) { struct tm *tmbuf; static char buf[128]; static int is_utf8 = -1; size_t len; if (G_UNLIKELY(is_utf8 == -1)) is_utf8 = mu_util_locale_is_utf8 () ? 1 : 0; g_return_val_if_fail (frm, NULL); tmbuf = localtime(&t); len = strftime (buf, sizeof(buf) - 1, frm, tmbuf); if (len == 0) return ""; /* not necessarily an error... */ if (!is_utf8) { /* charset is _not_ utf8, so we need to convert it, so * the date could contain locale-specific characters*/ gchar *conv; GError *err; err = NULL; conv = g_locale_to_utf8 (buf, -1, NULL, NULL, &err); if (err) { g_warning ("conversion failed: %s", err->message); g_error_free (err); strcpy (buf, "<error>"); } else strncpy (buf, conv, sizeof(buf)); g_free (conv); } return buf; } char* mu_date_str (const char *frm, time_t t) { return g_strdup (mu_date_str_s(frm, t)); } const char* mu_date_display_s (time_t t) { time_t now; static const time_t SECS_IN_DAY = 24 * 60 * 60; now = time (NULL); if (ABS(now - t) > SECS_IN_DAY) return mu_date_str_s ("%x", t); else return mu_date_str_s ("%X", t); } time_t mu_date_parse_hdwmy (const char *nptr) { long int num; char *endptr; time_t now, delta; time_t never = (time_t)-1; g_return_val_if_fail (nptr, never); num = strtol (nptr, &endptr, 10); if (num <= 0 || num > 9999) return never; if (endptr == NULL || *endptr == '\0') return never; switch (endptr[0]) { case 'h': /* hour */ case 'H': delta = num * 60 * 60; break; case 'd': /* day */ case 'D': delta = num * 24 * 60 * 60; break; case 'w': /* week */ case 'W': delta = num * 7 * 24 * 60 * 60; break; case 'm': /* month */ case 'M': delta = num * 30 * 24 * 60 * 60; break; case 'y': /* year */ case 'Y': delta = num * 365 * 24 * 60 * 60; break; default: return never; } now = time(NULL); return delta <= now ? now - delta : never; } /* clear a date of anything non-numberic; static string, non-reentrant */ static char* clear_date_s (const char *date) { static char cleandate [14 + 1]; unsigned u1, u2; for (u1 = u2 = 0; date[u1] != '\0'; ++u1) if (isdigit(date[u1])) cleandate[u2++] = date[u1]; cleandate[u2] = '\0'; return cleandate; } const char* mu_date_complete_s (const char *date, gboolean is_begin) { static char fulldate[14 + 1]; static const char* full_begin = "00000101000000"; static const char* full_end = "99991231235959"; char *cleardate; g_return_val_if_fail (date, NULL); cleardate = clear_date_s (date); strncpy (fulldate, is_begin ? full_begin : full_end, sizeof(fulldate)); memcpy (fulldate, cleardate, strlen(cleardate)); return fulldate; } char* mu_date_complete (const char *date, gboolean is_begin) { const char *s; g_return_val_if_fail (date, NULL); s = mu_date_complete_s (date, is_begin); return s ? g_strdup (s) : NULL; } const char* mu_date_interpret_s (const char *datespec, gboolean is_begin) { static char fulldate[14 + 1]; time_t now, t; g_return_val_if_fail (datespec, NULL); if (mu_str_is_empty (datespec) && is_begin) return "000000000000"; /* beginning of time*/ now = time(NULL); if (strcmp (datespec, "today") == 0) { strftime(fulldate, sizeof(fulldate), is_begin ? "%Y%m%d000000" : "%Y%m%d235959", localtime(&now)); return fulldate; } if (mu_str_is_empty (datespec) || strcmp (datespec, "now") == 0) { strftime(fulldate, sizeof(fulldate), "%Y%m%d%H%M%S", localtime(&now)); return fulldate; } t = mu_date_parse_hdwmy (datespec); if (t != (time_t)-1) { strftime(fulldate, sizeof(fulldate), "%Y%m%d%H%M%S", localtime(&t)); return fulldate; } return datespec; /* nothing changed */ } char* mu_date_interpret (const char *datespec, gboolean is_begin) { char *s; g_return_val_if_fail (datespec, NULL); s = mu_date_interpret (datespec, is_begin); return s ? g_strdup(s) : NULL; } time_t mu_date_str_to_time_t (const char* date, gboolean local) { struct tm tm; char mydate[14 + 1]; /* YYYYMMDDHHMMSS */ time_t t; memset (&tm, 0, sizeof(struct tm)); strncpy (mydate, date, 15); mydate[sizeof(mydate)-1] = '\0'; g_return_val_if_fail (date, (time_t)-1); tm.tm_sec = atoi (mydate + 12); mydate[12] = '\0'; tm.tm_min = atoi (mydate + 10); mydate[10] = '\0'; tm.tm_hour = atoi (mydate + 8); mydate[8] = '\0'; tm.tm_mday = atoi (mydate + 6); mydate[6] = '\0'; tm.tm_mon = atoi (mydate + 4) - 1; mydate[4] = '\0'; tm.tm_year = atoi (mydate) - 1900; tm.tm_isdst = -1; /* let timegm/mktime figure out the dst */ if (local) t = mktime (&tm); else t = timegm (&tm); /* GNU/BSD specific */ return t; } const char* mu_date_time_t_to_str_s (time_t t, gboolean local) { /* static char datestr[14 + 1]; /\* YYYYMMDDHHMMSS *\/ */ static char datestr[14+1]; /* YYYYMMDDHHMMSS */ static const char *frm = "%Y%m%d%H%M%S"; size_t len; len = strftime (datestr, sizeof(datestr), frm, local ? localtime (&t) : gmtime(&t)); if (len == 0) { g_warning ("bug: error converting time"); return "00000000000000"; } return datestr; } char* mu_date_time_t_to_str (time_t t, gboolean local) { const char* str; str = mu_date_time_t_to_str_s (t, local); return str ? g_strdup(str): NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-date.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000010547�12504061031�0014476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <glib.h> #ifndef __MU_DATE_H__ #define __MU_DATE_H__ G_BEGIN_DECLS /** * @addtogroup MuDate * Date-related functions * @{ */ /** * get a string for a given time_t * * mu_date_str_s returns a ptr to a static buffer, * while mu_date_str returns dynamically allocated * memory that must be freed after use. * * @param frm the format of the string (in strftime(3) format) * @param t the time as time_t * * @return a string representation of the time; see above for what to * do with it. Lenght is max. 128 bytes, inc. the ending \0. if the * format is too long, the value will be truncated. in practice this * should not happen. */ const char* mu_date_str_s (const char* frm, time_t t) G_GNUC_CONST; char* mu_date_str (const char* frm, time_t t) G_GNUC_WARN_UNUSED_RESULT; /** * get a display string for a given time_t; if the given is less than * 24h from the current time, we display the time, otherwise the date, * using the preferred date/time for the current locale * * mu_str_display_date_s returns a ptr to a static buffer, * * @param t the time as time_t * * @return a string representation of the time/date */ const char* mu_date_display_s (time_t t); /** * * parse strings like 1h, 3w, 2m to mean '1 hour before now', '3 weeks * before now' and '2 * 30 days before now' * * the format is <n>(h|d|w|m|y), where <n> is an integer > 0, and * h=hour, d=day, w=week, m=30 days, year=365 days. function returns * *now* minus this value as time_t (UTC) * * if the number cannot be parsed, return (time_t)-1 * * @param str a str * * @return the time_t of the point in time indicated by 'now' minus * the value, or (time_t)-1 otherwise */ time_t mu_date_parse_hdwmy (const char* str); /** * complete a date (a string of the form YYYYMMDDHHMMSS with [0..14] * of the rightmost characters missing) to the the full YYYYMMDDHHMMSS. * * if is_begin is TRUE, add to the 'floor' (e.g, * 20110101=>20110101000000), otherwise go to the 'ceiling', * e.g. 2009=>20091231235050) * * @param date a date string (assumed to have the beginning of the * date, this is not checked * @param is_begin if TRUE go to floor (as described), otherwise to * the ceiling * * @return mu_date_complete: return a newly allocated string (free * with g_free) with the full, 14-char date; mu_date_complete_s: * return a statically allocated string. NOT REENTRANT. */ char* mu_date_complete (const char *date, gboolean is_begin); const char* mu_date_complete_s (const char *date, gboolean is_begin); /** * * * @param datespec * @param is_begin * * @return */ const char* mu_date_interpret_s (const char *datespec, gboolean is_begin); char* mu_date_interpret (const char *datespec, gboolean is_begin); /** * convert a date of the form 'YYYYMMDDHHMMSS' into time_t * * @param date a date str of the form 'YYYYMMDDHHMMSS' * @param local if TRUE, source is assumed to bin in local time, UTC otherwise * * @return the corresponding time_t, or (time_t)-1 in case of error */ time_t mu_date_str_to_time_t (const char* date, gboolean local); /** * convert a time_t value into a date string of the form * 'YYYYMMDDHHMMSS'; assume UTC * * @param t a time_t value * @param local if TRUE, convert to local time, otherwise use UTC * * @return mu_date_time_t_to_str_s: a static string (don't modify, * non-reentrant) of the form 'YYYYMMDDHHMMSS'; mu_date_time_t_to_str: * return a newly allocated string with the same. */ const char* mu_date_time_t_to_str_s (time_t t, gboolean local); char* mu_date_time_t_to_str (time_t t, gboolean local); /** @} */ G_END_DECLS #endif /*__MU_DATE_H__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-flags.c����������������������������������������������������������������������������0000664�0000000�0000000�00000011103�12504061031�0014635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2012 <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include "mu-flags.h" struct _FlagInfo { MuFlags flag; char kar; const char *name; MuFlagType flag_type; }; typedef struct _FlagInfo FlagInfo; static const FlagInfo FLAG_INFO[] = { /* NOTE: order of this is significant, due to optimizations * below */ { MU_FLAG_DRAFT, 'D', "draft", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_FLAGGED, 'F', "flagged", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_PASSED, 'P', "passed", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_REPLIED, 'R', "replied", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_SEEN, 'S', "seen", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_TRASHED, 'T', "trashed", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_NEW, 'N', "new", MU_FLAG_TYPE_MAILDIR }, { MU_FLAG_SIGNED, 'z', "signed", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_ENCRYPTED, 'x', "encrypted", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_HAS_ATTACH, 'a', "attach", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_LIST, 'l', "list", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_UNREAD, 'u', "unread", MU_FLAG_TYPE_PSEUDO } }; MuFlagType mu_flag_type (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].flag_type; return MU_FLAG_TYPE_INVALID; } char mu_flag_char (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].kar; return 0; } static MuFlags mu_flag_from_char (char kar) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].kar == kar) return FLAG_INFO[u].flag; return MU_FLAG_INVALID; } const char* mu_flag_name (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].name; return NULL; } const char* mu_flags_to_str_s (MuFlags flags, MuFlagType types) { unsigned u,v; static char str[sizeof(FLAG_INFO) + 1]; for (u = 0, v = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) if (flags & FLAG_INFO[u].flag && types & FLAG_INFO[u].flag_type) str[v++] = FLAG_INFO[u].kar; str[v] = '\0'; return str; } MuFlags mu_flags_from_str (const char *str, MuFlagType types, gboolean ignore_invalid) { const char *cur; MuFlags flag; g_return_val_if_fail (str, MU_FLAG_INVALID); for (cur = str, flag = MU_FLAG_NONE; *cur; ++cur) { MuFlags f; f = mu_flag_from_char (*cur); if (f == MU_FLAG_INVALID) { if (ignore_invalid) continue; return MU_FLAG_INVALID; } if (mu_flag_type (f) & types) flag |= f; } return flag; } char* mu_flags_custom_from_str (const char *str) { char *custom; const char* cur; unsigned u; g_return_val_if_fail (str, NULL); for (cur = str, u = 0, custom = NULL; *cur; ++cur) { MuFlags flag; flag = mu_flag_from_char (*cur); /* if it's a valid file flag, ignore it */ if (flag != MU_FLAG_INVALID && mu_flag_type (flag) == MU_FLAG_TYPE_MAILFILE) continue; /* otherwise, add it to our custom string */ if (!custom) custom = g_new0 (char, strlen(str) + 1); custom[u++] = *cur; } return custom; } void mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data) { unsigned u; g_return_if_fail (func); for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) func (FLAG_INFO[u].flag, user_data); } MuFlags mu_flags_from_str_delta (const char *str, MuFlags oldflags, MuFlagType types) { const char *cur; MuFlags newflags; g_return_val_if_fail (str, MU_FLAG_INVALID); for (cur = str, newflags = oldflags; *cur; ++cur) { MuFlags f; if (*cur == '+' || *cur == '-') { f = mu_flag_from_char (cur[1]); if (f == 0) goto error; if (*cur == '+') newflags |= f; else newflags &= ~f; ++cur; continue; } goto error; } return newflags; error: g_warning ("invalid flag string"); return MU_FLAG_INVALID; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-flags.h����������������������������������������������������������������������������0000664�0000000�0000000�00000011253�12504061031�0014650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_FLAGS_H__ #define __MU_FLAGS_H__ #include <glib.h> G_BEGIN_DECLS enum _MuFlags { MU_FLAG_NONE = 0, /* next 6 are seen in the file-info part of maildir message * file names, ie., in a name like "1234345346:2,<fileinfo>", * <fileinfo> consists of zero or more of the following * characters (in ascii order) */ MU_FLAG_DRAFT = 1 << 0, MU_FLAG_FLAGGED = 1 << 1, MU_FLAG_PASSED = 1 << 2, MU_FLAG_REPLIED = 1 << 3, MU_FLAG_SEEN = 1 << 4, MU_FLAG_TRASHED = 1 << 5, /* decides on cur/ or new/ in the maildir */ MU_FLAG_NEW = 1 << 6, /* content flags -- not visible in the filename, but used for * searching */ MU_FLAG_SIGNED = 1 << 7, MU_FLAG_ENCRYPTED = 1 << 8, MU_FLAG_HAS_ATTACH = 1 << 9, /* pseudo-flag, only for queries, so we can search for * flag:unread, which is equivalent to 'flag:new OR NOT * flag:seen' */ MU_FLAG_UNREAD = 1 << 10, /* other content flags */ MU_FLAG_LIST = 1 << 11 }; typedef enum _MuFlags MuFlags; #define MU_FLAG_INVALID ((MuFlags)-1) enum _MuFlagType { MU_FLAG_TYPE_MAILFILE = 1 << 0, MU_FLAG_TYPE_MAILDIR = 1 << 1, MU_FLAG_TYPE_CONTENT = 1 << 2, MU_FLAG_TYPE_PSEUDO = 1 << 3 }; typedef enum _MuFlagType MuFlagType; #define MU_FLAG_TYPE_ANY ((MuFlagType)-1) #define MU_FLAG_TYPE_INVALID ((MuFlagType)-1) /** * Get the type of flag (mailfile, maildir, pseudo or content) * * @param flag a MuFlag * * @return the flag type or MU_FLAG_TYPE_INVALID in case of error */ MuFlagType mu_flag_type (MuFlags flag) G_GNUC_CONST; /** * Get the flag character * * @param flag a MuFlag (single) * * @return the character, or 0 if it's not a valid flag */ char mu_flag_char (MuFlags flag) G_GNUC_CONST; /** * Get the flag name * * @param flag a single MuFlag * * @return the name (don't free) as string or NULL in case of error */ const char* mu_flag_name (MuFlags flag) G_GNUC_CONST; /** * Get the string representation of an OR'ed set of flags * * @param flags MuFlag (OR'ed) * @param types allowable types (OR'ed) for the result; the rest is ignored * * @return The string representation (static, don't free), or NULL in * case of error */ const char* mu_flags_to_str_s (MuFlags flags, MuFlagType types); /** * Get the (OR'ed) flags corresponding to a string representation * * @param str the file info string * @param types the flag types to accept (other will be ignored) * @param ignore invalid if TRUE, ignore invalid flags, otherwise return * MU_FLAG_INVALID if an invalid flag is encountered * * @return the (OR'ed) flags */ MuFlags mu_flags_from_str (const char *str, MuFlagType types, gboolean ignore_invalid); /** * return the concatenation of all non-standard file flags in str * (ie., characters other than DFPRST) as a newly allocated string. * * @param str the file info string * * @return concatenation of all non-standard flags, as a string; free * with g_free when done. If there are no such flags, return NULL. */ char* mu_flags_custom_from_str (const char *str) G_GNUC_WARN_UNUSED_RESULT; /** * Update #oldflags with the flags in #str, where #str consists of the * the normal flag characters, but prefixed with either '+' or '-', * which means resp. "add this flag" or "remove this flag" from * oldflags. So, e.g. "-N+S" would unset the NEW flag and set the * SEEN flag, without affecting other flags. * * @param str the string representation * @param old flags to update * @param types the flag types to accept (other will be ignored) * * @return */ MuFlags mu_flags_from_str_delta (const char *str, MuFlags oldflags, MuFlagType types); typedef void (*MuFlagsForeachFunc) (MuFlags flag, gpointer user_data); /** * call a function for each available flag * * @param func a function to call * @param user_data a user pointer to pass to the function */ void mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_FLAGS_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-index.c����������������������������������������������������������������������������0000664�0000000�0000000�00000024662�12504061031�0014666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify 1** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "mu-index.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <glib.h> #include <glib/gstdio.h> #include <errno.h> #include "mu-maildir.h" #include "mu-store.h" #include "mu-util.h" #define MU_LAST_USED_MAILDIR_KEY "last_used_maildir" #define MU_INDEX_MAX_FILE_SIZE (50*1000*1000) /* 50 Mb */ struct _MuIndex { MuStore *_store; gboolean _needs_reindex; guint _max_filesize; }; MuIndex* mu_index_new (MuStore *store, GError **err) { MuIndex *index; unsigned count; g_return_val_if_fail (store, NULL); g_return_val_if_fail (!mu_store_is_read_only(store), NULL); index = g_new0 (MuIndex, 1); index->_store = mu_store_ref (store); /* set the default max file size */ index->_max_filesize = MU_INDEX_MAX_FILE_SIZE; count = mu_store_count (store, err); if (count == (unsigned)-1) return NULL; else if (count == 0) index->_needs_reindex = FALSE; /* FIXME */ /* else */ /* index->_needs_reindex = */ /* mu_store_database_needs_upgrade (xpath); */ return index; } void mu_index_destroy (MuIndex *index) { if (!index) return; mu_store_unref (index->_store); g_free (index); } struct _MuIndexCallbackData { MuIndexMsgCallback _idx_msg_cb; MuIndexDirCallback _idx_dir_cb; MuStore* _store; void* _user_data; MuIndexStats* _stats; gboolean _reindex; time_t _dirstamp; guint _max_filesize; }; typedef struct _MuIndexCallbackData MuIndexCallbackData; /* checks to determine if we need to (re)index this message note: * simply checking timestamps is not good enough because message may * be moved from other dirs (e.g. from 'new' to 'cur') and the time * stamps won't change. */ static inline gboolean needs_index (MuIndexCallbackData *data, const char *fullpath, time_t filestamp) { /* unconditionally reindex */ if (data->_reindex) return TRUE; /* it's not in the database yet (FIXME: GError)*/ if (!mu_store_contains_message (data->_store, fullpath, NULL)) return TRUE; /* it's there, but it's not up to date */ if ((unsigned)filestamp >= (unsigned)data->_dirstamp) return TRUE; return FALSE; /* index not needed */ } static MuError insert_or_update_maybe (const char *fullpath, const char *mdir, time_t filestamp, MuIndexCallbackData *data, gboolean *updated) { MuMsg *msg; GError *err; gboolean rv; *updated = FALSE; if (!needs_index (data, fullpath, filestamp)) return MU_OK; /* nothing to do for this one */ err = NULL; msg = mu_msg_new_from_file (fullpath, mdir, &err); if (!msg) { if (!err) g_warning ("error creating message object: %s", fullpath); else { g_warning ("%s", err->message); g_clear_error (&err); } /* warn, then simply continue */ return MU_OK; } /* we got a valid id; scan the message contents as well */ rv = mu_store_add_msg (data->_store, msg, &err); mu_msg_unref (msg); if (!rv) { g_warning ("error storing message object: %s", err ? err->message : "cause unknown"); g_clear_error (&err); return MU_ERROR; } *updated = TRUE; return MU_OK; } static MuError run_msg_callback_maybe (MuIndexCallbackData *data) { MuError result; if (!data || !data->_idx_msg_cb) return MU_OK; result = data->_idx_msg_cb (data->_stats, data->_user_data); if (G_UNLIKELY(result != MU_OK && result != MU_STOP)) g_warning ("error in callback"); return result; } static MuError on_run_maildir_msg (const char *fullpath, const char *mdir, struct stat *statbuf, MuIndexCallbackData *data) { MuError result; gboolean updated; /* protect against too big messages */ if (G_UNLIKELY(statbuf->st_size > data->_max_filesize)) { g_warning ("ignoring because bigger than %u bytes: %s", data->_max_filesize, fullpath); return MU_OK; /* not an error */ } result = run_msg_callback_maybe (data); if (result != MU_OK) return result; /* see if we need to update/insert anything... * use the ctime, so any status change will be visible (perms, * filename etc.)*/ result = insert_or_update_maybe (fullpath, mdir, statbuf->st_ctime, data, &updated); if (result == MU_OK && data && data->_stats) { /* update statistics */ ++data->_stats->_processed; updated ? ++data->_stats->_updated : ++data->_stats->_uptodate; } return result; } static MuError on_run_maildir_dir (const char* fullpath, gboolean enter, MuIndexCallbackData *data) { GError *err; err = NULL; /* xapian stores a per-dir timestamp; we use this timestamp * to determine whether a message is up-to-data */ if (enter) { data->_dirstamp = mu_store_get_timestamp (data->_store, fullpath, &err); g_debug ("entering %s (ts==%u)", fullpath, (unsigned)data->_dirstamp); } else { time_t now; now = time (NULL); mu_store_set_timestamp (data->_store, fullpath, now, &err); g_debug ("leaving %s (ts=%u)", fullpath, (unsigned)data->_dirstamp); } if (data->_idx_dir_cb) return data->_idx_dir_cb (fullpath, enter, data->_user_data); if (err) { MU_WRITE_LOG ("%s: %s", __FUNCTION__, err->message); g_clear_error(&err); } return MU_OK; } static gboolean check_path (const char *path) { g_return_val_if_fail (path, FALSE); if (!g_path_is_absolute (path)) { g_warning ("%s: not an absolute path: %s", __FUNCTION__, path); return FALSE; } if (access (path, R_OK) != 0) { g_warning ("%s: cannot open '%s': %s", __FUNCTION__, path, strerror (errno)); return FALSE; } return TRUE; } static void init_cb_data (MuIndexCallbackData *cb_data, MuStore *xapian, gboolean reindex, guint max_filesize, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data) { cb_data->_idx_msg_cb = msg_cb; cb_data->_idx_dir_cb = dir_cb; cb_data->_user_data = user_data; cb_data->_store = xapian; cb_data->_reindex = reindex; cb_data->_dirstamp = 0; cb_data->_max_filesize = max_filesize; cb_data->_stats = stats; if (cb_data->_stats) memset (cb_data->_stats, 0, sizeof(MuIndexStats)); } void mu_index_set_max_msg_size (MuIndex *index, guint max_size) { g_return_if_fail (index); if (max_size == 0) index->_max_filesize = MU_INDEX_MAX_FILE_SIZE; else index->_max_filesize = max_size; } void mu_index_set_xbatch_size (MuIndex *index, guint xbatchsize) { g_return_if_fail (index); mu_store_set_batch_size (index->_store, xbatchsize); } MuError mu_index_run (MuIndex *index, const char *path, gboolean reindex, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data) { MuIndexCallbackData cb_data; MuError rv; g_return_val_if_fail (index && index->_store, MU_ERROR); g_return_val_if_fail (msg_cb, MU_ERROR); if (!check_path (path)) return MU_ERROR; if (!reindex && index->_needs_reindex) { g_warning ("database not up-to-date; needs full reindex"); return MU_ERROR; } init_cb_data (&cb_data, index->_store, reindex, index->_max_filesize, stats, msg_cb, dir_cb, user_data); rv = mu_maildir_walk (path, (MuMaildirWalkMsgCallback)on_run_maildir_msg, (MuMaildirWalkDirCallback)on_run_maildir_dir, reindex, /* re-index, ie. do a full update */ &cb_data); mu_store_flush (index->_store); return rv; } static MuError on_stats_maildir_file (const char *fullpath, const char *mdir, struct stat *statbuf, MuIndexCallbackData *cb_data) { MuError result; if (cb_data && cb_data->_idx_msg_cb) result = cb_data->_idx_msg_cb (cb_data->_stats, cb_data->_user_data); else result = MU_OK; if (result == MU_OK) { if (cb_data->_stats) ++cb_data->_stats->_processed; return MU_OK; } return result; /* MU_STOP or MU_OK */ } MuError mu_index_stats (MuIndex *index, const char *path, MuIndexStats *stats, MuIndexMsgCallback cb_msg, MuIndexDirCallback cb_dir, void *user_data) { MuIndexCallbackData cb_data; g_return_val_if_fail (index, MU_ERROR); g_return_val_if_fail (cb_msg, MU_ERROR); if (!check_path (path)) return MU_ERROR; if (stats) memset (stats, 0, sizeof(MuIndexStats)); cb_data._idx_msg_cb = cb_msg; cb_data._idx_dir_cb = cb_dir; cb_data._stats = stats; cb_data._user_data = user_data; cb_data._dirstamp = 0; return mu_maildir_walk (path, (MuMaildirWalkMsgCallback)on_stats_maildir_file, NULL, FALSE, &cb_data); } struct _CleanupData { MuStore *_store; MuIndexStats *_stats; MuIndexCleanupDeleteCallback _cb; void *_user_data; }; typedef struct _CleanupData CleanupData; static MuError foreach_doc_cb (const char* path, CleanupData *cudata) { if (access (path, R_OK) != 0) { if (errno != EACCES) g_debug ("cannot access %s: %s", path, strerror(errno)); if (!mu_store_remove_path (cudata->_store, path)) return MU_ERROR; /* something went wrong... bail out */ if (cudata->_stats) ++cudata->_stats->_cleaned_up; } if (cudata->_stats) ++cudata->_stats->_processed; if (!cudata->_cb) return MU_OK; return cudata->_cb (cudata->_stats, cudata->_user_data); } MuError mu_index_cleanup (MuIndex *index, MuIndexStats *stats, MuIndexCleanupDeleteCallback cb, void *user_data, GError **err) { MuError rv; CleanupData cudata; g_return_val_if_fail (index, MU_ERROR); cudata._store = index->_store; cudata._stats = stats; cudata._cb = cb; cudata._user_data = user_data; rv = mu_store_foreach (index->_store, (MuStoreForeachFunc)foreach_doc_cb, &cudata, err); mu_store_flush (index->_store); return rv; } gboolean mu_index_stats_clear (MuIndexStats *stats) { if (!stats) return FALSE; memset (stats, 0, sizeof(MuIndexStats)); return TRUE; } ������������������������������������������������������������������������������mu-0.9.12/lib/mu-index.h����������������������������������������������������������������������������0000664�0000000�0000000�00000016236�12504061031�0014671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_INDEX_H__ #define __MU_INDEX_H__ #include <stdlib.h> #include <glib.h> #include <mu-util.h> /* for MuResult */ #include <mu-store.h> G_BEGIN_DECLS /* opaque structure */ struct _MuIndex; typedef struct _MuIndex MuIndex; struct _MuIndexStats { unsigned _processed; /* number of msgs processed or counted */ unsigned _updated; /* number of msgs new or updated */ unsigned _cleaned_up; /* number of msgs cleaned up */ unsigned _uptodate; /* number of msgs already uptodate */ }; typedef struct _MuIndexStats MuIndexStats; /** * create a new MuIndex instance. NOTE(1): the database does not have * to exist yet, but the directory must already exist; NOTE(2): before * doing anything with the returned Index object, make sure you haved * called g_type_init, and mu_msg_init somewhere in your code. * * @param store a writable MuStore object * @param err to receive error or NULL; there are only errors when this * function returns NULL. Possible errors: see mu-error.h * * @return a new MuIndex instance, or NULL in case of error */ MuIndex* mu_index_new (MuStore *store, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy the index instance * * @param index a MuIndex instance, or NULL */ void mu_index_destroy (MuIndex *index); /** * change the maximum file size that mu-index considers from its * default (MU_INDEX_MAX_FILE_SIZE). Note that the maximum size is a * protection against mu (or the libraries it uses) allocating too * much memory, which can lead to problems * * @param index a mu index object * @param max_size the maximum msg size, or 0 to reset to the default */ void mu_index_set_max_msg_size (MuIndex *index, guint max_size); /** * change batch size for Xapian store transaction (see * 'mu_store_set_batch_size') * * @param index a mu index object * @param max_size the batch size, or 0 to reset to the default */ void mu_index_set_xbatch_size (MuIndex *index, guint xbatchsize); /** * callback function for mu_index_(run|stats|cleanup), for each message * * @param stats pointer to structure to receive statistics data * @param user_data pointer to user data * * @return MU_OK to continue, MU_STOP to stop, or MU_ERROR in * case of some error. */ typedef MuError (*MuIndexMsgCallback) (MuIndexStats* stats, void *user_data); /** * callback function for mu_index_(run|stats|cleanup), for each dir enter/leave * * @param path dirpath we just entered / left * @param enter did we enter (TRUE) or leave(FALSE) the dir? * @param user_data pointer to user data * * @return MU_OK to contiue, MU_STOP to stopd or MU_ERROR in * case of some error. */ typedef MuError (*MuIndexDirCallback) (const char* path, gboolean enter, void *user_data); /** * start the indexing process * * @param index a valid MuIndex instance * @param path the path to index. This must be an absolute path * @param force if != 0, force re-indexing already index messages; this is * obviously a lot slower than only indexing new/changed messages * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param cb_msg a callback function called for every msg indexed; * @param cb_dir a callback function called for every dir entered/left or NULL * @param user_data a user pointer that will be passed to the callback function * * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_run (MuIndex *index, const char *path, gboolean force, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data); /** * gather some statistics about the Maildir; this is usually much faster * than mu_index_run, and can thus be used to provide some information to the user * note though that the statistics may be different from the reality that * mu_index_run sees, when there are updates in the Maildir * * @param index a valid MuIndex instance * @param path the path to get stats for; this must be an absolute path * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param msg_cb a callback function which will be called for every msg; * @param dir_cb a callback function which will be called for every dir or NULL * @param user_data a user pointer that will be passed to the callback function * xb * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_stats (MuIndex *index, const char *path, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data); /** * callback function called for each message * * @param MuIndexCleanupCallback * * @return a MuResult */ typedef MuError (*MuIndexCleanupDeleteCallback) (MuIndexStats *stats, void *user_data); /** * cleanup the database; ie. remove entries for which no longer a corresponding * file exists in the maildir * * @param index a valid MuIndex instance * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param cb a callback function which will be called for every msg; * @param user_data a user pointer that will be passed to the callback function * @param err to receive error info or NULL. err->code is MuError value * * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_cleanup (MuIndex *index, MuIndexStats *stats, MuIndexCleanupDeleteCallback cb, void *user_data, GError **err); /** * clear the stats structure * * @param stats a MuIndexStats object * * @return TRUE if stats != NULL, FALSE otherwise */ gboolean mu_index_stats_clear (MuIndexStats *stats); G_END_DECLS #endif /*__MU_INDEX_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-log.c������������������������������������������������������������������������������0000664�0000000�0000000�00000015344�12504061031�0014335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include <config.h> #endif /*HAVE_CONFIG_H*/ #include "mu-log.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <time.h> #include <errno.h> #include <string.h> #include "mu-util.h" #define MU_MAX_LOG_FILE_SIZE 1000 * 1000 /* 1 MB (SI units) */ #define MU_LOG_FILE "mu.log" struct _MuLog { int _fd; /* log file descriptor */ MuLogOptions _opts; gboolean _color_stdout; /* whether to use color */ gboolean _color_stderr; GLogFunc _old_log_func; }; typedef struct _MuLog MuLog; /* we use globals, because logging is a global operation as it * globally modifies the behaviour of g_warning and friends */ static MuLog* MU_LOG = NULL; static void log_write (const char* domain, GLogLevelFlags level, const gchar *msg); static void try_close (int fd) { if (fd < 0) return; if (close (fd) < 0) g_printerr ("%s: close() of fd %d failed: %s\n", __FUNCTION__, fd, strerror(errno)); } static void silence (void) { return; } gboolean mu_log_init_silence (void) { g_return_val_if_fail (!MU_LOG, FALSE); MU_LOG = g_new0 (MuLog, 1); MU_LOG->_fd = -1; mu_log_options_set (MU_LOG_OPTIONS_NONE); MU_LOG->_old_log_func = g_log_set_default_handler ((GLogFunc)silence, NULL); return TRUE; } static void log_handler (const gchar* log_domain, GLogLevelFlags log_level, const gchar* msg) { if ((log_level & G_LOG_LEVEL_DEBUG) && !(MU_LOG->_opts & MU_LOG_OPTIONS_DEBUG)) return; log_write (log_domain ? log_domain : "mu", log_level, msg); } void mu_log_options_set (MuLogOptions opts) { g_return_if_fail (MU_LOG); MU_LOG->_opts = opts; /* when color is, only enable it when output is to a tty */ if (MU_LOG->_opts & MU_LOG_OPTIONS_COLOR) { MU_LOG->_color_stdout = isatty(fileno(stdout)); MU_LOG->_color_stderr = isatty(fileno(stderr)); } } MuLogOptions mu_log_options_get (void) { g_return_val_if_fail (MU_LOG, MU_LOG_OPTIONS_NONE); return MU_LOG->_opts; } static gboolean move_log_file (const char *logfile) { gchar *logfile_old; int rv; logfile_old = g_strdup_printf ("%s.old", logfile); rv = rename (logfile, logfile_old); g_free (logfile_old); if (rv != 0) { g_warning ("failed to move %s to %s.old: %s", logfile, logfile, strerror(rv)); return FALSE; } else return TRUE; } static gboolean log_file_backup_maybe (const char *logfile) { struct stat statbuf; if (stat (logfile, &statbuf) != 0) { if (errno == ENOENT) return TRUE; /* it did not exist yet, no problem */ else { g_warning ("failed to stat(2) %s: %s", logfile, strerror(errno)); return FALSE; } } /* log file is still below the max size? */ if (statbuf.st_size <= MU_MAX_LOG_FILE_SIZE) return TRUE; /* log file is too big!; we move it to <logfile>.old, overwriting */ return move_log_file (logfile); } gboolean mu_log_init (const char* logfile, MuLogOptions opts) { int fd; /* only init once... */ g_return_val_if_fail (!MU_LOG, FALSE); g_return_val_if_fail (logfile, FALSE); if (opts & MU_LOG_OPTIONS_BACKUP) if (!log_file_backup_maybe(logfile)) { g_warning ("failed to backup log file"); return FALSE; } fd = open (logfile, O_WRONLY|O_CREAT|O_APPEND, 00600); if (fd < 0) { g_warning ("%s: open() of '%s' failed: %s", __FUNCTION__, logfile, strerror(errno)); return FALSE; } MU_LOG = g_new0 (MuLog, 1); MU_LOG->_fd = fd; mu_log_options_set (opts); MU_LOG->_old_log_func = g_log_set_default_handler ((GLogFunc)log_handler, NULL); MU_WRITE_LOG ("logging started"); return TRUE; } void mu_log_uninit (void) { if (!MU_LOG) return; MU_WRITE_LOG ("logging stopped"); try_close (MU_LOG->_fd); g_free (MU_LOG); MU_LOG = NULL; } static const char* levelstr (GLogLevelFlags level) { switch (level) { case G_LOG_LEVEL_WARNING: return " [WARN] "; case G_LOG_LEVEL_ERROR : return " [ERR ] "; case G_LOG_LEVEL_DEBUG: return " [DBG ] "; case G_LOG_LEVEL_CRITICAL: return " [CRIT] "; case G_LOG_LEVEL_MESSAGE: return " [MSG ] "; case G_LOG_LEVEL_INFO : return " [INFO] "; default: return " [LOG ] "; } } #define color_stdout_maybe(C) \ do{if (MU_LOG->_color_stdout) fputs ((C),stdout);} while (0) #define color_stderr_maybe(C) \ do{if (MU_LOG->_color_stderr) fputs ((C),stderr);} while (0) static void log_write_fd (GLogLevelFlags level, const gchar *msg) { time_t now; char timebuf [22]; const char *mylevel; /* get the time/date string */ now = time(NULL); strftime (timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", localtime(&now)); if (write (MU_LOG->_fd, timebuf, strlen (timebuf)) < 0) goto err; mylevel = levelstr (level); if (write (MU_LOG->_fd, mylevel, strlen (mylevel)) < 0) goto err; if (write (MU_LOG->_fd, msg, strlen (msg)) < 0) goto err; if (write (MU_LOG->_fd, "\n", strlen ("\n")) < 0) goto err; return; /* all went well */ err: fprintf (stderr, "%s: failed to write to log: %s\n", __FUNCTION__, strerror(errno)); } static void log_write_stdout_stderr (GLogLevelFlags level, const gchar *msg) { const char *mu; mu = MU_LOG->_opts & MU_LOG_OPTIONS_NEWLINE ? "\nmu: " : "mu: "; if (!(MU_LOG->_opts & MU_LOG_OPTIONS_QUIET) && (level & G_LOG_LEVEL_MESSAGE)) { color_stdout_maybe (MU_COLOR_GREEN); fputs (mu, stdout); fputs (msg, stdout); fputs ("\n", stdout); color_stdout_maybe (MU_COLOR_DEFAULT); } /* for errors, log them to stderr as well */ if (level & G_LOG_LEVEL_ERROR || level & G_LOG_LEVEL_CRITICAL || level & G_LOG_LEVEL_WARNING) { color_stderr_maybe (MU_COLOR_RED); fputs (mu, stderr); fputs (msg, stderr); fputs ("\n", stderr); color_stderr_maybe (MU_COLOR_DEFAULT); } } static void log_write (const char* domain, GLogLevelFlags level, const gchar *msg) { g_return_if_fail (MU_LOG); log_write_fd (level, msg); log_write_stdout_stderr (level, msg); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-log.h������������������������������������������������������������������������������0000664�0000000�0000000�00000005311�12504061031�0014333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_LOG_H__ #define __MU_LOG_H__ #include <glib.h> /* mu log is the global logging system */ G_BEGIN_DECLS enum _MuLogOptions { MU_LOG_OPTIONS_NONE = 0, /* when size of log file > MU_MAX_LOG_FILE_SIZE, move the log * file to <log file>.old and start a new one. The .old file will * overwrite existing files of that name */ MU_LOG_OPTIONS_BACKUP = 1 << 1, /* quiet: don't log non-errors to stdout/stderr */ MU_LOG_OPTIONS_QUIET = 1 << 2, /* should lines be preceded by \n? useful when errors come * during indexing */ MU_LOG_OPTIONS_NEWLINE = 1 << 3, /* color in output (iff output is to a tty) */ MU_LOG_OPTIONS_COLOR = 1 << 4, /* log everything to stderr */ MU_LOG_OPTIONS_STDERR = 1 << 5, /* debug: debug include debug-level information */ MU_LOG_OPTIONS_DEBUG = 1 << 6 }; typedef enum _MuLogOptions MuLogOptions; /** * write logging information to a log file * * @param full path to the log file (does not have to exist yet, but * it's directory must) * @param opts logging options * * @return TRUE if initialization succeeds, FALSE otherwise */ gboolean mu_log_init (const char *logfile, MuLogOptions opts) G_GNUC_WARN_UNUSED_RESULT; /** * be silent except for runtime errors, which will be written to * stderr. * * @return TRUE if initialization succeeds, FALSE otherwise */ gboolean mu_log_init_silence (void) G_GNUC_WARN_UNUSED_RESULT; /** * unitialize the logging system, and free all resources */ void mu_log_uninit (void); /** * set logging options, a logical-OR'd value of MuLogOptions * * @param opts the options (logically OR'd) */ void mu_log_options_set (MuLogOptions opts); /** * get logging options, a logical-OR'd value of MuLogOptions * * @param opts the options (logically OR'd) */ MuLogOptions mu_log_options_get (void); G_END_DECLS #endif /*__MU_LOG_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-maildir.c��������������������������������������������������������������������������0000664�0000000�0000000�00000053155�12504061031�0015177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to 59the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <glib/gprintf.h> #include "mu-util.h" #include "mu-maildir.h" #include "mu-str.h" #define MU_MAILDIR_NOINDEX_FILE ".noindex" #define MU_MAILDIR_NOUPDATE_FILE ".noupdate" /* On Linux (and some BSD), we have entry->d_type, but some file * systems (XFS, ReiserFS) do not support it, and set it DT_UNKNOWN. * On other OSs, notably Solaris, entry->d_type is not present at all. * For these cases, we use lstat (in get_dtype) as a slower fallback, * and return it in the d_type parameter */ #ifdef HAVE_STRUCT_DIRENT_D_TYPE #define GET_DTYPE(DE,FP) \ ((DE)->d_type == DT_UNKNOWN ? mu_util_get_dtype_with_lstat((FP)) : \ (DE)->d_type) #else #define GET_DTYPE(DE,FP) \ mu_util_get_dtype_with_lstat((FP)) #endif /*HAVE_STRUCT_DIRENT_D_TYPE*/ static gboolean create_maildir (const char *path, mode_t mode, GError **err) { int i; const gchar* subdirs[] = {"new", "cur", "tmp"}; for (i = 0; i != G_N_ELEMENTS(subdirs); ++i) { const char *fullpath; int rv; /* static buffer */ fullpath = mu_str_fullpath_s (path, subdirs[i]); /* if subdir already exists, don't try to re-create * it */ if (mu_util_check_dir (fullpath, TRUE, TRUE)) continue; rv = g_mkdir_with_parents (fullpath, (int)mode); /* note, g_mkdir_with_parents won't detect an error if * there's already such a dir, but with the wrong * permissions; so we need to check */ if (rv != 0 || !mu_util_check_dir(fullpath, TRUE, TRUE)) return mu_util_g_set_error (err,MU_ERROR_FILE_CANNOT_MKDIR, "creating dir failed for %s: %s", fullpath, strerror (errno)); } return TRUE; } static gboolean create_noindex (const char *path, GError **err) { /* create a noindex file if requested */ int fd; const char *noindexpath; /* static buffer */ noindexpath = mu_str_fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); fd = creat (noindexpath, 0644); /* note, if the 'close' failed, creation may still have * succeeded...*/ if (fd < 0 || close (fd) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_CREATE, "error in create_noindex: %s", strerror (errno)); return TRUE; } gboolean mu_maildir_mkdir (const char* path, mode_t mode, gboolean noindex, GError **err) { g_return_val_if_fail (path, FALSE); MU_WRITE_LOG ("%s (%s, %o, %s)", __FUNCTION__, path, mode, noindex ? "TRUE" : "FALSE"); if (!create_maildir (path, mode, err)) return FALSE; if (noindex && !create_noindex (path, err)) return FALSE; return TRUE; } /* determine whether the source message is in 'new' or in 'cur'; * we ignore messages in 'tmp' for obvious reasons */ static gboolean check_subdir (const char *src, gboolean *in_cur, GError **err) { gboolean rv; gchar *srcpath; srcpath = g_path_get_dirname (src); *in_cur = FALSE; rv = TRUE; if (g_str_has_suffix (srcpath, "cur")) *in_cur = TRUE; else if (!g_str_has_suffix (srcpath, "new")) rv = mu_util_g_set_error (err, MU_ERROR_FILE_INVALID_SOURCE, "invalid source message '%s'", src); g_free (srcpath); return rv; } static gchar* get_target_fullpath (const char* src, const gchar *targetpath, GError **err) { gchar *targetfullpath, *srcfile; gboolean in_cur; if (!check_subdir (src, &in_cur, err)) return NULL; srcfile = g_path_get_basename (src); /* create targetpath; note: make the filename *cough* unique * by including a hash of the srcname in the targetname. This * helps if there are copies of a message (which all have the * same basename) */ targetfullpath = g_strdup_printf ("%s%c%s%c%u_%s", targetpath, G_DIR_SEPARATOR, in_cur ? "cur" : "new", G_DIR_SEPARATOR, g_str_hash(src), srcfile); g_free (srcfile); return targetfullpath; } gboolean mu_maildir_link (const char* src, const char *targetpath, GError **err) { gchar *targetfullpath; int rv; g_return_val_if_fail (src, FALSE); g_return_val_if_fail (targetpath, FALSE); targetfullpath = get_target_fullpath (src, targetpath, err); if (!targetfullpath) return FALSE; rv = symlink (src, targetfullpath); if (rv != 0) mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_LINK, "error creating link %s => %s: %s", targetfullpath, src, strerror (errno)); g_free (targetfullpath); return rv == 0 ? TRUE: FALSE; } static MuError process_dir (const char* path, const gchar *mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data); static MuError process_file (const char* fullpath, const gchar* mdir, MuMaildirWalkMsgCallback msg_cb, void *data) { MuError result; struct stat statbuf; if (!msg_cb) return MU_OK; if (G_UNLIKELY(access(fullpath, R_OK) != 0)) { g_warning ("cannot access %s: %s", fullpath, strerror(errno)); return MU_ERROR; } if (G_UNLIKELY(stat (fullpath, &statbuf) != 0)) { g_warning ("cannot stat %s: %s", fullpath, strerror(errno)); return MU_ERROR; } result = (msg_cb)(fullpath, mdir, &statbuf, data); if (result == MU_STOP) g_debug ("callback said 'MU_STOP' for %s", fullpath); else if (result == MU_ERROR) g_warning ("%s: error in callback (%s)", __FUNCTION__, fullpath); return result; } /* * determine if path is a maildir leaf-dir; ie. if it's 'cur' or 'new' * (we're skipping 'tmp' for obvious reasons) */ G_GNUC_CONST static gboolean is_maildir_new_or_cur (const char *path) { size_t len; g_return_val_if_fail (path, FALSE); /* path is the full path; it cannot possibly be shorter * than 4 for a maildir (/cur or /new) */ len = strlen (path); if (G_UNLIKELY(len < 4)) return FALSE; /* optimization; one further idea would be cast the 4 bytes to an integer * and compare that -- need to think about alignment, endianness */ if (path[len - 4] == G_DIR_SEPARATOR && path[len - 3] == 'c' && path[len - 2] == 'u' && path[len - 1] == 'r') return TRUE; if (path[len - 4] == G_DIR_SEPARATOR && path[len - 3] == 'n' && path[len - 2] == 'e' && path[len - 1] == 'w') return TRUE; return FALSE; } /* check if there path contains file; used for checking if there is * MU_MAILDIR_NOINDEX_FILE or MU_MAILDIR_NOUPDATE_FILE in this * dir; */ static gboolean dir_contains_file (const char *path, const char *file) { const char* fullpath; /* static buffer */ fullpath = mu_str_fullpath_s (path, file); if (access (fullpath, F_OK) == 0) return TRUE; else if (G_UNLIKELY(errno != ENOENT && errno != EACCES)) g_warning ("error testing for %s/%s: %s", fullpath, file, strerror(errno)); return FALSE; } static gboolean is_dotdir_to_ignore (const char* dir) { int i; const char* ignore[] = { ".notmuch", ".nnmaildir", ".#evolution" }; /* when adding names, check the optimization below */ if (dir[0] != '.') return FALSE; /* not a dotdir */ if (dir[1] == '\0' || (dir[1] == '.' && dir[2] == '\0')) return TRUE; /* ignore '.' and '..' */ /* optimization: special dirs have 'n' or '#' in pos 1 */ if (dir[1] != 'n' && dir[1] != '#') return FALSE; /* not special: don't ignore */ for (i = 0; i != G_N_ELEMENTS(ignore); ++i) if (strcmp(dir, ignore[i]) == 0) return TRUE; return FALSE; /* don't ignore */ } static gboolean ignore_dir_entry (struct dirent *entry, unsigned char d_type) { if (G_LIKELY(d_type == DT_REG)) { /* ignore emacs tempfiles */ if (entry->d_name[0] == '#') return TRUE; /* ignore dovecot metadata */ if (entry->d_name[0] == 'd' && strncmp (entry->d_name, "dovecot", 7) == 0) return TRUE; /* ignore special files */ if (entry->d_name[0] == '.') return TRUE; /* ignore core files */ if (entry->d_name[0] == 'c' && strncmp (entry->d_name, "core", 4) == 0) return TRUE; return FALSE; /* other files: don't ignore */ } else if (d_type == DT_DIR) return is_dotdir_to_ignore (entry->d_name); else return TRUE; /* ignore non-normal files, non-dirs */ } /* * return the maildir value for the the path - this is the directory * for the message (with the top-level dir as "/"), and without the * leaf "/cur" or "/new". In other words, contatenate old_mdir + "/" + dir, * unless dir is either 'new' or 'cur'. The value will be used in queries. */ static gchar* get_mdir_for_path (const gchar *old_mdir, const gchar *dir) { /* if the current dir is not 'new' or 'cur', contatenate * old_mdir an dir */ if ((dir[0] == 'n' && strcmp(dir, "new") == 0) || (dir[0] == 'c' && strcmp(dir, "cur") == 0) || (dir[0] == 't' && strcmp(dir, "tmp") == 0)) return strdup (old_mdir ? old_mdir : G_DIR_SEPARATOR_S); else return g_strconcat (old_mdir ? old_mdir : "", G_DIR_SEPARATOR_S, dir, NULL); } static MuError process_dir_entry (const char* path, const char* mdir, struct dirent *entry, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data) { const char *fp; char* fullpath; unsigned char d_type; /* we have to copy the buffer from fullpath_s, because it * returns a static buffer, and we maybe called reentrantly */ fp = mu_str_fullpath_s (path, entry->d_name); fullpath = g_newa (char, strlen(fp) + 1); strcpy (fullpath, fp); d_type = GET_DTYPE(entry, fullpath); /* ignore special files/dirs */ if (ignore_dir_entry (entry, d_type)) return MU_OK; switch (d_type) { case DT_REG: /* we only want files in cur/ and new/ */ if (!is_maildir_new_or_cur (path)) return MU_OK; return process_file (fullpath, mdir, cb_msg, data); case DT_DIR: { char *my_mdir; MuError rv; /* my_mdir is the search maildir (the dir starting * with the top-level maildir as /, and without the * /tmp, /cur, /new */ my_mdir = get_mdir_for_path (mdir, entry->d_name); rv = process_dir (fullpath, my_mdir, cb_msg, cb_dir, full, data); g_free (my_mdir); return rv; } default: return MU_OK; /* ignore other types */ } } static const size_t DIRENT_ALLOC_SIZE = offsetof (struct dirent, d_name) + PATH_MAX; static struct dirent* dirent_new (void) { return (struct dirent*) g_slice_alloc (DIRENT_ALLOC_SIZE); } static void dirent_destroy (struct dirent *entry) { g_slice_free1 (DIRENT_ALLOC_SIZE, entry); } #ifdef HAVE_STRUCT_DIRENT_D_INO static int dirent_cmp (struct dirent *d1, struct dirent *d2) { /* we do it his way instead of a simple d1->d_ino - d2->d_ino * because this way, we don't need 64-bit numbers for the * actual sorting */ if (d1->d_ino < d2->d_ino) return -1; else if (d1->d_ino > d2->d_ino) return 1; else return 0; } #endif /*HAVE_STRUCT_DIRENT_D_INO*/ static MuError process_dir_entries (DIR *dir, const char* path, const char* mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data) { MuError result; GSList *lst, *c; for (lst = NULL;;) { int rv; struct dirent *entry, *res; entry = dirent_new (); rv = readdir_r (dir, entry, &res); if (rv == 0) { if (res) lst = g_slist_prepend (lst, entry); else { dirent_destroy (entry); break; /* last direntry reached */ } } else { dirent_destroy (entry); g_warning ("error scanning dir: %s", strerror(rv)); return MU_ERROR_FILE; } } /* we sort by inode; this makes things much faster on * extfs2,3 */ #if HAVE_STRUCT_DIRENT_D_INO c = lst = g_slist_sort (lst, (GCompareFunc)dirent_cmp); #endif /*HAVE_STRUCT_DIRENT_D_INO*/ for (c = lst, result = MU_OK; c && result == MU_OK; c = g_slist_next(c)) result = process_dir_entry (path, mdir, (struct dirent*)c->data, msg_cb, dir_cb, full, data); g_slist_foreach (lst, (GFunc)dirent_destroy, NULL); g_slist_free (lst); return result; } static MuError process_dir (const char* path, const char* mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data) { MuError result; DIR* dir; /* if it has a noindex file, we ignore this dir */ if (dir_contains_file (path, MU_MAILDIR_NOINDEX_FILE) || (!full && dir_contains_file (path, MU_MAILDIR_NOUPDATE_FILE))) { g_debug ("found noindex/noupdate: ignoring dir %s", path); return MU_OK; } dir = opendir (path); if (!dir) { g_warning ("cannot access %s: %s", path, strerror(errno)); return MU_OK; } if (dir_cb) { MuError rv; rv = dir_cb (path, TRUE, data); if (rv != MU_OK) { closedir (dir); return rv; } } result = process_dir_entries (dir, path, mdir, msg_cb, dir_cb, full, data); closedir (dir); /* only run dir_cb if it exists and so far, things went ok */ if (dir_cb && result == MU_OK) return dir_cb (path, FALSE, data); return result; } MuError mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data) { MuError rv; char *mypath; g_return_val_if_fail (path && cb_msg, MU_ERROR); g_return_val_if_fail (mu_util_check_dir(path, TRUE, FALSE), MU_ERROR); /* strip the final / or \ */ mypath = g_strdup (path); if (mypath[strlen(mypath)-1] == G_DIR_SEPARATOR) mypath[strlen(mypath)-1] = '\0'; rv = process_dir (mypath, NULL, cb_msg, cb_dir, full, data); g_free (mypath); return rv; } static gboolean clear_links (const gchar* dirname, DIR *dir, GError **err) { struct dirent *entry; gboolean rv; rv = TRUE; errno = 0; while ((entry = readdir (dir))) { const char *fp; char *fullpath; unsigned char d_type; /* ignore empty, dot thingies */ if (!entry->d_name || entry->d_name[0] == '.') continue; /* we have to copy the buffer from fullpath_s, because * it returns a static buffer and we are * recursive*/ fp = mu_str_fullpath_s (dirname, entry->d_name); fullpath = g_newa (char, strlen(fp) + 1); strcpy (fullpath, fp); d_type = GET_DTYPE (entry, fullpath); /* ignore non-links / non-dirs */ if (d_type != DT_LNK && d_type != DT_DIR) continue; if (d_type == DT_LNK) { if (unlink (fullpath) != 0) { /* don't use err */ g_warning ("error unlinking %s: %s", fullpath, strerror(errno)); rv = FALSE; } } else /* DT_DIR, see check before*/ rv = mu_maildir_clear_links (fullpath, err); } if (errno != 0) mu_util_g_set_error (err, MU_ERROR_FILE, "file error: %s", strerror(errno)); return (rv == FALSE && errno == 0); } gboolean mu_maildir_clear_links (const gchar* path, GError **err) { DIR *dir; gboolean rv; g_return_val_if_fail (path, FALSE); dir = opendir (path); if (!dir) return mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_OPEN, "failed to open %s: %s", path, strerror(errno)); rv = clear_links (path, dir, err); closedir (dir); return rv; } MuFlags mu_maildir_get_flags_from_path (const char *path) { g_return_val_if_fail (path, MU_FLAG_INVALID); /* try to find the info part */ /* note that we can use either the ':' or '!' as separator; * the former is the official, but as it does not work on e.g. VFAT * file systems, some Maildir implementations use the latter instead * (or both). For example, Tinymail/modest does this. The python * documentation at http://docs.python.org/lib/mailbox-maildir.html * mentions the '!' as well as a 'popular choice' */ /* we check the dir -- */ if (strstr (path, G_DIR_SEPARATOR_S "new" G_DIR_SEPARATOR_S)) { char *dir, *dir2; MuFlags flags; dir = g_path_get_dirname (path); dir2 = g_path_get_basename (dir); flags = MU_FLAG_NONE; if (g_strcmp0 (dir2, "new") == 0) flags = MU_FLAG_NEW; g_free (dir); g_free (dir2); /* NOTE: new/ message should not have :2,-stuff, as * per http://cr.yp.to/proto/maildir.html. If they, do * we ignore it */ if (flags == MU_FLAG_NEW) return flags; } /* get the file flags */ { char *info; info = strrchr (path, '2'); if (!info || info == path || (info[-1] != ':' && info[-1] != '!') || (info[1] != ',')) return MU_FLAG_NONE; else return mu_flags_from_str (&info[2], MU_FLAG_TYPE_MAILFILE, TRUE /*ignore invalid */); } } /* * take an exising message path, and return a new path, based on * whether it should be in 'new' or 'cur'; ie. * * /home/user/Maildir/foo/bar/cur/abc:2,F and flags == MU_FLAG_NEW * => /home/user/Maildir/foo/bar/new * and * /home/user/Maildir/foo/bar/new/abc and flags == MU_FLAG_REPLIED * => /home/user/Maildir/foo/bar/cur * * so the difference is whether MU_FLAG_NEW is set or not; and in the * latter case, no other flags are allowed. * */ static gchar* get_new_path (const char *mdir, const char *mfile, MuFlags flags, const char* custom_flags) { if (flags & MU_FLAG_NEW) return g_strdup_printf ("%s%cnew%c%s", mdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR, mfile); else { const char *flagstr; flagstr = mu_flags_to_str_s (flags, MU_FLAG_TYPE_MAILFILE); return g_strdup_printf ("%s%ccur%c%s:2,%s%s", mdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR, mfile, flagstr, custom_flags ? custom_flags : ""); } } char* mu_maildir_get_maildir_from_path (const char* path) { gchar *mdir; /* determine the maildir */ mdir = g_path_get_dirname (path); if (!g_str_has_suffix (mdir, "cur") && !g_str_has_suffix (mdir, "new")) { g_warning ("%s: not a valid maildir path: %s", __FUNCTION__, path); g_free (mdir); return NULL; } /* remove the 'cur' or 'new' */ mdir[strlen(mdir) - 4] = '\0'; return mdir; } static char* get_new_basename (void) { char date[9]; /* YYYYMMDD */ char hostname[32]; /* should be enough...*/ long int rnd; time_t now; now = time(NULL); strftime (date, sizeof(date), "%Y%m%d", localtime(&now)); if (gethostname (hostname, sizeof(hostname)) != 0) memcpy (hostname, "hostname", strlen("hostname")); rnd = random (); return g_strdup_printf ("%s-%08x-%s", date, (unsigned)rnd, hostname); } char* mu_maildir_get_new_path (const char *oldpath, const char *new_mdir, MuFlags newflags, gboolean new_name) { char *mfile, *mdir, *custom_flags, *newpath; g_return_val_if_fail (oldpath, NULL); mfile = newpath = custom_flags = NULL; /* determine the maildir */ mdir = mu_maildir_get_maildir_from_path (oldpath); if (!mdir) return NULL; if (new_name) mfile = get_new_basename (); else { /* determine the name of the mailfile, stripped of its flags, as well * as any custom (non-standard) flags */ char *cur; mfile = g_path_get_basename (oldpath); for (cur = &mfile[strlen(mfile)-1]; cur > mfile; --cur) { if ((*cur == ':' || *cur == '!') && (cur[1] == '2' && cur[2] == ',')) { /* get the custom flags (if any) */ custom_flags = mu_flags_custom_from_str (cur + 3); cur[0] = '\0'; /* strip the flags */ break; } } } newpath = get_new_path (new_mdir ? new_mdir : mdir, mfile, newflags, custom_flags); g_free (mfile); g_free (mdir); g_free (custom_flags); return newpath; } static gboolean msg_move_check_pre (const gchar *src, const gchar *dst, GError **err) { if (!g_path_is_absolute(src)) return mu_util_g_set_error (err, MU_ERROR_FILE, "source is not an absolute path: '%s'", src); if (!g_path_is_absolute(dst)) return mu_util_g_set_error (err, MU_ERROR_FILE, "target is not an absolute path: '%s'", dst); if (access (src, R_OK) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "cannot read %s", src); if (access (dst, F_OK) == 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "%s already exists", dst); return TRUE; } static gboolean msg_move_check_post (const char *src, const char *dst, GError **err) { /* double check -- is the target really there? */ if (access (dst, F_OK) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "can't find target (%s)", dst); if (access (src, F_OK) == 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "source still there (%s)", src); return TRUE; } static gboolean msg_move (const char* src, const char *dst, GError **err) { if (!msg_move_check_pre (src, dst, err)) return FALSE; if (rename (src, dst) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE,"error moving %s to %s", src, dst); return msg_move_check_post (src, dst, err); } gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, MuFlags newflags, gboolean ignore_dups, gboolean new_name, GError **err) { char *newfullpath; gboolean rv; gboolean src_is_target; g_return_val_if_fail (oldpath, FALSE); newfullpath = mu_maildir_get_new_path (oldpath, targetmdir, newflags, new_name); if (!newfullpath) { mu_util_g_set_error (err, MU_ERROR_FILE, "failed to determine targetpath"); return NULL; } src_is_target = (g_strcmp0 (oldpath, newfullpath) == 0); if (!ignore_dups && src_is_target) { mu_util_g_set_error (err, MU_ERROR_FILE_TARGET_EQUALS_SOURCE, "target equals source"); return NULL; } if (!src_is_target) { rv = msg_move (oldpath, newfullpath, err); if (!rv) { g_free (newfullpath); return NULL; } } return newfullpath; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-maildir.h��������������������������������������������������������������������������0000664�0000000�0000000�00000017613�12504061031�0015203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MAILDIR_H__ #define __MU_MAILDIR_H__ #include <glib.h> #include <time.h> #include <sys/types.h> /* for mode_t */ #include <mu-util.h> #include <mu-flags.h> G_BEGIN_DECLS /** * create a new maildir. if parts of the maildir already exists, those * will simply be ignored. IOW, if you try to create the same maildir * twice, the second will simply be a no-op (without any errors). * Note, if the function fails 'halfway', it will *not* try to remove * the parts the were created. it *will* create any parent dirs that * are not yet existent. * * * @param path the path (missing components will be created, as in 'mkdir -p') * @param mode the file mode (e.g., 0755) * @param noindex add a .noindex file to the maildir, so it will be excluded * from indexing by 'mu index' * @param err if function returns FALSE, receives error * information. err may be NULL. * * @return TRUE if creation succeeded (or already existed), FALSE otherwise */ gboolean mu_maildir_mkdir (const char* path, mode_t mode, gboolean noindex, GError **err); /** * create a symbolic link to a mail message * * @param src the full path to the source message * @param targetpath the path to the target maildir; ie., *not* * MyMaildir/cur, but just MyMaildir/. The function will figure out * the correct subdir then. * @param err if function returns FALSE, err may contain extra * information. if err is NULL, does nothing * * @return */ gboolean mu_maildir_link (const char* src, const char *targetpath, GError **err); /** * MuMaildirWalkMsgCallback -- callback function for * mu_path_walk_maildir; see the documentation there. It will be * called for each message found, with fullpath containing the full * path to the message, mdir containing the maildir -- that is, when * indexing ~/Maildir, a message ~/Maildir/foo/bar/cur/msg would have * the maildir "foo/bar". Then, the information from 'stat' of this * file (see stat(3)), and a user_data pointer */ typedef MuError (*MuMaildirWalkMsgCallback) (const char* fullpath, const char* mdir, struct stat *statinfo, void *user_data); /** * MuPathWalkDirCallback -- callback function for mu_path_walk_maildir; see the * documentation there. It will be called each time a dir is entered or left, * with 'enter' being TRUE upon entering, FALSE otherwise */ typedef MuError (*MuMaildirWalkDirCallback) (const char* fullpath, gboolean enter, void *user_data); /** * start a recursive walk of a maildir; for each file found, we call * callback with the path (with the Maildir path of scanner_new as * root), the filename, the timestamp (mtime) of the file,and the * *data pointer, for user data. dot-files are ignored, as well as * files outside cur/ and new/ dirs and unreadable files; however, * dotdirs are visited (ie. '.dotdir/cur'), so this enables Maildir++. * (http://www.inter7.com/courierimap/README.maildirquota.html, search * for 'Mission statement'). In addition, dirs containing a file named * '.noindex' are ignored, as are their subdirectories, and dirs * containing a file called '.noupdate' are ignored, unless @param * full is TRUE. * * mu_walk_maildir stops if the callbacks return something different * from MU_OK. For example, it can return MU_STOP to stop the scan, or * some error. * * @param path the maildir path to scan * @param cb_msg the callback function called for each msg * @param cb_dir the callback function called for each dir * @param full whether do a full scan, i.e., to ignore .noupdate files * @param data user data pointer * * @return a scanner result; MU_OK if everything went ok, * MU_STOP if we want to stop, or MU_ERROR in * case of error */ MuError mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data); /** * recursively delete all the symbolic links in a directory tree * * @param dir top dir * @param err if function returns FALSE, err may contain extra * information. if err is NULL, does nothing * * @return TRUE if it worked, FALSE in case of error */ gboolean mu_maildir_clear_links (const gchar* dir, GError **err); /** * get the Maildir flags from the full path of a mailfile. The flags * are as specified in http://cr.yp.to/proto/maildir.html, plus * MU_MSG_FLAG_NEW for new messages, ie the ones that live in * new/. The flags are logically OR'ed. Note that the file does not * have to exist; the flags are based on the path only. * * @param pathname of a mailfile; it does not have to refer to an * actual message * * @return the flags, or MU_MSG_FILE_FLAG_UNKNOWN in case of error */ MuFlags mu_maildir_get_flags_from_path (const char* pathname); /** * get the new pathname for a message, based on the old path and the * new flags and (optionally) a new maildir. Note that * setting/removing the MU_FLAG_NEW will change the directory in which * a message lives. The flags are as specified in * http://cr.yp.to/proto/maildir.html, plus MU_FLAG_NEW for new * messages, ie the ones that live in new/. The flags are logically * OR'ed. Note that the file does not have to exist; the flags are * based on the path only. * * * @param oldpath the old (current) full path to the message * (including the filename) * @param new_mdir the new maildir for this message, or NULL to keep * it in the current one. The maildir is the absolute file system * path, without the 'cur' or 'new' * @param new_flags the new flags for this message * @param new_name whether to create a new unique name, or keep the * old one * * @return a new path name; use g_free when done with. NULL in case of * error. */ char* mu_maildir_get_new_path (const char *oldpath, const char *new_mdir, MuFlags new_flags, gboolean new_name); /** * get the maildir for a certain message path, ie, the path *before* * cur/ or new/ * * @param path path for some message * * @return the maildir (free with g_free), or NULL in case of error */ char* mu_maildir_get_maildir_from_path (const char* path); /** * move a message file to another maildir; the function returns the full * path to the new message. * * @param msgpath an absolute file system path to an existing message in an * actual maildir * @param targetmdir the target maildir; note that this the base-level * Maildir, ie. /home/user/Maildir/archive, and must _not_ include the * 'cur' or 'new' part. Note that the target maildir must be on the * same filesystem. If you specify NULL for targetmdir, only the flags * of the message are affected; note that this may still involve a * moved to another directory (say, from new/ to cur/) * @param flags to set for the target (influences the filename, path) * @param ignore_dups whether to silent ignore the src=target case * (and return TRUE) * @param new_name whether to create a new unique name, or keep the * old one * @param err receives error information * * @return return the full path name of the target file (g_free) if * the move succeeded, NULL otherwise */ gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, MuFlags newflags, gboolean ignore_dups, gboolean new_name, GError **err); G_END_DECLS #endif /*__MU_MAILDIR_H__*/ ���������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-crypto.c�����������������������������������������������������������������������0000664�0000000�0000000�00000027424�12504061031�0015662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <string.h> #include "mu-msg.h" #include "mu-msg-priv.h" #include "mu-msg-part.h" #include "mu-date.h" #include <gmime/gmime.h> #include <gmime/gmime-multipart-signed.h> #define CALLBACK_DATA "callback-data" struct _CallbackData { MuMsgPartPasswordFunc pw_func; gpointer user_data; }; typedef struct _CallbackData CallbackData; static gboolean password_requester (GMimeCryptoContext *ctx, const char *user_id, const char* prompt_ctx, gboolean reprompt, GMimeStream *response, GError **err) { CallbackData *cbdata; gchar *password; ssize_t written; cbdata = g_object_get_data (G_OBJECT(ctx), CALLBACK_DATA); if (!cbdata || !cbdata->pw_func) return FALSE; password = cbdata->pw_func (user_id, prompt_ctx, reprompt, cbdata->user_data); if (!password) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get password"); return FALSE; } written = g_mime_stream_write_string (response, password); if (written != -1) written = g_mime_stream_write_string (response, "\n"); if (written == -1) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "writing password to mime stream failed"); /* it seems that GMime tries to flush the fd; however, this * does not work for pipes/sockets, causing getting a password * to fail. * * I have reported this, and it has been fixed now: * * http://git.gnome.org/browse/gmime/commit/ * ?id=bda4834d3d9a1fbefb6d97edfef2bc1da9357f58 * * however, it may take a while before everybody has this * version of GMime (ie. version > 2.6.10) * */ memset (password, 0, strlen(password)); g_free (password); return written != -1 ? TRUE : FALSE; } static char* dummy_password_func (const char *user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data) { g_print ("password requested for %s (%s) %s\n", user_id, prompt_ctx, reprompt ? "again" : ""); return NULL; } static char* get_gpg (GError **err) { char *path; const char *envpath; if ((envpath = g_getenv ("MU_GPG_PATH"))) { if (access (envpath, X_OK) != 0) { mu_util_g_set_error ( err, MU_ERROR, "'%s': not a valid gpg path: %s", envpath, strerror (errno)); return NULL; } return g_strdup (envpath); } if (!(path = g_find_program_in_path ("gpg")) && !(path = g_find_program_in_path ("gpg2"))) { mu_util_g_set_error (err, MU_ERROR, "gpg/gpg2 not found"); return NULL; } else return path; } static GMimeCryptoContext* get_gpg_crypto_context (MuMsgOptions opts, GError **err) { GMimeCryptoContext *cctx; char *gpg; cctx = NULL; if (!(gpg = get_gpg (err))) return NULL; cctx = g_mime_gpg_context_new ( (GMimePasswordRequestFunc)password_requester, gpg); g_free (gpg); if (!cctx) { mu_util_g_set_error (err, MU_ERROR, "failed to get GPG crypto context"); return NULL; } /* always try to use the agent */ g_mime_gpg_context_set_use_agent (GMIME_GPG_CONTEXT(cctx), TRUE); g_mime_gpg_context_set_auto_key_retrieve (GMIME_GPG_CONTEXT(cctx), opts & MU_MSG_OPTION_AUTO_RETRIEVE ? TRUE:FALSE); return cctx; } static GMimeCryptoContext* get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, gpointer user_data, GError **err) { CallbackData *cbdata; GMimeCryptoContext *cctx; cctx = get_gpg_crypto_context (opts, err); if (!cctx) return NULL; /* use gobject to pass data to the callback func */ cbdata = g_new0 (CallbackData, 1); cbdata->pw_func = password_func ? password_func : dummy_password_func; cbdata->user_data = user_data; g_object_set_data_full (G_OBJECT(cctx), CALLBACK_DATA, cbdata, (GDestroyNotify)g_free); return cctx; } static const char* get_pubkey_algo_name (GMimePubKeyAlgo algo) { switch (algo) { case GMIME_PUBKEY_ALGO_DEFAULT: return "default"; case GMIME_PUBKEY_ALGO_RSA: return "RSA"; case GMIME_PUBKEY_ALGO_RSA_E: return "RSA (encryption only)"; case GMIME_PUBKEY_ALGO_RSA_S: return "RSA (signing only)"; case GMIME_PUBKEY_ALGO_ELG_E: return "ElGamal (encryption only)"; case GMIME_PUBKEY_ALGO_DSA: return "DSA"; case GMIME_PUBKEY_ALGO_ELG: return "ElGamal"; default: return "unknown pubkey algorithm"; } } static const gchar* get_digestkey_algo_name (GMimeDigestAlgo algo) { switch (algo) { case GMIME_DIGEST_ALGO_DEFAULT: return "default"; case GMIME_DIGEST_ALGO_MD5: return "MD5"; case GMIME_DIGEST_ALGO_SHA1: return "SHA-1"; case GMIME_DIGEST_ALGO_RIPEMD160: return "RIPEMD160"; case GMIME_DIGEST_ALGO_MD2: return "MD2"; case GMIME_DIGEST_ALGO_TIGER192: return "TIGER-192"; case GMIME_DIGEST_ALGO_HAVAL5160: return "HAVAL-5-160"; case GMIME_DIGEST_ALGO_SHA256: return "SHA-256"; case GMIME_DIGEST_ALGO_SHA384: return "SHA-384"; case GMIME_DIGEST_ALGO_SHA512: return "SHA-512"; case GMIME_DIGEST_ALGO_SHA224: return "SHA-224"; case GMIME_DIGEST_ALGO_MD4: return "MD4"; default: return "unknown digest algorithm"; } } /* get data from the 'certificate' */ static char* get_cert_data (GMimeCertificate *cert) { const char /**email,*/ *name, *digest_algo, *pubkey_algo, *keyid, *trust; /* email = g_mime_certificate_get_email (cert); */ name = g_mime_certificate_get_name (cert); keyid = g_mime_certificate_get_key_id (cert); digest_algo = get_digestkey_algo_name (g_mime_certificate_get_digest_algo (cert)); pubkey_algo = get_pubkey_algo_name (g_mime_certificate_get_pubkey_algo (cert)); switch (g_mime_certificate_get_trust (cert)) { case GMIME_CERTIFICATE_TRUST_NONE: trust = "none"; break; case GMIME_CERTIFICATE_TRUST_NEVER: trust = "never"; break; case GMIME_CERTIFICATE_TRUST_UNDEFINED: trust = "undefined"; break; case GMIME_CERTIFICATE_TRUST_MARGINAL: trust = "marginal"; break; case GMIME_CERTIFICATE_TRUST_FULLY: trust = "full"; break; case GMIME_CERTIFICATE_TRUST_ULTIMATE: trust = "ultimate"; break; default: g_return_val_if_reached (NULL); } return g_strdup_printf ( "signer:%s, key:%s (%s,%s), trust:%s", name ? name : "?", /* email ? email : "?", */ keyid, pubkey_algo, digest_algo, trust); } /* get a human-readable report about the signature */ static char* get_verdict_report (GMimeSignature *msig) { time_t t; const char *status, *created, *expires; gchar *certdata, *report; switch (g_mime_signature_get_status (msig)) { case GMIME_SIGNATURE_STATUS_GOOD: status = "good"; break; case GMIME_SIGNATURE_STATUS_ERROR: status = "error"; break; case GMIME_SIGNATURE_STATUS_BAD: status = "bad"; break; default: g_return_val_if_reached (NULL); } t = g_mime_signature_get_created (msig); created = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); t = g_mime_signature_get_expires (msig); expires = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); certdata = get_cert_data (g_mime_signature_get_certificate (msig)); report = g_strdup_printf ("%s; created:%s, expires:%s, %s", status, created, expires, certdata ? certdata : "?"); g_free (certdata); return report; } static MuMsgPartSigStatusReport* get_status_report (GMimeSignatureList *sigs) { int i; MuMsgPartSigStatus status; MuMsgPartSigStatusReport *status_report; char *report; status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */ for (i = 0, report = NULL; i != g_mime_signature_list_length (sigs); ++i) { GMimeSignature *msig; GMimeSignatureStatus sigstat; gchar *rep; msig = g_mime_signature_list_get_signature (sigs, i); sigstat = g_mime_signature_get_status (msig); switch (sigstat) { case GMIME_SIGNATURE_STATUS_GOOD: break; case GMIME_SIGNATURE_STATUS_ERROR: status = MU_MSG_PART_SIG_STATUS_ERROR; break; case GMIME_SIGNATURE_STATUS_BAD: status = MU_MSG_PART_SIG_STATUS_BAD; break; default: g_return_val_if_reached (NULL); } rep = get_verdict_report (msig); report = g_strdup_printf ("%s%s%d: %s", report ? report : "", report ? "; " : "", i + 1, rep); g_free (rep); } status_report = g_slice_new (MuMsgPartSigStatusReport); status_report->verdict = status; status_report->report = report; return status_report; } void mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report) { if (!report) return; g_free ((char*)report->report); g_slice_free (MuMsgPartSigStatusReport, report); } static inline void tag_with_sig_status(GObject *part, MuMsgPartSigStatusReport *report) { g_object_set_data_full (part, SIG_STATUS_REPORT, report, (GDestroyNotify)mu_msg_part_sig_status_report_destroy); } void mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, GError **err) { /* the signature status */ MuMsgPartSigStatusReport *report; GMimeCryptoContext *ctx; GMimeSignatureList *sigs; g_return_if_fail (GMIME_IS_MULTIPART_SIGNED(sig)); ctx = get_crypto_context (opts, NULL, NULL, err); if (!ctx) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get crypto context"); return; } sigs = g_mime_multipart_signed_verify (sig, ctx, err); g_object_unref (ctx); if (!sigs) { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "verification failed"); return; } report = get_status_report (sigs); g_mime_signature_list_clear (sigs); /* tag this part with the signature status check */ tag_with_sig_status(G_OBJECT(sig), report); } static inline void check_decrypt_result(GMimeMultipartEncrypted *part, GMimeDecryptResult *res, GError **err) { GMimeSignatureList *sigs; MuMsgPartSigStatusReport *report; if (res) { /* Check if the decrypted part had any embed signatures */ sigs = res->signatures; if (sigs) { report = get_status_report (sigs); g_mime_signature_list_clear (sigs); /* tag this part with the signature status check */ tag_with_sig_status(G_OBJECT(part), report); } else { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "verification failed"); } g_object_unref (res); } } GMimeObject* /* this is declared in mu-msg-priv.h */ mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, MuMsgPartPasswordFunc func, gpointer user_data, GError **err) { GMimeObject *dec; GMimeCryptoContext *ctx; GMimeDecryptResult *res; g_return_val_if_fail (GMIME_IS_MULTIPART_ENCRYPTED(enc), NULL); ctx = get_crypto_context (opts, func, user_data, err); if (!ctx) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get crypto context"); return NULL; } /* at the time of writing, there is a small leak in * g_mime_multipart_encrypted_decrypt; I've notified its * author and it has been fixed 2012-09-12: * http://git.gnome.org/browse/gmime/commit/ * ?id=1bacd43b50d91bd03a4ae1dc9f46f5783dee61b1 * (or GMime > 2.6.10) * */ res = NULL; dec = g_mime_multipart_encrypted_decrypt (enc, ctx, &res, err); g_object_unref (ctx); check_decrypt_result(enc, res, err); if (!dec) { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "decryption failed"); return NULL; } return dec; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-doc.cc�������������������������������������������������������������������������0000664�0000000�0000000�00000006304�12504061031�0015244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <stdlib.h> #include <iostream> #include <string.h> #include <errno.h> #include <xapian.h> #include "mu-util.h" #include "mu-msg-fields.h" #include "mu-msg-doc.h" #include "mu-str.h" #include "mu-date.h" struct _MuMsgDoc { _MuMsgDoc (Xapian::Document *doc): _doc (doc) { } ~_MuMsgDoc () { delete _doc; } const Xapian::Document doc() const { return *_doc; } private: Xapian::Document *_doc; }; MuMsgDoc* mu_msg_doc_new (XapianDocument *doc, GError **err) { g_return_val_if_fail (doc, NULL); try { return new MuMsgDoc ((Xapian::Document*)doc); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, NULL); return FALSE; } void mu_msg_doc_destroy (MuMsgDoc *self) { try { delete self; } MU_XAPIAN_CATCH_BLOCK; } gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); // disable this check: // g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL); // because it's useful to get numerical field as strings, // for example when sorting (which is much faster if don't // have to convert to numbers first, esp. when it's a date // time_t) try { const std::string s (self->doc().get_value(mfid)); return s.empty() ? NULL : g_strdup (s.c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL); try { /* return a comma-separated string as a GSList */ const std::string s (self->doc().get_value(mfid)); return s.empty() ? NULL : mu_str_to_list(s.c_str(),',',TRUE); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } gint64 mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), -1); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1); /* date is a special case, because we store dates as * strings */ try { const std::string s (self->doc().get_value(mfid)); if (s.empty()) return 0; else if (mfid == MU_MSG_FIELD_ID_DATE) { time_t t; t = mu_date_str_to_time_t (s.c_str(), FALSE/*utc*/); return static_cast<gint64>(t); } else { return static_cast<gint64> (Xapian::sortable_unserialise(s)); } } MU_XAPIAN_CATCH_BLOCK_RETURN(-1); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-doc.h��������������������������������������������������������������������������0000664�0000000�0000000�00000005502�12504061031�0015105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_DOC_H__ #define __MU_MSG_DOC_H__ #include <glib.h> #include <mu-util.h> /* for XapianDocument */ G_BEGIN_DECLS struct _MuMsgDoc; typedef struct _MuMsgDoc MuMsgDoc; /** * create a new MuMsgDoc instance * * @param doc a Xapian::Document* (you'll need to cast the * Xapian::Document* to XapianDocument*, because only C (not C++) is * allowed in this header file. MuMsgDoc takes _ownership_ of this pointer; * don't touch it afterwards * @param err receives error info, or NULL * * @return a new MuMsgDoc instance (free with mu_msg_doc_destroy), or * NULL in case of error. */ MuMsgDoc* mu_msg_doc_new (XapianDocument *doc, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a MuMsgDoc instance -- free all the resources. Note, after * destroying, any strings returned from mu_msg_doc_get_str_field with * do_free==FALSE are no longer valid * * @param self a MuMsgDoc instance */ void mu_msg_doc_destroy (MuMsgDoc *self); /** * get a string parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a string field * * @return a string for the given field (see do_free), or NULL in case of error. * free with g_free */ gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid) G_GNUC_WARN_UNUSED_RESULT; /** * get a string-list parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a string-list field * * @return a list for the given field (see do_free), or NULL in case * of error. free with mu_str_free_list */ GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid) G_GNUC_WARN_UNUSED_RESULT; /** * * get a numeric parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a numeric field * * @return the numerical value, or -1 in case of error. You'll need to * cast this value to the actual type (e.g. time_t for MU_MSG_FIELD_ID_DATE) */ gint64 mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid); G_END_DECLS #endif /*__MU_MSG_DOC_H__*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-fields.c�����������������������������������������������������������������������0000664�0000000�0000000�00000024440�12504061031�0015603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include "mu-msg-fields.h" /* * note: the differences for our purposes between a xapian field and a * term: - there is only a single value for some item in per document * (msg), ie. one value containing the list of To: addresses - there * can be multiple terms, each containing e.g. one of the To: * addresses - searching uses terms, but to display some field, it * must be in the value (at least when using MuMsgIter) */ enum _FieldFlags { FLAG_GMIME = 1 << 0, /* field retrieved through * gmime */ FLAG_XAPIAN_INDEX = 1 << 1, /* field is indexed in * xapian (i.e., the text * is processed */ FLAG_XAPIAN_TERM = 1 << 2, /* field stored as term in * xapian (so it can be searched) */ FLAG_XAPIAN_VALUE = 1 << 3, /* field stored as value in * xapian (so the literal * value can be * retrieved) */ FLAG_XAPIAN_CONTACT = 1 << 4, /* field contains one or more * e-mail-addresses */ FLAG_XAPIAN_BOOLEAN = 1 << 5, /* use 'add_boolean_prefix' * for Xapian queries; * wildcards do NOT WORK * for such fields */ FLAG_PREPROCESS = 1 << 6, /* field needs flattening for * case/accents */ FLAG_DONT_CACHE = 1 << 7, /* don't cache this field in * the MuMsg cache */ FLAG_RANGE_FIELD = 1 << 8 /* whether this is a range field */ }; typedef enum _FieldFlags FieldFlags; /* * this struct describes the fields of an e-mail /*/ struct _MuMsgField { MuMsgFieldId _id; /* the id of the field */ MuMsgFieldType _type; /* the type of the field */ const char *_name; /* the name of the field */ const char _shortcut; /* the shortcut for use in * --fields and sorting */ const char _xprefix; /* the Xapian-prefix */ FieldFlags _flags; /* the flags that tells us * what to do */ }; typedef struct _MuMsgField MuMsgField; /* the name and shortcut fields must be lower case, or they might be * misinterpreted by the query-preprocesser which turns queries into * lowercase */ static const MuMsgField FIELD_DATA[] = { { MU_MSG_FIELD_ID_BCC, MU_MSG_FIELD_TYPE_STRING, "bcc" , 'h', 'H', /* 'hidden */ FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_BODY_TEXT, MU_MSG_FIELD_TYPE_STRING, "body", 'b', 'B', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_BODY_HTML, MU_MSG_FIELD_TYPE_STRING, "bodyhtml", 0, 0, FLAG_GMIME | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_CC, MU_MSG_FIELD_TYPE_STRING, "cc", 'c', 'C', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_DATE, MU_MSG_FIELD_TYPE_TIME_T, "date", 'd', 'D', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_BOOLEAN | FLAG_RANGE_FIELD }, { MU_MSG_FIELD_ID_EMBEDDED_TEXT, MU_MSG_FIELD_TYPE_STRING, "embed", 'e', 'E', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_FILE, MU_MSG_FIELD_TYPE_STRING, "file" , 'j', 'J', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_FLAGS, MU_MSG_FIELD_TYPE_INT, "flag", 'g', 'G', /* flaGs */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_FROM, MU_MSG_FIELD_TYPE_STRING, "from", 'f', 'F', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MAILDIR, MU_MSG_FIELD_TYPE_STRING, "maildir", 'm', 'M', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MAILING_LIST, MU_MSG_FIELD_TYPE_STRING, "list", 'v', 'V', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MIME, MU_MSG_FIELD_TYPE_STRING, "mime" , 'y', 'Y', FLAG_XAPIAN_TERM | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MSGID, MU_MSG_FIELD_TYPE_STRING, "msgid", 'i', 'I', /* 'i' for Id */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_PATH, MU_MSG_FIELD_TYPE_STRING, "path", 'l', 'L', /* 'l' for location */ FLAG_GMIME | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_BOOLEAN | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_PRIO, MU_MSG_FIELD_TYPE_INT, "prio", 'p', 'P', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_REFS, MU_MSG_FIELD_TYPE_STRING_LIST, "refs", 'r', 'R', FLAG_GMIME | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_SIZE, MU_MSG_FIELD_TYPE_BYTESIZE, "size", 'z', 'Z', /* siZe */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_RANGE_FIELD }, { MU_MSG_FIELD_ID_SUBJECT, MU_MSG_FIELD_TYPE_STRING, "subject", 's', 'S', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_TERM | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_TAGS, MU_MSG_FIELD_TYPE_STRING_LIST, "tag", 'x', 'X', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_PREPROCESS | FLAG_XAPIAN_VALUE }, { /* remember which thread this message is in */ MU_MSG_FIELD_ID_THREAD_ID, MU_MSG_FIELD_TYPE_STRING, "thread", 0, 'W', FLAG_XAPIAN_TERM }, { MU_MSG_FIELD_ID_TO, MU_MSG_FIELD_TYPE_STRING, "to", 't', 'T', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { /* special, internal field, to get a unique key */ MU_MSG_FIELD_ID_UID, MU_MSG_FIELD_TYPE_STRING, "uid", 0, 'U', FLAG_XAPIAN_TERM } /* note, mu-store also use the 'Q' internal prefix for its uids */ }; /* the MsgField data in an array, indexed by the MsgFieldId; * this allows for O(1) access */ static MuMsgField* _msg_field_data[MU_MSG_FIELD_ID_NUM]; static const MuMsgField* mu_msg_field (MuMsgFieldId id) { static gboolean _initialized; _initialized = FALSE; /* initialize the array, but only once... */ if (G_UNLIKELY(!_initialized)) { int i; for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) _msg_field_data[FIELD_DATA[i]._id] = (MuMsgField*)&FIELD_DATA[i]; _initialized = TRUE; } return _msg_field_data[id]; } void mu_msg_field_foreach (MuMsgFieldForeachFunc func, gconstpointer data) { int i; for (i = 0; i != MU_MSG_FIELD_ID_NUM; ++i) func (i, data); } MuMsgFieldId mu_msg_field_id_from_name (const char* str, gboolean err) { int i; g_return_val_if_fail (str, MU_MSG_FIELD_ID_NONE); for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) if (g_strcmp0(str, FIELD_DATA[i]._name) == 0) return FIELD_DATA[i]._id; if (err) g_return_val_if_reached (MU_MSG_FIELD_ID_NONE); return MU_MSG_FIELD_ID_NONE; } MuMsgFieldId mu_msg_field_id_from_shortcut (char kar, gboolean err) { int i; for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) if (kar == FIELD_DATA[i]._shortcut) return FIELD_DATA[i]._id; if (err) g_return_val_if_reached (MU_MSG_FIELD_ID_NONE); return MU_MSG_FIELD_ID_NONE; } gboolean mu_msg_field_gmime (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_GMIME ? TRUE: FALSE; } gboolean mu_msg_field_xapian_index (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_INDEX ? TRUE: FALSE; } gboolean mu_msg_field_xapian_value (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_VALUE ? TRUE: FALSE; } gboolean mu_msg_field_xapian_term (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_TERM ? TRUE: FALSE; } gboolean mu_msg_field_is_range_field (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_RANGE_FIELD ? TRUE: FALSE; } gboolean mu_msg_field_uses_boolean_prefix (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_BOOLEAN?TRUE:FALSE; } gboolean mu_msg_field_is_cacheable (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); /* note the FALSE: TRUE */ return mu_msg_field(id)->_flags & FLAG_DONT_CACHE ? FALSE : TRUE; } gboolean mu_msg_field_xapian_contact (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_CONTACT ? TRUE: FALSE; } gboolean mu_msg_field_preprocess (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_PREPROCESS ? TRUE: FALSE; } gboolean mu_msg_field_is_numeric (MuMsgFieldId mfid) { MuMsgFieldType type; g_return_val_if_fail (mu_msg_field_id_is_valid(mfid),FALSE); type = mu_msg_field_type (mfid); return type == MU_MSG_FIELD_TYPE_BYTESIZE || type == MU_MSG_FIELD_TYPE_TIME_T || type == MU_MSG_FIELD_TYPE_INT; } const char* mu_msg_field_name (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),NULL); return mu_msg_field(id)->_name; } char mu_msg_field_shortcut (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),0); return mu_msg_field(id)->_shortcut; } char mu_msg_field_xapian_prefix (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),0); return mu_msg_field(id)->_xprefix; } MuMsgFieldType mu_msg_field_type (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id), MU_MSG_FIELD_TYPE_NONE); return mu_msg_field(id)->_type; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-fields.h�����������������������������������������������������������������������0000664�0000000�0000000�00000020173�12504061031�0015607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_FIELDS_H__ #define __MU_MSG_FIELDS_H__ #include <glib.h> G_BEGIN_DECLS /* don't change the order, add new types at the end, as these numbers * are used in the database */ enum _MuMsgFieldId { /* first all the string-based ones */ MU_MSG_FIELD_ID_BCC = 0, MU_MSG_FIELD_ID_BODY_HTML, MU_MSG_FIELD_ID_BODY_TEXT, MU_MSG_FIELD_ID_CC, MU_MSG_FIELD_ID_EMBEDDED_TEXT, MU_MSG_FIELD_ID_FILE, MU_MSG_FIELD_ID_FROM, MU_MSG_FIELD_ID_MAILDIR, MU_MSG_FIELD_ID_MIME, /* mime-type */ MU_MSG_FIELD_ID_MSGID, MU_MSG_FIELD_ID_PATH, MU_MSG_FIELD_ID_SUBJECT, MU_MSG_FIELD_ID_TO, MU_MSG_FIELD_ID_UID, /* special, generated from path */ /* string list items... */ MU_MSG_FIELD_ID_REFS, MU_MSG_FIELD_ID_TAGS, /* then the numerical ones */ MU_MSG_FIELD_ID_DATE, MU_MSG_FIELD_ID_FLAGS, MU_MSG_FIELD_ID_PRIO, MU_MSG_FIELD_ID_SIZE, /* add new ones here... */ MU_MSG_FIELD_ID_MAILING_LIST, /* mailing list */ MU_MSG_FIELD_ID_THREAD_ID, MU_MSG_FIELD_ID_NUM }; typedef guint8 MuMsgFieldId; /* some specials... */ static const MuMsgFieldId MU_MSG_FIELD_ID_NONE = (MuMsgFieldId)-1; #define MU_MSG_STRING_FIELD_ID_NUM (MU_MSG_FIELD_ID_UID + 1) /* this is a shortcut for To/From/Cc/Bcc in queries; handled specially * in mu-query.cc and mu-str.c */ #define MU_MSG_FIELD_PSEUDO_CONTACT "contact" /* this is a shortcut for To/Cc/Bcc in queries; handled specially in * mu-query.cc and mu-str.c */ #define MU_MSG_FIELD_PSEUDO_RECIP "recip" #define mu_msg_field_id_is_valid(MFID) \ ((MFID) < MU_MSG_FIELD_ID_NUM) /* don't change the order, add new types at the end (before _NUM)*/ enum _MuMsgFieldType { MU_MSG_FIELD_TYPE_STRING, MU_MSG_FIELD_TYPE_STRING_LIST, MU_MSG_FIELD_TYPE_BYTESIZE, MU_MSG_FIELD_TYPE_TIME_T, MU_MSG_FIELD_TYPE_INT, MU_MSG_FIELD_TYPE_NUM }; typedef guint8 MuMsgFieldType; static const MuMsgFieldType MU_MSG_FIELD_TYPE_NONE = (MuMsgFieldType)-1; typedef void (*MuMsgFieldForeachFunc) (MuMsgFieldId id, gconstpointer data); /** * iterator over all possible message fields * * @param func a function called for each field * @param data a user data pointer passed the callback function */ void mu_msg_field_foreach (MuMsgFieldForeachFunc func, gconstpointer data); /** * get the name of the field -- this a name that can be use in queries, * ie. 'subject:foo', with 'subject' being the name * * @param id a MuMsgFieldId * * @return the name of the field as a constant string, or * NULL if the field is unknown */ const char* mu_msg_field_name (MuMsgFieldId id) G_GNUC_PURE; /** * get the shortcut of the field -- this a shortcut that can be use in * queries, ie. 's:foo', with 's' meaning 'subject' being the name * * @param id a MuMsgFieldId * * @return the shortcut character, or 0 if the field is unknown */ char mu_msg_field_shortcut (MuMsgFieldId id) G_GNUC_PURE; /** * get the xapian prefix of the field -- that is, the prefix used in * the Xapian database to identify the field * * @param id a MuMsgFieldId * * @return the xapian prefix char or 0 if the field is unknown */ char mu_msg_field_xapian_prefix (MuMsgFieldId id) G_GNUC_PURE; /** * get the type of the field (string, size, time etc.) * * @param field a MuMsgField * * @return the type of the field (a #MuMsgFieldType), or * MU_MSG_FIELD_TYPE_NONE if it is not found */ MuMsgFieldType mu_msg_field_type (MuMsgFieldId id) G_GNUC_PURE; /** * is the field a string? * * @param id a MuMsgFieldId * * @return TRUE if the field a string, FALSE otherwise */ #define mu_msg_field_is_string(MFID)\ (mu_msg_field_type((MFID))==MU_MSG_FIELD_TYPE_STRING?TRUE:FALSE) /** * is the field a string-list? * * @param id a MuMsgFieldId * * @return TRUE if the field a string-list, FALSE otherwise */ #define mu_msg_field_is_string_list(MFID)\ (mu_msg_field_type((MFID))==MU_MSG_FIELD_TYPE_STRING_LIST?TRUE:FALSE) /** * is the field numeric (has type MU_MSG_FIELD_TYPE_(BYTESIZE|TIME_T|INT))? * * @param id a MuMsgFieldId * * @return TRUE if the field is numeric, FALSE otherwise */ gboolean mu_msg_field_is_numeric (MuMsgFieldId id) G_GNUC_PURE; /** * whether the field value should be cached (in MuMsg) -- we cache * values so we can use the MuMsg without needing to keep the * underlying data source (the GMimeMessage or the database ptr) alive * in practice, the fields we *don't* cache are the message body * (html, txt), because they take too much memory */ gboolean mu_msg_field_is_cacheable (MuMsgFieldId id) G_GNUC_PURE; /** * is the field Xapian-indexable? That is, should this field be * indexed in the in the Xapian database, so we can use the all the * phrasing, stemming etc. magic * * @param id a MuMsgFieldId * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_index (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as a xapian term? * * @param id a MuMsgFieldId * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_term (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as a xapian value? * * @param field a MuMsgField * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_value (MuMsgFieldId id) G_GNUC_PURE; /** * whether we should use add_boolean_prefix (see Xapian documentation) * for this field in queries. Used in mu-query.cc * * @param id a MuMsgFieldId * * @return TRUE if this field wants add_boolean_prefix, FALSE * otherwise */ gboolean mu_msg_field_uses_boolean_prefix (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be escaped for xapian? in practice, should * word-breaking chars be replaced with '_'? Also, flatten accents, * downcase? * * @param field a MuMsgField * * @return TRUE if the field is to be preprocessed, FALSE otherwise */ gboolean mu_msg_field_preprocess (MuMsgFieldId id) G_GNUC_PURE; /** * is this a range-field? ie. date, or size * * @param id a MuMsgField * * @return TRUE if this field is a range field, FALSE otherwise */ gboolean mu_msg_field_is_range_field (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as contact information? This means that * e-mail address will be stored as terms, and names will be indexed * * @param id a MuMsgFieldId * * @return TRUE if the field should be stored as contact information, * FALSE otherwise */ gboolean mu_msg_field_xapian_contact (MuMsgFieldId id) G_GNUC_PURE; /** * is the field gmime-enabled? That is, can be field be retrieved * using GMime? * * @param id a MuMsgFieldId * * @return TRUE if the field is Gmime-enabled, FALSE otherwise */ gboolean mu_msg_field_gmime (MuMsgFieldId id) G_GNUC_PURE; /** * get the corresponding MuMsgField for a name (as in mu_msg_field_name) * * @param str a name * @param err, if TRUE, when the shortcut is not found, will issue a * g_critical warning * * @return a MuMsgField, or NULL if it could not be found */ MuMsgFieldId mu_msg_field_id_from_name (const char* str, gboolean err) G_GNUC_PURE; /** * get the corresponding MuMsgField for a shortcut (as in mu_msg_field_shortcut) * * @param kar a shortcut character * @param err, if TRUE, when the shortcut is not found, will issue a * g_critical warning * * @return a MuMsgField, or NULL if it could not be found */ MuMsgFieldId mu_msg_field_id_from_shortcut (char kar, gboolean err) G_GNUC_PURE; G_END_DECLS #endif /*__MU_MSG_FIELDS_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-file.c�������������������������������������������������������������������������0000664�0000000�0000000�00000042701�12504061031�0015254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <ctype.h> #include <gmime/gmime.h> #include "mu-util.h" #include "mu-str.h" #include "mu-maildir.h" #include "mu-msg-priv.h" static gboolean init_file_metadata (MuMsgFile *self, const char* path, const char *mdir, GError **err); static gboolean init_mime_msg (MuMsgFile *msg, const char *path, GError **err); MuMsgFile* mu_msg_file_new (const char* filepath, const char *mdir, GError **err) { MuMsgFile *self; g_return_val_if_fail (filepath, NULL); self = g_slice_new0 (MuMsgFile); if (!init_file_metadata (self, filepath, mdir, err)) { mu_msg_file_destroy (self); return NULL; } if (!init_mime_msg (self, filepath, err)) { mu_msg_file_destroy (self); return NULL; } return self; } void mu_msg_file_destroy (MuMsgFile *self) { if (!self) return; if (self->_mime_msg) g_object_unref (self->_mime_msg); g_slice_free (MuMsgFile, self); } static gboolean init_file_metadata (MuMsgFile *self, const char* path, const gchar* mdir, GError **err) { struct stat statbuf; if (access (path, R_OK) != 0) { mu_util_g_set_error (err, MU_ERROR_FILE, "cannot read file %s: %s", path, strerror(errno)); return FALSE; } if (stat (path, &statbuf) < 0) { mu_util_g_set_error (err, MU_ERROR_FILE, "cannot stat %s: %s", path, strerror(errno)); return FALSE; } if (!S_ISREG(statbuf.st_mode)) { mu_util_g_set_error (err, MU_ERROR_FILE, "not a regular file: %s", path); return FALSE; } self->_timestamp = statbuf.st_mtime; self->_size = (size_t)statbuf.st_size; /* remove double slashes, relative paths etc. from path & mdir */ if (!realpath (path, self->_path)) { mu_util_g_set_error (err, MU_ERROR_FILE, "could not get realpath for %s: %s", path, strerror(errno)); return FALSE; } strncpy (self->_maildir, mdir ? mdir : "", PATH_MAX); return TRUE; } static GMimeStream* get_mime_stream (MuMsgFile *self, const char *path, GError **err) { FILE *file; GMimeStream *stream; file = fopen (path, "r"); if (!file) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "cannot open %s: %s", path, strerror (errno)); return NULL; } stream = g_mime_stream_file_new (file); if (!stream) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "cannot create mime stream for %s", path); fclose (file); return NULL; } return stream; } static gboolean init_mime_msg (MuMsgFile *self, const char* path, GError **err) { GMimeStream *stream; GMimeParser *parser; stream = get_mime_stream (self, path, err); if (!stream) return FALSE; parser = g_mime_parser_new_with_stream (stream); g_object_unref (stream); if (!parser) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "cannot create mime parser for %s", path); return FALSE; } self->_mime_msg = g_mime_parser_construct_message (parser); g_object_unref (parser); if (!self->_mime_msg) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "message seems invalid, ignoring (%s)", path); return FALSE; } return TRUE; } static char* get_recipient (MuMsgFile *self, GMimeRecipientType rtype) { char *recip; InternetAddressList *recips; recips = g_mime_message_get_recipients (self->_mime_msg, rtype); /* FALSE --> don't encode */ recip = (char*)internet_address_list_to_string (recips, FALSE); if (recip && !g_utf8_validate (recip, -1, NULL)) { g_debug ("invalid recipient in %s\n", self->_path); mu_str_asciify_in_place (recip); /* ugly... */ } if (mu_str_is_empty(recip)) { g_free (recip); return NULL; } if (recip) mu_str_remove_ctrl_in_place (recip); return recip; } /* * let's try to guess the mailing list from some other * headers in the mail */ static gchar* get_fake_mailing_list_maybe (MuMsgFile *self) { const char* hdr; hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), "X-Feed2Imap-Version"); if (!hdr) return NULL; /* looks like a feed2imap header; guess the source-blog * from the msgid */ { const char *msgid, *e; msgid = g_mime_message_get_message_id (self->_mime_msg); if (msgid && (e = strchr (msgid, '-'))) return g_strndup (msgid, e - msgid); } return NULL; } static gchar* get_mailing_list (MuMsgFile *self) { const char *hdr, *b, *e; hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), "List-Id"); if (mu_str_is_empty (hdr)) return get_fake_mailing_list_maybe (self); e = NULL; b = strchr (hdr, '<'); if (b) e = strchr (b, '>'); if (b && e) return g_strndup (b + 1, e - b - 1); else return g_strdup (hdr); } static gboolean looks_like_attachment (GMimeObject *part) { GMimeContentDisposition *disp; GMimeContentType *ctype; const char *dispstr; disp = g_mime_object_get_content_disposition (part); if (!GMIME_IS_CONTENT_DISPOSITION(disp)) return FALSE; dispstr = g_mime_content_disposition_get_disposition (disp); if (g_ascii_strcasecmp (dispstr, "attachment") == 0) return TRUE; /* we also consider images, audio, and non-pgp-signature * application attachments to be attachments... */ ctype = g_mime_object_get_content_type (part); if (g_mime_content_type_is_type (ctype, "image", "*")) return TRUE; if (g_mime_content_type_is_type (ctype, "audio", "*")) return TRUE; if ((g_mime_content_type_is_type (ctype, "application", "*")) && !g_mime_content_type_is_type (ctype, "*", "pgp-signature")) return TRUE; return FALSE; } static void msg_cflags_cb (GMimeObject *parent, GMimeObject *part, MuFlags *flags) { if (GMIME_IS_MULTIPART_SIGNED(part)) *flags |= MU_FLAG_SIGNED; /* FIXME: An encrypted part might be signed at the same time. * In that case the signed flag is lost. */ if (GMIME_IS_MULTIPART_ENCRYPTED(part)) *flags |= MU_FLAG_ENCRYPTED; if (*flags & MU_FLAG_HAS_ATTACH) return; if (!GMIME_IS_PART(part)) return; if (*flags & MU_FLAG_HAS_ATTACH) return; if (looks_like_attachment (part)) *flags |= MU_FLAG_HAS_ATTACH; } static MuFlags get_content_flags (MuMsgFile *self) { MuFlags flags; char *ml; flags = MU_FLAG_NONE; if (GMIME_IS_MESSAGE(self->_mime_msg)) mu_mime_message_foreach (self->_mime_msg, FALSE, /* never decrypt for this */ (GMimeObjectForeachFunc)msg_cflags_cb, &flags); ml = get_mailing_list (self); if (ml) { flags |= MU_FLAG_LIST; g_free (ml); } return flags; } static MuFlags get_flags (MuMsgFile *self) { MuFlags flags; g_return_val_if_fail (self, MU_FLAG_INVALID); flags = mu_maildir_get_flags_from_path (self->_path); flags |= get_content_flags (self); /* pseudo-flag --> unread means either NEW or NOT SEEN, just * for searching convenience */ if ((flags & MU_FLAG_NEW) || !(flags & MU_FLAG_SEEN)) flags |= MU_FLAG_UNREAD; return flags; } static size_t get_size (MuMsgFile *self) { g_return_val_if_fail (self, 0); return self->_size; } static MuMsgPrio parse_prio_str (const char* priostr) { int i; struct { const char* _str; MuMsgPrio _prio; } str_prio[] = { { "high", MU_MSG_PRIO_HIGH }, { "1", MU_MSG_PRIO_HIGH }, { "2", MU_MSG_PRIO_HIGH }, { "normal", MU_MSG_PRIO_NORMAL }, { "3", MU_MSG_PRIO_NORMAL }, { "low", MU_MSG_PRIO_LOW }, { "list", MU_MSG_PRIO_LOW }, { "bulk", MU_MSG_PRIO_LOW }, { "4", MU_MSG_PRIO_LOW }, { "5", MU_MSG_PRIO_LOW } }; for (i = 0; i != G_N_ELEMENTS(str_prio); ++i) if (g_ascii_strcasecmp (priostr, str_prio[i]._str) == 0) return str_prio[i]._prio; /* e.g., last-fm uses 'fm-user'... as precedence */ return MU_MSG_PRIO_NORMAL; } static MuMsgPrio get_prio (MuMsgFile *self) { GMimeObject *obj; const char* priostr; g_return_val_if_fail (self, MU_MSG_PRIO_NONE); obj = GMIME_OBJECT(self->_mime_msg); priostr = g_mime_object_get_header (obj, "Precedence"); if (!priostr) priostr = g_mime_object_get_header (obj, "X-Priority"); if (!priostr) priostr = g_mime_object_get_header (obj, "Importance"); return priostr ? parse_prio_str (priostr) : MU_MSG_PRIO_NORMAL; } /* NOTE: buffer will be *freed* or returned unchanged */ static char* convert_to_utf8 (GMimePart *part, char *buffer) { GMimeContentType *ctype; const char* charset; ctype = g_mime_object_get_content_type (GMIME_OBJECT(part)); g_return_val_if_fail (GMIME_IS_CONTENT_TYPE(ctype), NULL); /* of course, the charset specified may be incorrect... */ charset = g_mime_content_type_get_parameter (ctype, "charset"); if (charset) { char *utf8; if ((utf8 = mu_str_convert_to_utf8 (buffer, g_mime_charset_iconv_name (charset)))) { g_free (buffer); buffer = utf8; } } else if (!g_utf8_validate (buffer, -1, NULL)) { /* if it's already utf8, nothing to do otherwise: no charset at all, or conversion failed; ugly * hack: replace all non-ascii chars with '.' */ mu_str_asciify_in_place (buffer); } return buffer; } static gchar* stream_to_string (GMimeStream *stream, size_t buflen) { char *buffer; ssize_t bytes; buffer = g_new(char, buflen + 1); g_mime_stream_reset (stream); /* we read everything in one go */ bytes = g_mime_stream_read (stream, buffer, buflen); if (bytes < 0) { g_warning ("%s: failed to read from stream", __FUNCTION__); g_free (buffer); return NULL; } buffer[bytes]='\0'; return buffer; } gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err) { GMimeDataWrapper *wrapper; GMimeStream *stream; ssize_t buflen; char *buffer; buffer = NULL; stream = NULL; g_return_val_if_fail (err, NULL); *err = TRUE; /* guilty until proven innocent */ g_return_val_if_fail (GMIME_IS_PART(part), NULL); wrapper = g_mime_part_get_content_object (part); if (!wrapper) { /* this happens with invalid mails */ g_debug ("failed to create data wrapper"); goto cleanup; } stream = g_mime_stream_mem_new (); if (!stream) { g_warning ("failed to create mem stream"); goto cleanup; } buflen = g_mime_data_wrapper_write_to_stream (wrapper, stream); if (buflen <= 0) {/* empty buffer, not an error */ *err = FALSE; goto cleanup; } buffer = stream_to_string (stream, (size_t)buflen); /* convert_to_utf8 will free the old 'buffer' if needed */ buffer = convert_to_utf8 (part, buffer); *err = FALSE; cleanup: if (G_IS_OBJECT(stream)) g_object_unref (stream); return buffer; } static gboolean contains (GSList *lst, const char *str) { for (; lst; lst = g_slist_next(lst)) if (g_strcmp0 ((char*)lst->data, str) == 0) return TRUE; return FALSE; } /* * NOTE: this will get the list of references with the oldest parent * at the beginning */ static GSList* get_references (MuMsgFile *self) { GSList *msgids; unsigned u; const char *headers[] = { "References", "In-reply-to", NULL }; for (msgids = NULL, u = 0; headers[u]; ++u) { char *str; const GMimeReferences *cur; GMimeReferences *mime_refs; str = mu_msg_file_get_header (self, headers[u]); if (!str) continue; mime_refs = g_mime_references_decode (str); g_free (str); for (cur = mime_refs; cur; cur = g_mime_references_get_next(cur)) { const char* msgid; msgid = g_mime_references_get_message_id (cur); /* don't include duplicates */ if (msgid && !contains (msgids, msgid)) /* explicitly ensure it's utf8-safe, * as GMime does not ensure that */ msgids = g_slist_prepend (msgids, g_strdup((msgid))); } g_mime_references_free (mime_refs); } /* reverse, because we used g_slist_prepend for performance * reasons */ return g_slist_reverse (msgids); } /* see: http://does-not-exist.org/mail-archives/mutt-dev/msg08249.html */ static GSList* get_tags (MuMsgFile *self) { GSList *lst; unsigned u; struct { const char *header; char sepa; } tagfields[] = { { "X-Label", ' ' }, { "X-Keywords", ',' }, { "Keywords", ' ' } }; for (lst = NULL, u = 0; u != G_N_ELEMENTS(tagfields); ++u) { gchar *hdr; hdr = mu_msg_file_get_header (self, tagfields[u].header); if (hdr) { GSList *hlst; hlst = mu_str_to_list (hdr, tagfields[u].sepa, TRUE); if (lst) (g_slist_last (lst))->next = hlst; else lst = hlst; g_free (hdr); } } return lst; } static char* cleanup_maybe (const char *str, gboolean *do_free) { char *s; if (!str) return NULL; if (!g_utf8_validate(str, -1, NULL)) { if (*do_free) s = mu_str_asciify_in_place ((char*)str); else { *do_free = TRUE; s = mu_str_asciify_in_place(g_strdup (str)); } } else s = (char*)str; mu_str_remove_ctrl_in_place (s); return s; } G_GNUC_CONST static GMimeRecipientType recipient_type (MuMsgFieldId mfid) { switch (mfid) { case MU_MSG_FIELD_ID_BCC: return GMIME_RECIPIENT_TYPE_BCC; case MU_MSG_FIELD_ID_CC : return GMIME_RECIPIENT_TYPE_CC; case MU_MSG_FIELD_ID_TO : return GMIME_RECIPIENT_TYPE_TO; default: g_return_val_if_reached (-1); } } static gchar* get_msgid (MuMsgFile *self, gboolean *do_free) { const char *msgid; msgid = g_mime_message_get_message_id (self->_mime_msg); if (msgid) return (char*)msgid; else { /* if there is none, fake it */ *do_free = TRUE; return g_strdup_printf ( "%s@fake-msgid", mu_util_get_hash (self->_path)); } } char* mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId mfid, gboolean *do_free) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL); *do_free = FALSE; /* default */ switch (mfid) { case MU_MSG_FIELD_ID_BCC: case MU_MSG_FIELD_ID_CC: case MU_MSG_FIELD_ID_TO: *do_free = TRUE; return get_recipient (self, recipient_type(mfid)); case MU_MSG_FIELD_ID_FROM: return (char*)cleanup_maybe (g_mime_message_get_sender (self->_mime_msg), do_free); case MU_MSG_FIELD_ID_PATH: return self->_path; case MU_MSG_FIELD_ID_MAILING_LIST: *do_free = TRUE; return (char*)get_mailing_list (self); case MU_MSG_FIELD_ID_SUBJECT: return (char*)cleanup_maybe (g_mime_message_get_subject (self->_mime_msg), do_free); case MU_MSG_FIELD_ID_MSGID: return get_msgid (self, do_free); case MU_MSG_FIELD_ID_MAILDIR: return self->_maildir; case MU_MSG_FIELD_ID_BODY_TEXT: /* use mu_msg_get_body_text */ case MU_MSG_FIELD_ID_BODY_HTML: /* use mu_msg_get_body_html */ case MU_MSG_FIELD_ID_EMBEDDED_TEXT: g_warning ("%s is not retrievable through: %s", mu_msg_field_name (mfid), __FUNCTION__); return NULL; default: g_return_val_if_reached (NULL); } } GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL); switch (mfid) { case MU_MSG_FIELD_ID_REFS: return get_references (self); case MU_MSG_FIELD_ID_TAGS: return get_tags (self); default: g_return_val_if_reached (NULL); } } gint64 mu_msg_file_get_num_field (MuMsgFile *self, const MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1); switch (mfid) { case MU_MSG_FIELD_ID_DATE: { time_t t; g_mime_message_get_date (self->_mime_msg, &t, NULL); return (time_t)t; } case MU_MSG_FIELD_ID_FLAGS: return (gint64)get_flags(self); case MU_MSG_FIELD_ID_PRIO: return (gint64)get_prio(self); case MU_MSG_FIELD_ID_SIZE: return (gint64)get_size(self); default: g_return_val_if_reached (-1); } } char* mu_msg_file_get_header (MuMsgFile *self, const char *header) { const gchar *hdr; g_return_val_if_fail (self, NULL); g_return_val_if_fail (header, NULL); /* sadly, g_mime_object_get_header may return non-ascii; * so, we need to ensure that */ hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), header); return hdr ? mu_str_utf8ify(hdr) : NULL; } struct _ForeachData { GMimeObjectForeachFunc user_func; gpointer user_data; gboolean decrypt; }; typedef struct _ForeachData ForeachData; static void foreach_cb (GMimeObject *parent, GMimeObject *part, ForeachData *fdata) { /* invoke the callback function */ fdata->user_func (parent, part, fdata->user_data); /* maybe iterate over decrypted parts */ if (fdata->decrypt && GMIME_IS_MULTIPART_ENCRYPTED (part)) { GMimeObject *dec; dec = mu_msg_crypto_decrypt_part (GMIME_MULTIPART_ENCRYPTED(part), MU_MSG_OPTION_NONE, NULL, NULL, NULL); if (!dec) return; if (GMIME_IS_MULTIPART (dec)) g_mime_multipart_foreach ( (GMIME_MULTIPART(dec)), (GMimeObjectForeachFunc)foreach_cb, fdata); else foreach_cb (parent, dec, fdata); g_object_unref (dec); } } void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, GMimeObjectForeachFunc func, gpointer user_data) { ForeachData fdata; g_return_if_fail (GMIME_IS_MESSAGE (msg)); g_return_if_fail (func); fdata.user_func = func; fdata.user_data = user_data; fdata.decrypt = decrypt; g_mime_message_foreach (msg, (GMimeObjectForeachFunc)foreach_cb, &fdata); } ���������������������������������������������������������������mu-0.9.12/lib/mu-msg-file.h�������������������������������������������������������������������������0000664�0000000�0000000�00000006134�12504061031�0015261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_FILE_H__ #define __MU_MSG_FILE_H__ struct _MuMsgFile; typedef struct _MuMsgFile MuMsgFile; /** * create a new message from a file * * @param path full path to the message * @param mdir * @param err error to receive (when function returns NULL), or NULL * * @return a new MuMsg, or NULL in case of error */ MuMsgFile *mu_msg_file_new (const char *path, const char* mdir, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a MuMsgFile object * * @param self object to destroy, or NULL */ void mu_msg_file_destroy (MuMsgFile *self); /** * get a specific header * * @param self a MuMsgFile instance * @param header a header (e.g. 'X-Mailer' or 'List-Id') * * @return the value of the header or NULL if not found; free with g_free */ char* mu_msg_file_get_header (MuMsgFile *self, const char *header); /** * get a string value for this message * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be of type string) * @param do_free receives TRUE or FALSE, conveying if this string * should be owned & freed (TRUE) or not by caller. In case 'FALSE', * this function should be treated as if it were returning a const * char*, and note that in that case the string is only valid as long * as the MuMsgFile is alive, ie. before mu_msg_file_destroy * * @return a string, or NULL */ char* mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId msfid, gboolean *do_free) G_GNUC_WARN_UNUSED_RESULT; /** * get a string-list value for this message * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be of type string-list) * * @return a GSList*, or NULL; free with mu_str_free_list */ GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId msfid) G_GNUC_WARN_UNUSED_RESULT; /** * get a numeric value for this message -- the return value should be * cast into the actual type, e.g., time_t, MuMsgPrio etc. * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be string-based one) * * @return the numeric value, or -1 in case of error */ gint64 mu_msg_file_get_num_field (MuMsgFile *self, MuMsgFieldId mfid); #endif /*__MU_MSG_FILE_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-iter.cc������������������������������������������������������������������������0000664�0000000�0000000�00000024576�12504061031�0015455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <stdlib.h> #include <unistd.h> #include <iostream> #include <string.h> #include <errno.h> #include <algorithm> #include <xapian.h> #include <string> #include <set> #include <map> #include "mu-util.h" #include "mu-msg.h" #include "mu-msg-iter.h" #include "mu-threader.h" struct ltstr { bool operator () (const std::string &s1, const std::string &s2) const { return g_strcmp0 (s1.c_str(), s2.c_str()) < 0; } }; typedef std::map <std::string, unsigned, ltstr> msgid_docid_map; class ThreadKeyMaker: public Xapian::KeyMaker { public: ThreadKeyMaker (GHashTable *threadinfo): _threadinfo(threadinfo) {} virtual std::string operator()(const Xapian::Document &doc) const { MuMsgIterThreadInfo *ti; ti = (MuMsgIterThreadInfo*)g_hash_table_lookup (_threadinfo, GUINT_TO_POINTER(doc.get_docid())); return std::string (ti && ti->threadpath ? ti->threadpath : ""); } private: GHashTable *_threadinfo; }; struct _MuMsgIter { public: _MuMsgIter (Xapian::Enquire &enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags): _enq(enq), _thread_hash (0), _msg(0), _flags(flags), _skip_unreadable(flags & MU_MSG_ITER_FLAG_SKIP_UNREADABLE), _skip_dups (flags & MU_MSG_ITER_FLAG_SKIP_DUPS) { bool descending = (flags & MU_MSG_ITER_FLAG_DESCENDING); bool threads = (flags & MU_MSG_ITER_FLAG_THREADS); // first, we get _all_ matches (G_MAXINT), based the threads // on that, then return <maxint> of those _matches = _enq.get_mset (0, G_MAXINT); if (_matches.empty()) return; if (threads) { _matches.fetch(); _cursor = _matches.begin(); // NOTE: temporarily turn-off skipping duplicates, since we // need threadinfo for *all* _skip_dups = false; _thread_hash = mu_threader_calculate (this, _matches.size(), sortfield, descending); _skip_dups = (flags & MU_MSG_ITER_FLAG_SKIP_DUPS); ThreadKeyMaker keymaker(_thread_hash); enq.set_sort_by_key (&keymaker, false); _matches = _enq.get_mset (0, maxnum); } else if (sortfield != MU_MSG_FIELD_ID_NONE) { enq.set_sort_by_value ((Xapian::valueno)sortfield, descending); _matches = _enq.get_mset (0, maxnum); _cursor = _matches.begin(); } _cursor = _matches.begin(); } ~_MuMsgIter () { if (_thread_hash) g_hash_table_destroy (_thread_hash); set_msg (NULL); } const Xapian::Enquire& enquire() const { return _enq; } Xapian::MSet& matches() { return _matches; } Xapian::MSet::const_iterator cursor () const { return _cursor; } void set_cursor (Xapian::MSetIterator cur) { _cursor = cur; } void cursor_next () { ++_cursor; } GHashTable *thread_hash () { return _thread_hash; } MuMsg *msg() const { return _msg; } MuMsg *set_msg (MuMsg *msg) { if (_msg) mu_msg_unref (_msg); return _msg = msg; } MuMsgIterFlags flags() const { return _flags; } const std::string msgid () const { const Xapian::Document doc (cursor().get_document()); return doc.get_value(MU_MSG_FIELD_ID_MSGID); } unsigned docid () const { const Xapian::Document doc (cursor().get_document()); return doc.get_docid(); } bool looks_like_dup () const { try { const Xapian::Document doc (cursor().get_document()); // is this message in the preferred map? if // so, it's not a duplicate, otherwise, it // isn't msgid_docid_map::const_iterator pref_iter (_preferred_map.find (msgid())); if (pref_iter != _preferred_map.end()) { //std::cerr << "in the set!" << std::endl; if ((*pref_iter).second == docid()) return false; // in the set: not a dup! else return true; } // otherwise, simply check if we've already seen this message-id, // and, if so, it's considered a dup if (_msg_uid_set.find (msgid()) != _msg_uid_set.end()) { return true; } else { _msg_uid_set.insert (msgid()); return false; } } catch (...) { return true; } } static void each_preferred (const char *msgid, gpointer docidp, msgid_docid_map *preferred_map) { (*preferred_map)[msgid] = GPOINTER_TO_SIZE(docidp); } void set_preferred_map (GHashTable *preferred_hash) { if (!preferred_hash) _preferred_map.clear(); else g_hash_table_foreach (preferred_hash, (GHFunc)each_preferred, &_preferred_map); } bool skip_dups () const { return _skip_dups; } bool skip_unreadable () const { return _skip_unreadable; } private: const Xapian::Enquire _enq; Xapian::MSet _matches; Xapian::MSet::const_iterator _cursor; GHashTable *_thread_hash; MuMsg *_msg; MuMsgIterFlags _flags; mutable std::set <std::string, ltstr> _msg_uid_set; bool _skip_unreadable; // the 'preferred map' (msgid->docid) is used when checking // for duplicates; if a message is in the preferred map, it // will not be excluded (but other messages with the same // msgid will) msgid_docid_map _preferred_map; bool _skip_dups; }; static gboolean is_msg_file_readable (MuMsgIter *iter) { gboolean readable; std::string path (iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_PATH)); if (path.empty()) return FALSE; readable = (access (path.c_str(), R_OK) == 0) ? TRUE : FALSE; return readable; } MuMsgIter * mu_msg_iter_new (XapianEnquire *enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags, GError **err) { g_return_val_if_fail (enq, NULL); /* sortfield should be set to .._NONE when we're not threading */ g_return_val_if_fail (mu_msg_field_id_is_valid (sortfield) || sortfield == MU_MSG_FIELD_ID_NONE, FALSE); try { MuMsgIter *iter (new MuMsgIter ((Xapian::Enquire&)*enq, maxnum, sortfield, flags)); // note: we check if it's a dup even for the first message, // since we need its uid in the set for checking later messages if ((iter->skip_unreadable() && !is_msg_file_readable (iter)) || (iter->skip_dups() && iter->looks_like_dup ())) mu_msg_iter_next (iter); /* skip! */ return iter; } catch (const Xapian::DatabaseModifiedError &dbmex) { mu_util_g_set_error (err, MU_ERROR_XAPIAN_MODIFIED, "database was modified; please reopen"); return 0; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } void mu_msg_iter_destroy (MuMsgIter *iter) { try { delete iter; } MU_XAPIAN_CATCH_BLOCK; } void mu_msg_iter_set_preferred (MuMsgIter *iter, GHashTable *preferred_hash) { g_return_if_fail (iter); iter->set_preferred_map (preferred_hash); } MuMsg* mu_msg_iter_get_msg_floating (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { MuMsg *msg; GError *err; Xapian::Document *docp; docp = new Xapian::Document(iter->cursor().get_document()); err = NULL; msg = iter->set_msg (mu_msg_new_from_doc((XapianDocument*)docp, &err)); if (!msg) MU_HANDLE_G_ERROR(err); return msg; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } gboolean mu_msg_iter_reset (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); try { iter->set_cursor(iter->matches().begin()); } MU_XAPIAN_CATCH_BLOCK_RETURN (FALSE); return TRUE; } gboolean mu_msg_iter_next (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); if (mu_msg_iter_is_done(iter)) return FALSE; try { iter->cursor_next(); if (iter->cursor() == iter->matches().end()) return FALSE; if ((iter->skip_unreadable() && !is_msg_file_readable (iter)) || (iter->skip_dups() && iter->looks_like_dup ())) return mu_msg_iter_next (iter); /* skip! */ return TRUE; } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } gboolean mu_msg_iter_is_done (MuMsgIter *iter) { g_return_val_if_fail (iter, TRUE); try { return iter->cursor() == iter->matches().end() ? TRUE : FALSE; } MU_XAPIAN_CATCH_BLOCK_RETURN (TRUE); } /* hmmm.... is it impossible to get a 0 docid, or just very improbable? */ unsigned mu_msg_iter_get_docid (MuMsgIter *iter) { g_return_val_if_fail (iter, (unsigned int)-1); g_return_val_if_fail (!mu_msg_iter_is_done(iter), (unsigned int)-1); try { return iter->docid(); } MU_XAPIAN_CATCH_BLOCK_RETURN ((unsigned int)-1); } char* mu_msg_iter_get_msgid (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { const char *msgid (iter->msgid().c_str()); return msgid ? g_strdup (msgid) : NULL; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } char** mu_msg_iter_get_refs (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { std::string refs ( iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_REFS)); if (refs.empty()) return NULL; return g_strsplit (refs.c_str(),",", -1); } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } char* mu_msg_iter_get_thread_id (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { const std::string thread_id ( iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_THREAD_ID).c_str()); return thread_id.empty() ? NULL : g_strdup (thread_id.c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } const MuMsgIterThreadInfo* mu_msg_iter_get_thread_info (MuMsgIter *iter) { g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); /* maybe we don't have thread info */ if (!iter->thread_hash()) return NULL; try { const MuMsgIterThreadInfo *ti; unsigned int docid; docid = mu_msg_iter_get_docid (iter); ti = (const MuMsgIterThreadInfo*)g_hash_table_lookup (iter->thread_hash(), GUINT_TO_POINTER(docid)); if (!ti) g_warning ("no ti for %u\n", docid); return ti; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } ����������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-iter.h�������������������������������������������������������������������������0000664�0000000�0000000�00000014200�12504061031�0015276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_ITER_H__ #define __MU_MSG_ITER_H__ #include <glib.h> #include <mu-msg.h> G_BEGIN_DECLS /** * MuMsgIter is a structure to iterate over the results of a * query. You can iterate only in one-direction, and you can do it * only once. * */ struct _MuMsgIter; typedef struct _MuMsgIter MuMsgIter; enum _MuMsgIterFlags { MU_MSG_ITER_FLAG_NONE = 0, /* sort Z->A (only for threads) */ MU_MSG_ITER_FLAG_DESCENDING = 1 << 0, /* ignore results for which there is no existing * readable message-file? */ MU_MSG_ITER_FLAG_SKIP_UNREADABLE = 1 << 1, /* ignore duplicate messages? */ MU_MSG_ITER_FLAG_SKIP_DUPS = 1 << 2, /* calculate threads? */ MU_MSG_ITER_FLAG_THREADS = 1 << 3 }; typedef unsigned MuMsgIterFlags; /** * create a new MuMsgIter -- basically, an iterator over the search * results * * @param enq a Xapian::Enquire* cast to XapianEnquire* (because this * is C, not C++),providing access to search results * @param maxnum the maximum number of results * @param sortfield field to sort by * @param flags flags for this iterator (see MsgIterFlags) * @param err receives error information. if the error is * MU_ERROR_XAPIAN_MODIFIED, the database should be reloaded. * * @return a new MuMsgIter, or NULL in case of error */ MuMsgIter *mu_msg_iter_new (XapianEnquire *enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get the next message (which you got from * e.g. mu_query_run) * * @param iter a valid MuMsgIter iterator * * @return TRUE if it succeeded, FALSE otherwise (e.g., because there * are no more messages in the query result) */ gboolean mu_msg_iter_next (MuMsgIter *iter); /** * reset the iterator to the beginning * * @param iter a valid MuMsgIter iterator * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_msg_iter_reset (MuMsgIter *iter); /** * does this iterator point past the end of the list? * * @param iter a valid MuMsgIter iterator * * @return TRUE if the iter points past end of the list, FALSE * otherwise */ gboolean mu_msg_iter_is_done (MuMsgIter *iter); /** * destroy the sequence of messages; ie. /all/ of them * * @param msg a valid MuMsgIter message or NULL */ void mu_msg_iter_destroy (MuMsgIter *iter); /** * get the corresponding MuMsg for this iter; this instance is owned * by MuMsgIter, and becomes invalid after either mu_msg_iter_destroy * or mu_msg_iter_next. _do not_ unref it; it's a floating reference. * * @param iter a valid MuMsgIter instance* * * @return a MuMsg instance, or NULL in case of error */ MuMsg* mu_msg_iter_get_msg_floating (MuMsgIter *iter) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Provide a preferred_hash, which is a hashtable msgid->docid to * indicate the messages which should /not/ be seen as duplicates. * * @param iter a valid MuMsgIter iterator * @param preferred_hash a hashtable msgid->docid of message /not/ to * mark as duplicates, or NULL */ void mu_msg_iter_set_preferred (MuMsgIter *iter, GHashTable *preferred_hash); /** * get the document id for the current message * * @param iter a valid MuMsgIter iterator * * @return the docid or (unsigned int)-1 in case of error */ guint mu_msg_iter_get_docid (MuMsgIter *iter); /** * calculate the message threads * * @param iter a valid MuMsgIter iterator * * @return TRUE if it worked, FALSE otherwsie. */ gboolean mu_msg_iter_calculate_threads (MuMsgIter *iter); enum _MuMsgIterThreadProp { MU_MSG_ITER_THREAD_PROP_NONE = 0 << 0, MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0, MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1, MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 2, MU_MSG_ITER_THREAD_PROP_DUP = 1 << 3, MU_MSG_ITER_THREAD_PROP_HAS_CHILD = 1 << 4 }; typedef guint8 MuMsgIterThreadProp; struct _MuMsgIterThreadInfo { gchar *threadpath; /* a string decribing the thread-path in * such a way that we can sort by this * string to get the right order. */ guint level; /* thread-depth -- [0...] */ MuMsgIterThreadProp prop; }; typedef struct _MuMsgIterThreadInfo MuMsgIterThreadInfo; /** * get a the MuMsgThreaderInfo struct for this message; this only * works when you created the mu-msg-iter with threading enabled * (otherwise, return NULL) * * @param iter a valid MuMsgIter iterator * * @return an info struct */ const MuMsgIterThreadInfo* mu_msg_iter_get_thread_info (MuMsgIter *iter); /** * get the message-id for this message * * @param iter a valid MuMsgIter iterator * * @return the message-id; free with g_free(). */ char* mu_msg_iter_get_msgid (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /** * get the list of references for this messages as a NULL-terminated * string array * * @param iter a valid MuMsgIter iterator * * @return a NULL-terminated string array. free with g_strfreev when * it's no longer needed. */ char** mu_msg_iter_get_refs (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /** * get the thread-id for this message * * @param iter a valid MuMsgIter iterator * * @return the thread-id; free with g_free(). */ char* mu_msg_iter_get_thread_id (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /* FIXME */ const char* mu_msg_iter_get_path (MuMsgIter *iter); G_END_DECLS #endif /*__MU_MSG_ITER_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-part.c�������������������������������������������������������������������������0000664�0000000�0000000�00000054470�12504061031�0015311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2014 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <string.h> #include <unistd.h> #include "mu-util.h" #include "mu-str.h" #include "mu-msg-priv.h" #include "mu-msg-part.h" struct _DoData { GMimeObject *mime_obj; unsigned index; }; typedef struct _DoData DoData; static void do_it_with_index (MuMsg *msg, MuMsgPart *part, DoData *ddata) { if (ddata->mime_obj) return; if (part->index == ddata->index) { /* Add a reference to this object, this way if it is * encrypted it will not be garbage collected before * we are done with it. */ g_object_ref (part->data); ddata->mime_obj = (GMimeObject*)part->data; } } static GMimeObject* get_mime_object_at_index (MuMsg *msg, MuMsgOptions opts, unsigned index) { DoData ddata; ddata.mime_obj = NULL; ddata.index = index; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)do_it_with_index, &ddata); return ddata.mime_obj; } typedef gboolean (*MuMsgPartMatchFunc) (MuMsgPart *, gpointer); struct _MatchData { MuMsgPartMatchFunc match_func; gpointer user_data; int index; }; typedef struct _MatchData MatchData; static void check_match (MuMsg *msg, MuMsgPart *part, MatchData *mdata) { if (mdata->index != -1) return; if (mdata->match_func (part, mdata->user_data)) mdata->index = part->index; } static int get_matching_part_index (MuMsg *msg, MuMsgOptions opts, MuMsgPartMatchFunc func, gpointer user_data) { MatchData mdata; mdata.match_func = func; mdata.user_data = user_data; mdata.index = -1; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)check_match, &mdata); return mdata.index; } static void accumulate_text_message (MuMsg *msg, MuMsgPart *part, GString **gstrp) { const gchar *str; char *adrs; GMimeMessage *mimemsg; InternetAddressList *addresses; /* put sender, recipients and subject in the string, so they * can be indexed as well */ mimemsg = GMIME_MESSAGE (part->data); str = g_mime_message_get_sender (mimemsg); g_string_append_printf (*gstrp, "%s%s", str ? str : "", str ? "\n" : ""); str = g_mime_message_get_subject (mimemsg); g_string_append_printf (*gstrp, "%s%s", str ? str : "", str ? "\n" : ""); addresses = g_mime_message_get_all_recipients (mimemsg); adrs = internet_address_list_to_string (addresses, FALSE); g_object_unref (addresses); g_string_append_printf (*gstrp, "%s%s", adrs ? adrs : "", adrs ? "\n" : ""); g_free (adrs); } static void accumulate_text_part (MuMsg *msg, MuMsgPart *part, GString **gstrp) { GMimeContentType *ctype; gboolean err; char *txt; ctype = g_mime_object_get_content_type ((GMimeObject*)part->data); if (!g_mime_content_type_is_type (ctype, "text", "plain")) return; /* not plain text */ txt = mu_msg_mime_part_to_string ((GMimePart*)part->data, &err); if (txt) g_string_append (*gstrp, txt); g_free (txt); } static void accumulate_text (MuMsg *msg, MuMsgPart *part, GString **gstrp) { if (GMIME_IS_MESSAGE(part->data)) accumulate_text_message (msg, part, gstrp); else if (GMIME_IS_PART (part->data)) accumulate_text_part (msg, part, gstrp); } /* declaration, so we can use it earlier */ static gboolean handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data); static char* get_text_from_mime_msg (MuMsg *msg, GMimeMessage *mmsg, MuMsgOptions opts) { GString *gstr; unsigned index; index = 1; gstr = g_string_sized_new (4096); handle_mime_object (msg, mmsg->mime_part, (GMimeObject *) mmsg, opts, &index, FALSE, (MuMsgPartForeachFunc)accumulate_text, &gstr); return g_string_free (gstr, FALSE); } char* mu_msg_part_get_text (MuMsg *msg, MuMsgPart *self, MuMsgOptions opts) { GMimeObject *mobj; GMimeMessage *mime_msg; gboolean err; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (self && GMIME_IS_OBJECT(self->data), NULL); mobj = (GMimeObject*)self->data; err = FALSE; if (GMIME_IS_PART (mobj)) { if (self->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN) return mu_msg_mime_part_to_string ((GMimePart*)mobj, &err); else return NULL; /* non-text MimePart */ } mime_msg = NULL; if (GMIME_IS_MESSAGE_PART (mobj)) mime_msg = g_mime_message_part_get_message ((GMimeMessagePart*)mobj); else if (GMIME_IS_MESSAGE (mobj)) mime_msg = (GMimeMessage*)mobj; /* apparently, g_mime_message_part_get_message may still * return NULL */ if (mime_msg) return get_text_from_mime_msg (msg, mime_msg, opts); return NULL; } /* note: this will return -1 in case of error or if the size is * unknown */ static ssize_t get_part_size (GMimePart *part) { GMimeDataWrapper *wrapper; GMimeStream *stream; wrapper = g_mime_part_get_content_object (part); if (!GMIME_IS_DATA_WRAPPER(wrapper)) return -1; stream = g_mime_data_wrapper_get_stream (wrapper); if (!stream) return -1; /* no stream -> size is 0 */ else return g_mime_stream_length (stream); /* NOTE: stream/wrapper are owned by gmime, no unreffing */ } static void cleanup_filename (char *fname) { gchar *cur; /* remove slashes, spaces, colons... */ for (cur = fname; *cur; ++cur) if (*cur == '/' || *cur == ' ' || *cur == ':') *cur = '-'; } static char* mime_part_get_filename (GMimeObject *mobj, unsigned index, gboolean construct_if_needed) { gchar *fname; fname = NULL; if (GMIME_IS_PART (mobj)) { /* the easy case: the part has a filename */ fname = (gchar*)g_mime_part_get_filename (GMIME_PART(mobj)); if (fname) /* don't include directory components */ fname = g_path_get_basename (fname); } if (!fname && !construct_if_needed) return NULL; if (GMIME_IS_MESSAGE_PART(mobj)) { GMimeMessage *msg; const char *subj; msg = g_mime_message_part_get_message (GMIME_MESSAGE_PART(mobj)); subj = g_mime_message_get_subject (msg); fname = g_strdup_printf ("%s.eml", subj ? subj : "message"); } if (!fname) fname = g_strdup_printf ("%u.part", index); /* remove slashes, spaces, colons... */ cleanup_filename (fname); return fname; } char* mu_msg_part_get_filename (MuMsgPart *mpart, gboolean construct_if_needed) { g_return_val_if_fail (mpart, NULL); g_return_val_if_fail (GMIME_IS_OBJECT(mpart->data), NULL); return mime_part_get_filename ((GMimeObject*)mpart->data, mpart->index, construct_if_needed); } static MuMsgPartType get_disposition (GMimeObject *mobj) { const char *disp; disp = g_mime_object_get_disposition (mobj); if (!disp) return MU_MSG_PART_TYPE_NONE; if (strcasecmp (disp, GMIME_DISPOSITION_ATTACHMENT) == 0) return MU_MSG_PART_TYPE_ATTACHMENT; if (strcasecmp (disp, GMIME_DISPOSITION_INLINE) == 0) return MU_MSG_PART_TYPE_INLINE; return MU_MSG_PART_TYPE_NONE; } /* call 'func' with information about this MIME-part */ static inline void check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts) { GError *err; err = NULL; mu_msg_crypto_verify_part (part, opts, &err); if (err) { g_warning ("error verifying signature: %s", err->message); g_clear_error (&err); } } /* Note: this is function will be called by GMime when it needs a * password. However, GMime <= 2.6.10 does not handle * getting passwords correctly, so this might fail. see: * password_requester in mu-msg-crypto.c */ static gchar* get_console_pw (const char* user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data) { char *prompt, *pass; if (!g_mime_check_version(2,6,11)) g_printerr ( "*** the gmime library you are using has version " "%u.%u.%u (<= 2.6.10)\n" "*** this version has a bug in its password " "retrieval routine, and probably won't work.\n", gmime_major_version, gmime_minor_version, gmime_micro_version); if (reprompt) g_print ("Authentication failed. Please try again\n"); prompt = g_strdup_printf ("Password for %s: ", user_id); pass = mu_util_read_password (prompt); g_free (prompt); return pass; } static gboolean handle_encrypted_part (MuMsg *msg, GMimeMultipartEncrypted *part, MuMsgOptions opts, unsigned *index, MuMsgPartForeachFunc func, gpointer user_data) { GError *err; gboolean rv; GMimeObject *dec; MuMsgPartPasswordFunc pw_func; if (opts & MU_MSG_OPTION_CONSOLE_PASSWORD) pw_func = (MuMsgPartPasswordFunc)get_console_pw; else pw_func = NULL; err = NULL; dec = mu_msg_crypto_decrypt_part (part, opts, pw_func, NULL, &err); if (err) { g_warning ("error decrypting part: %s", err->message); g_clear_error (&err); } if (dec) { rv = handle_mime_object (msg, dec, (GMimeObject *) part, opts, index, TRUE, func, user_data); g_object_unref (dec); } else { /* On failure to decrypt, list the encrypted part as * an attachment */ GMimeObject *encrypted; encrypted = g_mime_multipart_get_part ( GMIME_MULTIPART (part), 1); g_return_val_if_fail (GMIME_IS_PART(encrypted), FALSE); rv = handle_mime_object (msg, encrypted, (GMimeObject *) part, opts, index, FALSE, func, user_data); } return rv; } /* call 'func' with information about this MIME-part */ static gboolean handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { GMimeContentType *ct; MuMsgPart msgpart; memset (&msgpart, 0, sizeof(MuMsgPart)); msgpart.size = get_part_size (part); msgpart.part_type = MU_MSG_PART_TYPE_LEAF; msgpart.part_type |= get_disposition ((GMimeObject*)part); if (decrypted) msgpart.part_type |= MU_MSG_PART_TYPE_DECRYPTED; else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (parent)) msgpart.part_type |= MU_MSG_PART_TYPE_ENCRYPTED; ct = g_mime_object_get_content_type ((GMimeObject*)part); if (GMIME_IS_CONTENT_TYPE(ct)) { msgpart.type = g_mime_content_type_get_media_type (ct); msgpart.subtype = g_mime_content_type_get_media_subtype (ct); /* store as in the part_type as well, for quick * checking */ if (g_mime_content_type_is_type (ct, "text", "plain")) msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_PLAIN; else if (g_mime_content_type_is_type (ct, "text", "html")) msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_HTML; } /* put the verification info in the pgp-signature and every * descendent of a pgp-encrypted part */ msgpart.sig_status_report = NULL; if (g_ascii_strcasecmp (msgpart.subtype, "pgp-signature") == 0 || decrypted) { msgpart.sig_status_report = (MuMsgPartSigStatusReport*) g_object_get_data (G_OBJECT(parent), SIG_STATUS_REPORT); if (msgpart.sig_status_report) msgpart.part_type |= MU_MSG_PART_TYPE_SIGNED; } msgpart.data = (gpointer)part; msgpart.index = (*index)++; func (msg, &msgpart, user_data); return TRUE; } /* call 'func' with information about this MIME-part */ static gboolean handle_message_part (MuMsg *msg, GMimeMessagePart *mimemsgpart, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { MuMsgPart msgpart; memset (&msgpart, 0, sizeof(MuMsgPart)); msgpart.type = "message"; msgpart.subtype = "rfc822"; msgpart.index = (*index)++; /* msgpart.size = 0; /\* maybe calculate this? *\/ */ msgpart.part_type = MU_MSG_PART_TYPE_MESSAGE; msgpart.part_type |= get_disposition ((GMimeObject*)mimemsgpart); msgpart.data = (gpointer)mimemsgpart; func (msg, &msgpart, user_data); if (opts & MU_MSG_OPTION_RECURSE_RFC822) { GMimeMessage *mmsg; /* may return NULL for some * messages */ mmsg = g_mime_message_part_get_message (mimemsgpart); if (mmsg) return handle_mime_object (msg, mmsg->mime_part, parent, opts, index, decrypted, func, user_data); } return TRUE; } static gboolean handle_multipart (MuMsg *msg, GMimeMultipart *mpart, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { gboolean res; GMimeObject *part; guint i; res = TRUE; for (i = 0; i < mpart->children->len; i++) { part = (GMimeObject *) mpart->children->pdata[i]; res &= handle_mime_object (msg, part, parent, opts, index, decrypted, func, user_data); } return res; } static gboolean handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { if (GMIME_IS_PART (mobj)) return handle_part (msg, GMIME_PART(mobj), parent, opts, index, decrypted, func, user_data); else if (GMIME_IS_MESSAGE_PART (mobj)) return handle_message_part (msg, GMIME_MESSAGE_PART(mobj), parent, opts, index, decrypted, func, user_data); else if ((opts & MU_MSG_OPTION_VERIFY) && GMIME_IS_MULTIPART_SIGNED (mobj)) { check_signature (msg, GMIME_MULTIPART_SIGNED (mobj), opts); return handle_multipart (msg, GMIME_MULTIPART (mobj), mobj, opts, index, decrypted, func, user_data); } else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (mobj)) return handle_encrypted_part (msg, GMIME_MULTIPART_ENCRYPTED (mobj), opts, index, func, user_data); else if (GMIME_IS_MULTIPART (mobj)) return handle_multipart (msg, GMIME_MULTIPART (mobj), parent, opts, index, decrypted, func, user_data); return TRUE; } gboolean mu_msg_part_foreach (MuMsg *msg, MuMsgOptions opts, MuMsgPartForeachFunc func, gpointer user_data) { unsigned index; index = 1; g_return_val_if_fail (msg, FALSE); if (!mu_msg_load_msg_file (msg, NULL)) return FALSE; return handle_mime_object (msg, msg->_file->_mime_msg->mime_part, (GMimeObject *) msg->_file->_mime_msg, opts, &index, FALSE, func, user_data); } gboolean write_part_to_fd (GMimePart *part, int fd, GError **err) { GMimeStream *stream; GMimeDataWrapper *wrapper; gboolean rv; stream = g_mime_stream_fs_new (fd); if (!GMIME_IS_STREAM(stream)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to create stream"); return FALSE; } g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE); wrapper = g_mime_part_get_content_object (part); if (!GMIME_IS_DATA_WRAPPER(wrapper)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to create wrapper"); g_object_unref (stream); return FALSE; } g_object_ref (part); /* FIXME: otherwise, the unrefs below * give errors...*/ if (g_mime_data_wrapper_write_to_stream (wrapper, stream) == -1) { rv = FALSE; g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to write to stream"); } else rv = TRUE; /* g_object_unref (wrapper); we don't own it */ g_object_unref (stream); return rv; } static gboolean write_object_to_fd (GMimeObject *obj, int fd, GError **err) { gchar *str; str = g_mime_object_to_string (obj); if (!str) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "could not get string from object"); return FALSE; } if (write (fd, str, strlen(str)) == -1) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to write object: %s", strerror(errno)); return FALSE; } return TRUE; } static gboolean save_object (GMimeObject *obj, MuMsgOptions opts, const char *fullpath, GError **err) { int fd; gboolean rv; gboolean use_existing, overwrite; use_existing = opts & MU_MSG_OPTION_USE_EXISTING; overwrite = opts & MU_MSG_OPTION_OVERWRITE; /* don't try to overwrite when we already have it; useful when * you're sure it's not a different file with the same name */ if (use_existing && access (fullpath, F_OK) == 0) return TRUE; /* ok, try to create the file */ fd = mu_util_create_writeable_fd (fullpath, 0600, overwrite); if (fd == -1) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "could not open '%s' for writing: %s", fullpath, errno ? strerror(errno) : "error"); return FALSE; } if (GMIME_IS_PART (obj)) rv = write_part_to_fd ((GMimePart*)obj, fd, err); else rv = write_object_to_fd (obj, fd, err); /* Unref it since it was referenced earlier by * get_mime_object_at_index */ g_object_unref (obj); if (close (fd) != 0 && !err) { /* don't write on top of old err */ g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "could not close '%s': %s", fullpath, errno ? strerror(errno) : "error"); return FALSE; } return rv; } gchar* mu_msg_part_get_path (MuMsg *msg, MuMsgOptions opts, const char* targetdir, unsigned index, GError **err) { char *fname, *filepath; GMimeObject* mobj; g_return_val_if_fail (msg, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; mobj = get_mime_object_at_index (msg, opts, index); if (!mobj){ mu_util_g_set_error (err, MU_ERROR_GMIME, "cannot find part %u", index); return NULL; } fname = mime_part_get_filename (mobj, index, TRUE); filepath = g_build_path (G_DIR_SEPARATOR_S, targetdir ? targetdir : "", fname, NULL); /* Unref it since it was referenced earlier by * get_mime_object_at_index */ g_object_unref (mobj); g_free (fname); return filepath; } gchar* mu_msg_part_get_cache_path (MuMsg *msg, MuMsgOptions opts, guint partid, GError **err) { char *dirname, *filepath; const char* path; g_return_val_if_fail (msg, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; path = mu_msg_get_path (msg); /* g_compute_checksum_for_string may be better, but requires * rel. new glib (2.16) */ dirname = g_strdup_printf ("%s%c%x%c%u", mu_util_cache_dir(), G_DIR_SEPARATOR, g_str_hash (path), G_DIR_SEPARATOR, partid); if (!mu_util_create_dir_maybe (dirname, 0700, FALSE)) { mu_util_g_set_error (err, MU_ERROR_FILE, "failed to create dir %s", dirname); g_free (dirname); return NULL; } filepath = mu_msg_part_get_path (msg, opts, dirname, partid, err); g_free (dirname); return filepath; } gboolean mu_msg_part_save (MuMsg *msg, MuMsgOptions opts, const char *fullpath, guint partidx, GError **err) { GMimeObject *part; g_return_val_if_fail (msg, FALSE); g_return_val_if_fail (fullpath, FALSE); g_return_val_if_fail (!((opts & MU_MSG_OPTION_OVERWRITE) && (opts & MU_MSG_OPTION_USE_EXISTING)), FALSE); if (!mu_msg_load_msg_file (msg, err)) return FALSE; part = get_mime_object_at_index (msg, opts, partidx); /* special case: convert a message-part into a message */ if (GMIME_IS_MESSAGE_PART (part)) part = (GMimeObject*)g_mime_message_part_get_message (GMIME_MESSAGE_PART (part)); if (!GMIME_IS_PART(part) && !GMIME_IS_MESSAGE(part)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "unexpected type %s for part %u", G_OBJECT_TYPE_NAME((GObject*)part), partidx); return FALSE; } return save_object (part, opts, fullpath, err); } gchar* mu_msg_part_save_temp (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) { gchar *filepath; filepath = mu_msg_part_get_cache_path (msg, opts, partidx, err); if (!filepath) return NULL; if (!mu_msg_part_save (msg, opts, filepath, partidx, err)) { g_free (filepath); return NULL; } return filepath; } static gboolean match_cid (MuMsgPart *mpart, const char *cid) { const char *this_cid; this_cid = g_mime_object_get_content_id ((GMimeObject*)mpart->data); return g_strcmp0 (this_cid, cid) ? TRUE : FALSE; } int mu_msg_find_index_for_cid (MuMsg *msg, MuMsgOptions opts, const char *sought_cid) { const char* cid; g_return_val_if_fail (msg, -1); g_return_val_if_fail (sought_cid, -1); if (!mu_msg_load_msg_file (msg, NULL)) return -1; cid = g_str_has_prefix (sought_cid, "cid:") ? sought_cid + 4 : sought_cid; return get_matching_part_index (msg, opts, (MuMsgPartMatchFunc)match_cid, (gpointer)(char*)cid); } struct _RxMatchData { GSList *_lst; const GRegex *_rx; guint _idx; }; typedef struct _RxMatchData RxMatchData; static void match_filename_rx (MuMsg *msg, MuMsgPart *mpart, RxMatchData *mdata) { char *fname; fname = mu_msg_part_get_filename (mpart, FALSE); if (!fname) return; if (g_regex_match (mdata->_rx, fname, 0, NULL)) mdata->_lst = g_slist_prepend (mdata->_lst, GUINT_TO_POINTER(mpart->index)); g_free (fname); } GSList* mu_msg_find_files (MuMsg *msg, MuMsgOptions opts, const GRegex *pattern) { RxMatchData mdata; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (pattern, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; mdata._lst = NULL; mdata._rx = pattern; mdata._idx = 0; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)match_filename_rx, &mdata); return mdata._lst; } gboolean mu_msg_part_maybe_attachment (MuMsgPart *part) { g_return_val_if_fail (part, FALSE); /* attachments must be leaf parts */ if (!(part->part_type & MU_MSG_PART_TYPE_LEAF)) return FALSE; /* parts other than text/plain, text/html are considered * attachments as well */ if (!(part->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN) && !(part->part_type & MU_MSG_PART_TYPE_TEXT_HTML)) return TRUE; return part->part_type & MU_MSG_PART_TYPE_ATTACHMENT ? TRUE : FALSE; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-part.h�������������������������������������������������������������������������0000664�0000000�0000000�00000016371�12504061031�0015314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_PART_H__ #define __MU_MSG_PART_H__ #include <glib.h> #include <unistd.h> /* for ssize_t */ #define SIG_STATUS_REPORT "sig-status-report" G_BEGIN_DECLS enum _MuMsgPartType { MU_MSG_PART_TYPE_NONE = 0, /* MIME part without children */ MU_MSG_PART_TYPE_LEAF = 1 << 1, /* an RFC822 message part? */ MU_MSG_PART_TYPE_MESSAGE = 1 << 2, /* disposition inline? */ MU_MSG_PART_TYPE_INLINE = 1 << 3, /* disposition attachment? */ MU_MSG_PART_TYPE_ATTACHMENT = 1 << 4, /* a signed part? */ MU_MSG_PART_TYPE_SIGNED = 1 << 5, /* an encrypted part? */ MU_MSG_PART_TYPE_ENCRYPTED = 1 << 6, /* a decrypted part? */ MU_MSG_PART_TYPE_DECRYPTED = 1 << 7, /* a text/plain part? */ MU_MSG_PART_TYPE_TEXT_PLAIN = 1 << 8, /* a text/html part? */ MU_MSG_PART_TYPE_TEXT_HTML = 1 << 9 }; typedef enum _MuMsgPartType MuMsgPartType; /* the signature status */ enum _MuMsgPartSigStatus { MU_MSG_PART_SIG_STATUS_UNSIGNED = 0, MU_MSG_PART_SIG_STATUS_GOOD, MU_MSG_PART_SIG_STATUS_BAD, MU_MSG_PART_SIG_STATUS_ERROR, MU_MSG_PART_SIG_STATUS_FAIL }; typedef enum _MuMsgPartSigStatus MuMsgPartSigStatus; struct _MuMsgPartSigStatusReport { MuMsgPartSigStatus verdict; const char *report; }; typedef struct _MuMsgPartSigStatusReport MuMsgPartSigStatusReport; /** * destroy a MuMsgPartSignatureStatusReport object * * @param report a MuMsgPartSignatureStatusReport object */ void mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report); struct _MuMsgPart { /* index of this message part */ unsigned index; /* cid */ /* const char *content_id; */ /* content-type: type/subtype, ie. text/plain */ const char *type; const char *subtype; /* size of the part; or < 0 if unknown */ ssize_t size; gpointer data; /* opaque data */ MuMsgPartType part_type; MuMsgPartSigStatusReport *sig_status_report; }; typedef struct _MuMsgPart MuMsgPart; /** * get some appropriate file name for the mime-part * * @param mpart a MuMsgPart * @param construct_if_needed if there is no * real filename, construct one. * * @return the file name (free with g_free) */ char *mu_msg_part_get_filename (MuMsgPart *mpart, gboolean construct_if_needed) G_GNUC_WARN_UNUSED_RESULT; /** * get the text in the MuMsgPart (ie. in its GMimePart) * * @param msg a MuMsg * @param part a MuMsgPart * @param opts MuMsgOptions * * @return utf8 string for this MIME part, to be freed by caller */ char* mu_msg_part_get_text (MuMsg *msg, MuMsgPart *part, MuMsgOptions opts) G_GNUC_WARN_UNUSED_RESULT; /** * does this msg part look like an attachment? * * @param part a message part * * @return TRUE if it looks like an attachment, FALSE otherwise */ gboolean mu_msg_part_maybe_attachment (MuMsgPart *part); /** * save a specific attachment to some targetdir * * @param msg a valid MuMsg instance * @param opts mu-message options (OVERWRITE/USE_EXISTING) * @gchar filepath the filepath to save * @param partidx index of the attachment you want to save * @param err receives error information (when function returns NULL) * * @return full path to the message part saved or NULL in case or * error; free with g_free */ gboolean mu_msg_part_save (MuMsg *msg, MuMsgOptions opts, const char *filepath, guint partidx, GError **err); /** * save a message part to a temporary file and return the full path to * this file * * @param msg a MuMsg message * @param opts mu-message options (OVERWRITE/USE_EXISTING) * @param partidx index of the part to save * @param err receives error information if any * * @return the full path to the temp file, or NULL in case of error */ gchar* mu_msg_part_save_temp (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get a filename for the saving the message part; try the filename * specified for the message part if any, otherwise determine a unique * name based on the partidx and the message path * * @param msg a msg * @param opts mu-message options * @param targetdir where to store the part * @param partidx the part for which to determine a filename * @param err receives error information (when function returns NULL) * * @return a filepath (g_free when done with it) or NULL in case of error */ gchar* mu_msg_part_get_path (MuMsg *msg, MuMsgOptions opts, const char* targetdir, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get a full path name for a file for saving the message part INDEX; * this path is unique (1:1) for this particular message and part for * this user. Thus, it can be used as a cache. * * Will create the directory if needed. * * @param msg a msg * @param opts mu-message options * @param partidx the part for which to determine a filename * @param err receives error information (when function returns NULL) * * @return a filepath (g_free when done with it) or NULL in case of error */ gchar* mu_msg_part_get_cache_path (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get the part index for the message part with a certain content-id * * @param msg a message * @param content_id a content-id to search * * @return the part index number of the found part, or -1 if it was not found */ int mu_msg_find_index_for_cid (MuMsg *msg, MuMsgOptions opts, const char* content_id); /** * retrieve a list of indices for mime-parts with filenames matching a regex * * @param msg a message * @param opts * @param a regular expression to match the filename with * * @return a list with indices for the files matching the pattern; the * indices are the GPOINTER_TO_UINT(lst->data) of the list. They must * be freed with g_slist_free */ GSList* mu_msg_find_files (MuMsg *msg, MuMsgOptions opts, const GRegex *pattern); typedef void (*MuMsgPartForeachFunc) (MuMsg *msg, MuMsgPart*, gpointer); /** * call a function for each of the mime part in a message * * @param msg a valid MuMsg* instance * @param func a callback function to call for each contact; when * the callback does not return TRUE, it won't be called again * @param user_data a user-provide pointer that will be passed to the callback * @param options, bit-wise OR'ed * * @return FALSE in case of error, TRUE otherwise */ gboolean mu_msg_part_foreach (MuMsg *msg, MuMsgOptions opts, MuMsgPartForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_PART_H__*/ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-prio.c�������������������������������������������������������������������������0000664�0000000�0000000�00000003201�12504061031�0015276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include "mu-msg-prio.h" const char* mu_msg_prio_name (MuMsgPrio prio) { switch (prio) { case MU_MSG_PRIO_LOW : return "low"; case MU_MSG_PRIO_NORMAL : return "normal"; case MU_MSG_PRIO_HIGH : return "high"; default : g_return_val_if_reached (NULL); } } MuMsgPrio mu_msg_prio_from_char (char k) { g_return_val_if_fail (k == 'l' || k == 'n' || k == 'h', MU_MSG_PRIO_NONE); return (MuMsgPrio)k; } char mu_msg_prio_char (MuMsgPrio prio) { if (!(prio == 'l' || prio == 'n' || prio == 'h')) { g_warning ("prio: %c", (char)prio); } g_return_val_if_fail (prio == 'l' || prio == 'n' || prio == 'h', 0); return (char)prio; } void mu_msg_prio_foreach (MuMsgPrioForeachFunc func, gpointer user_data) { g_return_if_fail (func); func (MU_MSG_PRIO_LOW, user_data); func (MU_MSG_PRIO_NORMAL, user_data); func (MU_MSG_PRIO_HIGH, user_data); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-prio.h�������������������������������������������������������������������������0000664�0000000�0000000�00000004320�12504061031�0015306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_PRIO_H__ #define __MU_MSG_PRIO_H__ #include <glib.h> G_BEGIN_DECLS enum _MuMsgPrio { MU_MSG_PRIO_LOW = 'l', MU_MSG_PRIO_NORMAL = 'n', MU_MSG_PRIO_HIGH = 'h' }; typedef enum _MuMsgPrio MuMsgPrio; static const MuMsgPrio MU_MSG_PRIO_NONE = (MuMsgPrio)0; /** * get a printable name for the message priority * (ie., MU_MSG_PRIO_LOW=>"low" etc.) * * @param prio a message priority * * @return a printable name for this priority */ const char* mu_msg_prio_name (MuMsgPrio prio) G_GNUC_CONST; /** * get the MuMsgPriority corresponding to a one-character shortcut * ('l'=>MU_MSG_PRIO_, 'n'=>MU_MSG_PRIO_NORMAL or * 'h'=>MU_MSG_PRIO_HIGH) * * @param k a character * * @return a message priority */ MuMsgPrio mu_msg_prio_from_char (char k) G_GNUC_CONST; /** * get the one-character shortcut corresponding to a message priority * ('l'=>MU_MSG_PRIO_, 'n'=>MU_MSG_PRIO_NORMAL or * 'h'=>MU_MSG_PRIO_HIGH) * * @param prio a mesage priority * * @return a shortcut character or 0 in case of error */ char mu_msg_prio_char (MuMsgPrio prio) G_GNUC_CONST; typedef void (*MuMsgPrioForeachFunc) (MuMsgPrio prio, gpointer user_data); /** * call a function for each message priority * * @param func a callback function * @param user_data a user pointer to pass to the callback */ void mu_msg_prio_foreach (MuMsgPrioForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_PRIO_H__*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-priv.h�������������������������������������������������������������������������0000664�0000000�0000000�00000010310�12504061031�0015311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_PRIV_H__ #define __MU_MSG_PRIV_H__ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <gmime/gmime.h> #include <stdlib.h> #include <mu-msg.h> #include <mu-msg-file.h> #include <mu-msg-doc.h> #include "mu-msg-part.h" G_BEGIN_DECLS struct _MuMsgFile { GMimeMessage *_mime_msg; time_t _timestamp; size_t _size; char _path [PATH_MAX + 1]; char _maildir [PATH_MAX + 1]; }; /* we put the the MuMsg definition in this separate -priv file, so we * can split the mu_msg implementations over separate files */ struct _MuMsg { guint _refcount; /* our two backend */ MuMsgFile *_file; /* based on GMime, ie. a file on disc */ MuMsgDoc *_doc; /* based on Xapian::Document */ /* lists where we push allocated strings / GSLists of string * so we can free them when the struct gets destroyed (and we * can return them as 'const to callers) */ GSList *_free_later_str; GSList *_free_later_lst; }; /** * convert a GMimePart to a string * * @param part a GMimePart * @param err will receive TRUE if there was an error, FALSE * otherwise. Must NOT be NULL. * * @return utf8 string for this MIME part, to be freed by caller */ gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Like g_mime_message_foreach, but will recurse into encrypted parts * if @param decrypt is TRUE and mu was built with crypto support * * @param msg a GMimeMessage * @param decrypt whether to try to automatically decrypt * @param func user callback function for each part * @param user_data user point passed to callback function * @param err receives error information * */ void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, GMimeObjectForeachFunc func, gpointer user_data); /** * callback function to retrieve a password from the user * * @param user_id the user name / id to get the password for * @param prompt_ctx a string containing some helpful context for the prompt * @param reprompt whether this is a reprompt after an earlier, incorrect password * @param user_data the user_data pointer passed to mu_msg_part_decrypt_foreach * * @return a newly allocated (g_free'able) string */ typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data); /** * verify the signature of a signed message part * * @param sig a signed message part * @param opts message options * @param err receive error information * * @return a status report object, free with mu_msg_part_sig_status_report_destroy */ void mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, GError **err); /** * decrypt the given encrypted mime multipart * * @param enc encrypted part * @param opts options * @param password_func callback function to retrieve as password (or NULL) * @param user_data pointer passed to the password func * @param err receives error data * * @return the decrypted part, or NULL in case of error */ GMimeObject* mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, MuMsgPartPasswordFunc func, gpointer user_data, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_MSG_PRIV_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg-sexp.c�������������������������������������������������������������������������0000664�0000000�0000000�00000030351�12504061031�0015312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include <ctype.h> #include "mu-str.h" #include "mu-msg.h" #include "mu-msg-iter.h" #include "mu-msg-part.h" #include "mu-maildir.h" static void append_sexp_attr_list (GString *gstr, const char* elm, const GSList *lst) { const GSList *cur; if (!lst) return; /* empty list, don't include */ g_string_append_printf (gstr, "\t:%s ( ", elm); for (cur = lst; cur; cur = g_slist_next(cur)) { char *str; str = mu_str_escape_c_literal ((const gchar*)cur->data, TRUE); g_string_append_printf (gstr, "%s ", str); g_free (str); } g_string_append (gstr, ")\n"); } static void append_sexp_attr (GString *gstr, const char* elm, const char *str) { gchar *esc, *utf8, *cur; if (!str || strlen(str) == 0) return; /* empty: don't include */ utf8 = mu_str_utf8ify (str); for (cur = utf8; *cur; ++cur) if (iscntrl(*cur)) *cur = ' '; esc = mu_str_escape_c_literal (utf8, TRUE); g_free (utf8); g_string_append_printf (gstr, "\t:%s %s\n", elm, esc); g_free (esc); } static void append_sexp_body_attr (GString *gstr, const char* elm, const char *str) { gchar *esc; if (!str || strlen(str) == 0) return; /* empty: don't include */ esc = mu_str_escape_c_literal (str, TRUE); g_string_append_printf (gstr, "\t:%s %s\n", elm, esc); g_free (esc); } struct _ContactData { gboolean from, to, cc, bcc, reply_to; GString *gstr; MuMsgContactType prev_ctype; }; typedef struct _ContactData ContactData; static gchar* get_name_addr_pair (MuMsgContact *c) { gchar *name, *addr, *pair; name = (char*)mu_msg_contact_name(c); addr = (char*)mu_msg_contact_address(c); name = name ? mu_str_escape_c_literal (name, TRUE) : NULL; addr = addr ? mu_str_escape_c_literal (addr, TRUE) : NULL; pair = g_strdup_printf ("(%s . %s)", name ? name : "nil", addr ? addr : "nil"); g_free (name); g_free (addr); return pair; } static void add_prefix_maybe (GString *gstr, gboolean *field, const char *prefix) { /* if there's nothing in the field yet, add the prefix */ if (!*field) g_string_append (gstr, prefix); *field = TRUE; } static gboolean each_contact (MuMsgContact *c, ContactData *cdata) { char *pair; MuMsgContactType ctype; ctype = mu_msg_contact_type (c); /* if the current type is not the previous type, close the * previous first */ if (cdata->prev_ctype != ctype && cdata->prev_ctype != (unsigned)-1) g_string_append (cdata->gstr, ")\n"); switch (ctype) { case MU_MSG_CONTACT_TYPE_FROM: add_prefix_maybe (cdata->gstr, &cdata->from, "\t:from ("); break; case MU_MSG_CONTACT_TYPE_TO: add_prefix_maybe (cdata->gstr, &cdata->to, "\t:to ("); break; case MU_MSG_CONTACT_TYPE_CC: add_prefix_maybe (cdata->gstr, &cdata->cc, "\t:cc ("); break; case MU_MSG_CONTACT_TYPE_BCC: add_prefix_maybe (cdata->gstr, &cdata->bcc, "\t:bcc ("); break; case MU_MSG_CONTACT_TYPE_REPLY_TO: add_prefix_maybe (cdata->gstr, &cdata->reply_to, "\t:reply-to ("); break; default: g_return_val_if_reached (FALSE); } cdata->prev_ctype = ctype; pair = get_name_addr_pair (c); g_string_append (cdata->gstr, pair); g_free (pair); return TRUE; } static void append_sexp_contacts (GString *gstr, MuMsg *msg) { ContactData cdata; cdata.from = cdata.to = cdata.cc = cdata.bcc = cdata.reply_to = FALSE; cdata.gstr = gstr; cdata.prev_ctype = (unsigned)-1; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact, &cdata); if (cdata.from || cdata.to || cdata.cc || cdata.bcc || cdata.reply_to) gstr = g_string_append (gstr, ")\n"); } struct _FlagData { char *flagstr; MuFlags msgflags; }; typedef struct _FlagData FlagData; static void each_flag (MuFlags flag, FlagData *fdata) { if (!(flag & fdata->msgflags)) return; if (!fdata->flagstr) fdata->flagstr = g_strdup (mu_flag_name(flag)); else { gchar *tmp; tmp = g_strconcat (fdata->flagstr, " ", mu_flag_name(flag), NULL); g_free (fdata->flagstr); fdata->flagstr = tmp; } } static void append_sexp_flags (GString *gstr, MuMsg *msg) { FlagData fdata; fdata.msgflags = mu_msg_get_flags (msg); fdata.flagstr = NULL; mu_flags_foreach ((MuFlagsForeachFunc)each_flag, &fdata); if (fdata.flagstr) g_string_append_printf (gstr, "\t:flags (%s)\n", fdata.flagstr); g_free (fdata.flagstr); } static char* get_temp_file (MuMsg *msg, MuMsgOptions opts, unsigned index) { char *path; GError *err; err = NULL; path = mu_msg_part_get_cache_path (msg, opts, index, &err); if (!path) goto errexit; if (!mu_msg_part_save (msg, opts, path, index, &err)) goto errexit; return path; errexit: g_warning ("failed to save mime part: %s", err->message ? err->message : "something went wrong"); g_clear_error (&err); g_free (path); return NULL; } static gchar* get_temp_file_maybe (MuMsg *msg, MuMsgPart *part, MuMsgOptions opts) { char *tmp, *tmpfile; opts |= MU_MSG_OPTION_USE_EXISTING; if (!(opts & MU_MSG_OPTION_EXTRACT_IMAGES) || g_ascii_strcasecmp (part->type, "image") != 0) return NULL; tmp = get_temp_file (msg, opts, part->index); if (!tmp) return NULL; tmpfile = mu_str_escape_c_literal (tmp, TRUE); g_free (tmp); return tmpfile; } struct _PartInfo { char *parts; MuMsgOptions opts; }; typedef struct _PartInfo PartInfo; static const char* sig_verdict (MuMsgPart *mpart) { MuMsgPartSigStatusReport *report; report = mpart->sig_status_report; if (!report) return ""; switch (report->verdict) { case MU_MSG_PART_SIG_STATUS_GOOD: return ":signature verified"; case MU_MSG_PART_SIG_STATUS_BAD: return ":signature bad"; case MU_MSG_PART_SIG_STATUS_ERROR: return ":signature unverified"; default: return ""; } } static const char* dec_verdict (MuMsgPart *mpart) { MuMsgPartType ptype; ptype = mpart->part_type; if (ptype & MU_MSG_PART_TYPE_DECRYPTED) return ":decryption succeeded"; else if (ptype & MU_MSG_PART_TYPE_ENCRYPTED) return ":decryption failed"; else return ""; } static gchar * get_part_type_string (MuMsgPartType ptype) { GString *gstr; unsigned u; struct PartTypes { MuMsgPartType ptype; const char* name; } ptypes[] = { { MU_MSG_PART_TYPE_LEAF, "leaf" }, { MU_MSG_PART_TYPE_MESSAGE, "message" }, { MU_MSG_PART_TYPE_INLINE, "inline" }, { MU_MSG_PART_TYPE_ATTACHMENT, "attachment" }, { MU_MSG_PART_TYPE_SIGNED, "signed" }, { MU_MSG_PART_TYPE_ENCRYPTED, "encrypted" } }; gstr = g_string_sized_new (100); /* more than enough */ gstr = g_string_append_c (gstr, '('); for (u = 0; u!= G_N_ELEMENTS(ptypes); ++u) { if (ptype & ptypes[u].ptype) { if (gstr->len > 1) gstr = g_string_append_c (gstr, ' '); gstr = g_string_append (gstr, ptypes[u].name); } } gstr = g_string_append_c (gstr, ')'); return g_string_free (gstr, FALSE); } static void each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) { char *name, *tmp, *parttype; char *tmpfile; name = mu_msg_part_get_filename (part, TRUE); tmpfile = get_temp_file_maybe (msg, part, pinfo->opts); parttype = get_part_type_string (part->part_type); tmp = g_strdup_printf ("%s(:index %d :name \"%s\" :mime-type \"%s/%s\"%s%s " ":type %s " ":attachment %s :size %i %s %s)", pinfo->parts ? pinfo->parts: "", part->index, name ? name : "noname", part->type ? part->type : "application", part->subtype ? part->subtype : "octet-stream", tmpfile ? " :temp" : "", tmpfile ? tmpfile : "", parttype, mu_msg_part_maybe_attachment (part) ? "t" : "nil", (int)part->size, sig_verdict (part), dec_verdict (part)); g_free (pinfo->parts); pinfo->parts = tmp; } static void append_sexp_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) { PartInfo pinfo; pinfo.parts = NULL; pinfo.opts = opts; if (!mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part, &pinfo)) { /* do nothing */ } else if (pinfo.parts) { g_string_append_printf (gstr, "\t:parts (%s)\n", pinfo.parts); g_free (pinfo.parts); } } static void append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti) { g_string_append_printf (gstr, "\t:thread (:path \"%s\" :level %u%s%s%s%s)\n", ti->threadpath, ti->level, ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD ? " :first-child t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT ? " :empty-parent t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_DUP ? " :duplicate t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_HAS_CHILD ? " :has-child t" : ""); } static void append_message_file_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) { GError *err; err = NULL; if (!mu_msg_load_msg_file (msg, &err)) { g_warning ("failed to load message file: %s", err ? err->message : "some error occured"); g_clear_error (&err); return; } append_sexp_parts (gstr, msg, opts); append_sexp_contacts (gstr, msg); append_sexp_attr_list (gstr, "references", mu_msg_get_references (msg)); append_sexp_attr (gstr, "in-reply-to", mu_msg_get_header (msg, "In-Reply-To")); append_sexp_body_attr (gstr, "body-txt", mu_msg_get_body_text(msg, opts)); append_sexp_body_attr (gstr, "body-html", mu_msg_get_body_html(msg, opts)); } static void append_sexp_date_and_size (GString *gstr, MuMsg *msg) { time_t t; size_t s; t = mu_msg_get_date (msg); if (t == (time_t)-1) /* invalid date? */ t = 0; s = mu_msg_get_size (msg); if (s == (size_t)-1) /* invalid size? */ s = 0; g_string_append_printf (gstr, "\t:date (%u %u 0)\n\t:size %u\n", (unsigned)(t >> 16), (unsigned)(t & 0xffff), (unsigned)s); } static void append_sexp_tags (GString *gstr, MuMsg *msg) { const GSList *tags, *t; gchar *tagesc; GString *tagstr = g_string_new(""); tags = mu_msg_get_tags (msg); for(t = tags; t; t = t->next) { if (t != tags) g_string_append(tagstr, " "); tagesc = mu_str_escape_c_literal((const gchar *)t->data, TRUE); g_string_append(tagstr, tagesc); g_free(tagesc); } if (tagstr->len > 0) g_string_append_printf (gstr, "\t:tags (%s)\n", tagstr->str); g_string_free (tagstr, TRUE); } char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti, MuMsgOptions opts) { GString *gstr; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (!((opts & MU_MSG_OPTION_HEADERS_ONLY) && (opts & MU_MSG_OPTION_EXTRACT_IMAGES)),NULL); gstr = g_string_sized_new ((opts & MU_MSG_OPTION_HEADERS_ONLY) ? 1024 : 8192); if (docid == 0) g_string_append (gstr, "(\n"); else g_string_append_printf (gstr, "(\n\t:docid %u\n", docid); if (ti) append_sexp_thread_info (gstr, ti); append_sexp_attr (gstr, "subject", mu_msg_get_subject (msg)); /* in the no-headers-only case (see below) we get a more * complete list of contacts, so no need to get them here if * that's the case */ if (opts & MU_MSG_OPTION_HEADERS_ONLY) append_sexp_contacts (gstr, msg); append_sexp_date_and_size (gstr, msg); append_sexp_attr (gstr, "message-id", mu_msg_get_msgid (msg)); append_sexp_attr (gstr, "mailing-list", mu_msg_get_mailing_list (msg)); append_sexp_attr (gstr, "path", mu_msg_get_path (msg)); append_sexp_attr (gstr, "maildir", mu_msg_get_maildir (msg)); g_string_append_printf (gstr, "\t:priority %s\n", mu_msg_prio_name(mu_msg_get_prio(msg))); append_sexp_flags (gstr, msg); append_sexp_tags (gstr, msg); /* headers are retrieved from the database, views from the * message file file attr things can only be gotten from the * file (ie., mu view), not from the database (mu find). */ if (!(opts & MU_MSG_OPTION_HEADERS_ONLY)) append_message_file_parts (gstr, msg, opts); g_string_append (gstr, ")\n"); return g_string_free (gstr, FALSE); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-msg.c������������������������������������������������������������������������������0000664�0000000�0000000�00000044674�12504061031�0014352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <ctype.h> #include <gmime/gmime.h> #include "mu-msg-priv.h" /* include before mu-msg.h */ #include "mu-msg.h" #include "mu-util.h" #include "mu-str.h" #include "mu-maildir.h" /* note, we do the gmime initialization here rather than in * mu-runtime, because this way we don't need mu-runtime for simple * cases -- such as our unit tests. Also note that we need gmime init * even for the doc backend, as we use the address parsing functions * also there. */ static gboolean _gmime_initialized = FALSE; static void gmime_init (void) { g_return_if_fail (!_gmime_initialized); #ifdef GMIME_ENABLE_RFC2047_WORKAROUNDS g_mime_init(GMIME_ENABLE_RFC2047_WORKAROUNDS); #else g_mime_init(0); #endif /* GMIME_ENABLE_RFC2047_WORKAROUNDS */ _gmime_initialized = TRUE; } static void gmime_uninit (void) { g_return_if_fail (_gmime_initialized); g_mime_shutdown(); _gmime_initialized = FALSE; } static MuMsg* msg_new (void) { MuMsg *self; self = g_slice_new0 (MuMsg); self->_refcount = 1; return self; } MuMsg* mu_msg_new_from_file (const char *path, const char *mdir, GError **err) { MuMsg *self; MuMsgFile *msgfile; g_return_val_if_fail (path, NULL); if (G_UNLIKELY(!_gmime_initialized)) { gmime_init (); atexit (gmime_uninit); } msgfile = mu_msg_file_new (path, mdir, err); if (!msgfile) return NULL; self = msg_new (); self->_file = msgfile; return self; } MuMsg* mu_msg_new_from_doc (XapianDocument *doc, GError **err) { MuMsg *self; MuMsgDoc *msgdoc; g_return_val_if_fail (doc, NULL); if (G_UNLIKELY(!_gmime_initialized)) { gmime_init (); atexit (gmime_uninit); } msgdoc = mu_msg_doc_new (doc, err); if (!msgdoc) return NULL; self = msg_new (); self->_doc = msgdoc; return self; } static void mu_msg_destroy (MuMsg *self) { if (!self) return; mu_msg_file_destroy (self->_file); mu_msg_doc_destroy (self->_doc); { /* cleanup the strings / lists we stored */ mu_str_free_list (self->_free_later_str); g_slist_foreach (self->_free_later_lst, (GFunc)mu_str_free_list, NULL); g_slist_free (self->_free_later_lst); } g_slice_free (MuMsg, self); } MuMsg* mu_msg_ref (MuMsg *self) { g_return_val_if_fail (self, NULL); ++self->_refcount; return self; } void mu_msg_unref (MuMsg *self) { g_return_if_fail (self); g_return_if_fail (self->_refcount >= 1); if (--self->_refcount == 0) mu_msg_destroy (self); } static const gchar* free_later_str (MuMsg *self, gchar *str) { if (str) self->_free_later_str = g_slist_prepend (self->_free_later_str, str); return str; } static const GSList* free_later_lst (MuMsg *self, GSList *lst) { if (lst) self->_free_later_lst = g_slist_prepend (self->_free_later_lst, lst); return lst; } /* use this instead of mu_msg_get_path so we don't get into infinite * regress...*/ static const char* get_path (MuMsg *self) { char *val; gboolean do_free; do_free = TRUE; val = NULL; if (self->_doc) val = mu_msg_doc_get_str_field (self->_doc, MU_MSG_FIELD_ID_PATH); /* not in the cache yet? try to get it from the file backend, * in case we are using that */ if (!val && self->_file) val = mu_msg_file_get_str_field (self->_file, MU_MSG_FIELD_ID_PATH, &do_free); /* shouldn't happen */ if (!val) g_warning ("%s: cannot find path", __FUNCTION__); return free_later_str (self, val); } /* for some data, we need to read the message file from disk */ gboolean mu_msg_load_msg_file (MuMsg *self, GError **err) { const char *path; g_return_val_if_fail (self, FALSE); if (self->_file) return TRUE; /* nothing to do */ if (!(path = get_path (self))) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "cannot get path for message"); return FALSE; } self->_file = mu_msg_file_new (path, NULL, err); return (self->_file != NULL); } void mu_msg_unload_msg_file (MuMsg *msg) { g_return_if_fail (msg); mu_msg_file_destroy (msg->_file); msg->_file = NULL; } static const GSList* get_str_list_field (MuMsg *self, MuMsgFieldId mfid) { GSList *val; val = NULL; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_str_list_field (self->_doc, mfid); else if (mu_msg_field_gmime (mfid)) { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; val = mu_msg_file_get_str_list_field (self->_file, mfid); } return free_later_lst (self, val); } static const char* get_str_field (MuMsg *self, MuMsgFieldId mfid) { char *val; gboolean do_free; do_free = TRUE; val = NULL; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_str_field (self->_doc, mfid); else if (mu_msg_field_gmime (mfid)) { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; val = mu_msg_file_get_str_field (self->_file, mfid, &do_free); } else val = NULL; return do_free ? free_later_str (self, val) : val; } static gint64 get_num_field (MuMsg *self, MuMsgFieldId mfid) { guint64 val; val = -1; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_num_field (self->_doc, mfid); else { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return -1; val = mu_msg_file_get_num_field (self->_file, mfid); } return val; } const char* mu_msg_get_header (MuMsg *self, const char *header) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (header, NULL); /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; return free_later_str (self, mu_msg_file_get_header (self->_file, header)); } time_t mu_msg_get_timestamp (MuMsg *self) { const char *path; struct stat statbuf; g_return_val_if_fail (self, 0); if (self->_file) return self->_file->_timestamp; path = mu_msg_get_path (self); if (!path || stat (path, &statbuf) < 0) return 0; return statbuf.st_mtime; } const char* mu_msg_get_path (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_PATH); } const char* mu_msg_get_subject (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_SUBJECT); } const char* mu_msg_get_msgid (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_MSGID); } const char* mu_msg_get_mailing_list (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_MAILING_LIST); } const char* mu_msg_get_maildir (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_MAILDIR); } const char* mu_msg_get_from (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_FROM); } const char* mu_msg_get_to (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_TO); } const char* mu_msg_get_cc (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_CC); } const char* mu_msg_get_bcc (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_BCC); } time_t mu_msg_get_date (MuMsg *self) { g_return_val_if_fail (self, (time_t)-1); return (time_t)get_num_field (self, MU_MSG_FIELD_ID_DATE); } MuFlags mu_msg_get_flags (MuMsg *self) { g_return_val_if_fail (self, MU_FLAG_NONE); return (MuFlags)get_num_field (self, MU_MSG_FIELD_ID_FLAGS); } size_t mu_msg_get_size (MuMsg *self) { g_return_val_if_fail (self, (size_t)-1); return (size_t)get_num_field (self, MU_MSG_FIELD_ID_SIZE); } MuMsgPrio mu_msg_get_prio (MuMsg *self) { g_return_val_if_fail (self, MU_MSG_PRIO_NORMAL); return (MuMsgPrio)get_num_field (self, MU_MSG_FIELD_ID_PRIO); } struct _BodyData { GString *gstr; gboolean want_html; }; typedef struct _BodyData BodyData; static void accumulate_body (MuMsg *msg, MuMsgPart *mpart, BodyData *bdata) { char *txt; gboolean err; /* if it looks like an attachment, skip it */ if (mpart->part_type & MU_MSG_PART_TYPE_ATTACHMENT) return; if (!GMIME_IS_PART(mpart->data)) return; txt = NULL; err = TRUE; if (!bdata->want_html && (mpart->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN)) txt = mu_msg_mime_part_to_string ( (GMimePart*)mpart->data, &err); else if (bdata->want_html && (mpart->part_type & MU_MSG_PART_TYPE_TEXT_HTML)) txt = mu_msg_mime_part_to_string ( (GMimePart*)mpart->data, &err); if (!err && txt) bdata->gstr = g_string_append (bdata->gstr, txt); g_free (txt); } static char* get_body (MuMsg *self, MuMsgOptions opts, gboolean want_html) { BodyData bdata; bdata.want_html = want_html; bdata.gstr = g_string_sized_new (4096); mu_msg_part_foreach (self, opts, (MuMsgPartForeachFunc)accumulate_body, &bdata); if (bdata.gstr->len == 0) { g_string_free (bdata.gstr, TRUE); return NULL; } else return g_string_free (bdata.gstr, FALSE); } const char* mu_msg_get_body_html (MuMsg *self, MuMsgOptions opts) { g_return_val_if_fail (self, NULL); return free_later_str (self, get_body (self, opts, TRUE)); } const char* mu_msg_get_body_text (MuMsg *self, MuMsgOptions opts) { g_return_val_if_fail (self, NULL); return free_later_str (self, get_body (self, opts, FALSE)); } const GSList* mu_msg_get_references (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, MU_MSG_FIELD_ID_REFS); } const GSList* mu_msg_get_tags (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, MU_MSG_FIELD_ID_TAGS); } const char* mu_msg_get_field_string (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); return get_str_field (self, mfid); } const GSList* mu_msg_get_field_string_list (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, mfid); } gint64 mu_msg_get_field_numeric (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); return get_num_field (self, mfid); } static gboolean fill_contact (MuMsgContact *self, InternetAddress *addr, MuMsgContactType ctype) { if (!addr) return FALSE; self->name = internet_address_get_name (addr); self->type = ctype; /* we only support internet mailbox addresses; if we don't * check, g_mime hits an assert */ if (INTERNET_ADDRESS_IS_MAILBOX(addr)) self->address = internet_address_mailbox_get_addr (INTERNET_ADDRESS_MAILBOX(addr)); else self->address = NULL; /* if there's no address, just a name, it's probably a local * address (without @) */ if (self->name && !self->address) self->address = self->name; /* note, the address could NULL e.g. when the recipient is something like * 'Undisclosed recipients' */ return self->address != NULL; } static void address_list_foreach (InternetAddressList *addrlist, MuMsgContactType ctype, MuMsgContactForeachFunc func, gpointer user_data) { int i, len; if (!addrlist) return; len = internet_address_list_length(addrlist); for (i = 0; i != len; ++i) { MuMsgContact contact; if (!fill_contact(&contact, internet_address_list_get_address (addrlist, i), ctype)) continue; if (!(func)(&contact, user_data)) break; } } static void addresses_foreach (const char* addrs, MuMsgContactType ctype, MuMsgContactForeachFunc func, gpointer user_data) { InternetAddressList *addrlist; if (!addrs) return; addrlist = internet_address_list_parse_string (addrs); if (addrlist) { address_list_foreach (addrlist, ctype, func, user_data); g_object_unref (addrlist); } } static void msg_contact_foreach_file (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { int i; struct { GMimeRecipientType _gmime_type; MuMsgContactType _type; } ctypes[] = { {GMIME_RECIPIENT_TYPE_TO, MU_MSG_CONTACT_TYPE_TO}, {GMIME_RECIPIENT_TYPE_CC, MU_MSG_CONTACT_TYPE_CC}, {GMIME_RECIPIENT_TYPE_BCC, MU_MSG_CONTACT_TYPE_BCC}, }; /* sender */ addresses_foreach (g_mime_message_get_sender (msg->_file->_mime_msg), MU_MSG_CONTACT_TYPE_FROM, func, user_data); /* reply_to */ addresses_foreach (g_mime_message_get_reply_to (msg->_file->_mime_msg), MU_MSG_CONTACT_TYPE_REPLY_TO, func, user_data); /* get to, cc, bcc */ for (i = 0; i != G_N_ELEMENTS(ctypes); ++i) { InternetAddressList *addrlist; addrlist = g_mime_message_get_recipients (msg->_file->_mime_msg, ctypes[i]._gmime_type); address_list_foreach (addrlist, ctypes[i]._type, func, user_data); } } static void msg_contact_foreach_doc (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { addresses_foreach (mu_msg_get_from (msg), MU_MSG_CONTACT_TYPE_FROM, func, user_data); addresses_foreach (mu_msg_get_to (msg), MU_MSG_CONTACT_TYPE_TO, func, user_data); addresses_foreach (mu_msg_get_cc (msg), MU_MSG_CONTACT_TYPE_CC, func, user_data); addresses_foreach (mu_msg_get_bcc (msg), MU_MSG_CONTACT_TYPE_BCC, func, user_data); } void mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { g_return_if_fail (msg); g_return_if_fail (func); if (msg->_file) msg_contact_foreach_file (msg, func, user_data); else if (msg->_doc) msg_contact_foreach_doc (msg, func, user_data); else g_return_if_reached (); } static int cmp_str (const char *s1, const char *s2) { if (s1 == s2) return 0; else if (!s1) return -1; else if (!s2) return 1; /* optimization 1: ascii */ if (isascii(s1[0]) && isascii(s2[0])) { int diff; diff = tolower(s1[0]) - tolower(s2[0]); if (diff != 0) return diff; } /* utf 8 */ { char *u1, *u2; int diff; u1 = g_utf8_strdown (s1, -1); u2 = g_utf8_strdown (s2, -1); diff = g_utf8_collate (u1, u2); g_free (u1); g_free (u2); return diff; } } static int cmp_subject (const char* s1, const char *s2) { if (s1 == s2) return 0; else if (!s1) return -1; else if (!s2) return 1; return cmp_str ( mu_str_subject_normalize (s1), mu_str_subject_normalize (s2)); } int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid) { g_return_val_if_fail (m1, 0); g_return_val_if_fail (m2, 0); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), 0); /* even though date is a numeric field, we can sort it by its * string repr. in the database, which is much faster */ if (mfid == MU_MSG_FIELD_ID_DATE || mu_msg_field_is_string (mfid)) return cmp_str (get_str_field (m1, mfid), get_str_field (m2, mfid)); if (mfid == MU_MSG_FIELD_ID_SUBJECT) return cmp_subject (get_str_field (m1, mfid), get_str_field (m2, mfid)); /* TODO: note, we cast (potentially > MAXINT to int) */ if (mu_msg_field_is_numeric (mfid)) return get_num_field(m1, mfid) - get_num_field(m2, mfid); return 0; /* TODO: handle lists */ } gboolean mu_msg_is_readable (MuMsg *self) { g_return_val_if_fail (self, FALSE); return access (mu_msg_get_path (self), R_OK) == 0 ? TRUE : FALSE; } /* we need do to determine the * /home/foo/Maildir/bar * from the /bar * that we got */ char* get_target_mdir (MuMsg *msg, const char *target_maildir, GError **err) { char *rootmaildir, *rv; const char *maildir; gboolean not_top_level; /* maildir is the maildir stored in the message, e.g. '/foo' */ maildir = mu_msg_get_maildir(msg); if (!maildir) { mu_util_g_set_error (err, MU_ERROR_GMIME, "message without maildir"); return NULL; } /* the 'rootmaildir' is the filesystem path from root to * maildir, ie. /home/user/Maildir/foo */ rootmaildir = mu_maildir_get_maildir_from_path (mu_msg_get_path(msg)); if (!rootmaildir) { mu_util_g_set_error (err, MU_ERROR_GMIME, "cannot determine maildir"); return NULL; } /* we do a sanity check: verify that that maildir is a suffix of * rootmaildir;*/ not_top_level = TRUE; if (!g_str_has_suffix (rootmaildir, maildir) && /* special case for the top-level '/' maildir, and * remember not_top_level */ (not_top_level = (g_strcmp0 (maildir, "/") != 0))) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "path is '%s', but maildir is '%s' ('%s')", rootmaildir, mu_msg_get_maildir(msg), mu_msg_get_path (msg)); g_free (rootmaildir); return NULL; } /* if we're not at the top-level, remove the final '/' from * the rootmaildir */ if (not_top_level) rootmaildir[strlen(rootmaildir) - strlen (mu_msg_get_maildir(msg))] = '\0'; rv = g_strconcat (rootmaildir, target_maildir, NULL); g_free (rootmaildir); return rv; } /* * move a msg to another maildir, trying to maintain 'integrity', * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be * super-paranoid here... */ gboolean mu_msg_move_to_maildir (MuMsg *self, const char *maildir, MuFlags flags, gboolean ignore_dups, gboolean new_name, GError **err) { char *newfullpath; char *targetmdir; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (maildir, FALSE); /* i.e. "/inbox" */ /* targetmdir is the full path to maildir, i.e., * /home/foo/Maildir/inbox */ targetmdir = get_target_mdir (self, maildir, err); if (!targetmdir) return FALSE; newfullpath = mu_maildir_move_message (mu_msg_get_path (self), targetmdir, flags, ignore_dups, new_name, err); if (!newfullpath) { g_free (targetmdir); return FALSE; } /* clear the old backends */ mu_msg_doc_destroy (self->_doc); self->_doc = NULL; mu_msg_file_destroy (self->_file); /* and create a new one */ self->_file = mu_msg_file_new (newfullpath, maildir, err); g_free (targetmdir); return self->_file ? TRUE : FALSE; } ��������������������������������������������������������������������mu-0.9.12/lib/mu-msg.h������������������������������������������������������������������������������0000664�0000000�0000000�00000037770�12504061031�0014356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_H__ #define __MU_MSG_H__ #include <mu-flags.h> #include <mu-msg-fields.h> #include <mu-msg-prio.h> #include <mu-util.h> /* for MuError and XapianDocument */ G_BEGIN_DECLS struct _MuMsg; typedef struct _MuMsg MuMsg; /* options for various functions */ enum _MuMsgOptions { MU_MSG_OPTION_NONE = 0, /* 1 << 0 is still free! */ /* for -> sexp conversion */ MU_MSG_OPTION_HEADERS_ONLY = 1 << 1, MU_MSG_OPTION_EXTRACT_IMAGES = 1 << 2, /* below options are for checking signatures; only effective * if mu was built with crypto support */ MU_MSG_OPTION_VERIFY = 1 << 4, MU_MSG_OPTION_AUTO_RETRIEVE = 1 << 5, MU_MSG_OPTION_USE_AGENT = 1 << 6, /* MU_MSG_OPTION_USE_PKCS7 = 1 << 7, /\* gpg is the default *\/ */ /* get password from console if needed */ MU_MSG_OPTION_CONSOLE_PASSWORD = 1 << 7, MU_MSG_OPTION_DECRYPT = 1 << 8, /* misc */ MU_MSG_OPTION_OVERWRITE = 1 << 9, MU_MSG_OPTION_USE_EXISTING = 1 << 10, /* recurse into submessages */ MU_MSG_OPTION_RECURSE_RFC822 = 1 << 11 }; typedef enum _MuMsgOptions MuMsgOptions; /** * create a new MuMsg* instance which parses a message and provides * read access to its properties; call mu_msg_unref when done with it. * * @param path full path to an email message file * @param mdir the maildir for this message; ie, if the path is * ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can * pass NULL for this parameter, in which case some maildir-specific * information is not available. * @param err receive error information (MU_ERROR_FILE or * MU_ERROR_GMIME), or NULL. There will only be err info if the * function returns NULL * * @return a new MuMsg instance or NULL in case of error; call * mu_msg_unref when done with this message */ MuMsg *mu_msg_new_from_file (const char* filepath, const char *maildir, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * create a new MuMsg* instance based on a Xapian::Document * * @param store a MuStore ptr * @param doc a ptr to a Xapian::Document (but cast to XapianDocument, * because this is C not C++). MuMsg takes _ownership_ of this pointer; * don't touch it afterwards * @param err receive error information, or NULL. There * will only be err info if the function returns NULL * * @return a new MuMsg instance or NULL in case of error; call * mu_msg_unref when done with this message */ MuMsg *mu_msg_new_from_doc (XapianDocument* doc, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * if we don't have a message file yet (because this message is * database-backed), load it. * * @param msg a MuMsg * @param err receives error information * * @return TRUE if this succceeded, FALSE in case of error */ gboolean mu_msg_load_msg_file (MuMsg *msg, GError **err); /** * close the file-backend, if any; this function is for the use case * where you have a large amount of messages where you need some * file-backed field (body or attachments). If you don't close the * file-backend after retrieving the desired field, you'd quickly run * out of file descriptors. If this message does not have a * file-backend, do nothing. * * @param msg a message object */ void mu_msg_unload_msg_file (MuMsg *msg); /** * increase the reference count for this message * * @param msg a message * * @return the message with its reference count increased, or NULL in * case of error. */ MuMsg *mu_msg_ref (MuMsg *msg); /** * decrease the reference count for this message. if the reference * count reaches 0, the message will be destroyed. * * @param msg a message */ void mu_msg_unref (MuMsg *msg); /** * cache the values from the backend (file or db), so we don't the * backend anymore * * @param self a message */ void mu_msg_cache_values (MuMsg *self); /** * get the plain text body of this message * * @param msg a valid MuMsg* instance * @param opts options for getting the body * * @return the plain text body or NULL in case of error or if there is no * such body. the returned string should *not* be modified or freed. * The returned data is in UTF8 or NULL. */ const char* mu_msg_get_body_text (MuMsg *msg, MuMsgOptions opts); /** * get the html body of this message * * @param msg a valid MuMsg* instance * @param opts options for getting the body * * @return the html body or NULL in case of error or if there is no * such body. the returned string should *not* be modified or freed. */ const char* mu_msg_get_body_html (MuMsg *msgMu, MuMsgOptions opts); /** * get the sender (From:) of this message * * @param msg a valid MuMsg* instance * * @return the sender of this Message or NULL in case of error or if there * is no sender. the returned string should *not* be modified or freed. */ const char* mu_msg_get_from (MuMsg *msg); /** * get the recipients (To:) of this message * * @param msg a valid MuMsg* instance * * @return the sender of this Message or NULL in case of error or if there * are no recipients. the returned string should *not* be modified or freed. */ const char* mu_msg_get_to (MuMsg *msg); /** * get the carbon-copy recipients (Cc:) of this message * * @param msg a valid MuMsg* instance * * @return the Cc: recipients of this Message or NULL in case of error or if * there are no such recipients. the returned string should *not* be modified * or freed. */ const char* mu_msg_get_cc (MuMsg *msg); /** * get the blind carbon-copy recipients (Bcc:) of this message; this * field usually only appears in outgoing messages * * @param msg a valid MuMsg* instance * * @return the Bcc: recipients of this Message or NULL in case of * error or if there are no such recipients. the returned string * should *not* be modified or freed. */ const char* mu_msg_get_bcc (MuMsg *msg); /** * get the file system path of this message * * @param msg a valid MuMsg* instance * * @return the path of this Message or NULL in case of error. * the returned string should *not* be modified or freed. */ const char* mu_msg_get_path (MuMsg *msg); /** * get the maildir this message lives in; ie, if the path is * ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar * * @param msg a valid MuMsg* instance * * @return the maildir requested or NULL in case of error. The returned * string should *not* be modified or freed. */ const char* mu_msg_get_maildir (MuMsg *msg); /** * get the subject of this message * * @param msg a valid MuMsg* instance * * @return the subject of this Message or NULL in case of error or if there * is no subject. the returned string should *not* be modified or freed. */ const char* mu_msg_get_subject (MuMsg *msg); /** * get the Message-Id of this message * * @param msg a valid MuMsg* instance * * @return the Message-Id of this message (without the enclosing <>), * or a fake message-id for messages that don't have them, or NULL in * case of error. */ const char* mu_msg_get_msgid (MuMsg *msg); /** * get the mailing list for a message, i.e. the mailing-list * identifier in the List-Id header. * * @param msg a valid MuMsg* instance * * @return the mailing list id for this message (without the enclosing <>) * or NULL in case of error or if there is none. the returned string * should *not* be modified or freed. */ const char* mu_msg_get_mailing_list (MuMsg *msg); /** * get any arbitrary header from this message * * @param msg a valid MuMsg* instance * @header the header requested * * @return the header requested or NULL in case of error or if there * is no such header. the returned string should *not* be modified or freed. */ const char* mu_msg_get_header (MuMsg *msg, const char* header); /** * get the message date/time (the Date: field) as time_t, using UTC * * @param msg a valid MuMsg* instance * * @return message date/time or 0 in case of error or if there * is no such header. */ time_t mu_msg_get_date (MuMsg *msg); /** * get the flags for this message * * @param msg valid MuMsg* instance * * @return the file/content flags as logically OR'd #MuMsgFlags or 0 * if there are none. Non-standard flags are ignored. */ MuFlags mu_msg_get_flags (MuMsg *msg); /** * get the file size in bytes of this message * * @param msg a valid MuMsg* instance * * @return the filesize */ size_t mu_msg_get_size (MuMsg *msg); /** * get some field value as string * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a string-typed field * * @return a string that should not be freed */ const char* mu_msg_get_field_string (MuMsg *msg, MuMsgFieldId mfid); /** * get some field value as string-list * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a string-list-typed field * * @return a list that should not be freed */ const GSList* mu_msg_get_field_string_list (MuMsg *self, MuMsgFieldId mfid); /** * get some field value as string * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a numeric field * * @return a string that should not be freed */ gint64 mu_msg_get_field_numeric (MuMsg *msg, MuMsgFieldId mfid); /** * get the message priority for this message (MU_MSG_PRIO_LOW, * MU_MSG_PRIO_NORMAL or MU_MSG_PRIO_HIGH) the X-Priority, * X-MSMailPriority, Importance and Precedence header are checked, in * that order. if no known or explicit priority is set, * MU_MSG_PRIO_NORMAL is assumed * * @param msg a valid MuMsg* instance * * @return the message priority (!= 0) or 0 in case of error */ MuMsgPrio mu_msg_get_prio (MuMsg *msg); /** * get the timestamp (mtime) for the file containing this message * * @param msg a valid MuMsg* instance * * @return the timestamp or 0 in case of error */ time_t mu_msg_get_timestamp (MuMsg *msg); /** * get a specific header from the message. This value will _not_ be * cached * * @param self a MuMsg instance * @param header a specific header (like 'X-Mailer' or 'Organization') * * @return a header string which is valid as long as this MuMsg is */ const char* mu_msg_get_header (MuMsg *self, const char *header); /** * get the list of references (consisting of both the References and * In-Reply-To fields), with the oldest first and the direct parent as * the last one. Note, any reference (message-id) will appear at most * once, duplicates are filtered out. * * @param msg a valid MuMsg * * @return a list with the references for this msg. Don't modify/free */ const GSList* mu_msg_get_references (MuMsg *msg); /** * get the list of tags (ie., X-Label) * * @param msg a valid MuMsg * * @return a list with the tags for this msg. Don't modify/free */ const GSList* mu_msg_get_tags (MuMsg *self); /** * compare two messages for sorting * * @param m1 a message * @param m2 another message * @param mfid the message to use for the comparison * * @return negative if m1 is smaller, positive if m1 is smaller, 0 if * they are equal */ int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid); /** * check whether there there's a readable file behind this message * * @param self a MuMsg* * * @return TRUE if the message file is readable, FALSE otherwise */ gboolean mu_msg_is_readable (MuMsg *self); struct _MuMsgIterThreadInfo; /** * convert the msg to a Lisp symbolic expression (for further processing in * e.g. emacs) * * @param msg a valid message * @param docid the docid for this message, or 0 * @param ti thread info for the current message, or NULL * @param opts, bitwise OR'ed; * - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be * obtained from the database (this is much faster if the MuMsg is * database-backed, so no file needs to be opened) * - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary * files and include links to those in the sexp * and for message parts: * MU_MSG_OPTION_CHECK_SIGNATURES: check signatures * MU_MSG_OPTION_AUTO_RETRIEVE_KEY: attempt to retrieve keys online * MU_MSG_OPTION_USE_AGENT: attempt to use GPG-agent * MU_MSG_OPTION_USE_PKCS7: attempt to use PKCS (instead of gpg) * * @return a string with the sexp (free with g_free) or NULL in case of error */ char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const struct _MuMsgIterThreadInfo *ti, MuMsgOptions ops) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * move a message to another maildir; note that this does _not_ update * the database * * @param msg a message with an existing file system path in an actual * maildir * @param maildir the subdir where the message should go, relative to * rootmaildir. e.g. "/archive" * @param flags to set for the target (influences the filename, path) * @param silently ignore the src=target case (return TRUE) * @param new_name whether to create a new unique name, or keep the * old one * @param err (may be NULL) may contain error information; note if the * function return FALSE, err is not set for all error condition * (ie. not for parameter error * * @return TRUE if it worked, FALSE otherwise */ gboolean mu_msg_move_to_maildir (MuMsg *msg, const char *maildir, MuFlags flags, gboolean ignore_dups, gboolean new_name, GError **err); enum _MuMsgContactType { /* Reply-To:? */ MU_MSG_CONTACT_TYPE_TO = 0, MU_MSG_CONTACT_TYPE_FROM, MU_MSG_CONTACT_TYPE_CC, MU_MSG_CONTACT_TYPE_BCC, MU_MSG_CONTACT_TYPE_REPLY_TO, MU_MSG_CONTACT_TYPE_NUM }; typedef guint MuMsgContactType; /* not a 'real' contact type */ #define MU_MSG_CONTACT_TYPE_ALL (MU_MSG_CONTACT_TYPE_NUM + 1) #define mu_msg_contact_type_is_valid(MCT)\ ((MCT) < MU_MSG_CONTACT_TYPE_NUM) struct _MuMsgContact { const char *name; /* Foo Bar */ const char *address; /* foo@bar.cuux */ MuMsgContactType type; /* MU_MSG_CONTACT_TYPE_{ TO, * CC, BCC, FROM, REPLY_TO} */ }; typedef struct _MuMsgContact MuMsgContact; /** * macro to get the name of a contact * * @param ct a MuMsgContact * * @return the name */ #define mu_msg_contact_name(ct) ((ct)->name) /** * macro to get the address of a contact * * @param ct a MuMsgContact * * @return the address */ #define mu_msg_contact_address(ct) ((ct)->address) /** * macro to get the contact type * * @param ct a MuMsgContact * * @return the contact type */ #define mu_msg_contact_type(ct) ((ct)->type) /** * callback function * * @param contact * @param user_data a user provided data pointer * * @return TRUE if we should continue the foreach, FALSE otherwise */ typedef gboolean (*MuMsgContactForeachFunc) (MuMsgContact* contact, gpointer user_data); /** * call a function for each of the contacts in a message; the order is: * from to cc bcc (of each there are zero or more) * * @param msg a valid MuMsgGMime* instance * @param func a callback function to call for each contact; when * the callback does not return TRUE, it won't be called again * @param user_data a user-provide pointer that will be passed to the callback * */ void mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_H__*/ ��������mu-0.9.12/lib/mu-query.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000036402�12504061031�0015062�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <stdexcept> #include <string> #include <cctype> #include <cstring> #include <stdlib.h> #include <xapian.h> #include <glib/gstdio.h> #include "mu-query.h" #include "mu-msg-fields.h" #include "mu-msg-iter.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" /* * custom parser for date ranges */ class MuDateRangeProcessor : public Xapian::StringValueRangeProcessor { public: MuDateRangeProcessor(): Xapian::StringValueRangeProcessor( (Xapian::valueno)MU_MSG_FIELD_ID_DATE) {} Xapian::valueno operator()(std::string &begin, std::string &end) { if (!clear_prefix (begin)) return Xapian::BAD_VALUENO; begin = to_sortable (begin, true); end = to_sortable (end, false); if (begin > end) throw Xapian::QueryParserError ("end time is before begin"); return (Xapian::valueno)MU_MSG_FIELD_ID_DATE; } private: std::string to_sortable (std::string& s, bool is_begin) { const char* str; time_t t; // note: if s is empty and not is_begin, xapian seems // to repeat it. if (s.empty() || g_str_has_suffix (s.c_str(), "..")) { str = mu_date_complete_s ("", is_begin); } else { str = mu_date_interpret_s (s.c_str(), is_begin ? TRUE: FALSE); str = mu_date_complete_s (str, is_begin ? TRUE: FALSE); t = mu_date_str_to_time_t (str, TRUE /*local*/); str = mu_date_time_t_to_str_s (t, FALSE /*UTC*/); } return s = std::string(str); } bool clear_prefix (std::string& begin) { const std::string colon (":"); const std::string name (mu_msg_field_name (MU_MSG_FIELD_ID_DATE) + colon); const std::string shortcut ( std::string(1, mu_msg_field_shortcut (MU_MSG_FIELD_ID_DATE)) + colon); if (begin.find (name) == 0) { begin.erase (0, name.length()); return true; } else if (begin.find (shortcut) == 0) { begin.erase (0, shortcut.length()); return true; } else return false; } }; class MuSizeRangeProcessor : public Xapian::NumberValueRangeProcessor { public: MuSizeRangeProcessor(): Xapian::NumberValueRangeProcessor(MU_MSG_FIELD_ID_SIZE) { } Xapian::valueno operator()(std::string &begin, std::string &end) { if (!clear_prefix (begin)) return Xapian::BAD_VALUENO; if (!substitute_size (begin) || !substitute_size (end)) return Xapian::BAD_VALUENO; /* swap if b > e */ if (begin > end) std::swap (begin, end); begin = Xapian::sortable_serialise (atol(begin.c_str())); end = Xapian::sortable_serialise (atol(end.c_str())); return (Xapian::valueno)MU_MSG_FIELD_ID_SIZE; } private: bool clear_prefix (std::string& begin) { const std::string colon (":"); const std::string name (mu_msg_field_name (MU_MSG_FIELD_ID_SIZE) + colon); const std::string shortcut ( std::string(1, mu_msg_field_shortcut (MU_MSG_FIELD_ID_SIZE)) + colon); if (begin.find (name) == 0) { begin.erase (0, name.length()); return true; } else if (begin.find (shortcut) == 0) { begin.erase (0, shortcut.length()); return true; } else return false; } bool substitute_size (std::string& size) { gchar str[16]; gint64 num = mu_str_size_parse_bkm(size.c_str()); if (num < 0) throw Xapian::QueryParserError ("invalid size"); snprintf (str, sizeof(str), "%" G_GUINT64_FORMAT, num); size = str; return true; } }; static void add_prefix (MuMsgFieldId field, Xapian::QueryParser* qparser); struct _MuQuery { public: _MuQuery (MuStore *store): _store(mu_store_ref(store)) { _qparser.set_database (db()); _qparser.set_default_op (Xapian::Query::OP_AND); _qparser.add_valuerangeprocessor (&_date_range_processor); _qparser.add_valuerangeprocessor (&_size_range_processor); mu_msg_field_foreach ((MuMsgFieldForeachFunc)add_prefix, &_qparser); /* add some convenient special prefixes */ add_special_prefixes (); } ~_MuQuery () { mu_store_unref (_store); } Xapian::Database& db() const { Xapian::Database* db; db = reinterpret_cast<Xapian::Database*> (mu_store_get_read_only_database (_store)); if (!db) throw std::runtime_error ("no database"); return *db; } Xapian::QueryParser& query_parser () { return _qparser; } private: void add_special_prefixes () { char pfx[] = { '\0', '\0' }; /* add 'contact' as a shortcut for From/Cc/Bcc/To: */ pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_FROM); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_TO); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_CC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_BCC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); /* add 'recip' as a shortcut for Cc/Bcc/To: */ pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_TO); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_CC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_BCC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); } Xapian::QueryParser _qparser; MuDateRangeProcessor _date_range_processor; MuSizeRangeProcessor _size_range_processor; MuStore *_store; }; static const Xapian::Query get_query (MuQuery *mqx, const char* searchexpr, GError **err) { Xapian::Query query; char *preprocessed; preprocessed = mu_query_preprocess (searchexpr, err); if (!preprocessed) throw std::runtime_error ("parse error while preprocessing query"); try { query = mqx->query_parser().parse_query (preprocessed, Xapian::QueryParser::FLAG_BOOLEAN | Xapian::QueryParser::FLAG_PURE_NOT | Xapian::QueryParser::FLAG_AUTO_SYNONYMS | Xapian::QueryParser::FLAG_WILDCARD | Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE ); g_free (preprocessed); return query; } catch (...) { mu_util_g_set_error (err,MU_ERROR_XAPIAN_QUERY, "parse error in query"); g_free (preprocessed); throw; } } static void add_prefix (MuMsgFieldId mfid, Xapian::QueryParser* qparser) { if (!mu_msg_field_xapian_index(mfid) && !mu_msg_field_xapian_term(mfid) && !mu_msg_field_xapian_contact(mfid)) return; try { const std::string pfx (1, mu_msg_field_xapian_prefix (mfid)); const std::string shortcut (1, mu_msg_field_shortcut (mfid)); if (mu_msg_field_uses_boolean_prefix (mfid)) { qparser->add_boolean_prefix (mu_msg_field_name(mfid), pfx); qparser->add_boolean_prefix (shortcut, pfx); } else { qparser->add_prefix (mu_msg_field_name(mfid), pfx); qparser->add_prefix (shortcut, pfx); } // all fields are also matched implicitly, without // any prefix qparser->add_prefix ("", pfx); } MU_XAPIAN_CATCH_BLOCK; } MuQuery* mu_query_new (MuStore *store, GError **err) { g_return_val_if_fail (store, NULL); if (mu_store_count (store, err) == 0) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_IS_EMPTY, "database is empty"); return 0; } try { return new MuQuery (store); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); return 0; } void mu_query_destroy (MuQuery *self) { try { delete self; } MU_XAPIAN_CATCH_BLOCK; } /* preprocess a query to make them a bit more promiscuous */ char* mu_query_preprocess (const char *query, GError **err) { GSList *parts, *cur; gchar *myquery; g_return_val_if_fail (query, NULL); /* convert the query to a list of query terms, and escape them * separately */ parts = mu_str_esc_to_list (query); if (!parts) return NULL; for (cur = parts; cur; cur = g_slist_next(cur)) { char *data; data = (gchar*)cur->data; cur->data = mu_str_process_query_term (data); g_free (data); /* run term fixups */ data = (gchar*)cur->data; cur->data = mu_str_xapian_fixup_terms (data); g_free (data); } myquery = mu_str_from_list (parts, ' '); mu_str_free_list (parts); return myquery ? myquery : g_strdup (""); } /* this function is for handling the case where a DatabaseModified * exception is raised. We try to reopen the database, and run the * query again. */ static MuMsgIter * try_requery (MuQuery *self, const char* searchexpr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) { try { /* let's assume that infinite regression is * impossible */ self->db().reopen(); MU_WRITE_LOG ("reopening db after modification"); return mu_query_run (self, searchexpr, sortfieldid, maxnum, flags, err); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } static MuMsgIterFlags msg_iter_flags (MuQueryFlags flags) { MuMsgIterFlags iflags; iflags = MU_MSG_ITER_FLAG_NONE; if (flags & MU_QUERY_FLAG_DESCENDING) iflags |= MU_MSG_ITER_FLAG_DESCENDING; if (flags & MU_QUERY_FLAG_SKIP_UNREADABLE) iflags |= MU_MSG_ITER_FLAG_SKIP_UNREADABLE; if (flags & MU_QUERY_FLAG_SKIP_DUPS) iflags |= MU_MSG_ITER_FLAG_SKIP_DUPS; if (flags & MU_QUERY_FLAG_THREADS) iflags |= MU_MSG_ITER_FLAG_THREADS; return iflags; } static Xapian::Enquire get_enquire (MuQuery *self, const char *searchexpr, MuMsgFieldId sortfieldid, bool descending, GError **err) { Xapian::Enquire enq (self->db()); try { /* empty or "" means "matchall" */ if (!mu_str_is_empty(searchexpr) && g_strcmp0 (searchexpr, "\"\"") != 0) /* NULL or "" or """" */ enq.set_query(get_query (self, searchexpr, err)); else enq.set_query(Xapian::Query::MatchAll); } catch (...) { mu_util_g_set_error (err, MU_ERROR_XAPIAN_QUERY, "parse error in query"); throw; } enq.set_cutoff(0,0); return enq; } /* * record all threadids for the messages; also 'orig_set' receives all * original matches (a map msgid-->docid), so we can make sure the * originals are not seen as 'duplicates' later (when skipping * duplicates). We want to favor the originals over the related * messages, when skipping duplicates. */ static GHashTable* get_thread_ids (MuMsgIter *iter, GHashTable **orig_set) { GHashTable *ids; ids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); *orig_set = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); while (!mu_msg_iter_is_done (iter)) { char *thread_id, *msgid; unsigned docid; /* record the thread id for the message */ if ((thread_id = mu_msg_iter_get_thread_id (iter))) g_hash_table_insert (ids, thread_id, GSIZE_TO_POINTER(TRUE)); /* record the original set */ docid = mu_msg_iter_get_docid(iter); if (docid != 0 && (msgid = mu_msg_iter_get_msgid (iter))) g_hash_table_insert (*orig_set, msgid, GSIZE_TO_POINTER(docid)); if (!mu_msg_iter_next (iter)) break; } return ids; } static Xapian::Query get_related_query (MuMsgIter *iter, GHashTable **orig_set) { GHashTable *hash; GList *id_list, *cur; std::vector<Xapian::Query> qvec; static std::string pfx (1, mu_msg_field_xapian_prefix (MU_MSG_FIELD_ID_THREAD_ID)); /* orig_set receives the hash msgid->docid of the set of * original matches */ hash = get_thread_ids (iter, orig_set); /* id_list now gets a list of all thread-ids seen in the query * results; either in the Message-Id field or in * References. */ id_list = g_hash_table_get_keys (hash); // now, we create a vector with queries for each of the // thread-ids, which we combine below. This is /much/ faster // than creating the query as 'query = Query (OR, query)'... for (cur = id_list; cur; cur = g_list_next(cur)) qvec.push_back (Xapian::Query((std::string (pfx + (char*)cur->data)))); g_hash_table_destroy (hash); g_list_free (id_list); return Xapian::Query (Xapian::Query::OP_OR, qvec.begin(), qvec.end()); } static void include_related (MuQuery *self, MuMsgIter **iter, int maxnum, MuMsgFieldId sortfieldid, MuQueryFlags flags) { GHashTable *orig_set; Xapian::Enquire enq (self->db()); MuMsgIter *rel_iter; orig_set = NULL; enq.set_query(get_related_query (*iter, &orig_set)); enq.set_cutoff(0,0); rel_iter= mu_msg_iter_new ( reinterpret_cast<XapianEnquire*>(&enq), maxnum, sortfieldid, msg_iter_flags (flags), NULL); mu_msg_iter_destroy (*iter); // set the preferred set for the iterator (ie., the set of // messages not considered to be duplicates) to be the // original matches -- the matches without considering // 'related' mu_msg_iter_set_preferred (rel_iter, orig_set); g_hash_table_destroy (orig_set); *iter = rel_iter; } MuMsgIter* mu_query_run (MuQuery *self, const char *searchexpr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (searchexpr, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid (sortfieldid) || sortfieldid == MU_MSG_FIELD_ID_NONE, NULL); try { MuMsgIter *iter; MuQueryFlags first_flags; bool inc_related = flags & MU_QUERY_FLAG_INCLUDE_RELATED; bool descending = flags & MU_QUERY_FLAG_DESCENDING; Xapian::Enquire enq (get_enquire(self, searchexpr, sortfieldid, descending, err)); /* when we're doing a 'include-related query', we're * actually doing /two/ queries; one to get the * initial matches, and based on that one to get all * messages in threads in those matches. */ /* get the 'real' maxnum if it was specified as < 0 */ maxnum = maxnum < 0 ? self->db().get_doccount() : maxnum; /* if we do a include-related query, it's wasted * effort to calculate threads already in the first * query since we can do it in the second one */ first_flags = inc_related ? (flags & ~MU_QUERY_FLAG_THREADS) : flags; iter = mu_msg_iter_new ( reinterpret_cast<XapianEnquire*>(&enq), maxnum, /* with inc_related, we do the sorting in the * second query */ inc_related ? MU_MSG_FIELD_ID_NONE : sortfieldid, msg_iter_flags (first_flags), err); /* * if we want related messages, do a second query, * based on the message ids / refs of the first one * */ if (inc_related) include_related (self, &iter, maxnum, sortfieldid, flags); if (err && *err && (*err)->code == MU_ERROR_XAPIAN_MODIFIED) { g_clear_error (err); return try_requery (self, searchexpr, sortfieldid, maxnum, flags, err); } else return iter; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } char* mu_query_as_string (MuQuery *self, const char *searchexpr, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (searchexpr, NULL); try { Xapian::Query query (get_query(self, searchexpr, err)); return g_strdup(query.get_description().c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-query.h����������������������������������������������������������������������������0000664�0000000�0000000�00000010072�12504061031�0014717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_QUERY_H__ #define __MU_QUERY_H__ #include <glib.h> #include <mu-store.h> #include <mu-msg-iter.h> #include <mu-util.h> G_BEGIN_DECLS struct _MuQuery; typedef struct _MuQuery MuQuery; /** * create a new MuQuery instance. * * @param store a MuStore object * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * possble errors (err->code) are MU_ERROR_XAPIAN_DIR and * MU_ERROR_XAPIAN_NOT_UPTODATE * * @return a new MuQuery instance, or NULL in case of error. * when the instance is no longer needed, use mu_query_destroy * to free it */ MuQuery *mu_query_new (MuStore *store, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy the MuQuery instance * * @param self a MuQuery instance, or NULL */ void mu_query_destroy (MuQuery *self); /** * get a version string for the database * * @param store a valid MuQuery * * @return the version string (free with g_free), or NULL in case of error */ char* mu_query_version (MuQuery *store) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; enum _MuQueryFlags { MU_QUERY_FLAG_NONE = 0, MU_QUERY_FLAG_DESCENDING = 1 << 0, /**< sort z->a */ MU_QUERY_FLAG_SKIP_UNREADABLE = 1 << 1, /**< skip unreadable msgs */ MU_QUERY_FLAG_SKIP_DUPS = 1 << 2, /**< skip duplicate msgs */ MU_QUERY_FLAG_INCLUDE_RELATED = 1 << 3, /**< include related msgs */ MU_QUERY_FLAG_THREADS = 1 << 4 /**< calculate threading info */ }; typedef int MuQueryFlags; /** * run a Xapian query; for the syntax, please refer to the mu-find * manpage, or http://xapian.org/docs/queryparser.html * * @param self a valid MuQuery instance * @param expr the search expression; use "" to match all messages * @param sortfield the field id to sort by or MU_MSG_FIELD_ID_NONE if * sorting is not desired * @param maxnum maximum number of search results to return, or <= 0 for * unlimited * @param flags bitwise OR'd flags to influence the query (see MuQueryFlags) * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * possible error (err->code) is MU_ERROR_QUERY, * * @return a MuMsgIter instance you can iterate over, or NULL in * case of error */ MuMsgIter* mu_query_run (MuQuery *self, const char* expr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * get a string representation of the Xapian search query * * @param self a MuQuery instance * @param searchexpr a xapian search expression * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * * @return the string representation of the xapian query, or NULL in case of * error; free the returned value with g_free */ char* mu_query_as_string (MuQuery *self, const char* searchexpr, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * pre-process the query; this function is useful mainly for debugging mu * * @param query a query string * * @return a pre-processed query, free it with g_free */ char* mu_query_preprocess (const char *query, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_QUERY_H__*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-runtime.c��������������������������������������������������������������������������0000664�0000000�0000000�00000011171�12504061031�0015231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include "mu-runtime.h" #include <glib-object.h> #include <locale.h> /* for setlocale() */ #include <stdio.h> /* for fileno() */ #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include "mu-msg.h" #include "mu-log.h" #include "mu-util.h" #define MU_XAPIAN_DIRNAME "xapian" #define MU_BOOKMARKS_FILENAME "bookmarks" #define MU_CACHE_DIRNAME "cache" #define MU_CONTACTS_FILENAME "contacts" #define MU_LOG_DIRNAME "log" struct _MuRuntimeData { gchar *_str[MU_RUNTIME_PATH_NUM]; gchar *_name; /* e.g., 'mu', 'mug' */ }; typedef struct _MuRuntimeData MuRuntimeData; /* static, global data for this singleton */ static gboolean _initialized = FALSE; static MuRuntimeData *_data = NULL; static void runtime_free (void); static gboolean init_paths (const char* muhome, MuRuntimeData *data); static const char* runtime_path (MuRuntimePath path); static gboolean init_log (const char *muhome, const char *name, MuLogOptions opts) { gboolean rv; char *logpath; logpath = g_strdup_printf ("%s%c%s%c%s.log", muhome, G_DIR_SEPARATOR, MU_LOG_DIRNAME, G_DIR_SEPARATOR, name); rv = mu_log_init (logpath, opts); g_free (logpath); return rv; } gboolean mu_runtime_init (const char* muhome_arg, const char *name) { gchar *muhome; g_return_val_if_fail (!_initialized, FALSE); g_return_val_if_fail (name, FALSE); setlocale (LC_ALL, ""); #ifndef GLIB_VERSION_2_36 g_type_init (); #endif /*GLIB_VERSION_2_36*/ if (muhome_arg) muhome = g_strdup (muhome_arg); else muhome = mu_util_guess_mu_homedir (); if (!mu_util_create_dir_maybe (muhome, 0700, TRUE)) { g_printerr ("mu: invalid mu homedir specified;" " use --muhome=<dir>\n"); runtime_free (); return FALSE; } _data = g_new0 (MuRuntimeData, 1); _data->_str[MU_RUNTIME_PATH_MUHOME] = muhome; init_paths (muhome, _data); _data->_name = g_strdup (name); if (!init_log (muhome, name, MU_LOG_OPTIONS_BACKUP)) { runtime_free (); g_free (muhome); return FALSE; } return _initialized = TRUE; } static void runtime_free (void) { int i; mu_log_uninit(); if (!_data) return; if (_data->_str) for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i) g_free (_data->_str[i]); g_free (_data->_name); g_free (_data); } void mu_runtime_uninit (void) { if (!_initialized) return; runtime_free (); _initialized = FALSE; } static gboolean create_dirs_maybe (MuRuntimeData *data) { if (!mu_util_create_dir_maybe (data->_str[MU_RUNTIME_PATH_CACHE], 0700, TRUE)) { g_warning ("failed to create cache dir"); return FALSE; } if (!mu_util_create_dir_maybe (data->_str[MU_RUNTIME_PATH_LOG], 0700, TRUE)) { g_warning ("failed to create log dir"); return FALSE; } return TRUE; } static gboolean init_paths (const char* muhome, MuRuntimeData *data) { data->_str [MU_RUNTIME_PATH_XAPIANDB] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_XAPIAN_DIRNAME); data->_str [MU_RUNTIME_PATH_BOOKMARKS] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_BOOKMARKS_FILENAME); data->_str [MU_RUNTIME_PATH_CACHE] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_CACHE_DIRNAME); data->_str [MU_RUNTIME_PATH_CONTACTS] = g_strdup_printf ("%s%c%s", data->_str[MU_RUNTIME_PATH_CACHE], G_DIR_SEPARATOR, MU_CONTACTS_FILENAME); data->_str [MU_RUNTIME_PATH_LOG] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_LOG_DIRNAME); if (!create_dirs_maybe (data)) return FALSE; return TRUE; } /* so we can called when _initialized is FALSE still */ static const char* runtime_path (MuRuntimePath path) { return _data->_str[path]; } const char* mu_runtime_path (MuRuntimePath path) { g_return_val_if_fail (_initialized, NULL); g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL); return runtime_path (path); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-runtime.h��������������������������������������������������������������������������0000664�0000000�0000000�00000005312�12504061031�0015236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_RUNTIME_H__ #define __MU_RUNTIME_H__ #include <glib.h> #include <mu-log.h> G_BEGIN_DECLS /** * initialize the mu runtime system; initializes logging and other * systems. To uninitialize, use mu_runtime_uninit * * @param muhome path where to find the mu home directory (typicaly, ~/.mu) * @param name of the main program, ie. 'mu', 'mug' or * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ gboolean mu_runtime_init (const char *muhome, const char *name); /** * initialize the mu runtime system with comand line argument; this * will parse the command line assuming the parameters of the 'mu' * program. Initializes logging and other systems. To uninitialize, * use mu_runtime_uninit * * @param ptr to the param count (typically, argc) * @param ptr to the params (typically, argv) * @param name of the main program, ie. 'mu', 'mug' or * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv, const char *name); /** * free all resources * */ void mu_runtime_uninit (void); enum _MuRuntimePath { MU_RUNTIME_PATH_MUHOME, /* mu home path */ MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */ MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */ MU_RUNTIME_PATH_CACHE, /* mu cache path */ MU_RUNTIME_PATH_LOG, /* mu path for log files */ MU_RUNTIME_PATH_CONTACTS, /* mu path to the contacts cache */ MU_RUNTIME_PATH_NUM }; typedef enum _MuRuntimePath MuRuntimePath; /** * get a file system path to some 'special' file or directory * * @return ma string which should be not be modified/freed, or NULL in * case of error. */ const char* mu_runtime_path (MuRuntimePath path); G_END_DECLS #endif /*__MU_RUNTIME_H__*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-script.c���������������������������������������������������������������������������0000664�0000000�0000000�00000016604�12504061031�0015060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #ifdef BUILD_GUILE #include <libguile.h> #endif /*BUILD_GUILE*/ #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <dirent.h> #include <errno.h> #include <unistd.h> #include "mu-str.h" #include "mu-script.h" #include "mu-util.h" /** * Structure with information about a certain script. * the values will be *freed* when MuScriptInfo is freed */ struct _MuScriptInfo { char *_name; /* filename-sans-extension */ char *_path; /* full path to script */ char *_oneline; /* one-line description */ char *_descr; /* longer description */ }; /* create a new MuScriptInfo* object*/ static MuScriptInfo* script_info_new (void) { return g_slice_new0 (MuScriptInfo); } /* destroy a MuScriptInfo* object */ static void script_info_destroy (MuScriptInfo *msi) { if (!msi) return; g_free (msi->_name); g_free (msi->_path); g_free (msi->_oneline); g_free (msi->_descr); g_slice_free (MuScriptInfo, msi); } /* compare two MuScripInfo* objects (for sorting) */ static int script_info_cmp (MuScriptInfo *msi1, MuScriptInfo *msi2) { return strcmp (msi1->_name, msi2->_name); } const char* mu_script_info_name (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_name; } const char* mu_script_info_path (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_path; } const char* mu_script_info_one_line (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_oneline; } const char* mu_script_info_description (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_descr; } gboolean mu_script_info_matches_regex (MuScriptInfo *msi, const char *rxstr, GError **err) { GRegex *rx; gboolean match; g_return_val_if_fail (msi, FALSE); g_return_val_if_fail (rxstr, FALSE); rx = g_regex_new (rxstr, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0, err); if (!rx) return FALSE; match = FALSE; if (msi->_name) match = g_regex_match (rx, msi->_name, 0, NULL); if (!match && msi->_oneline) match = g_regex_match (rx, msi->_oneline, 0, NULL); return match; } void mu_script_info_list_destroy (GSList *lst) { g_slist_foreach (lst, (GFunc)script_info_destroy, NULL); g_slist_free (lst); } static GIOChannel * open_channel (const char *path) { GError *err; GIOChannel *io_chan; err = NULL; io_chan = g_io_channel_new_file (path, "r", &err); if (!io_chan) { g_warning ("failed to open '%s': %s", path, err ? err->message : "something went wrong"); g_clear_error (&err); return NULL; } return io_chan; } static void end_channel (GIOChannel *io_chan) { GIOStatus status; GError *err; err = NULL; status = g_io_channel_shutdown (io_chan, FALSE, &err); if (status != G_IO_STATUS_NORMAL) { g_warning ("failed to shutdown io-channel: %s", err ? err->message : "something went wrong"); g_clear_error (&err); } g_io_channel_unref (io_chan); } static gboolean get_descriptions (MuScriptInfo *msi, const char *prefix) { GIOStatus io_status; GIOChannel *script_io; GError *err; char *line, *descr, *oneline; if (!prefix) return TRUE; /* not an error */ if (!(script_io = open_channel (msi->_path))) return FALSE; err = NULL; line = descr = oneline = NULL; do { g_free (line); io_status = g_io_channel_read_line (script_io, &line, NULL, NULL, &err); if (io_status != G_IO_STATUS_NORMAL) break; if (!g_str_has_prefix (line, prefix)) continue; if (!oneline) oneline = g_strdup (line + strlen (prefix)); else { char *tmp; tmp = descr; descr = g_strdup_printf ("%s%s", descr ? descr : "", line + strlen(prefix)); g_free (tmp); } } while (TRUE); if (io_status != G_IO_STATUS_EOF) { g_warning ("error reading %s: %s", msi->_path, err ? err->message : "something went wrong"); g_clear_error (&err); } end_channel (script_io); msi->_oneline = oneline; msi->_descr = descr; return TRUE; } GSList* mu_script_get_script_info_list (const char *path, const char *ext, const char *descprefix, GError **err) { DIR *dir; GSList *lst; struct dirent *dentry; g_return_val_if_fail (path, NULL); dir = opendir (path); if (!dir) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_OPEN, "failed to open '%s': %s", path, strerror(errno)); return NULL; } /* create a list of names, paths */ lst = NULL; while ((dentry = readdir (dir))) { MuScriptInfo *msi; /* only consider files with certain extensions, * if ext != NULL */ if (ext && !g_str_has_suffix (dentry->d_name, ext)) continue; msi = script_info_new (); msi->_name = g_strdup (dentry->d_name); if (ext) /* strip the extension */ msi->_name[strlen(msi->_name) - strlen(ext)] = '\0'; msi->_path = g_strdup_printf ("%s%c%s", path, G_DIR_SEPARATOR, dentry->d_name); /* set the one-line and long description */ get_descriptions (msi, descprefix); lst = g_slist_prepend (lst, msi); } closedir (dir); /* ignore error checking... */ return g_slist_sort (lst, (GCompareFunc)script_info_cmp); } MuScriptInfo* mu_script_find_script_with_name (GSList *lst, const char *name) { GSList *cur; g_return_val_if_fail (name, NULL); for (cur = lst; cur; cur = g_slist_next (cur)) { MuScriptInfo *msi; msi = (MuScriptInfo*)cur->data; if (g_strcmp0 (name, mu_script_info_name (msi)) == 0) return msi; } return NULL; } #ifdef BUILD_GUILE static void guile_shell (void *closure, int argc, char **argv) { scm_shell (argc, argv); } gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err) { char *mainargs, *expr; char *argv[] = { "guile", "-l", NULL, "-c", NULL, NULL }; g_return_val_if_fail (msi, FALSE); g_return_val_if_fail (muhome, FALSE); if (access (mu_script_info_path (msi), R_OK) != 0) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_READ, "failed to read script: %s", strerror(errno)); return FALSE; } argv[2] = (char*)mu_script_info_path (msi); mainargs = mu_str_quoted_from_strv (args); expr = g_strdup_printf ( "(main '(\"%s\" \"--muhome=%s\" %s))", mu_script_info_name (msi), muhome, mainargs ? mainargs : ""); g_free (mainargs); argv[4] = expr; scm_boot_guile (5, argv, guile_shell, NULL); /* never reached but let's be correct(TM)*/ g_free (expr); return TRUE; } #else /*!BUILD_GUILE*/ gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "this mu does not have guile support"); return FALSE; } #endif /*!BUILD_GUILE*/ ����������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-script.h���������������������������������������������������������������������������0000664�0000000�0000000�00000007100�12504061031�0015054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_SCRIPT_H__ #define __MU_SCRIPT_H__ #include <glib.h> G_BEGIN_DECLS /* Opaque structure with information about a script */ struct _MuScriptInfo; typedef struct _MuScriptInfo MuScriptInfo; /** * get the name of the script (sans-extension, if some extension was * provided to mu_script_get_scripts) * * @param msi a MuScriptInfo structure * * @return the name */ const char* mu_script_info_name (MuScriptInfo *msi); /** * get the full filesystem path of the script * * @param msi a MuScriptInfo structure * * @return the path */ const char* mu_script_info_path (MuScriptInfo *msi); /** * get a one-line description for the script * * @param msi a MuScriptInfo structure * * @return the description, or NULL if there was none */ const char* mu_script_info_one_line (MuScriptInfo *msi); /** * get a full description for the script * * @param msi a MuScriptInfo structure * * @return the description, or NULL if there was none */ const char* mu_script_info_description (MuScriptInfo *msi); /** * check whether either the name or one-line description of a * MuScriptInfo matches regular expression rxstr * * @param msi a MuScriptInfo * @param rxstr a regular expression string * @param err receives error information * * @return TRUE if it matches, FALSE if not or in case of error */ gboolean mu_script_info_matches_regex (MuScriptInfo *msi, const char *rxstr, GError **err); /** * Get the list of all scripts in path with extension ext * * @param path a file system path * @param ext an extension (e.g., ".scm"), or NULL * @param prefix for the one-line description * (e.g., ";; DESCRIPTION: "), or NULL * @param err receives error information, if any * * @return a list of Mu */ GSList *mu_script_get_script_info_list (const char *path, const char *ext, const char *descprefix, GError **err); /** * destroy a list of MuScriptInfo* objects * * @param scriptslst a list of MuScriptInfo* objects */ void mu_script_info_list_destroy (GSList *lst); /** * find the MuScriptInfo object for the first script with a certain * name, or return NULL if not found. * * @param lst a list of MuScriptInfo* objects * @param name the name to search for * * @return a MuScriptInfo* object, or NULL if not found. */ MuScriptInfo* mu_script_find_script_with_name (GSList *lst, const char *name); /** * run the guile script at path * * @param msi MuScriptInfo object for the script * @param muhome path to the mu home dir * @param args NULL-terminated array of strings (argv for the script) * @param err receives error information * * @return FALSE in case of error -- otherwise, this function will * _not return_ */ gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err); G_END_DECLS #endif /*__MU_SCRIPT_H__*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-store-priv.hh����������������������������������������������������������������������0000664�0000000�0000000�00000015321�12504061031�0016036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2011-2012 <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_STORE_PRIV_HH__ #define __MU_STORE_PRIV_HH__ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include <unistd.h> #include "mu-store.h" #include "mu-contacts.h" #include "mu-str.h" class MuStoreError { public: MuStoreError (MuError err, const std::string& what): _err (err), _what(what) {} MuError mu_error () const { return _err; } const std::string& what() const { return _what; } private: MuError _err; const std::string _what; }; struct _MuStore { public: /* create a read-write MuStore */ _MuStore (const char *path, const char *contacts_path, bool rebuild) { init (path, contacts_path, rebuild, false); if (rebuild) _db = new Xapian::WritableDatabase (path, Xapian::DB_CREATE_OR_OVERWRITE); else _db = new Xapian::WritableDatabase (path, Xapian::DB_CREATE_OR_OPEN); check_set_version (); if (contacts_path) { /* when rebuilding, attempt to clear the * contacts path */ if (rebuild && access (contacts_path, W_OK) == 0) (void)unlink (contacts_path); _contacts = mu_contacts_new (contacts_path); if (!_contacts) throw MuStoreError (MU_ERROR_FILE, ("failed to init contacts cache")); } MU_WRITE_LOG ("%s: opened %s (batch size: %u) for read-write", __FUNCTION__, this->path(), (unsigned)batch_size()); } /* create a read-only MuStore */ _MuStore (const char *path) { init (path, NULL, false, false); _db = new Xapian::Database (path); if (!mu_store_versions_match(this)) { char *errstr = g_strdup_printf ("db version: %s, but we need %s; " "database rebuild is required", mu_store_version (this), MU_STORE_SCHEMA_VERSION); MuStoreError exc (MU_ERROR_XAPIAN_VERSION_MISMATCH, errstr); g_free (errstr); throw exc; } MU_WRITE_LOG ("%s: opened %s read-only", __FUNCTION__, this->path()); } void init (const char *path, const char *contacts_path, bool rebuild, bool read_only) { _my_addresses = NULL; _batch_size = DEFAULT_BATCH_SIZE; _contacts = 0; _in_transaction = false; _path = path; _processed = 0; _read_only = read_only; _ref_count = 1; _version = NULL; } void set_my_addresses (const char **my_addresses) { if (_my_addresses) { mu_str_free_list (_my_addresses); _my_addresses = NULL; } while (my_addresses && *my_addresses) { _my_addresses = g_slist_prepend (_my_addresses, g_strdup (*my_addresses)); ++my_addresses; } } void check_set_version () { if (_version) return; _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); if (!_version) { mu_store_set_metadata (this, MU_STORE_VERSION_KEY, MU_STORE_SCHEMA_VERSION, NULL); _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); } else if (g_strcmp0 (_version, MU_STORE_SCHEMA_VERSION) != 0) throw MuStoreError (MU_ERROR_XAPIAN_VERSION_MISMATCH, "the database needs a rebuild"); } ~_MuStore () { try { if (_ref_count != 0) g_warning ("ref count != 0"); mu_contacts_destroy (_contacts); if (!_read_only) mu_store_flush (this); g_free (_version); mu_str_free_list (_my_addresses); MU_WRITE_LOG ("closing xapian database with %d document(s)", (int)db_read_only()->get_doccount()); delete _db; } MU_XAPIAN_CATCH_BLOCK; } /* close the old database, and write an empty one on top of it */ void clear () { if (is_read_only()) throw std::runtime_error ("database is read-only"); // clear the database db_writable()->close (); delete _db; _db = new Xapian::WritableDatabase (path(), Xapian::DB_CREATE_OR_OVERWRITE); // clear the contacts cache if (_contacts) mu_contacts_clear (_contacts); } std::string get_uid_term (const char *path) const; MuContacts* contacts() { return _contacts; } const char *version () const { if (!_version) _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); return _version; } void set_version (const char *version) { g_free (_version); _version = NULL; mu_store_set_metadata (this, MU_STORE_VERSION_KEY, version, NULL); } static unsigned max_term_length() { return MAX_TERM_LENGTH; } void begin_transaction (); void commit_transaction (); void rollback_transaction (); Xapian::WritableDatabase* db_writable() { if (G_UNLIKELY(is_read_only())) throw std::runtime_error ("database is read-only"); return (Xapian::WritableDatabase*)_db; } Xapian::Database* db_read_only() const { return _db; } const char* path () const { return _path.c_str(); } bool is_read_only () const { return _read_only; } size_t batch_size () const { return _batch_size;} size_t set_batch_size (size_t n) { return _batch_size = ( n == 0) ? DEFAULT_BATCH_SIZE : n; } bool in_transaction () const { return _in_transaction; } bool in_transaction (bool in_tx) { return _in_transaction = in_tx; } int processed () const { return _processed; } int set_processed (int n) { return _processed = n;} int inc_processed () { return ++_processed; } /* MuStore is ref-counted */ guint ref () { return ++_ref_count; } guint unref () { if (_ref_count < 1) g_critical ("ref count error"); return --_ref_count; } GSList *my_addresses () { return _my_addresses; } /* by default, use transactions of 30000 messages */ static const unsigned DEFAULT_BATCH_SIZE = 30000; /* http://article.gmane.org/gmane.comp.search.xapian.general/3656 */ static const unsigned MAX_TERM_LENGTH = 240; private: /* transaction handling */ bool _in_transaction; int _processed; size_t _batch_size; /* batch size of a xapian transaction */ /* contacts object to cache all the contact information */ MuContacts *_contacts; std::string _path; mutable char *_version; Xapian::Database *_db; bool _read_only; guint _ref_count; GSList *_my_addresses; }; #endif /*__MU_STORE_PRIV_HH__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-store-read.cc����������������������������������������������������������������������0000664�0000000�0000000�00000014123�12504061031�0015756�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include <limits.h> #include <stdlib.h> #include <errno.h> #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" // note: not re-entrant std::string _MuStore::get_uid_term (const char* path) const { char real_path[PATH_MAX + 1]; static const std::string uid_prefix ( 1, mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_UID)); /* check profile to see if realpath is expensive; we need * realpath here (and in mu-msg-file) to ensure that the same * messages are only considered ones (ignore e.g. symlinks and * '//' in paths) */ // note: realpath fails when there's no file at path if (!realpath (path, real_path)) strcpy (real_path, path); return std::string (uid_prefix + mu_util_get_hash (real_path)); } MuStore* mu_store_new_read_only (const char* xpath, GError **err) { g_return_val_if_fail (xpath, NULL); try { return new _MuStore (xpath); } catch (const MuStoreError& merr) { mu_util_g_set_error (err, merr.mu_error(), "%s", merr.what().c_str()); } MU_XAPIAN_CATCH_BLOCK_G_ERROR(err, MU_ERROR_XAPIAN); return NULL; } gboolean mu_store_is_read_only (const MuStore *store) { g_return_val_if_fail (store, FALSE); try { return store->is_read_only() ? TRUE : FALSE; } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } unsigned mu_store_count (const MuStore *store, GError **err) { g_return_val_if_fail (store, (unsigned)-1); try { return store->db_read_only()->get_doccount(); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, (unsigned)-1); } const char* mu_store_version (const MuStore *store) { g_return_val_if_fail (store, NULL); return store->version(); } gboolean mu_store_versions_match (const MuStore *store) { g_return_val_if_fail (store, TRUE); return g_strcmp0 (mu_store_version (store), MU_STORE_SCHEMA_VERSION) == 0; } char* mu_store_get_metadata (const MuStore *store, const char *key, GError **err) { g_return_val_if_fail (store, NULL); g_return_val_if_fail (store->db_read_only(), NULL); g_return_val_if_fail (key, NULL); try { std::string val; val = store->db_read_only()->get_metadata (key); if (!val.empty()) return g_strdup (val.c_str()); else return NULL; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, NULL); } XapianDatabase* mu_store_get_read_only_database (MuStore *store) { g_return_val_if_fail (store, NULL); return (XapianWritableDatabase*)store->db_read_only(); } gboolean mu_store_contains_message (const MuStore *store, const char* path, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); try { const std::string term (store->get_uid_term(path)); return store->db_read_only()->term_exists (term) ? TRUE: FALSE; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, FALSE); } unsigned mu_store_get_docid_for_path (const MuStore *store, const char* path, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); try { const std::string term (store->get_uid_term(path)); Xapian::Query query (term); Xapian::Enquire enq (*store->db_read_only()); enq.set_query (query); Xapian::MSet mset (enq.get_mset (0,1)); if (mset.empty()) throw MuStoreError (MU_ERROR_NO_MATCHES, "message not found"); return *mset.begin(); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, MU_STORE_INVALID_DOCID); } time_t mu_store_get_timestamp (const MuStore *store, const char *msgpath, GError **err) { char *stampstr; time_t rv; g_return_val_if_fail (store, 0); g_return_val_if_fail (msgpath, 0); stampstr = mu_store_get_metadata (store, msgpath, err); if (!stampstr) return (time_t)0; rv = (time_t) g_ascii_strtoull (stampstr, NULL, 10); g_free (stampstr); return rv; } MuError mu_store_foreach (MuStore *self, MuStoreForeachFunc func, void *user_data, GError **err) { g_return_val_if_fail (self, MU_ERROR); g_return_val_if_fail (func, MU_ERROR); try { Xapian::Enquire enq (*self->db_read_only()); enq.set_query (Xapian::Query::MatchAll); enq.set_cutoff (0,0); Xapian::MSet matches (enq.get_mset (0, self->db_read_only()->get_doccount())); if (matches.empty()) return MU_OK; /* database is empty */ for (Xapian::MSet::iterator iter = matches.begin(); iter != matches.end(); ++iter) { Xapian::Document doc (iter.get_document()); const std::string path(doc.get_value(MU_MSG_FIELD_ID_PATH)); MuError res = func (path.c_str(), user_data); if (res != MU_OK) return res; } } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, MU_ERROR_XAPIAN); return MU_OK; } MuMsg* mu_store_get_msg (const MuStore *self, unsigned docid, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (docid != 0, NULL); try { Xapian::Document *doc = new Xapian::Document (self->db_read_only()->get_document (docid)); return mu_msg_new_from_doc ((XapianDocument*)doc, err); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-store-write.cc���������������������������������������������������������������������0000664�0000000�0000000�00000050552�12504061031�0016203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" void _MuStore::begin_transaction () { try { db_writable()->begin_transaction(); in_transaction (true); } MU_XAPIAN_CATCH_BLOCK; } void _MuStore::commit_transaction () { try { in_transaction (false); db_writable()->commit_transaction(); } MU_XAPIAN_CATCH_BLOCK; } void _MuStore::rollback_transaction () { try { in_transaction (false); db_writable()->cancel_transaction(); } MU_XAPIAN_CATCH_BLOCK; } /* we cache these prefix strings, so we don't have to allocate them all * the time; this should save 10-20 string allocs per message */ G_GNUC_CONST static const std::string& prefix (MuMsgFieldId mfid) { static std::string fields[MU_MSG_FIELD_ID_NUM]; static bool initialized = false; if (G_UNLIKELY(!initialized)) { for (int i = 0; i != MU_MSG_FIELD_ID_NUM; ++i) fields[i] = std::string (1, mu_msg_field_xapian_prefix ((MuMsgFieldId)i)); initialized = true; } return fields[mfid]; } static void add_synonym_for_flag (MuFlags flag, Xapian::WritableDatabase *db) { static const std::string pfx(prefix(MU_MSG_FIELD_ID_FLAGS)); db->clear_synonyms (pfx + mu_flag_name (flag)); db->add_synonym (pfx + mu_flag_name (flag), pfx + (std::string(1, (char)(tolower(mu_flag_char(flag)))))); } static void add_synonym_for_prio (MuMsgPrio prio, Xapian::WritableDatabase *db) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_PRIO)); std::string s1 (pfx + mu_msg_prio_name (prio)); std::string s2 (pfx + (std::string(1, mu_msg_prio_char (prio)))); db->clear_synonyms (s1); db->clear_synonyms (s2); db->add_synonym (s1, s2); } static void add_synonyms (MuStore *store) { mu_flags_foreach ((MuFlagsForeachFunc)add_synonym_for_flag, store->db_writable()); mu_msg_prio_foreach ((MuMsgPrioForeachFunc)add_synonym_for_prio, store->db_writable()); } MuStore* mu_store_new_writable (const char* xpath, const char *contacts_cache, gboolean rebuild, GError **err) { g_return_val_if_fail (xpath, NULL); try { try { MuStore *store; store = new _MuStore (xpath, contacts_cache, rebuild ? true : false); add_synonyms (store); return store; } MU_STORE_CATCH_BLOCK_RETURN(err,NULL); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, NULL); } void mu_store_set_batch_size (MuStore *store, guint batchsize) { g_return_if_fail (store); store->set_batch_size (batchsize); } gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (key, FALSE); g_return_val_if_fail (val, FALSE); try { try { store->db_writable()->set_metadata (key, val); return TRUE; } MU_STORE_CATCH_BLOCK_RETURN(err, FALSE); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, FALSE); } gboolean mu_store_clear (MuStore *store, GError **err) { g_return_val_if_fail (store, FALSE); try { try { store->clear(); return TRUE; } MU_STORE_CATCH_BLOCK_RETURN(err, FALSE); } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } void mu_store_flush (MuStore *store) { g_return_if_fail (store); try { if (store->in_transaction()) store->commit_transaction (); store->db_writable()->commit (); } MU_XAPIAN_CATCH_BLOCK; } static void add_terms_values_date (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { time_t t; const char *datestr; t = (time_t)mu_msg_get_field_numeric (msg, mfid); datestr = mu_date_time_t_to_str_s (t, FALSE /*UTC*/); doc.add_value ((Xapian::valueno)mfid, datestr); } G_GNUC_CONST static const std::string& flag_val (char flagchar) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_FLAGS)), draftstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_DRAFT))), flaggedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_FLAGGED))), passedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_PASSED))), repliedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_REPLIED))), seenstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_SEEN))), trashedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_TRASHED))), newstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_NEW))), signedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_SIGNED))), cryptstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_ENCRYPTED))), attachstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_HAS_ATTACH))), unreadstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_UNREAD))), liststr (pfx + (char)tolower(mu_flag_char(MU_FLAG_LIST))); switch (flagchar) { case 'D': return draftstr; case 'F': return flaggedstr; case 'P': return passedstr; case 'R': return repliedstr; case 'S': return seenstr; case 'T': return trashedstr; case 'N': return newstr; case 'z': return signedstr; case 'x': return cryptstr; case 'a': return attachstr; case 'l': return liststr; case 'u': return unreadstr; default: g_return_val_if_reached (flaggedstr); return flaggedstr; } } /* pre-calculate; optimization */ G_GNUC_CONST static const std::string& prio_val (MuMsgPrio prio) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_PRIO)); static const std::string low (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_LOW))), norm (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_NORMAL))), high (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_HIGH))); switch (prio) { case MU_MSG_PRIO_LOW: return low; case MU_MSG_PRIO_NORMAL: return norm; case MU_MSG_PRIO_HIGH: return high; default: g_return_val_if_reached (norm); return norm; } } static void add_terms_values_number (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { gint64 num = mu_msg_get_field_numeric (msg, mfid); const std::string numstr (Xapian::sortable_serialise((double)num)); doc.add_value ((Xapian::valueno)mfid, numstr); if (mfid == MU_MSG_FIELD_ID_FLAGS) { const char *cur = mu_flags_to_str_s ((MuFlags)num,(MuFlagType)MU_FLAG_TYPE_ANY); g_return_if_fail (cur); while (*cur) { doc.add_term (flag_val(*cur)); ++cur; } } else if (mfid == MU_MSG_FIELD_ID_PRIO) doc.add_term (prio_val((MuMsgPrio)num)); } /* for string and string-list */ static void add_terms_values_msgid (Xapian::Document& doc, MuMsg *msg) { char *str; const char *orig; if (!(orig = mu_msg_get_field_string ( msg, MU_MSG_FIELD_ID_MSGID))) return; /* nothing to do */ str = mu_str_process_msgid (orig, FALSE); doc.add_value ((Xapian::valueno)MU_MSG_FIELD_ID_MSGID, orig); doc.add_term (prefix(MU_MSG_FIELD_ID_MSGID) + std::string(str, 0, _MuStore::MAX_TERM_LENGTH)); g_free (str); } /* for string and string-list */ static void add_terms_values_str (Xapian::Document& doc, const char *val, MuMsgFieldId mfid) { char *str; if (mu_msg_field_preprocess (mfid)) str = mu_str_process_term (val); else str = g_strdup (val); if (mu_msg_field_xapian_index (mfid)) { Xapian::TermGenerator termgen; termgen.set_document (doc); termgen.index_text_without_positions (str, 1, prefix(mfid)); if (g_strcmp0 (val, str) != 0) termgen.index_text_without_positions ( val, 1, prefix(mfid)); } if (mu_msg_field_xapian_term(mfid)) doc.add_term (prefix(mfid) + std::string(str, 0, _MuStore::MAX_TERM_LENGTH)); g_free (str); } static void add_terms_values_string (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const char *orig; if (!(orig = mu_msg_get_field_string (msg, mfid))) return; /* nothing to do */ /* the value is what we display in search results; the * unchanged original */ if (mu_msg_field_xapian_value(mfid)) doc.add_value ((Xapian::valueno)mfid, orig); add_terms_values_str (doc, orig, mfid); } static void add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const GSList *lst; lst = mu_msg_get_field_string_list (msg, mfid); if (!lst) return; if (mu_msg_field_xapian_value (mfid)) { gchar *str; str = mu_str_from_list (lst, ','); if (str) doc.add_value ((Xapian::valueno)mfid, str); g_free (str); } if (mu_msg_field_xapian_term (mfid)) { for (; lst; lst = g_slist_next ((GSList*)lst)) add_terms_values_str (doc, (const gchar*)lst->data, mfid); } } struct PartData { PartData (Xapian::Document& doc, MuMsgFieldId mfid): _doc (doc), _mfid(mfid) {} Xapian::Document _doc; MuMsgFieldId _mfid; }; /* index non-body text parts */ static void maybe_index_text_part (MuMsg *msg, MuMsgPart *part, PartData *pdata) { char *txt, *str; Xapian::TermGenerator termgen; /* only deal with attachments/messages; inlines are indexed as * body parts */ if (!(part->part_type & MU_MSG_PART_TYPE_ATTACHMENT) && !(part->part_type & MU_MSG_PART_TYPE_MESSAGE)) return; txt = mu_msg_part_get_text (msg, part, MU_MSG_OPTION_NONE); if (!txt) return; termgen.set_document(pdata->_doc); str = mu_str_process_text (txt); termgen.index_text_without_positions (str, 1, prefix(MU_MSG_FIELD_ID_EMBEDDED_TEXT)); g_free (txt); g_free (str); } static void each_part (MuMsg *msg, MuMsgPart *part, PartData *pdata) { char *fname; static const std::string file (prefix(MU_MSG_FIELD_ID_FILE)), mime (prefix(MU_MSG_FIELD_ID_MIME)); /* save the mime type of any part */ if (part->type) { /* note, we use '_' instead of '/' to separate * type/subtype -- Xapian doesn't treat '/' as * desired, so we use '_' and pre-process queries; see * mu_query_preprocess */ char ctype[MuStore::MAX_TERM_LENGTH + 1]; snprintf (ctype, sizeof(ctype), "%s_%s", part->type, part->subtype); pdata->_doc.add_term (mime + std::string(ctype, 0, MuStore::MAX_TERM_LENGTH)); } if ((fname = mu_msg_part_get_filename (part, FALSE))) { char *str; str = mu_str_process_term (fname); g_free (fname); pdata->_doc.add_term (file + std::string(str, 0, MuStore::MAX_TERM_LENGTH)); g_free (str); } maybe_index_text_part (msg, part, pdata); } static void add_terms_values_attach (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { PartData pdata (doc, mfid); mu_msg_part_foreach (msg, MU_MSG_OPTION_RECURSE_RFC822, (MuMsgPartForeachFunc)each_part, &pdata); } static void add_terms_values_body (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const char *str; char *flat; if (mu_msg_get_flags(msg) & MU_FLAG_ENCRYPTED) return; /* ignore encrypted bodies */ str = mu_msg_get_body_text (msg, MU_MSG_OPTION_NONE); if (!str) /* FIXME: html->txt fallback needed */ str = mu_msg_get_body_html (msg, MU_MSG_OPTION_NONE); if (!str) return; /* no body... */ Xapian::TermGenerator termgen; termgen.set_document(doc); flat = mu_str_process_text (str); // g_print ("\n--\n%s\n--\n", flat); termgen.index_text_without_positions (flat, 1, prefix(mfid)); g_free (flat); } struct _MsgDoc { Xapian::Document *_doc; MuMsg *_msg; MuStore *_store; /* callback data, to determine whether this message is 'personal' */ gboolean _personal; GSList *_my_addresses; }; typedef struct _MsgDoc MsgDoc; static void add_terms_values_default (MuMsgFieldId mfid, MsgDoc *msgdoc) { if (mu_msg_field_is_numeric (mfid)) add_terms_values_number (*msgdoc->_doc, msgdoc->_msg, mfid); else if (mu_msg_field_is_string (mfid)) add_terms_values_string (*msgdoc->_doc, msgdoc->_msg, mfid); else if (mu_msg_field_is_string_list(mfid)) add_terms_values_string_list (*msgdoc->_doc, msgdoc->_msg, mfid); else g_return_if_reached (); } static void add_terms_values (MuMsgFieldId mfid, MsgDoc* msgdoc) { /* note: contact-stuff (To/Cc/From) will handled in * each_contact_info, not here */ if (!mu_msg_field_xapian_index(mfid) && !mu_msg_field_xapian_term(mfid) && !mu_msg_field_xapian_value(mfid)) return; // if (mu_msg_field_xapian_contact (mfid)) // return; /* handled in new_doc_from_message */ switch (mfid) { case MU_MSG_FIELD_ID_DATE: add_terms_values_date (*msgdoc->_doc, msgdoc->_msg, mfid); break; case MU_MSG_FIELD_ID_BODY_TEXT: add_terms_values_body (*msgdoc->_doc, msgdoc->_msg, mfid); break; /* note: add_terms_values_attach handles _FILE, _MIME and * _ATTACH_TEXT msgfields */ case MU_MSG_FIELD_ID_FILE: add_terms_values_attach (*msgdoc->_doc, msgdoc->_msg, mfid); break; case MU_MSG_FIELD_ID_MIME: case MU_MSG_FIELD_ID_EMBEDDED_TEXT: break; case MU_MSG_FIELD_ID_MSGID: add_terms_values_msgid (*msgdoc->_doc, msgdoc->_msg); break; case MU_MSG_FIELD_ID_THREAD_ID: case MU_MSG_FIELD_ID_UID: break; /* already taken care of elsewhere */ default: return add_terms_values_default (mfid, msgdoc); } } static const std::string& xapian_pfx (MuMsgContact *contact) { static const std::string empty; /* use ptr to string to prevent copy... */ switch (contact->type) { case MU_MSG_CONTACT_TYPE_TO: return prefix(MU_MSG_FIELD_ID_TO); case MU_MSG_CONTACT_TYPE_FROM: return prefix(MU_MSG_FIELD_ID_FROM); case MU_MSG_CONTACT_TYPE_CC: return prefix(MU_MSG_FIELD_ID_CC); case MU_MSG_CONTACT_TYPE_BCC: return prefix(MU_MSG_FIELD_ID_BCC); default: g_warning ("unsupported contact type %u", (unsigned)contact->type); return empty; } } static void add_address_subfields (Xapian::Document& doc, const char *addr, const std::string& pfx) { const char *at, *domain_part; char *name_part, *f1, *f2; /* add "foo" and "bar.com" as terms as well for * "foo@bar.com" */ if (G_UNLIKELY(!(at = (g_strstr_len (addr, -1, "@"))))) return; name_part = g_strndup(addr, at - addr); // foo domain_part = at + 1; f1 = mu_str_process_term (name_part); f2 = mu_str_process_term (domain_part); g_free (name_part); doc.add_term (pfx + std::string(f1, 0, _MuStore::MAX_TERM_LENGTH)); doc.add_term (pfx + std::string(f2, 0, _MuStore::MAX_TERM_LENGTH)); g_free (f1); g_free (f2); } static void each_contact_info (MuMsgContact *contact, MsgDoc *msgdoc) { /* for now, don't store reply-to addresses */ if (mu_msg_contact_type (contact) == MU_MSG_CONTACT_TYPE_REPLY_TO) return; const std::string pfx (xapian_pfx(contact)); if (pfx.empty()) return; /* unsupported contact type */ if (!mu_str_is_empty(contact->name)) { Xapian::TermGenerator termgen; termgen.set_document (*msgdoc->_doc); char *flat = mu_str_process_text (contact->name); termgen.index_text_without_positions (flat, 1, pfx); g_free (flat); } if (!mu_str_is_empty(contact->address)) { char *flat; flat = mu_str_process_term (contact->address); msgdoc->_doc->add_term (std::string (pfx + flat, 0, MuStore::MAX_TERM_LENGTH)); g_free (flat); add_address_subfields (*msgdoc->_doc, contact->address, pfx); /* store it also in our contacts cache */ if (msgdoc->_store->contacts()) mu_contacts_add (msgdoc->_store->contacts(), contact->address, contact->name, msgdoc->_personal, mu_msg_get_date(msgdoc->_msg)); } } static void each_contact_check_if_personal (MuMsgContact *contact, MsgDoc *msgdoc) { GSList *cur; if (msgdoc->_personal || !contact->address) return; for (cur = msgdoc->_my_addresses; cur; cur = g_slist_next (cur)) { if (g_ascii_strcasecmp (contact->address, (const char*)cur->data) == 0) msgdoc->_personal = TRUE; } } Xapian::Document new_doc_from_message (MuStore *store, MuMsg *msg) { Xapian::Document doc; MsgDoc docinfo = {&doc, msg, store, 0, FALSE}; mu_msg_field_foreach ((MuMsgFieldForeachFunc)add_terms_values, &docinfo); /* determine whether this is 'personal' email, ie. one of my * e-mail addresses is explicitly mentioned -- it's not a * mailing list message. Callback will update docinfo->_personal */ if (store->my_addresses()) { docinfo._my_addresses = store->my_addresses(); mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact_check_if_personal, &docinfo); } /* also store the contact-info as separate terms, and add it * to the cache */ mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact_info, &docinfo); // g_printerr ("\n--%s\n--\n", doc.serialise().c_str()); return doc; } static void update_threading_info (Xapian::WritableDatabase* db, MuMsg *msg, Xapian::Document& doc) { const GSList *refs; // refs contains a list of parent messages, with the oldest // one first until the last one, which is the direct parent of // the current message. of course, it may be empty. // // NOTE: there may be cases where the the list is truncated; // we happily ignore that case. refs = mu_msg_get_references (msg); std::string thread_id; if (refs) thread_id = mu_util_get_hash ((const char*)refs->data); else thread_id = mu_util_get_hash (mu_msg_get_msgid (msg)); doc.add_term (prefix(MU_MSG_FIELD_ID_THREAD_ID) + thread_id); doc.add_value((Xapian::valueno)MU_MSG_FIELD_ID_THREAD_ID, thread_id); } unsigned add_or_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); try { Xapian::docid id; Xapian::Document doc (new_doc_from_message(store, msg)); const std::string term (store->get_uid_term (mu_msg_get_path(msg))); if (!store->in_transaction()) store->begin_transaction(); doc.add_term (term); // update the threading info if this message has a message id if (mu_msg_get_msgid (msg)) update_threading_info (store->db_writable(), msg, doc); if (docid == 0) id = store->db_writable()->replace_document (term, doc); else { store->db_writable()->replace_document (docid, doc); id = docid; } if (store->inc_processed() % store->batch_size() == 0) store->commit_transaction(); return id; } MU_XAPIAN_CATCH_BLOCK_G_ERROR (err, MU_ERROR_XAPIAN_STORE_FAILED); if (store->in_transaction()) store->rollback_transaction(); return MU_STORE_INVALID_DOCID; } unsigned mu_store_add_msg (MuStore *store, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); return add_or_update_msg (store, 0, msg, err); } unsigned mu_store_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); g_return_val_if_fail (docid != 0, MU_STORE_INVALID_DOCID); return add_or_update_msg (store, docid, msg, err); } unsigned mu_store_add_path (MuStore *store, const char *path, const char *maildir, GError **err) { MuMsg *msg; unsigned docid; g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); msg = mu_msg_new_from_file (path, maildir, err); if (!msg) return MU_STORE_INVALID_DOCID; docid = add_or_update_msg (store, 0, msg, err); mu_msg_unref (msg); return docid; } XapianWritableDatabase* mu_store_get_writable_database (MuStore *store) { g_return_val_if_fail (store, NULL); return (XapianWritableDatabase*)store->db_writable(); } gboolean mu_store_remove_path (MuStore *store, const char *msgpath) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (msgpath, FALSE); try { const std::string term (store->get_uid_term(msgpath)); store->db_writable()->delete_document (term); store->inc_processed(); return TRUE; } MU_XAPIAN_CATCH_BLOCK_RETURN (FALSE); } gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, time_t stamp, GError **err) { char buf[21]; g_return_val_if_fail (store, FALSE); g_return_val_if_fail (msgpath, FALSE); sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp); return mu_store_set_metadata (store, msgpath, buf, err); } ������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-store.cc���������������������������������������������������������������������������0000664�0000000�0000000�00000005207�12504061031�0015050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include <unistd.h> #include <errno.h> #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" MuStore* mu_store_ref (MuStore *store) { g_return_val_if_fail (store, NULL); store->ref(); return store; } MuStore* mu_store_unref (MuStore *store) { g_return_val_if_fail (store, NULL); if (store->unref() == 0) { try { delete store; } MU_XAPIAN_CATCH_BLOCK; } return NULL; } static char* xapian_get_metadata (const gchar *xpath, const gchar *key) { g_return_val_if_fail (xpath, NULL); g_return_val_if_fail (key, NULL); if (!access(xpath, F_OK) == 0) { g_warning ("cannot access %s: %s", xpath, strerror(errno)); return NULL; } try { Xapian::Database db (xpath); const std::string val(db.get_metadata (key)); return val.empty() ? NULL : g_strdup (val.c_str()); } MU_XAPIAN_CATCH_BLOCK; return NULL; } char* mu_store_database_version (const gchar *xpath) { g_return_val_if_fail (xpath, NULL); return xapian_get_metadata (xpath, MU_STORE_VERSION_KEY); } gboolean mu_store_database_is_locked (const gchar *xpath) { g_return_val_if_fail (xpath, FALSE); try { Xapian::WritableDatabase db (xpath, Xapian::DB_OPEN); } catch (const Xapian::DatabaseLockError& xer) { return TRUE; } catch (const Xapian::Error &xer) { g_warning ("%s: error: %s", __FUNCTION__, xer.get_msg().c_str()); } return FALSE; } void mu_store_set_my_addresses (MuStore *store, const char **my_addresses) { g_return_if_fail (store); store->set_my_addresses (my_addresses); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-store.h����������������������������������������������������������������������������0000664�0000000�0000000�00000026764�12504061031�0014725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_STORE_H__ #define __MU_STORE_H__ #include <glib.h> #include <inttypes.h> #include <mu-msg.h> #include <mu-util.h> /* for MuError, MuError */ G_BEGIN_DECLS struct _MuStore; typedef struct _MuStore MuStore; /** * create a new writable Xapian store, a place to store documents * * @param path the path to the database * @param ccachepath path where to cache the contacts information, or NULL * @param err to receive error info or NULL. err->code is MuError value * * @return a new MuStore object with ref count == 1, or NULL in case * of error; free with mu_store_unref */ MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath, gboolean rebuild, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * create a new read-only Xapian store, for querying documents * * @param path the path to the database * @param err to receive error info or NULL. err->code is MuError value * * @return a new MuStore object with ref count == 1, or NULL in case * of error; free with mu_store_unref */ MuStore* mu_store_new_read_only (const char* xpath, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * increase the reference count for this store with 1 * * @param store a valid store object * * @return the same store with increased ref count, or NULL in case of * error */ MuStore* mu_store_ref (MuStore *store); /** * decrease the reference count for this store with 1 * * @param store a valid store object * * @return NULL */ MuStore* mu_store_unref (MuStore *store); /** * we need this when using Xapian::WritableDatabase* from C */ typedef gpointer XapianWritableDatabase; /** * get the underlying writable database object for this store; not * that this pointer becomes in valid after mu_store_destroy * * @param store a valid store * * @return a Xapian::WritableDatabase (you'll need to cast in C++), or * NULL in case of error. */ XapianWritableDatabase* mu_store_get_writable_database (MuStore *store); /** * we need this when using Xapian::WritableDatabase* from C */ typedef gpointer XapianDatabase; /** * get the underlying read-only database object for this store; not that this * pointer becomes in valid after mu_store_destroy * * @param store a valid store * * @return a Xapian::Database (you'll need to cast in C++), or * NULL in case of error. */ XapianDatabase* mu_store_get_read_only_database (MuStore *store); /** * set the Xapian batch size for this store. Normally, there's no need * to use this function as the default is good enough; however, if you * use mu in a very memory-constrained environment, you can set the * batchsize to e.g. 1000 at the cost of significant slow-down. * * @param store a valid store object * @param batchsize the new batch size; or 0 to reset to * the default batch size */ void mu_store_set_batch_size (MuStore *store, guint batchsize); /** * register a char** of email addresses as 'my' addresses, ie. mark * message that have these addresses in one of the address fields as * 'personal' (e.g., in mu-contacts). calling this function overrides * any 'my addresses' that were set before, using this function or * through mu_store_new_writable * * @param store a valid store object * @param my_addresses a char** of email addresses */ void mu_store_set_my_addresses (MuStore *store, const char **my_addresses); /** * get the numbers of documents in the database * * @param index a valid MuStore instance * @param err to receive error info or NULL. err->code is MuError value * * @return the number of documents in the database; (unsigned)-1 in * case of error */ unsigned mu_store_count (const MuStore *store, GError **err); /** * get a version string for the database; it's a const string, which * is valid as long MuStore exists and mu_store_version is not called * again. * * @param store a valid MuStore * * @return the version string or NULL in case of error */ const char* mu_store_version (const MuStore *store); /** * try to flush/commit all outstanding work * * @param store a valid xapian store */ void mu_store_flush (MuStore *store); #define MU_STORE_INVALID_DOCID 0 /** * store an email message in the XapianStore * * @param store a valid store * @param msg a valid message * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_add_msg (MuStore *store, MuMsg *msg, GError **err); /** * update an email message in the XapianStore * * @param store a valid store * @param the docid for the message * @param msg a valid message * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err); /** * store an email message in the XapianStore; similar to * mu_store_store, but instead takes a path as parameter instead of a * MuMsg* * * @param store a valid store * @param path full filesystem path to a valid message * @param maildir set the maildir (e.g. "/drafts") for this message, or NULL * note that you cannot mu_msg_move_msg_to_maildir unless maildir is set. * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_add_path (MuStore *store, const char *path, const char* maildir, GError **err); /** * remove a message from the database based on its path * * @param store a valid store * @param msgpath path of the message (note, this is only used to * *identify* the message; a common use of this function is to remove * a message from the database, for which there is no message anymore * in the filesystem. * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_store_remove_path (MuStore *store, const char* msgpath); /** * does a certain message exist in the database already? * * @param store a store * @param path the message path * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if the message exists, FALSE otherwise */ gboolean mu_store_contains_message (const MuStore *store, const char* path, GError **err); /** * get the docid for message at path * * @param store a store * @param path the message path * @param err to receive error info or NULL. err->code is MuError value * * @return the docid if the message was found, MU_STORE_INVALID_DOCID (0) otherwise * */ unsigned mu_store_get_docid_for_path (const MuStore *store, const char* path, GError **err); /** * store a timestamp for a directory * * @param store a valid store * @param msgpath path to a maildir * @param stamp a timestamp * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if setting the timestamp succeeded, FALSE otherwise */ gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, time_t stamp, GError **err); /** * get the timestamp for a directory * * @param store a valid store * @param msgpath path to a maildir * @param err to receive error info or NULL. err->code is MuError value * * @return the timestamp, or 0 in case of error */ time_t mu_store_get_timestamp (const MuStore *store, const char* msgpath, GError **err); /** * check whether this store is read-only * * @param store a store * * @return TRUE if the store is read-only, FALSE otherwise (and in * case of error) */ gboolean mu_store_is_read_only (const MuStore *store); /** * call a function for each document in the database * * @param self a valid store * @param func a callback function to to call for each document * @param user_data a user pointer passed to the callback function * @param err to receive error info or NULL. err->code is MuError value * * @return MU_OK if all went well, MU_STOP if the foreach was interrupted, * MU_ERROR in case of error */ typedef MuError (*MuStoreForeachFunc) (const char* path, gpointer user_data); MuError mu_store_foreach (MuStore *self, MuStoreForeachFunc func, void *user_data, GError **err); /** * set metadata for this MuStore * * @param store a store * @param key metadata key * @param val metadata value * @param err to receive error info or NULL. err->code is the MuError value * * @return TRUE if succeeded, FALSE otherwise */ gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val, GError **err); /** * get metadata for this MuStore * * @param store a store * @param key the metadata key * @param err to receive error info or NULL. err->code is MuError value * * @return the value of the metadata (gfree when done with it), or * NULL in case of error */ char* mu_store_get_metadata (const MuStore *store, const char *key, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** " * get the version of the xapian database (ie., the version of the * 'schema' we are using). If this version != MU_STORE_SCHEMA_VERSION, * it's means we need to a full reindex. * * @param xpath path to the xapian database * * @return the version of the database as a newly allocated string * (free with g_free); if there is no version yet, it will return NULL */ gchar* mu_store_database_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; /** * check whether the database schema's version is the same as the one * that the current mu uses. If they are not the same, we'll need a * database rebuild. * * @param store a MuStore instance * * @return TRUE if the versions are the same, FALSE otherwise. */ gboolean mu_store_versions_match (const MuStore *store); /** * clear the database, ie., remove all of the contents. This is a * destructive operation, but the database can be restored be doing a * full scan of the maildirs. Also, clear the contacts cache file * * @param store a MuStore object * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if the clearing succeeded, FALSE otherwise. */ gboolean mu_store_clear (MuStore *store, GError **err); /** * check if the database is locked for writing * * @param xpath path to a xapian database * * @return TRUE if it is locked, FALSE otherwise (or in case of error) */ gboolean mu_store_database_is_locked (const gchar *xpath); /** * get a specific message, based on its Xapian docid * * @param self a valid MuQuery instance * @param docid the Xapian docid for the wanted message * @param err receives error information, or NULL * * @return a MuMsg instance (use mu_msg_unref when done with it), or * NULL in case of error */ MuMsg* mu_store_get_msg (const MuStore *self, unsigned docid, GError **err) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_STORE_H__*/ ������������mu-0.9.12/lib/mu-str.c������������������������������������������������������������������������������0000664�0000000�0000000�00000052203�12504061031�0014357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <stdio.h> #include "mu-util.h" /* PATH_MAX */ #include "mu-str.h" #include "mu-msg-fields.h" const char* mu_str_size_s (size_t s) { static char buf[32]; #ifdef HAVE_GLIB216 char *tmp; tmp = g_format_size_for_display ((goffset)s); strncpy (buf, tmp, sizeof(buf)); buf[sizeof(buf) -1] = '\0'; /* just in case */ g_free (tmp); #else if (s >= 1000 * 1000) g_snprintf(buf, sizeof(buf), "%.1f MB", (double)s/(1000*1000)); else g_snprintf(buf, sizeof(buf), "%.1f kB", (double)s/(1000)); #endif /*HAVE_GLIB216*/ return buf; } char* mu_str_size (size_t s) { return g_strdup (mu_str_size_s(s)); } const char* mu_str_flags_s (MuFlags flags) { return mu_flags_to_str_s (flags, MU_FLAG_TYPE_ANY); } char* mu_str_flags (MuFlags flags) { return g_strdup (mu_str_flags_s(flags)); } char* mu_str_summarize (const char* str, size_t max_lines) { char *summary; size_t nl_seen; unsigned i,j; gboolean last_was_blank; g_return_val_if_fail (str, NULL); g_return_val_if_fail (max_lines > 0, NULL); /* len for summary <= original len */ summary = g_new (gchar, strlen(str) + 1); /* copy the string up to max_lines lines, replace CR/LF/tab with * single space */ for (i = j = 0, nl_seen = 0, last_was_blank = TRUE; nl_seen < max_lines && str[i] != '\0'; ++i) { if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == ' ' ) { if (str[i] == '\n') ++nl_seen; /* no double-blanks or blank at end of str */ if (!last_was_blank && str[i+1] != '\0') summary[j++] = ' '; last_was_blank = TRUE; } else { summary[j++] = str[i]; last_was_blank = FALSE; } } summary[j] = '\0'; return summary; } static void cleanup_contact (char *contact) { char *c, *c2; /* replace "'<> with space */ for (c2 = contact; *c2; ++c2) if (*c2 == '"' || *c2 == '\'' || *c2 == '<' || *c2 == '>') *c2 = ' '; /* remove everything between '()' if it's after the 5th pos; * good to cleanup corporate contact address spam... */ c = g_strstr_len (contact, -1, "("); if (c && c - contact > 5) *c = '\0'; g_strstrip (contact); } /* this is still somewhat simplistic... */ const char* mu_str_display_contact_s (const char *str) { static gchar contact[255]; gchar *c, *c2; str = str ? str : ""; g_strlcpy (contact, str, sizeof(contact)); /* we check for '<', so we can strip out the address stuff in * e.g. 'Hello World <hello@world.xx>, but only if there is * something alphanumeric before the < */ c = g_strstr_len (contact, -1, "<"); if (c != NULL) { for (c2 = contact; c2 < c && !(isalnum(*c2)); ++c2); if (c2 != c) /* apparently, there was something, * so we can remove the <... part*/ *c = '\0'; } cleanup_contact (contact); return contact; } char* mu_str_display_contact (const char *str) { g_return_val_if_fail (str, NULL); return g_strdup (mu_str_display_contact_s (str)); } gint64 mu_str_size_parse_bkm (const char* str) { gint64 num; g_return_val_if_fail (str, -1); if (!isdigit(str[0])) return -1; num = atoi(str); for (++str; isdigit(*str); ++str); switch (tolower(*str)) { case '\0': case 'b' : return num; /* bytes */ case 'k': return num * 1000; /* kilobyte */ case 'm': return num * 1000 * 1000; /* megabyte */ default: return -1; } } char* mu_str_replace (const char *str, const char *substr, const char *repl) { GString *gstr; const char *cur; g_return_val_if_fail (str, NULL); g_return_val_if_fail (substr, NULL); g_return_val_if_fail (repl, NULL); gstr = g_string_sized_new (2 * strlen (str)); for (cur = str; *cur; ++cur) { if (g_str_has_prefix (cur, substr)) { g_string_append (gstr, repl); cur += strlen (substr) - 1; } else g_string_append_c (gstr, *cur); } return g_string_free (gstr, FALSE); } char* mu_str_from_list (const GSList *lst, char sepa) { const GSList *cur; char *str; g_return_val_if_fail (sepa, NULL); for (cur = lst, str = NULL; cur; cur = g_slist_next(cur)) { char *tmp; /* two extra dummy '\0' so -Wstack-protector won't complain */ char sep[4] = { '\0', '\0', '\0', '\0' }; sep[0] = cur->next ? sepa : '\0'; tmp = g_strdup_printf ("%s%s%s", str ? str : "", (gchar*)cur->data, sep); g_free (str); str = tmp; } return str; } GSList* mu_str_to_list (const char *str, char sepa, gboolean strip) { GSList *lst; gchar **strs, **cur; /* two extra dummy '\0' so -Wstack-protector won't complain */ char sep[4] = { '\0', '\0', '\0', '\0' }; g_return_val_if_fail (sepa, NULL); if (!str) return NULL; sep[0] = sepa; strs = g_strsplit (str, sep, -1); for (cur = strs, lst = NULL; cur && *cur; ++cur) { char *elm; elm = g_strdup(*cur); if (strip) elm = g_strstrip (elm); lst = g_slist_prepend (lst, elm); } lst = g_slist_reverse (lst); g_strfreev (strs); return lst; } GSList* mu_str_esc_to_list (const char *strings) { GSList *lst; GString *part; unsigned u; gboolean quoted, escaped; g_return_val_if_fail (strings, NULL); part = g_string_new (NULL); for (u = 0, lst = NULL, quoted = FALSE, escaped = FALSE; u != strlen (strings); ++u) { char kar; kar = strings[u]; if (kar == '\\') { if (escaped) g_string_append_c (part, '\\'); escaped = !escaped; continue; } if (quoted && kar != '"') { g_string_append_c (part, kar); continue; } switch (kar) { case '"': if (!escaped) quoted = !quoted; else g_string_append_c (part, kar); continue; case ' ': if (part->len > 0) { lst = g_slist_prepend (lst, g_string_free (part, FALSE)); part = g_string_new (NULL); } continue; default: g_string_append_c (part, kar); } } if (part->len) lst = g_slist_prepend (lst, g_string_free (part, FALSE)); return g_slist_reverse (lst); } void mu_str_free_list (GSList *lst) { g_slist_foreach (lst, (GFunc)g_free, NULL); g_slist_free (lst); } /* this function is critical for sorting performance; therefore, no * regexps, but just some good old c pointer magic */ const gchar* mu_str_subject_normalize (const gchar* str) { const char* cur; g_return_val_if_fail (str, NULL); cur = str; while (isspace(*cur)) ++cur; /* skip space */ /* starts with Re:? */ if (tolower(cur[0]) == 'r' && tolower(cur[1]) == 'e') cur += 2; /* starts with Fwd:? */ else if (tolower(cur[0]) == 'f' && tolower(cur[1]) == 'w' && tolower(cur[2]) == 'd') cur += 3; else /* nope, different string */ return str; /* we're now past either 'Re' or 'Fwd'. Maybe there's a [<num>] now? * ie., the Re[3]: foo case */ if (cur[0] == '[') { /* handle the Re[3]: case */ if (isdigit(cur[1])) { do { ++cur; } while (isdigit(*cur)); if ( cur[0] != ']') { return str; /* nope: no ending ']' */ } else /* skip ']' and space */ do { ++cur; } while (isspace(*cur)); } else /* nope: no number after '[' */ return str; } /* now, cur points past either 're' or 'fwd', possibly with * [<num>]; check if it's really a prefix -- after re or fwd * there should either a ':' and possibly some space */ if (cur[0] == ':') { do { ++cur; } while (isspace(*cur)); /* note: there may still be another prefix, such as * Re[2]: Fwd: foo */ return mu_str_subject_normalize (cur); } else return str; /* nope, it was not a prefix */ } struct _CheckPrefix { const char *str; gboolean match; gboolean range_field; }; typedef struct _CheckPrefix CheckPrefix; static void each_check_prefix (MuMsgFieldId mfid, CheckPrefix *cpfx) { const char *pfx; char pfx_short[3] = { 'X', ':', '\0'}; char k; if (!cpfx || cpfx->match) return; k = pfx_short[0] = mu_msg_field_shortcut (mfid); if (k && g_str_has_prefix (cpfx->str, pfx_short)) { cpfx->match = TRUE; cpfx->range_field = mu_msg_field_is_range_field (mfid); } pfx = mu_msg_field_name (mfid); if (pfx && g_str_has_prefix (cpfx->str, pfx) && cpfx->str[strlen(pfx)] == ':') { cpfx->match = TRUE; cpfx->range_field = mu_msg_field_is_range_field (mfid); } } /* check if it looks like either i:<msgid> or msgid:<msgid> */ static gboolean is_msgid_field (const char *str) { const char *name; if (!str || strlen(str) < 3) return FALSE; if (str[0] == mu_msg_field_shortcut (MU_MSG_FIELD_ID_MSGID) && str[1] == ':') return TRUE; name = mu_msg_field_name (MU_MSG_FIELD_ID_MSGID); if (g_str_has_prefix (str, name) && str[strlen(name)] == ':') return TRUE; return FALSE; } /* message-ids need a bit more massaging -- we replace all * non-alphanum with '_'. Note, this function assumes we're looking at * a msg-id field, ie. i:<msgid> or msgid:<msgid> */ char* mu_str_process_msgid (const char *str, gboolean query) { char *s, *c; g_return_val_if_fail (str, NULL); g_return_val_if_fail (!query || strchr(str, ':'), NULL); if (!str) return NULL; s = g_strdup (str); if (query) c = strchr (s, ':') + 1; else c = s; for (; *c; ++c) *c = isalnum (*c) ? tolower (*c) : '_'; return s; } static void check_for_field (const char *str, gboolean *is_field, gboolean *is_range_field) { CheckPrefix pfx; pfx.str = str; /* skip any non-alphanum starts in cpfx->str; this is to * handle the case where we have e.g. "(maildir:/abc)" */ while (pfx.str && *pfx.str && !isalnum(*pfx.str)) ++pfx.str; pfx.match = pfx.range_field = FALSE; mu_msg_field_foreach ((MuMsgFieldForeachFunc)each_check_prefix, &pfx); /* also check special prefixes... */ if (!pfx.match) pfx.match = g_str_has_prefix (str, MU_MSG_FIELD_PSEUDO_CONTACT ":") || g_str_has_prefix (str, MU_MSG_FIELD_PSEUDO_RECIP ":"); *is_field = pfx.match; *is_range_field = pfx.range_field; } static gboolean handle_esc_maybe (GString *gstr, char **cur, gunichar uc, gboolean query_esc, gboolean range_field) { char kar; kar = *cur[0]; if (query_esc) { switch (kar) { case ':': case '(': case ')': case '*': case '&': case '"': g_string_append_c (gstr, kar); return TRUE; case '.': if (!range_field) break; if ((*cur)[1] == '.' && (*cur)[2] != '.') { g_string_append (gstr, ".."); *cur = g_utf8_next_char (*cur); return TRUE; } default: break; } } if (g_unichar_ispunct(uc) || isblank(kar)) { g_string_append_c (gstr, '_'); return TRUE; } return FALSE; } static char* process_str (const char *str, gboolean xapian_esc, gboolean query_esc) { GString *gstr; char *norm, *cur; gboolean is_field, is_range_field; norm = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); if (G_UNLIKELY(!norm)) { /* not valid utf8? */ char *u8; u8 = mu_str_utf8ify (str); norm = g_utf8_normalize (u8, -1, G_NORMALIZE_ALL); g_free (u8); } if (!norm) return NULL; /* msg-id needs some special care in queries */ if (query_esc && is_msgid_field (str)) return mu_str_process_msgid (str, TRUE); check_for_field (str, &is_field, &is_range_field); gstr = g_string_sized_new (strlen (norm)); for (cur = norm; cur && *cur; cur = g_utf8_next_char (cur)) { gunichar uc; uc = g_utf8_get_char (cur); if (xapian_esc) if (handle_esc_maybe (gstr, &cur, uc, query_esc, is_range_field)) continue; if (g_unichar_ismark(uc)) continue; if (!is_range_field) uc = g_unichar_tolower (uc); g_string_append_unichar (gstr, uc); } g_free (norm); return g_string_free (gstr, FALSE); } char* mu_str_process_text (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, FALSE, FALSE); } char* mu_str_process_term (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, TRUE, FALSE); } char* mu_str_process_query_term (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, TRUE, TRUE); } /* * Split simple search term into prefix, expression and suffix. * Meant to handle cases like "(maildir:/abc)", prefix and * suffix are the non-alphanumeric stuff at the beginning * and the end of string. * * Values of *pfx, *cond and *sfx will be allocated from heap * and must be g_free()d. * * Returns TRUE if all went fine and FALSE if some error was * occured. */ static gboolean split_term (const gchar *term, const gchar **pfx, const gchar **cond, const gchar **sfx) { size_t l; const gchar *start, *tail; const gchar *p, *c, *s; g_return_val_if_fail (term, FALSE); g_return_val_if_fail (pfx, FALSE); g_return_val_if_fail (cond, FALSE); g_return_val_if_fail (sfx, FALSE); l = strlen (term); if (l == 0) { p = g_strdup (""); c = g_strdup (""); s = g_strdup (""); goto _done; } /* * Invariants: * - start will point to the first symbol after leading * non-alphanumerics (can be alphanumeric or '\0'); * - tail will point to the beginning of trailing * non-alphanumerics or '\0'. * So: * - len (prefix) = start - term; * - len (cond) = tail - start; * - len (suffix) = term + len (term) - tail. */ for (start = term; *start && !isalnum (*start); start++); for (tail = term + l; tail > start && !isalnum (*(tail-1)); tail--); p = g_strndup (term, start - term); c = g_strndup (start, tail - start); s = g_strndup (tail, term + l - tail); _done: if (!p || !c || !s) { g_free ((gchar *)p); g_free ((gchar *)c); g_free ((gchar *)s); return FALSE; } else { *pfx = p; *cond = c; *sfx = s; return TRUE; } /* NOTREACHED */ } /* * Fixup handlers. * * Every fixup handler will take three string arguments, * prefix, condition and suffix (as split by split_term). * * It will either return NULL that means "no fixup was done" * or the pointer to the newly-allocated string with the * new contents. */ typedef gchar * (*fixup_handler_t)(const gchar *pfx, const gchar *cond, const gchar *sfx); static gchar* fixup_date(const gchar *pfx, const gchar *cond, const gchar *sfx) { const gchar *p; p = cond + sizeof ("date:") - 1; if (strstr (p, "..")) return NULL; return g_strdup_printf ("%s%s..%s%s", pfx, cond, p, sfx); } /* * Looks up fixup handler for the given condition. * * Returns fixup handler if we can and NULL if there is * no fixup for this condition. */ static fixup_handler_t find_fixup (const gchar *cond) { size_t n; /* NULL-terminated list of term names for fixups. */ static struct { const char *name; size_t len; fixup_handler_t handler; } fixups[] = { {"date:", sizeof("date:") - 1, fixup_date}, {NULL, 0, NULL} }; g_return_val_if_fail (cond, NULL); for (n = 0; fixups[n].name; n++) { if (!strncasecmp (cond, fixups[n].name, fixups[n].len)) break; } return fixups[n].handler; } gchar* mu_str_xapian_fixup_terms (const gchar *term) { gboolean is_field, is_range_field; const gchar *cond, *pfx, *sfx; gchar *retval; fixup_handler_t fixup; g_return_val_if_fail (term, NULL); if (strlen (term) == 0) return g_strdup (term); check_for_field (term, &is_field, &is_range_field); if (!is_field || !is_range_field) return g_strdup (term); if (!split_term (term, &pfx, &cond, &sfx)) return g_strdup (term); retval = NULL; fixup = find_fixup (cond); if (fixup) retval = fixup (pfx, cond, sfx); if (!retval) retval = g_strdup (term); /* At this point retval should contain the result */ g_free ((gchar *)pfx); g_free ((gchar *)sfx); g_free ((gchar *)cond); return retval; } /* note: this function is *not* re-entrant, it returns a static buffer */ const char* mu_str_fullpath_s (const char* path, const char* name) { static char buf[PATH_MAX + 1]; g_return_val_if_fail (path, NULL); snprintf (buf, sizeof(buf), "%s%c%s", path, G_DIR_SEPARATOR, name ? name : ""); return buf; } char* mu_str_escape_c_literal (const gchar* str, gboolean in_quotes) { const char* cur; GString *tmp; g_return_val_if_fail (str, NULL); tmp = g_string_sized_new (2 * strlen(str)); if (in_quotes) g_string_append_c (tmp, '"'); for (cur = str; *cur; ++cur) switch (*cur) { case '\\': tmp = g_string_append (tmp, "\\\\"); break; case '"': tmp = g_string_append (tmp, "\\\""); break; default: tmp = g_string_append_c (tmp, *cur); } if (in_quotes) g_string_append_c (tmp, '"'); return g_string_free (tmp, FALSE); } /* turn \0-terminated buf into ascii (which is a utf8 subset); convert * any non-ascii into '.' */ char* mu_str_asciify_in_place (char *buf) { char *c; g_return_val_if_fail (buf, NULL); for (c = buf; c && *c; ++c) { if ((!isprint(*c) && !isspace (*c)) || !isascii(*c)) *c = '.'; } return buf; } char* mu_str_utf8ify (const char *buf) { char *utf8; g_return_val_if_fail (buf, NULL); utf8 = g_strdup (buf); if (!g_utf8_validate (buf, -1, NULL)) mu_str_asciify_in_place (utf8); return utf8; } gchar* mu_str_convert_to_utf8 (const char* buffer, const char *charset) { GError *err; gchar * utf8; g_return_val_if_fail (buffer, NULL); g_return_val_if_fail (charset, NULL ); err = NULL; utf8 = g_convert_with_fallback (buffer, -1, "UTF-8", charset, NULL, NULL, NULL, &err); if (!utf8) /* maybe the charset lied; try 8859-15 */ utf8 = g_convert_with_fallback (buffer, -1, "UTF-8", "ISO8859-15", NULL, NULL, NULL, &err); /* final attempt, maybe it was utf-8 already */ if (!utf8 && g_utf8_validate (buffer, -1, NULL)) utf8 = g_strdup (buffer); if (!utf8) { g_warning ("%s: conversion failed from %s: %s", __FUNCTION__, charset, err ? err->message : ""); } g_clear_error (&err); return utf8; } gchar* mu_str_quoted_from_strv (const gchar **params) { GString *str; int i; g_return_val_if_fail (params, NULL); if (!params[0]) return g_strdup (""); str = g_string_sized_new (64); /* just a guess */ for (i = 0; params[i]; ++i) { if (i > 0) g_string_append_c (str, ' '); g_string_append_c (str, '"'); g_string_append (str, params[i]); g_string_append_c (str, '"'); } return g_string_free (str, FALSE); } static char* read_key (const char *str, const char **val, GError **err) { const char *cur; GString *gstr; cur = str; gstr = g_string_sized_new (strlen(cur)); while (*cur && *cur != ':') { g_string_append_c (gstr, *cur); ++cur; } if (*cur != ':' || gstr->len == 0) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "expected: '<alphanum>+:' (%s)", str); g_string_free (gstr, TRUE); *val = NULL; return NULL; } else { *val = cur + 1; return g_string_free (gstr, FALSE); } } static char* read_val (const char *str, const char **endval, GError **err) { const char *cur; gboolean quoted; GString *gstr; gstr = g_string_sized_new (strlen(str)); for (quoted = FALSE, cur = str; *cur; ++cur) { if (*cur == '\\') { if (cur[1] != '"' && cur[1] != '\\') { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "invalid escaping"); goto errexit; } else { ++cur; g_string_append_c (gstr, *cur); continue; } } else if (*cur == '"') { quoted = !quoted; continue; } else if (isblank(*cur) && !quoted) break; else g_string_append_c (gstr, *cur); } if (quoted) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "error in quoting"); goto errexit; } *endval = cur; return g_string_free (gstr, FALSE); errexit: g_string_free (gstr, TRUE); return NULL; } GHashTable* mu_str_parse_arglist (const char *args, GError **err) { GHashTable *hash; const char *cur; g_return_val_if_fail (args, NULL); hash = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free); cur = args; while ((isblank(*cur))) ++cur; do { char *key, *val; const char *valstart, *valend; key = read_key (cur, &valstart, err); if (!key) goto errexit; val = read_val (valstart, &valend, err); if (!val) goto errexit; /* g_print ("%s->%s\n", key, val); */ g_hash_table_insert (hash, key, val); cur = valend; while ((isblank(*cur))) ++cur; } while (*cur); return hash; errexit: g_hash_table_destroy (hash); return NULL; } char * mu_str_remove_ctrl_in_place (char *str) { char *cur; g_return_val_if_fail (str, NULL); for (cur = str; *cur; ++cur) { GString *gstr; if (!iscntrl(*cur)) continue; if (isspace(*cur)) { /* squash special white space into a simple space */ *cur = ' '; } else { /* remove other control characters */ gstr = g_string_sized_new (strlen (str)); for (cur = str; *cur; ++cur) if (!iscntrl (*cur)) g_string_append_c (gstr, *cur); memcpy (str, gstr->str, gstr->len); /* fits */ g_string_free (gstr, TRUE); break; } } return str; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-str.h������������������������������������������������������������������������������0000664�0000000�0000000�00000023241�12504061031�0014364�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_STR_H__ #define __MU_STR_H__ #include <time.h> #include <sys/types.h> #include <mu-msg.h> #include <mu-flags.h> G_BEGIN_DECLS /** * @addtogroup MuStr * Various string utilities * @{ */ /** * create a 'display contact' from an email header To/Cc/Bcc/From-type address * ie., turn * "Foo Bar" <foo@bar.com> * into * Foo Bar * Note that this is based on some simple heuristics. Max length is 255 bytes. * * mu_str_display_contact_s returns a statically allocated * buffer (ie, non-reentrant), while mu_str_display_contact * returns a newly allocated string that you must free with g_free * when done with it. * * @param str a 'contact str' (ie., what is in the To/Cc/Bcc/From * fields), or NULL * * @return a newly allocated string with a display contact */ const char* mu_str_display_contact_s (const char *str) G_GNUC_CONST; char *mu_str_display_contact (const char *str) G_GNUC_WARN_UNUSED_RESULT; /** * get a display size for a given size_t; uses M for sizes > * 1000*1000, k for smaller sizes. Note: this function use the * 10-based SI units, _not_ the powers-of-2 based ones. * * mu_str_size_s returns a ptr to a static buffer, * while mu_str_size returns dynamically allocated * memory that must be freed after use. * * @param t the size as an size_t * * @return a string representation of the size; see above * for what to do with it */ const char* mu_str_size_s (size_t s) G_GNUC_CONST; char* mu_str_size (size_t s) G_GNUC_WARN_UNUSED_RESULT; /** * Replace all occurences of substr in str with repl * * @param str a string * @param substr some string to replace * @param repl a replacement string * * @return a newly allocated string with the substr replaced by repl; free with g_free */ char *mu_str_replace (const char *str, const char *substr, const char *repl); /** * get a display string for a given set of flags, OR'ed in * @param flags; one character per flag: * D=draft,F=flagged,N=new,P=passed,R=replied,S=seen,T=trashed * a=has-attachment,s=signed, x=encrypted * * mu_str_file_flags_s returns a ptr to a static buffer, * while mu_str_file_flags returns dynamically allocated * memory that must be freed after use. * * @param flags file flags * * @return a string representation of the flags; see above * for what to do with it */ const char* mu_str_flags_s (MuFlags flags) G_GNUC_CONST; char* mu_str_flags (MuFlags flags) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * get a 'summary' of the string, ie. the first /n/ lines of the * strings, with all newlines removed, replaced by single spaces * * @param str the source string * @param max_lines the maximum number of lines to include in the summary * * @return a newly allocated string with the summary. use g_free to free it. */ char* mu_str_summarize (const char* str, size_t max_lines) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some text (e.g. message bodies) -- flatten (remove accents * etc.), and remove some punctuation. * * @param text some text * * @return the processed text, free with g_free */ char* mu_str_process_text (const char *text) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some term (e.g., an e-mail address, subject field): * remove accents, replace some punctuation by _ * * @param term some term * * @return the processed text, free with g_free */ char* mu_str_process_term (const char *term) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some query term (e.g., an e-mail address, subject field): * remove accents, replace some punctuation by _, but leave some query * metachars alone. * * @param qterm some query term * * @return the processed text, free with g_free */ char* mu_str_process_query_term (const char *qterm) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Handle the message-id in a special way * * @param str the message-id str * @param query is this a query? * * @return the massaged message-id */ char* mu_str_process_msgid (const char *str, gboolean query); /** * Fixup values for some fields in the DWIM manner: * - if term is date:YYYYMMDD, replace it with the range * date:YYYYMMDD..YYYYMMDD. * * @param query a query string * * @return the fixup'd string that must be g_free()d * after use or NULL in case of error. */ gchar* mu_str_xapian_fixup_terms (const gchar *term); /** * parse a byte size; a size is a number, with optionally a * unit. Units recognized are b/B (bytes) k/K (1000) and m/M * (1000*1000). Only the first letter is checked and the function is * not case-sensitive, so 1000Kb, 3M will work equally well. Note, * for kB, MB etc., we then follow the SI standards, not 2^10 etc. The * 'b' may be omitted. * * practical sizes for email messages are in terms of Mb; even in * extreme cases it should be under 100 Mb. Function return * GUINT64_MAX if there a parsing error * * @param str a string with a size, such a "100", "100Kb", "1Mb" * * @return the corresponding size in bytes, or -1 in case of error */ gint64 mu_str_size_parse_bkm (const char* str); /** * create a full path from a path + a filename. function is _not_ * reentrant. * * @param path a path (!= NULL) * @param name a name (may be NULL) * * @return the path as a statically allocated buffer. don't free. */ const char* mu_str_fullpath_s (const char* path, const char* name); /** * escape a string like a string literal in C; ie. replace \ with \\, * and " with \" * * @param str a non-NULL str * @param in_quotes whether the result should be enclosed in "" * * @return the escaped string, newly allocated (free with g_free) */ char* mu_str_escape_c_literal (const gchar* str, gboolean in_quotes) G_GNUC_WARN_UNUSED_RESULT; /** * turn a string into plain ascii by replacing each non-ascii * character with a dot ('.'). Replacement is done in-place. * * @param buf a buffer to asciify * * @return the buf ptr (as to allow for function composition) */ char* mu_str_asciify_in_place (char *buf); /** * turn string in buf into valid utf8. If this string is not valid * utf8 already, the function massages the offending characters. * * @param buf a buffer to utf8ify * * @return a newly allocated utf8 string */ char* mu_str_utf8ify (const char *buf); /** * convert a string in a certain charset into utf8 * * @param buffer a buffer to convert * @param charset source character set. * * @return a UTF8 string (which you need to g_free when done with it), * or NULL in case of error */ gchar* mu_str_convert_to_utf8 (const char* buffer, const char *charset); /** * macro to check whether the string is empty, ie. if it's NULL or * it's length is 0 * * @param S a string * * @return TRUE if the string is empty, FALSE otherwise */ #define mu_str_is_empty(S) ((!(S)||!(*S))?TRUE:FALSE) /** * convert a GSList of strings to a #sepa-separated list * * @param lst a GSList * @param the separator character * * @return a newly allocated string */ char* mu_str_from_list (const GSList *lst, char sepa); /** * convert a #sepa-separated list of strings in to a GSList * * @param str a #sepa-separated list of strings * @param the separator character * @param remove leading/trailing whitespace from the string * * @return a newly allocated GSList (free with mu_str_free_list) */ GSList* mu_str_to_list (const char *str, char sepa, gboolean strip); /** * convert a string (with possible escaping) to a list. list items are * separated by one or more spaces. list items can be quoted (using * '"'). * * @param str a string * * @return a list of elements or NULL in case of error, free with * mu_str_free_list */ GSList* mu_str_esc_to_list (const char *str); /** * Parse a list of <key>:<value> arguments, where <value> supports * quoting and escaping. * * @param args a list of arguments * @param err receives error information * * @return a hash table with key->value, or NULL in case of * error. Free with g_hash_table_destroy. */ GHashTable* mu_str_parse_arglist (const char *args, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * free a GSList consisting of allocated strings * * @param lst a GSList */ void mu_str_free_list (GSList *lst); /** * strip the subject of Re:, Fwd: etc. * * @param str a subject string * * @return a new string -- this is pointing somewhere inside the @str; * no copy is made, don't free */ const gchar* mu_str_subject_normalize (const gchar* str); /** * take a list of strings, and return the concatenation of their * quoted forms * * @param params NULL-terminated array of strings * * @return the quoted concatenation of the strings */ gchar* mu_str_quoted_from_strv (const gchar **params); /** * Remove control characters from a string * * @param str a string * * @return the str with control characters removed */ char* mu_str_remove_ctrl_in_place (char *str); /** @} */ G_END_DECLS #endif /*__MU_STR_H__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-threader.c�������������������������������������������������������������������������0000664�0000000�0000000�00000031242�12504061031�0015345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <math.h> /* for log, ceil */ #include <string.h> /* for memset */ #include "mu-threader.h" #include "mu-container.h" #include "mu-str.h" /* msg threading implementation based on JWZ's algorithm, as described in: * http://www.jwz.org/doc/threading.html * * the implementation follows the terminology from that doc, so should * be understandable from that... I did change things a bit though * * the end result of the threading operation is a hashtable which maps * docids (ie., Xapian documents == messages) to 'thread paths'; a * thread path is a string denoting the 2-dimensional place of a * message in a list of messages, * * Msg1 => 00000 * Msg2 => 00001 * Msg3 (child of Msg2) => 00001:00000 * Msg4 (child of Msg2) => 00001:00001 * Msg5 (child of Msg4) => 00001:00001:00000 * Msg6 => 00002 * * the padding-0's are added to make them easy to sort using strcmp; * the number hexadecimal numbers, and the length of the 'segments' * (the parts separated by the ':') is equal to ceil(log_16(matchnum)) * */ /* step 1 */ static GHashTable* create_containers (MuMsgIter *iter); /* step 2 */ static MuContainer *find_root_set (GHashTable *ids); static MuContainer* prune_empty_containers (MuContainer *root); /* static void group_root_set_by_subject (GSList *root_set); */ GHashTable* create_doc_id_thread_path_hash (MuContainer *root, size_t match_num); /* msg threading algorithm, based on JWZ's algorithm, * http://www.jwz.org/doc/threading.html */ GHashTable* mu_threader_calculate (MuMsgIter *iter, size_t matchnum, MuMsgFieldId sortfield, gboolean descending) { GHashTable *id_table, *thread_ids; MuContainer *root_set; g_return_val_if_fail (iter, FALSE); g_return_val_if_fail (mu_msg_field_id_is_valid (sortfield) || sortfield == MU_MSG_FIELD_ID_NONE, FALSE); /* step 1 */ id_table = create_containers (iter); if (matchnum == 0) return id_table; /* just return an empty table */ /* step 2 -- the root_set is the list of children without parent */ root_set = find_root_set (id_table); /* step 3: skip until the end; we still need to containers */ /* step 4: prune empty containers */ root_set = prune_empty_containers (root_set); /* sort root set */ if (sortfield != MU_MSG_FIELD_ID_NONE) root_set = mu_container_sort (root_set, sortfield, descending, NULL); /* step 5: group root set by subject */ /* group_root_set_by_subject (root_set); */ /* sort */ mu_msg_iter_reset (iter); /* go all the way back */ /* finally, deliver the docid => thread-path hash */ thread_ids = mu_container_thread_info_hash_new (root_set, matchnum); g_hash_table_destroy (id_table); /* step 3*/ return thread_ids; } G_GNUC_UNUSED static void check_dup (const char *msgid, MuContainer *c, GHashTable *hash) { if (g_hash_table_lookup (hash, c)) { g_warning ("ALREADY!!"); mu_container_dump (c, FALSE); g_assert (0); } else g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); } G_GNUC_UNUSED static void assert_no_duplicates (GHashTable *ids) { GHashTable *hash; hash = g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_foreach (ids, (GHFunc)check_dup, hash); g_hash_table_destroy (hash); } /* a referred message is a message that is refered by some other * message */ static MuContainer* find_or_create_referred (GHashTable *id_table, const char *msgid, gboolean *created) { MuContainer *c; g_return_val_if_fail (msgid, NULL); c = g_hash_table_lookup (id_table, msgid); *created = !c; if (!c) { c = mu_container_new (NULL, 0, msgid); g_hash_table_insert (id_table, (gpointer)msgid, c); /* assert_no_duplicates (id_table); */ } return c; } /* find a container for the given msgid; if it does not exist yet, * create a new one, and register it */ static MuContainer* find_or_create (GHashTable *id_table, MuMsg *msg, guint docid) { MuContainer *c; const char* msgid; char fake[32]; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (docid != 0, NULL); msgid = mu_msg_get_msgid (msg); if (!msgid) msgid = mu_msg_get_path (msg); /* fake it */ if (!msgid) { /* no path either? seems to happen... */ g_warning ("message without path"); snprintf (fake, sizeof(fake), "fake:%p", (gpointer)msg); msgid = fake; } /* XXX the '<none>' works around a crash; find a better * solution */ c = g_hash_table_lookup (id_table, msgid); /* If id_table contains an empty MuContainer for this ID: * * * Store this message in the MuContainer's message slot. */ if (c) { if (!c->msg) { c->msg = mu_msg_ref (msg); c->docid = docid; return c; } else { /* special case, not in the JWZ algorithm: the * container exists already and has a message; this * means that we are seeing *another message* with a * message-id we already saw... create this message, * and mark it as a duplicate, and a child of the one * we saw before; use its path as a fake message-id * */ MuContainer *c2; const char* fake_msgid; fake_msgid = mu_msg_get_path (msg); c2 = mu_container_new (msg, docid, fake_msgid); c2->flags = MU_CONTAINER_FLAG_DUP; /*c = */ mu_container_append_children (c, c2); g_hash_table_insert (id_table, (gpointer)fake_msgid, c2); return NULL; /* don't process this message further */ } } else { /* Else: Create a new MuContainer object holding this message; Index the MuContainer by Message-ID in id_table. */ c = mu_container_new (msg, docid, msgid); g_hash_table_insert (id_table, (gpointer)msgid, c); /* assert_no_duplicates (id_table); */ return c; } } static gboolean child_elligible (MuContainer *parent, MuContainer *child, gboolean created) { if (!parent || !child) return FALSE; if (child->parent) return FALSE; /* if (created) */ /* return TRUE; */ if (mu_container_reachable (parent, child)) return FALSE; if (mu_container_reachable (child, parent)) return FALSE; return TRUE; } static void /* 1B */ handle_references (GHashTable *id_table, MuContainer *c) { const GSList *refs, *cur; MuContainer *parent; gboolean created; refs = mu_msg_get_references (c->msg); if (!refs) return; /* nothing to do */ /* For each element in the message's References field: Find a MuContainer object for the given Message-ID: If there's one in id_table use that; Otherwise, make (and index) one with a null Message. */ /* go over over our list of refs, until 1 before the last... */ created = FALSE; for (parent = NULL, cur = refs; cur; cur = g_slist_next (cur)) { MuContainer *child; child = find_or_create_referred (id_table, (gchar*)cur->data, &created); /* if we find the current message in their own refs, break now so that parent != c in next step */ if (child == c) break; /*Link the References field's MuContainers together in * the order implied by the References header. If they are already linked, don't change the existing links. Do not add a link if adding that link would introduce a loop: that is, before asserting A->B, search down the children of B to see if A is reachable, and also search down the children of A to see if B is reachable. If either is already reachable as a child of the other, don't add the link. */ if (child_elligible (parent, child, created)) /*parent =*/ mu_container_append_children (parent, child); parent = child; } /* 'parent' points to the last ref: our direct parent; Set the parent of this message to be the last element in References. Note that this message may have a parent already: this can happen because we saw this ID in a References field, and presumed a parent based on the other entries in that field. Now that we have the actual message, we can be more definitive, so throw away the old parent and use this new one. Find this MuContainer in the parent's children list, and unlink it. Note that this could cause this message to now have no parent, if it has no references field, but some message referred to it as the non-first element of its references. (Which would have been some kind of lie...) Note that at all times, the various ``parent'' and ``child'' fields must be kept inter-consistent. */ /* optimization: if the the message was newly added, it's by definition not reachable yet */ /* So, we move c and its descendants to become a child of parent if: * both are not NULL * parent is not a descendant of c. * both are different from each other (guaranteed in last loop) */ if (parent && c && !(c->child && mu_container_reachable (c->child, parent))) { /* if c already has a parent, remove c from its parent children and reparent it, as now we know who is c's parent reliably */ if (c->parent) { mu_container_remove_child(c->parent, c); c->next = c->last = c->parent = NULL; } /*parent = */mu_container_append_children (parent, c); } } /* step 1: create the containers, connect them, and fill the id_table */ static GHashTable* create_containers (MuMsgIter *iter) { GHashTable *id_table; id_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mu_container_destroy); for (mu_msg_iter_reset (iter); !mu_msg_iter_is_done (iter); mu_msg_iter_next (iter)) { MuContainer *c; MuMsg *msg; unsigned docid; /* 1.A */ msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */ docid = mu_msg_iter_get_docid (iter); c = find_or_create (id_table, msg, docid); /* 1.B and C */ if (c) handle_references (id_table, c); } return id_table; } static void filter_root_set (const gchar *msgid, MuContainer *c, MuContainer **root_set) { /* ignore children */ if (c->parent) return; /* ignore duplicates */ if (c->flags & MU_CONTAINER_FLAG_DUP) return; if (*root_set == NULL) { *root_set = c; return; } else *root_set = mu_container_append_siblings (*root_set, c); } /* 2. Walk over the elements of id_table, and gather a list of the MuContainer objects that have no parents, but do have children */ static MuContainer* find_root_set (GHashTable *ids) { MuContainer *root_set; root_set = NULL; g_hash_table_foreach (ids, (GHFunc)filter_root_set, &root_set); return root_set; } static gboolean prune_maybe (MuContainer *c) { MuContainer *cur; for (cur = c->child; cur; cur = cur->next) { if (cur->flags & MU_CONTAINER_FLAG_DELETE) { c = mu_container_remove_child (c, cur); } else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) { c = mu_container_splice_grandchildren (c, cur); c = mu_container_remove_child (c, cur); } } g_return_val_if_fail (c, FALSE); /* don't touch containers with messages */ if (c->msg) return TRUE; /* A. If it is an msg-less container with no children, mark it * for deletion. */ if (!c->child) { c->flags |= MU_CONTAINER_FLAG_DELETE; return TRUE; } /* B. If the MuContainer has no Message, but does have * children, remove this container but promote its * children to this level (that is, splice them in to * the current child list.) * * Do not promote the children if doing so would * promote them to the root set -- unless there is * only one child, in which case, do. */ if (c->child->next) /* ie., > 1 child */ return TRUE; c->flags |= MU_CONTAINER_FLAG_SPLICE; return TRUE; } static MuContainer* prune_empty_containers (MuContainer *root_set) { MuContainer *cur; mu_container_foreach (root_set, (MuContainerForeachFunc)prune_maybe, NULL); /* and prune the root_set itself... */ for (cur = root_set; cur; cur = cur->next) { if (cur->flags & MU_CONTAINER_FLAG_DELETE) { root_set = mu_container_remove_sibling (root_set, cur); } else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) { root_set = mu_container_splice_children (root_set, cur); root_set = mu_container_remove_sibling (root_set, cur); } } return root_set; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/mu-threader.h�������������������������������������������������������������������������0000664�0000000�0000000�00000003651�12504061031�0015355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_THREADER_H__ #define __MU_THREADER_H__ #include <glib.h> #include <mu-msg-iter.h> G_BEGIN_DECLS /** * takes an iter and the total number of matches, and from this * generates a hash-table with information about the thread structure * of these matches. * * the algorithm to find this structure is based on JWZ's * message-threading algorithm, as descrbed in: * http://www.jwz.org/doc/threading.html * * the returned hashtable maps the Xapian docid of iter (msg) to a ptr * to a MuMsgIterThreadInfo structure (see mu-msg-iter.h) * * @param iter an iter; note this function will mu_msgi_iter_reset this iterator * @param matches the number of matches in the set * * @param sortfield the field to sort results by, or * MU_MSG_FIELD_ID_NONE if no sorting should be performed * @param revert if TRUE, if revert the sorting order * * @return a hashtable; free with g_hash_table_destroy when done with it */ GHashTable *mu_threader_calculate (MuMsgIter *iter, size_t matches, MuMsgFieldId sortfield, gboolean revert); G_END_DECLS #endif /*__MU_THREADER_H__*/ ���������������������������������������������������������������������������������������mu-0.9.12/lib/mu-util.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000026760�12504061031�0014535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include <config.h> #endif /*HAVE_CONFIG_H*/ #include "mu-util.h" #define _XOPEN_SOURCE 500 #ifdef HAVE_WORDEXP_H #include <wordexp.h> /* for shell-style globbing */ #endif /*HAVE_WORDEXP_H*/ #include <stdlib.h> #include <string.h> #include <locale.h> /* for setlocale() */ #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <glib-object.h> #include <glib/gstdio.h> #include <errno.h> #include <langinfo.h> static char* do_wordexp (const char *path) { #ifdef HAVE_WORDEXP_H wordexp_t wexp; char *dir; if (!path) { /* g_debug ("%s: path is empty", __FUNCTION__); */ return NULL; } if (wordexp (path, &wexp, 0) != 0) { /* g_debug ("%s: expansion failed for %s", __FUNCTION__, path); */ return NULL; } /* we just pick the first one */ dir = g_strdup (wexp.we_wordv[0]); /* strangely, below seems to lead to a crash on MacOS (BSD); so we have to allow for a tiny leak here on that platform... maybe instead of __APPLE__ it should be __BSD__? Hmmm., cannot reproduce that crash anymore, so commenting it out for now... */ /* #ifndef __APPLE__ */ wordfree (&wexp); /* #endif /\*__APPLE__*\/ */ return dir; # else /*!HAVE_WORDEXP_H*/ /* E.g. OpenBSD does not have wordexp.h, so we ignore it */ return path ? g_strdup (path) : NULL; #endif /*HAVE_WORDEXP_H*/ } /* note, the g_debugs are commented out because this function may be * called before the log handler is installed. */ char* mu_util_dir_expand (const char *path) { char *dir; char resolved[PATH_MAX + 1]; g_return_val_if_fail (path, NULL); dir = do_wordexp (path); if (!dir) return NULL; /* error */ /* don't try realpath if the dir does not exist */ if (access (dir, F_OK) != 0) return dir; /* now resolve any symlinks, .. etc. */ if (realpath (dir, resolved) == NULL) { /* g_debug ("%s: could not get realpath for '%s': %s", */ /* __FUNCTION__, dir, strerror(errno)); */ g_free (dir); return NULL; } else g_free (dir); return g_strdup (resolved); } char* mu_util_create_tmpdir (void) { gchar *dirname; dirname = g_strdup_printf ("%s%cmu-%d%c%x", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid(), G_DIR_SEPARATOR, (int)random()*getpid()*(int)time(NULL)); if (!mu_util_create_dir_maybe (dirname, 0700, FALSE)) { g_free (dirname); return NULL; } return dirname; } GQuark mu_util_error_quark (void) { static GQuark error_domain = 0; if (G_UNLIKELY(error_domain == 0)) error_domain = g_quark_from_static_string ("mu-error-quark"); return error_domain; } const char* mu_util_cache_dir (void) { static char cachedir [PATH_MAX]; snprintf (cachedir, sizeof(cachedir), "%s%cmu-%u", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid()); return cachedir; } gboolean mu_util_check_dir (const gchar* path, gboolean readable, gboolean writeable) { int mode; struct stat statbuf; if (!path) return FALSE; mode = F_OK | (readable ? R_OK : 0) | (writeable ? W_OK : 0); if (access (path, mode) != 0) { /* g_debug ("Cannot access %s: %s", path, strerror (errno)); */ return FALSE; } if (stat (path, &statbuf) != 0) { /* g_debug ("Cannot stat %s: %s", path, strerror (errno)); */ return FALSE; } return S_ISDIR(statbuf.st_mode) ? TRUE: FALSE; } gchar* mu_util_guess_maildir (void) { const gchar *mdir1, *home; /* first, try MAILDIR */ mdir1 = g_getenv ("MAILDIR"); if (mdir1 && mu_util_check_dir (mdir1, TRUE, FALSE)) return g_strdup (mdir1); /* then, try <home>/Maildir */ home = g_get_home_dir(); if (home) { char *mdir2; mdir2 = g_strdup_printf ("%s%cMaildir", home, G_DIR_SEPARATOR); if (mu_util_check_dir (mdir2, TRUE, FALSE)) return mdir2; g_free (mdir2); } /* nope; nothing found */ return NULL; } gchar* mu_util_guess_mu_homedir (void) { const char* home; /* g_get_home_dir use /etc/passwd, not $HOME; this is better, * as HOME may be wrong when using 'sudo' etc.*/ home = g_get_home_dir (); if (!home) { MU_WRITE_LOG ("failed to determine homedir"); return NULL; } return g_strdup_printf ("%s%c%s", home ? home : ".", G_DIR_SEPARATOR, ".mu"); } gboolean mu_util_create_dir_maybe (const gchar *path, mode_t mode, gboolean nowarn) { struct stat statbuf; g_return_val_if_fail (path, FALSE); /* if it exists, it must be a readable dir */ if (stat (path, &statbuf) == 0) { if ((!S_ISDIR(statbuf.st_mode)) || (access (path, W_OK|R_OK) != 0)) { if (!nowarn) g_warning ("not a read-writable" "directory: %s", path); return FALSE; } } if (g_mkdir_with_parents (path, mode) != 0) { if (!nowarn) g_warning ("failed to create %s: %s", path, strerror(errno)); return FALSE; } return TRUE; } gchar* mu_util_str_from_strv (const gchar **params) { GString *str; int i; g_return_val_if_fail (params, NULL); if (!params[0]) return g_strdup (""); str = g_string_sized_new (64); /* just a guess */ for (i = 0; params[i]; ++i) { if (i > 0) g_string_append_c (str, ' '); g_string_append (str, params[i]); } return g_string_free (str, FALSE); } int mu_util_create_writeable_fd (const char* path, mode_t mode, gboolean overwrite) { errno = 0; /* clear! */ g_return_val_if_fail (path, -1); if (overwrite) return open (path, O_WRONLY|O_CREAT|O_TRUNC, mode); else return open (path, O_WRONLY|O_CREAT|O_EXCL, mode); } gboolean mu_util_is_local_file (const char* path) { /* if it starts with file:// it's a local file (for the * purposes of this function -- if it's on a remote FS it's * still considered local) */ if (g_ascii_strncasecmp ("file://", path, strlen("file://")) == 0) return TRUE; if (access (path, R_OK) == 0) return TRUE; return FALSE; } gboolean mu_util_supports (MuFeature feature) { /* check for Guile support */ #ifndef BUILD_GUILE if (feature & MU_FEATURE_GUILE) return FALSE; #endif /*BUILD_GUILE*/ /* check for Gnuplot */ if (feature & MU_FEATURE_GNUPLOT) if (!mu_util_program_in_path ("gnuplot")) return FALSE; return TRUE; } gboolean mu_util_program_in_path (const char *prog) { gchar *path; g_return_val_if_fail (prog, FALSE); path = g_find_program_in_path (prog); g_free (path); return (path != NULL) ? TRUE : FALSE; } gboolean mu_util_play (const char *path, gboolean allow_local, gboolean allow_remote, GError **err) { gboolean rv; const gchar *argv[3]; const char *prog; g_return_val_if_fail (path, FALSE); g_return_val_if_fail (mu_util_is_local_file (path) || allow_remote, FALSE); g_return_val_if_fail (!mu_util_is_local_file (path) || allow_local, FALSE); prog = g_getenv ("MU_PLAY_PROGRAM"); if (!prog) { #ifdef __APPLE__ prog = "open"; #else prog = "xdg-open"; #endif /*!__APPLE__*/ } if (!mu_util_program_in_path (prog)) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_EXECUTE, "cannot find '%s' in path", prog); return FALSE; } argv[0] = prog; argv[1] = path; argv[2] = NULL; err = NULL; rv = g_spawn_async (NULL, (gchar**)&argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, err); return rv; } unsigned char mu_util_get_dtype_with_lstat (const char *path) { struct stat statbuf; g_return_val_if_fail (path, DT_UNKNOWN); if (lstat (path, &statbuf) != 0) { g_warning ("stat failed on %s: %s", path, strerror(errno)); return DT_UNKNOWN; } /* we only care about dirs, regular files and links */ if (S_ISREG (statbuf.st_mode)) return DT_REG; else if (S_ISDIR (statbuf.st_mode)) return DT_DIR; else if (S_ISLNK (statbuf.st_mode)) return DT_LNK; return DT_UNKNOWN; } gboolean mu_util_locale_is_utf8 (void) { const gchar *dummy; static int is_utf8 = -1; if (G_UNLIKELY(is_utf8 == -1)) is_utf8 = g_get_charset(&dummy) ? 1 : 0; return is_utf8 ? TRUE : FALSE; } gboolean mu_util_fputs_encoded (const char *str, FILE *stream) { int rv; unsigned bytes; char *conv; g_return_val_if_fail (str, FALSE); g_return_val_if_fail (stream, FALSE); /* g_get_charset return TRUE when the locale is UTF8 */ if (mu_util_locale_is_utf8()) return fputs (str, stream) == EOF ? FALSE : TRUE; /* charset is _not_ utf8, so we actually have to convert * it */ conv = NULL; if (g_utf8_validate (str, -1, NULL)) /* it _seems_ that on the bsds, the final err param * may receive garbage... so we don't use it */ conv = g_locale_from_utf8 (str, -1, (gsize*)&bytes, NULL, NULL); /* conversion failed; this happens because is some cases GMime * may gives us non-UTF-8 strings from e.g. wrongly encoded * message-subjects; if so, we escape the string */ if (!conv) conv = g_strescape (str, "\n\t"); rv = conv ? fputs (conv, stream) : EOF; g_free (conv); return (rv == EOF) ? FALSE : TRUE; } gboolean mu_util_g_set_error (GError **err, MuError errcode, const char *frm, ...) { va_list ap; char *msg; /* don't bother with NULL errors, or errors already set */ if (!err || *err) return FALSE; msg = NULL; va_start (ap, frm); g_vasprintf (&msg, frm, ap); va_end (ap); g_set_error (err, MU_ERROR_DOMAIN, errcode, "%s", msg); g_free (msg); return FALSE; } static gboolean print_args (FILE *stream, const char *frm, va_list args) { gchar *str; gboolean rv; str = g_strdup_vprintf (frm, args); rv = mu_util_fputs_encoded (str, stream); g_free (str); return rv; } gboolean mu_util_print_encoded (const char *frm, ...) { va_list args; gboolean rv; g_return_val_if_fail (frm, FALSE); va_start (args, frm); rv = print_args (stdout, frm, args); va_end (args); return rv; } gboolean mu_util_printerr_encoded (const char *frm, ...) { va_list args; gboolean rv; g_return_val_if_fail (frm, FALSE); va_start (args, frm); rv = print_args (stderr, frm, args); va_end (args); return rv; } char* mu_util_read_password (const char *prompt) { char *pass; g_return_val_if_fail (prompt, NULL); /* note: getpass is obsolete; replace with something better */ pass = getpass (prompt); /* returns static mem, don't free */ if (!pass) { if (errno) g_warning ("error: %s", strerror(errno)); return NULL; } return g_strdup (pass); } const char* mu_util_get_hash (const char* str) { unsigned djbhash, bkdrhash, bkdrseed; unsigned u; static char hex[18]; g_return_val_if_fail (str, NULL); djbhash = 5381; bkdrhash = 0; bkdrseed = 1313; for(u = 0; str[u]; ++u) { djbhash = ((djbhash << 5) + djbhash) + str[u]; bkdrhash = bkdrhash * bkdrseed + str[u]; } snprintf (hex, sizeof(hex), "%08x%08x", djbhash, bkdrhash); return hex; } ����������������mu-0.9.12/lib/mu-util.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000035354�12504061031�0014541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_UTIL_H__ #define __MU_UTIL_H__ #include <glib.h> #include <stdio.h> #include <dirent.h> #include <sys/stat.h> /* for mode_t */ /* hopefully, this should get us a sane PATH_MAX */ #include <limits.h> /* not all systems provide PATH_MAX in limits.h */ #ifndef PATH_MAX #include <sys/param.h> #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif /*!PATH_MAX*/ #endif /*PATH_MAX*/ G_BEGIN_DECLS /** * get the expanded path; ie. perform shell expansion on the path. the * path does not have to exist * * @param path path to expand * * @return the expanded path as a newly allocated string, or NULL in * case of error */ char* mu_util_dir_expand (const char* path) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * guess the maildir; first try $MAILDIR; if it is unset or * non-existant, try ~/Maildir if both fail, return NULL * * @return full path of the guessed Maildir, or NULL; must be freed (gfree) */ char* mu_util_guess_maildir (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * guess the place of the mu homedir (typically, ~/.mu). Note, this * directory does not necessarily exist. mu_util_check_dir can be used * to check that * * @return the guessed mu homedir, which needs to be freed with g_free * when no longer needed. */ gchar* mu_util_guess_mu_homedir (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * if path exists, check that's a read/writeable dir; otherwise try to * create it (with perms 0700) * * @param path path to the dir * @param mode to set for the dir (as per chmod(1)) * @param nowarn, if TRUE, don't write warnings (if any) to stderr * * @return TRUE if a read/writeable directory `path' exists after * leaving this function, FALSE otherwise */ gboolean mu_util_create_dir_maybe (const gchar *path, mode_t mode, gboolean nowarn) G_GNUC_WARN_UNUSED_RESULT; /** * check whether path is a directory, and optionally, if it's readable * and/or writeable * * @param path dir path * @param readable check for readability * @param writeable check for writability * * @return TRUE if dir exist and has the specified properties */ gboolean mu_util_check_dir (const gchar* path, gboolean readable, gboolean writeable) G_GNUC_WARN_UNUSED_RESULT; /** * get our the cache directory, typically, /tmp/mu-<userid>/ * * @return the cache directory; don't free */ const char* mu_util_cache_dir (void) G_GNUC_CONST; /** * create a writeable file and return its file descriptor (which * you'll need to close(2) when done with it.) * * @param path the full path of the file to create * @param the mode to open (ie. 0644 or 0600 etc., see chmod(3) * @param overwrite should we allow for overwriting existing files? * * @return a file descriptor, or -1 in case of error. If it's a file * system error, 'errno' may contain more info. use 'close()' when done * with the file descriptor */ int mu_util_create_writeable_fd (const char* path, mode_t mode, gboolean overwrite) G_GNUC_WARN_UNUSED_RESULT; /** * check if file is local, ie. on the local file system. this means * that it's either having a file URI, *or* that it's an existing file * * @param path a path * * @return TRUE if the file is local, FALSE otherwise */ gboolean mu_util_is_local_file (const char* path); /** * is the current locale utf-8 compatible? * * @return TRUE if it's utf8 compatible, FALSE otherwise */ gboolean mu_util_locale_is_utf8 (void) G_GNUC_CONST; /** * write a string (assumed to be in utf8-format) to a stream, * converted to the current locale * * @param str a string * @param stream a stream * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_fputs_encoded (const char *str, FILE *stream); /** * print a formatted string (assumed to be in utf8-format) to stdout, * converted to the current locale * * @param a standard printf() format string, followed by a parameter list * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_print_encoded (const char *frm, ...) G_GNUC_PRINTF(1,2); /** * print a formatted string (assumed to be in utf8-format) to stderr, * converted to the current locale * * @param a standard printf() format string, followed by a parameter list * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_printerr_encoded (const char *frm, ...) G_GNUC_PRINTF(1,2); /** * read a password from stdin (without echoing), and return it. * * @param prompt the prompt text before the password * * @return the password (free with g_free), or NULL */ char* mu_util_read_password (const char *prompt) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Try to 'play' (ie., open with it's associated program) a file. On * MacOS, the the program 'open' is used for this; on other platforms * 'xdg-open' to do the actual opening. In addition you can set it to another program * by setting the MU_PLAY_PROGRAM environment variable * * @param path full path of the file to open * @param allow_local allow local files (ie. with file:// prefix or fs paths) * @param allow_remote allow URIs (ie., http, mailto) * @param err receives error information, if any * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_util_play (const char *path, gboolean allow_local, gboolean allow_remote, GError **err); /** * Check whether program prog exists in PATH * * @param prog a program (executable) * * @return TRUE if it exists and is executable, FALSE otherwise */ gboolean mu_util_program_in_path (const char *prog); enum _MuFeature { MU_FEATURE_GUILE = 1 << 0, /* do we support Guile 2.0? */ MU_FEATURE_GNUPLOT = 1 << 1, /* do we have gnuplot installed? */ MU_FEATURE_CRYPTO = 1 << 2 /* do we support crypto (Gmime >= 2.6) */ }; typedef enum _MuFeature MuFeature; /** * Check whether mu supports some particular feature * * @param feature a feature (multiple features can be logical-or'd together) * * @return TRUE if the feature is supported, FALSE otherwise */ gboolean mu_util_supports (MuFeature feature); /** * Get an error-query for mu, to be used in `g_set_error'. Recent * version of Glib warn when using 0 for the error-domain in * g_set_error. * * * @return an error quark for mu */ GQuark mu_util_error_quark (void) G_GNUC_CONST; #define MU_ERROR_DOMAIN (mu_util_error_quark()) /** * convert a string array in to a string, with the elements separated * by ' ' * * @param params a non-NULL, NULL-terminated string array * * @return a newly allocated string */ gchar* mu_util_str_from_strv (const gchar **params) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /* * for OSs with out support for direntry->d_type, like Solaris */ #ifndef DT_UNKNOWN enum { DT_UNKNOWN = 0, # define DT_UNKNOWN DT_UNKNOWN DT_FIFO = 1, # define DT_FIFO DT_FIFO DT_CHR = 2, # define DT_CHR DT_CHR DT_DIR = 4, # define DT_DIR DT_DIR DT_BLK = 6, # define DT_BLK DT_BLK DT_REG = 8, # define DT_REG DT_REG DT_LNK = 10, # define DT_LNK DT_LNK DT_SOCK = 12, # define DT_SOCK DT_SOCK DT_WHT = 14 # define DT_WHT DT_WHT }; #endif /*DT_UNKNOWN*/ /** * get the d_type (as in direntry->d_type) for the file at path, using * lstat(3) * * @param path full path * * @return DT_REG, DT_DIR, DT_LNK, or DT_UNKNOWN (other values are not * supported currently ) */ unsigned char mu_util_get_dtype_with_lstat (const char *path); /** * we need this when using Xapian::Document* from C * */ typedef gpointer XapianDocument; /** * we need this when using Xapian::Enquire* from C * */ typedef gpointer XapianEnquire; /* print a warning for a GError, and free it */ #define MU_HANDLE_G_ERROR(GE) \ do { \ if (!(GE)) \ g_warning ("%s:%u: an error occured in %s", \ __FILE__, __LINE__, __FUNCTION__); \ else { \ g_warning ("error %u: %s", (GE)->code, (GE)->message); \ g_error_free ((GE)); \ } \ } while (0) /** * * don't repeat these catch blocks everywhere... * */ #define MU_STORE_CATCH_BLOCK_RETURN(GE,R) \ catch (const MuStoreError& merr) { \ mu_util_g_set_error ((GE), \ merr.mu_error(), "%s", \ merr.what().c_str()); \ return (R); \ } \ #define MU_XAPIAN_CATCH_BLOCK \ catch (const Xapian::Error &xerr) { \ g_critical ("%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ } catch (...) { \ g_critical ("%s: caught exception", __FUNCTION__); \ } #define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \ catch (const Xapian::DatabaseLockError &xerr) { \ mu_util_g_set_error ((GE), \ MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \ "%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ } catch (const Xapian::DatabaseCorruptError &xerr) { \ mu_util_g_set_error ((GE), \ MU_ERROR_XAPIAN_CORRUPTION, \ "%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ } catch (const Xapian::DatabaseError &xerr) { \ mu_util_g_set_error ((GE),MU_ERROR_XAPIAN, \ "%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ } catch (const Xapian::Error &xerr) { \ mu_util_g_set_error ((GE),(E), \ "%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ } catch (...) { \ mu_util_g_set_error ((GE),(MU_ERROR_INTERNAL), \ "%s: caught exception", __FUNCTION__); \ } #define MU_XAPIAN_CATCH_BLOCK_RETURN(R) \ catch (const Xapian::Error &xerr) { \ g_critical ("%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ return (R); \ } catch (...) { \ g_critical ("%s: caught exception", __FUNCTION__); \ return (R); \ } #define MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(GE,E,R) \ catch (const Xapian::Error &xerr) { \ mu_util_g_set_error ((GE),(E), \ "%s: xapian error '%s'", \ __FUNCTION__, xerr.get_msg().c_str()); \ return (R); \ } catch (...) { \ if ((GE)&&!(*(GE))) \ mu_util_g_set_error ((GE), \ (MU_ERROR_INTERNAL), \ "%s: caught exception", __FUNCTION__); \ return (R); \ } /* the name of the (leaf) dir which has the xapian database */ #define MU_XAPIAN_DIR_NAME "xapian" /* name of the bookmark file */ #define MU_BOOKMARK_FILENAME "bookmarks" /* metadata key for the xapian 'schema' version */ #define MU_STORE_VERSION_KEY "db_version" /** * log something in the log file; note, we use G_LOG_LEVEL_INFO * for such messages */ #define MU_WRITE_LOG(...) \ G_STMT_START { \ g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_INFO, \ __VA_ARGS__); \ } G_STMT_END #define MU_G_ERROR_CODE(GE) ((GE)&&(*(GE))?(*(GE))->code:MU_ERROR) enum _MuError { /* no error at all! */ MU_OK = 0, /* generic error */ MU_ERROR = 1, MU_ERROR_IN_PARAMETERS = 2, MU_ERROR_INTERNAL = 3, MU_ERROR_NO_MATCHES = 4, MU_ERROR_SCRIPT_NOT_FOUND = 8, /* general xapian related error */ MU_ERROR_XAPIAN = 11, /* (parsing) error in the query */ MU_ERROR_XAPIAN_QUERY = 13, /* xapian dir is not accessible */ MU_ERROR_XAPIAN_DIR_NOT_ACCESSIBLE = 14, /* database version is not up-to-date */ MU_ERROR_XAPIAN_VERSION_MISMATCH = 15, /* missing data for a document */ MU_ERROR_XAPIAN_MISSING_DATA = 16, /* database corruption */ MU_ERROR_XAPIAN_CORRUPTION = 17, /* can't get write lock */ MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK = 18, /* database is empty */ MU_ERROR_XAPIAN_IS_EMPTY = 19, /* could not write */ MU_ERROR_XAPIAN_STORE_FAILED = 20, /* could not remove */ MU_ERROR_XAPIAN_REMOVE_FAILED = 21, /* database was modified; reload */ MU_ERROR_XAPIAN_MODIFIED = 22, /* GMime related errors */ /* gmime parsing related error */ MU_ERROR_GMIME = 30, /* contacts related errors */ MU_ERROR_CONTACTS = 50, MU_ERROR_CONTACTS_CANNOT_RETRIEVE = 51, /* crypto related errors */ MU_ERROR_CRYPTO = 60, /* File errors */ /* generic file-related error */ MU_ERROR_FILE = 70, MU_ERROR_FILE_INVALID_NAME = 71, MU_ERROR_FILE_CANNOT_LINK = 72, MU_ERROR_FILE_CANNOT_OPEN = 73, MU_ERROR_FILE_CANNOT_READ = 74, MU_ERROR_FILE_CANNOT_EXECUTE = 75, MU_ERROR_FILE_CANNOT_CREATE = 76, MU_ERROR_FILE_CANNOT_MKDIR = 77, MU_ERROR_FILE_STAT_FAILED = 78, MU_ERROR_FILE_READDIR_FAILED = 79, MU_ERROR_FILE_INVALID_SOURCE = 80, MU_ERROR_FILE_TARGET_EQUALS_SOURCE = 81, MU_ERROR_FILE_CANNOT_WRITE = 82, MU_ERROR_FILE_CANNOT_UNLINK = 83, /* not really an error, used in callbacks */ MU_STOP = 99 }; typedef enum _MuError MuError; /** * set an error if it's not already set, and return FALSE * * @param err errptr, or NULL * @param errcode error code * @param frm printf-style format, followed by paremeters * * @return FALSE */ gboolean mu_util_g_set_error (GError **err, MuError errcode, const char *frm, ...) G_GNUC_PRINTF(3,4); /** * calculate a 64-bit hash for the given string, based on a * combination of the DJB and BKDR hash functions * * @param a string * * @return the hash as a static string, which stays valid until this * function is called again. */ const char* mu_util_get_hash (const char* str); #define MU_COLOR_RED "\x1b[31m" #define MU_COLOR_GREEN "\x1b[32m" #define MU_COLOR_YELLOW "\x1b[33m" #define MU_COLOR_BLUE "\x1b[34m" #define MU_COLOR_MAGENTA "\x1b[35m" #define MU_COLOR_CYAN "\x1b[36m" #define MU_COLOR_DEFAULT "\x1b[0m" G_END_DECLS #endif /*__MU_UTIL_H__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0014124�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/Makefile.am���������������������������������������������������������������������0000664�0000000�0000000�00000013113�12504061031�0016157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by the ## Free Software Foundation; either version 3, or (at your option) any ## later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -DMU_TESTMAILDIR=\"${abs_srcdir}/testdir\" \ -DMU_TESTMAILDIR2=\"${abs_srcdir}/testdir2\" \ -DMU_TESTMAILDIR3=\"${abs_srcdir}/testdir3\" \ -DMU_TESTMAILDIR4=\"${abs_srcdir}/testdir4\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_PROGRAMS= $(TEST_PROGS) noinst_LTLIBRARIES=libtestmucommon.la TEST_PROGS += test-mu-util test_mu_util_SOURCES= test-mu-util.c dummy.cc test_mu_util_LDADD= libtestmucommon.la TEST_PROGS += test-mu-str test_mu_str_SOURCES= test-mu-str.c dummy.cc test_mu_str_LDADD= libtestmucommon.la TEST_PROGS += test-mu-maildir test_mu_maildir_SOURCES= test-mu-maildir.c dummy.cc test_mu_maildir_LDADD= libtestmucommon.la TEST_PROGS += test-mu-msg-fields test_mu_msg_fields_SOURCES= test-mu-msg-fields.c dummy.cc test_mu_msg_fields_LDADD= libtestmucommon.la TEST_PROGS += test-mu-msg test_mu_msg_SOURCES= test-mu-msg.c dummy.cc test_mu_msg_LDADD= libtestmucommon.la TEST_PROGS += test-mu-store test_mu_store_SOURCES= test-mu-store.c dummy.cc test_mu_store_LDADD= libtestmucommon.la TEST_PROGS += test-mu-date test_mu_date_SOURCES= test-mu-date.c dummy.cc test_mu_date_LDADD= libtestmucommon.la TEST_PROGS += test-mu-flags test_mu_flags_SOURCES= test-mu-flags.c dummy.cc test_mu_flags_LDADD= libtestmucommon.la TEST_PROGS += test-mu-container test_mu_container_SOURCES= test-mu-container.c dummy.cc test_mu_container_LDADD= libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES= \ dummy.cc dummy.cc: touch dummy.cc libtestmucommon_la_SOURCES= \ test-mu-common.c \ test-mu-common.h libtestmucommon_la_LIBADD= ../libmu.la # note the question marks; make does not like files with ':', so we # use the (also supported) version with '!' instead. We could escape # the : with \: but automake does not recognize that.... # test messages, the '.ignore' message should be ignored # when indexing EXTRA_DIST= \ testdir/tmp/1220863087.12663.ignore \ testdir/new/1220863087.12663_9.mindcrime \ testdir/new/1220863087.12663_25.mindcrime \ testdir/new/1220863087.12663_21.mindcrime \ testdir/new/1220863087.12663_23.mindcrime \ testdir/cur/1220863087.12663_5.mindcrime!2,S \ testdir/cur/1220863087.12663_7.mindcrime!2,RS \ testdir/cur/1220863087.12663_15.mindcrime!2,PS \ testdir/cur/1220863087.12663_19.mindcrime!2,S \ testdir/cur/1220863042.12663_1.mindcrime!2,S \ testdir/cur/1220863060.12663_3.mindcrime!2,S \ testdir/cur/1283599333.1840_11.cthulhu!2, \ testdir/cur/1305664394.2171_402.cthulhu!2, \ testdir/cur/1252168370_3.14675.cthulhu!2,S \ testdir/cur/encrypted!2,S \ testdir/cur/multimime!2,FS \ testdir/cur/signed!2,S \ testdir/cur/signed-encrypted!2,S \ testdir/cur/special!2,Sabc \ testdir2/bar/cur/mail1 \ testdir2/bar/cur/mail2 \ testdir2/bar/cur/mail3 \ testdir2/bar/cur/mail4 \ testdir2/bar/cur/mail5 \ testdir2/bar/cur/181736.eml \ testdir2/bar/cur/mail6 \ testdir2/bar/tmp/.noindex \ testdir2/bar/new/.noindex \ testdir2/Foo/cur/mail5 \ testdir2/Foo/cur/arto.eml \ testdir2/Foo/cur/fraiche.eml \ testdir2/Foo/tmp/.noindex \ testdir2/Foo/new/.noindex \ testdir2/wom_bat/cur/atomic \ testdir2/wom_bat/cur/rfc822.1 \ testdir2/wom_bat/cur/rfc822.2 \ testdir3/cycle \ testdir3/cycle/new/.noindex \ testdir3/cycle/cur/rogue0 \ testdir3/cycle/cur/cycle0 \ testdir3/cycle/cur/cycle0.0 \ testdir3/cycle/cur/cycle0.0.0 \ testdir3/cycle/tmp/.noindex \ testdir3/tree/new/.noindex \ testdir3/tree/cur/child0.0 \ testdir3/tree/cur/child4.0 \ testdir3/tree/cur/root2 \ testdir3/tree/cur/root1 \ testdir3/tree/cur/child3.0.0.0.0 \ testdir3/tree/cur/root0 \ testdir3/tree/cur/child2.0.0 \ testdir3/tree/cur/child0.1 \ testdir3/tree/cur/child0.1.0 \ testdir3/tree/cur/child4.1 \ testdir3/tree/tmp/.noindex \ testdir4/1220863087.12663_19.mindcrime!2,S \ testdir4/1220863042.12663_1.mindcrime!2,S \ testdir4/1283599333.1840_11.cthulhu!2, \ testdir4/1305664394.2171_402.cthulhu!2, \ testdir4/1252168370_3.14675.cthulhu!2,S \ testdir4/mail1 \ testdir4/mail5 \ testdir4/181736.eml \ testdir4/encrypted!2,S \ testdir4/multimime!2,FS \ testdir4/signed!2,S \ testdir4/signed-bad!2,S \ testdir4/signed-encrypted!2,S \ testdir4/special!2,Sabc �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/dummy.cc������������������������������������������������������������������������0000664�0000000�0000000�00000000000�12504061031�0015554�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-common.c����������������������������������������������������������������0000664�0000000�0000000�00000003523�12504061031�0017157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <langinfo.h> #include <locale.h> #include "test-mu-common.h" char* test_mu_common_get_random_tmpdir (void) { char*dir; int res; dir = g_strdup_printf ( "%s%cmu-test-%d%ctest-%x", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid(), G_DIR_SEPARATOR, (int)random()*getpid()*(int)time(NULL)); res = g_mkdir_with_parents (dir, 0700); g_assert (res != -1); return dir; } const char* set_tz (const char* tz) { static const char* oldtz; oldtz = getenv ("TZ"); if (tz) setenv ("TZ", tz, 1); else unsetenv ("TZ"); tzset (); return oldtz; } gboolean set_en_us_utf8_locale (void) { setenv ("LC_ALL", "en_US.utf8", 1); setlocale (LC_ALL, "en_US.utf8"); if (strcmp (nl_langinfo(CODESET), "UTF-8") != 0) { g_print ("Note: Unit tests require the en_US.utf8 locale. " "Ignoring test cases.\n"); return FALSE; } return TRUE; } void black_hole (void) { return; /* do nothing */ } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-common.h����������������������������������������������������������������0000664�0000000�0000000�00000002701�12504061031�0017161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __TEST_MU_COMMON_H__ #define __TEST_MU_COMMON_H__ #include <glib.h> G_BEGIN_DECLS /** * get a dir name for a random temporary directory to do tests * * @return a random dir name, g_free when it's no longer needed */ char* test_mu_common_get_random_tmpdir (void); /** * set the output to /dev/null * */ void black_hole (void); /** * set the timezone * * @param tz timezone * * @return the old timezone */ const char* set_tz (const char* tz); /** * switch the locale to en_US.utf8, return TRUE if it succeeds * * @return TRUE if the switch succeeds, FALSE otherwise */ gboolean set_en_us_utf8_locale (void); G_END_DECLS #endif /*__TEST_MU_COMMON_H__*/ ���������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-container.c�������������������������������������������������������������0000664�0000000�0000000�00000004332�12504061031�0017650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2014 Jakub Sitnicki <jsitnicki@gmail.com> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include "test-mu-common.h" #include "mu-container.h" static gboolean container_has_children (const MuContainer *c) { return c && c->child; } static gboolean container_is_sibling_of (const MuContainer *c, const MuContainer *sibling) { const MuContainer *cur; for (cur = c; cur; cur = cur->next) { if (cur == sibling) return TRUE; } return container_is_sibling_of (sibling, c); } static void test_mu_container_splice_children_when_parent_has_no_siblings (void) { MuContainer *child, *parent, *root_set; child = mu_container_new (NULL, 0, "child"); parent = mu_container_new (NULL, 0, "parent"); parent = mu_container_append_children (parent, child); root_set = parent; root_set = mu_container_splice_children (root_set, parent); g_assert (root_set != NULL); g_assert (!container_has_children(parent)); g_assert (container_is_sibling_of (root_set, child)); mu_container_destroy(parent); mu_container_destroy(child); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/mu-container/mu-container-splice-children-when-parent-has-no-siblings", test_mu_container_splice_children_when_parent_has_no_siblings); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-date.c������������������������������������������������������������������0000664�0000000�0000000�00000012315�12504061031�0016603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-date.h" static void test_mu_date_str (void) { struct tm *tmbuf; char buf[64]; gchar *tmp; time_t some_time; some_time = 1234567890; tmbuf = localtime (&some_time); strftime (buf, 64, "%x", tmbuf); /* $ date -ud@1234567890; Fri Feb 13 23:31:30 UTC 2009 */ g_assert_cmpstr (mu_date_str_s ("%x", some_time), ==, buf); /* date -ud@987654321 Thu Apr 19 04:25:21 UTC 2001 */ some_time = 987654321; tmbuf = localtime (&some_time); strftime (buf, 64, "%c", tmbuf); tmp = mu_date_str ("%c", some_time); g_assert_cmpstr (tmp, ==, buf); g_free (tmp); } static void test_mu_date_parse_hdwmy (void) { time_t diff; diff = time(NULL) - mu_date_parse_hdwmy ("3h"); g_assert (diff > 0); g_assert_cmpuint (3 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("5y"); g_assert (diff > 0); g_assert_cmpuint (5 * 365 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("3m"); g_assert (diff > 0); g_assert_cmpuint (3 * 30 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("21d"); g_assert (diff > 0); g_assert_cmpuint (21 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("2w"); g_assert (diff > 0); g_assert_cmpuint (2 * 7 * 24 * 60 * 60 - diff, <=, 1); g_assert_cmpint (mu_date_parse_hdwmy("-1y"),==, (time_t)-1); } static void test_mu_date_complete_begin (void) { g_assert_cmpstr (mu_date_complete_s("2000", TRUE), ==, "20000101000000"); g_assert_cmpstr (mu_date_complete_s("2", TRUE), ==, "20000101000000"); g_assert_cmpstr (mu_date_complete_s ("", TRUE), ==, "00000101000000"); g_assert_cmpstr (mu_date_complete_s ("201007", TRUE), ==, "20100701000000"); g_assert_cmpstr (mu_date_complete_s ("19721214", TRUE), ==, "19721214000000"); g_assert_cmpstr (mu_date_complete_s ("19721214234512", TRUE), ==, "19721214234512"); g_assert_cmpstr (mu_date_complete_s ("2010-07", TRUE), ==, "20100701000000"); g_assert_cmpstr (mu_date_complete_s ("1972/12/14", TRUE), ==, "19721214000000"); g_assert_cmpstr (mu_date_complete_s ("1972-12-14 23:45:12", TRUE), ==, "19721214234512"); } static void test_mu_date_complete_end (void) { g_assert_cmpstr (mu_date_complete_s ("2000", FALSE), ==, "20001231235959"); g_assert_cmpstr (mu_date_complete_s ("2", FALSE), ==, "29991231235959"); g_assert_cmpstr (mu_date_complete_s ("", FALSE), ==, "99991231235959"); g_assert_cmpstr (mu_date_complete_s ("201007", FALSE), ==, "20100731235959"); g_assert_cmpstr (mu_date_complete_s ("19721214", FALSE), ==, "19721214235959"); g_assert_cmpstr (mu_date_complete_s ("19721214234512", FALSE), ==, "19721214234512"); g_assert_cmpstr (mu_date_complete_s ("2010-07", FALSE), ==, "20100731235959"); g_assert_cmpstr (mu_date_complete_s ("1972.12.14", FALSE), ==, "19721214235959"); g_assert_cmpstr (mu_date_complete_s ("1972.12.14 23:45/12", FALSE), ==, "19721214234512"); } static void test_mu_date_interpret_begin (void) { time_t now; now = time (NULL); g_assert_cmpstr (mu_date_interpret_s ("now", TRUE) , ==, mu_date_str_s("%Y%m%d%H%M%S", now)); g_assert_cmpstr (mu_date_interpret_s ("today", TRUE) , ==, mu_date_str_s("%Y%m%d000000", now)); } static void test_mu_date_interpret_end (void) { time_t now; now = time (NULL); g_assert_cmpstr (mu_date_interpret_s ("now", FALSE) , ==, mu_date_str_s("%Y%m%d%H%M%S", now)); g_assert_cmpstr (mu_date_interpret_s ("today", FALSE) , ==, mu_date_str_s("%Y%m%d235959", now)); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_str_date */ g_test_add_func ("/mu-str/mu-date-str", test_mu_date_str); g_test_add_func ("/mu-str/mu_date_parse_hdwmy", test_mu_date_parse_hdwmy); g_test_add_func ("/mu-str/mu_date_complete_begin", test_mu_date_complete_begin); g_test_add_func ("/mu-str/mu_date_complete_end", test_mu_date_complete_end); g_test_add_func ("/mu-str/mu_date_interpret_begin", test_mu_date_interpret_begin); g_test_add_func ("/mu-str/mu_date_interpret_end", test_mu_date_interpret_end); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-flags.c�����������������������������������������������������������������0000664�0000000�0000000�00000014157�12504061031�0016770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include "mu-flags.h" #include "test-mu-common.h" static void test_mu_flag_char (void) { g_assert_cmpuint (mu_flag_char (MU_FLAG_DRAFT), ==, 'D'); g_assert_cmpuint (mu_flag_char (MU_FLAG_FLAGGED), ==, 'F'); g_assert_cmpuint (mu_flag_char (MU_FLAG_PASSED), ==, 'P'); g_assert_cmpuint (mu_flag_char (MU_FLAG_REPLIED), ==, 'R'); g_assert_cmpuint (mu_flag_char (MU_FLAG_SEEN), ==, 'S'); g_assert_cmpuint (mu_flag_char (MU_FLAG_TRASHED), ==, 'T'); g_assert_cmpuint (mu_flag_char (MU_FLAG_NEW), ==, 'N'); g_assert_cmpuint (mu_flag_char (MU_FLAG_SIGNED), ==, 'z'); g_assert_cmpuint (mu_flag_char (MU_FLAG_ENCRYPTED), ==, 'x'); g_assert_cmpuint (mu_flag_char (MU_FLAG_HAS_ATTACH), ==, 'a'); g_assert_cmpuint (mu_flag_char (MU_FLAG_UNREAD), ==, 'u'); g_assert_cmpuint (mu_flag_char (12345), ==, 0); } static void test_mu_flag_name (void) { g_assert_cmpstr (mu_flag_name (MU_FLAG_DRAFT), ==, "draft"); g_assert_cmpstr (mu_flag_name (MU_FLAG_FLAGGED), ==, "flagged"); g_assert_cmpstr (mu_flag_name (MU_FLAG_PASSED), ==, "passed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_REPLIED), ==, "replied"); g_assert_cmpstr (mu_flag_name (MU_FLAG_SEEN), ==, "seen"); g_assert_cmpstr (mu_flag_name (MU_FLAG_TRASHED), ==, "trashed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_NEW), ==, "new"); g_assert_cmpstr (mu_flag_name (MU_FLAG_SIGNED), ==, "signed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_ENCRYPTED), ==, "encrypted"); g_assert_cmpstr (mu_flag_name (MU_FLAG_HAS_ATTACH), ==, "attach"); g_assert_cmpstr (mu_flag_name (MU_FLAG_UNREAD), ==, "unread"); g_assert_cmpstr (mu_flag_name (12345), ==, NULL); } static void test_mu_flags_to_str_s (void) { g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_PASSED|MU_FLAG_SIGNED, MU_FLAG_TYPE_ANY), ==, "Pz"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_ANY), ==, "N"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_HAS_ATTACH|MU_FLAG_TRASHED, MU_FLAG_TYPE_ANY), ==, "Ta"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_ANY), ==, ""); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_PASSED|MU_FLAG_SIGNED, MU_FLAG_TYPE_CONTENT), ==, "z"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_MAILDIR), ==, "N"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_HAS_ATTACH|MU_FLAG_TRASHED, MU_FLAG_TYPE_MAILFILE), ==, "T"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_PSEUDO), ==, ""); } static void test_mu_flags_from_str (void) { /* note, the 3rd arg to mu_flags_from_str determines whether * invalid flags will be ignored (if TRUE) or MU_FLAG_INVALID (if FALSE) */ g_assert_cmpuint (mu_flags_from_str ("RP", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_REPLIED | MU_FLAG_PASSED); g_assert_cmpuint (mu_flags_from_str ("Nz", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_NEW | MU_FLAG_SIGNED); g_assert_cmpuint (mu_flags_from_str ("axD", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_HAS_ATTACH | MU_FLAG_ENCRYPTED | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str ("RP", MU_FLAG_TYPE_MAILFILE, TRUE), ==, MU_FLAG_REPLIED | MU_FLAG_PASSED); g_assert_cmpuint (mu_flags_from_str ("Nz", MU_FLAG_TYPE_MAILFILE, TRUE), ==, MU_FLAG_NONE); /* ignore errors or not */ g_assert_cmpuint (mu_flags_from_str ("qwi", MU_FLAG_TYPE_MAILFILE, FALSE), ==, MU_FLAG_INVALID); g_assert_cmpuint (mu_flags_from_str ("qwi", MU_FLAG_TYPE_MAILFILE, TRUE), ==, 0); } static void test_mu_flags_from_str_delta (void) { g_assert_cmpuint (mu_flags_from_str_delta ("+S-R", MU_FLAG_REPLIED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_SEEN | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str_delta ("", MU_FLAG_REPLIED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_REPLIED | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str_delta ("-N+P+S-D", MU_FLAG_SIGNED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_PASSED | MU_FLAG_SEEN | MU_FLAG_SIGNED); } static void test_mu_flags_custom_from_str (void) { unsigned u; struct { const char *str; const char *expected; } cases[] = { { "ABC", "ABC" }, { "PAF", "A" }, { "ShelloPwoFrDldR123", "helloworld123" }, { "SPD", NULL } }; for (u = 0; u != G_N_ELEMENTS(cases); ++u) { char *cust; cust = mu_flags_custom_from_str (cases[u].str); if (g_test_verbose()) g_print ("%s: str:%s; got:%s; expected:%s\n", __FUNCTION__, cases[u].str, cust, cases[u].expected); g_assert_cmpstr (cust, ==, cases[u].expected); g_free (cust); } } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-flags/test-mu-flag-char", test_mu_flag_char); g_test_add_func ("/mu-flags/test-mu-flag-name",test_mu_flag_name); g_test_add_func ("/mu-flags/test-mu-flags-to-str-s",test_mu_flags_to_str_s); g_test_add_func ("/mu-flags/test-mu-flags-from-str",test_mu_flags_from_str); g_test_add_func ("/mu-flags/test-mu-flags-from-str-delta",test_mu_flags_from_str_delta ); g_test_add_func ("/mu-flags/test-mu-flags-custom-from-str", test_mu_flags_custom_from_str); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-maildir.c���������������������������������������������������������������0000664�0000000�0000000�00000040541�12504061031�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "test-mu-common.h" #include "mu-maildir.h" #include "mu-util.h" static void test_mu_maildir_mkdir_01 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), !=, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_02 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, TRUE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), ==, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_03 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); /* create part of the structure already... */ { gchar *dir; dir = g_strdup_printf ("%s%ccur", mdir, G_DIR_SEPARATOR); g_assert_cmpuint (g_mkdir_with_parents (dir, 0755), ==, 0); g_free (dir); } /* this should still work */ g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), !=, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_04 (void) { gchar *tmpdir, *mdir; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); /* create part of the structure already... */ { gchar *dir; g_assert_cmpuint (g_mkdir_with_parents (mdir, 0755), ==, 0); dir = g_strdup_printf ("%s%ccur", mdir, G_DIR_SEPARATOR); g_assert_cmpuint (g_mkdir_with_parents (dir, 0000), ==, 0); g_free (dir); } /* this should fail now, because cur is not read/writable */ g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, (geteuid()==0 ? TRUE : FALSE)); g_free (tmpdir); g_free (mdir); } static gboolean ignore_error (const char* log_domain, GLogLevelFlags log_level, const gchar* msg, gpointer user_data) { return FALSE; /* don't abort */ } static void test_mu_maildir_mkdir_05 (void) { /* this must fail */ g_test_log_set_fatal_handler ((GTestLogFatalFunc)ignore_error, NULL); g_assert_cmpuint (mu_maildir_mkdir (NULL, 0755, TRUE, NULL), ==, FALSE); } static gchar* copy_test_data (void) { gchar *dir, *cmd; dir = test_mu_common_get_random_tmpdir(); cmd = g_strdup_printf ("mkdir -m 0700 %s", dir); if (g_test_verbose()) g_print ("cmd: %s\n", cmd); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); cmd = g_strdup_printf ("cp -R %s %s", MU_TESTMAILDIR, dir); if (g_test_verbose()) g_print ("cmd: %s\n", cmd); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); return dir; } typedef struct { int _file_count; int _dir_entered; int _dir_left; } WalkData; static MuError dir_cb (const char *fullpath, gboolean enter, WalkData *data) { if (enter) ++data->_dir_entered; else ++data->_dir_left; if (g_test_verbose()) g_print ("%s: %s: %s (%u)\n", __FUNCTION__, enter ? "entering" : "leaving", fullpath, enter ? data->_dir_entered : data->_dir_left); return MU_OK; } static MuError msg_cb (const char *fullpath, const char* mdir, struct stat *statinfo, WalkData *data) { ++data->_file_count; return MU_OK; } static void test_mu_maildir_walk_01 (void) { char *tmpdir; WalkData data; MuError rv; tmpdir = copy_test_data (); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 18); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); g_free (tmpdir); } static void test_mu_maildir_walk (void) { char *tmpdir, *cmd, *dir; WalkData data; MuError rv; tmpdir = copy_test_data (); memset (&data, 0, sizeof(WalkData)); /* mark the 'new' dir with '.noindex', to ignore it */ dir = g_strdup_printf ("%s%ctestdir%cnew", tmpdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR); cmd = g_strdup_printf ("chmod 700 %s", dir); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); cmd = g_strdup_printf ("touch %s%c.noindex", dir, G_DIR_SEPARATOR); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); g_free (dir); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 14); g_assert_cmpuint (data._dir_entered,==, 4); g_assert_cmpuint (data._dir_left,==, 4); g_free (tmpdir); } static void test_mu_maildir_walk_with_noupdate (void) { char *tmpdir, *cmd, *dir; WalkData data; MuError rv; tmpdir = copy_test_data (); /* mark the 'new' dir with '.noindex', to ignore it */ dir = g_strdup_printf ("%s%ctestdir%cnew", tmpdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR); cmd = g_strdup_printf ("chmod 700 %s", dir); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, FALSE, /* ie., non-full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 18); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); /* again, full update. results should be the same, since there * is no noupdate yet */ memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, /* ie., full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 18); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); /* add a '.noupdate' file; this affects the outcome when the * 4th arg to mu_maildir_walk is FALSE */ cmd = g_strdup_printf ("touch %s%c.noupdate", dir, G_DIR_SEPARATOR); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, FALSE, /* non-full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 14); g_assert_cmpuint (data._dir_entered,==, 4); g_assert_cmpuint (data._dir_left,==, 4); /* now run again, but do a full update */ memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, /* full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 18); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); g_free (dir); g_free (tmpdir); } static void test_mu_maildir_get_flags_from_path (void) { int i; struct { const char *path; MuFlags flags; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FSR", MU_FLAG_REPLIED | MU_FLAG_SEEN | MU_FLAG_FLAGGED }, { "/home/foo/Maildir/test/new/123456", MU_FLAG_NEW }, { /* NOTE: when in new/, the :2,.. stuff is ignored */ "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_NEW }, { "/home/foo/Maildir/test/cur/123456:2,DTP", MU_FLAG_DRAFT | MU_FLAG_TRASHED | MU_FLAG_PASSED }, { "/home/foo/Maildir/test/cur/123456:2,S", MU_FLAG_SEEN } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { MuFlags flags; flags = mu_maildir_get_flags_from_path(paths[i].path); g_assert_cmpuint(flags, ==, paths[i].flags); } } static void assert_matches_regexp (const char *str, const char *rx) { if (!g_regex_match_simple (rx, str, 0, 0)) { if (g_test_verbose ()) g_print ("%s does not match %s", str, rx); g_assert (0); } } static void test_mu_maildir_get_new_path_new (void) { int i; struct { const char *oldpath; MuFlags flags; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/foo/Maildir/test/new/123456" }, { "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS" }, { "/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", MU_FLAG_SEEN, "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { char *str, *newbase; str = mu_maildir_get_new_path (paths[i].oldpath, NULL, paths[i].flags, TRUE); newbase = g_path_get_basename (str); assert_matches_regexp (newbase, "\\d{8}-[[:xdigit:]]{8}-[[:alpha:]]+(:2,.*)?"); g_free (newbase); g_free(str); } } static void test_mu_maildir_get_new_path_01 (void) { int i; struct { const char *oldpath; MuFlags flags; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/foo/Maildir/test/new/123456" }, { "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS" }, { "/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", MU_FLAG_SEEN, "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, NULL, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_new_path_02 (void) { int i; struct { const char *oldpath; MuFlags flags; const char *targetdir; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/bar/Maildir/coffee", "/home/bar/Maildir/coffee/new/123456" }, { "/home/foo/Maildir/test/new/123456", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/cuux/Maildir/tea", "/home/cuux/Maildir/tea/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/boy/Maildir/stuff", "/home/boy/Maildir/stuff/cur/1313038887_0.697:2,FPS" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, paths[i].targetdir, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_new_path_custom (void) { int i; struct { const char *oldpath; MuFlags flags; const char *targetdir; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,hFeRllo123", MU_FLAG_FLAGGED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,Fhello123" }, { "/home/foo/Maildir/test/cur/123456:2,abc", MU_FLAG_PASSED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,Pabc" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, paths[i].targetdir, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_maildir_from_path (void) { unsigned u; struct { const char *path, *exp; } cases[] = { {"/home/foo/Maildir/test/cur/123456:2,FR", "/home/foo/Maildir/test"}, {"/home/foo/Maildir/lala/new/1313038887_0.697:2,", "/home/foo/Maildir/lala"} }; for (u = 0; u != G_N_ELEMENTS(cases); ++u) { gchar *mdir; mdir = mu_maildir_get_maildir_from_path (cases[u].path); g_assert_cmpstr(mdir,==,cases[u].exp); g_free (mdir); } } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_util_maildir_mkmdir */ g_test_add_func ("/mu-maildir/mu-maildir-mkdir-01", test_mu_maildir_mkdir_01); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-02", test_mu_maildir_mkdir_02); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-03", test_mu_maildir_mkdir_03); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-04", test_mu_maildir_mkdir_04); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-05", test_mu_maildir_mkdir_05); /* mu_util_maildir_walk */ g_test_add_func ("/mu-maildir/mu-maildir-walk-01", test_mu_maildir_walk_01); g_test_add_func ("/mu-maildir/mu-maildir-walk", test_mu_maildir_walk); g_test_add_func ("/mu-maildir/mu-maildir-walk-with-noupdate", test_mu_maildir_walk_with_noupdate); /* get/set flags */ g_test_add_func("/mu-maildir/mu-maildir-get-new-path-new", test_mu_maildir_get_new_path_new); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-01", test_mu_maildir_get_new_path_01); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-02", test_mu_maildir_get_new_path_02); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-custom", test_mu_maildir_get_new_path_custom); g_test_add_func("/mu-maildir/mu-maildir-get-flags-from-path", test_mu_maildir_get_flags_from_path); g_test_add_func("/mu-maildir/mu-maildir-get-maildir-from-path", test_mu_maildir_get_maildir_from_path); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-msg-fields.c������������������������������������������������������������0000664�0000000�0000000�00000006655�12504061031�0017732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-msg-fields.h" static void test_mu_msg_field_body (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_BODY_TEXT; g_assert_cmpstr (mu_msg_field_name(field),==, "body"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'b'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'B'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, FALSE); } static void test_mu_msg_field_subject (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_SUBJECT; g_assert_cmpstr (mu_msg_field_name(field),==, "subject"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 's'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'S'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==,FALSE); } static void test_mu_msg_field_to (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_TO; g_assert_cmpstr (mu_msg_field_name(field),==, "to"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 't'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'T'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, FALSE); } static void test_mu_msg_field_prio (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_PRIO; g_assert_cmpstr (mu_msg_field_name(field),==, "prio"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'p'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'P'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, TRUE); } static void test_mu_msg_field_flags (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_FLAGS; g_assert_cmpstr (mu_msg_field_name(field),==, "flag"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'g'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'G'); g_assert_cmpuint (mu_msg_field_is_numeric(field),==, TRUE); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-msg-fields/mu-msg-field-body", test_mu_msg_field_body); g_test_add_func ("/mu-msg-fields/mu-msg-field-subject", test_mu_msg_field_subject); g_test_add_func ("/mu-msg-fields/mu-msg-field-to", test_mu_msg_field_to); g_test_add_func ("/mu-msg-fields/mu-msg-field-prio", test_mu_msg_field_prio); g_test_add_func ("/mu-msg-fields/mu-msg-field-flags", test_mu_msg_field_flags); /* FIXME: add tests for mu_msg_str_flags; but note the * function simply calls mu_msg_field_str */ g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } �����������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-msg.c�������������������������������������������������������������������0000664�0000000�0000000�00000032223�12504061031�0016454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-msg.h" #include "mu-str.h" static MuMsg* get_msg (const char *path) { GError *err; MuMsg *msg; if (g_test_verbose ()) g_print (">> %s\n", path); err = NULL; msg = mu_msg_new_from_file (path, NULL, &err); if (!msg) { g_printerr ("failed to load %s: %s\n", path, err ? err->message : "something went wrong"); g_clear_error (&err); g_assert (0); } return msg; } static gboolean check_contact_01 (MuMsgContact *contact, int *idx) { switch (*idx) { case 0: g_assert_cmpstr (mu_msg_contact_name (contact), ==, "Mickey Mouse"); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "anon@example.com"); break; case 1: g_assert_cmpstr (mu_msg_contact_name (contact), ==, "Donald Duck"); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "gcc-help@gcc.gnu.org"); break; default: g_assert_not_reached (); } ++(*idx); return TRUE; } static void test_mu_msg_01 (void) { MuMsg *msg; gint i; msg = get_msg (MU_TESTMAILDIR4 "/1220863042.12663_1.mindcrime!2,S"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Donald Duck <gcc-help@gcc.gnu.org>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "gcc include search order"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Mickey Mouse <anon@example.com>"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "3BE9E6535E3029448670913581E7A1A20D852173@" "emss35m06.us.lmco.com"); g_assert_cmpstr (mu_msg_get_header(msg, "Mailing-List"), ==, "contact gcc-help-help@gcc.gnu.org; run by ezmlm"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'klub' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1217530645); i = 0; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)check_contact_01, &i); g_assert_cmpint (i,==,2); mu_msg_unref (msg); } static gboolean check_contact_02 (MuMsgContact *contact, int *idx) { switch (*idx) { case 0: g_assert_cmpstr (mu_msg_contact_name (contact), ==, NULL); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "anon@example.com"); break; case 1: g_assert_cmpstr (mu_msg_contact_name (contact), ==, NULL); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "help-gnu-emacs@gnu.org"); break; default: g_assert_not_reached (); } ++(*idx); return TRUE; } static void test_mu_msg_02 (void) { MuMsg *msg; int i; msg = get_msg (MU_TESTMAILDIR4 "/1220863087.12663_19.mindcrime!2,S"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "help-gnu-emacs@gnu.org"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Re: Learning LISP; Scheme vs elisp."); g_assert_cmpstr (mu_msg_get_from(msg), ==, "anon@example.com"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "r6bpm5-6n6.ln1@news.ducksburg.com"); g_assert_cmpstr (mu_msg_get_header(msg, "Errors-To"), ==, "help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_LOW); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1218051515); i = 0; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)check_contact_02, &i); g_assert_cmpint (i,==,2); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_SEEN|MU_FLAG_LIST); mu_msg_unref (msg); } static void test_mu_msg_03 (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/1283599333.1840_11.cthulhu!2,"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Bilbo Baggins <bilbo@anotherexample.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Greetings from Lothlórien"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Frodo Baggins <frodo@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); g_assert_cmpstr (mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE), ==, "\nLet's write some fünkÿ text\nusing umlauts.\n\nFoo.\n"); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_UNREAD); /* not seen => unread */ mu_msg_unref (msg); } static void test_mu_msg_04 (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/mail5"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "George Custer <gac@example.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "pics for you"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Sitting Bull <sb@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_HAS_ATTACH|MU_FLAG_UNREAD); mu_msg_unref (msg); } static void test_mu_msg_multimime (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/multimime!2,FS"); /* ie., are text parts properly concatenated? */ g_assert_cmpstr (mu_msg_get_subject(msg), ==, "multimime"); g_assert_cmpstr (mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE), ==, "abcdef"); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_FLAGGED | MU_FLAG_SEEN | MU_FLAG_HAS_ATTACH); mu_msg_unref (msg); } static void test_mu_msg_flags (void) { unsigned u; struct { const char *path; MuFlags flags; } msgflags [] = { { MU_TESTMAILDIR4 "/multimime!2,FS", MU_FLAG_FLAGGED | MU_FLAG_SEEN | MU_FLAG_HAS_ATTACH }, { MU_TESTMAILDIR4 "/special!2,Sabc", MU_FLAG_SEEN } }; for (u = 0; u != G_N_ELEMENTS(msgflags); ++u) { MuMsg *msg; MuFlags flags; g_assert ((msg = get_msg (msgflags[u].path))); flags = mu_msg_get_flags (msg); if (g_test_verbose()) g_print ("=> %s [ %s, %u] <=> [ %s, %u]\n", msgflags[u].path, mu_flags_to_str_s(msgflags[u].flags, MU_FLAG_TYPE_ANY), (unsigned)msgflags[u].flags, mu_flags_to_str_s(flags, MU_FLAG_TYPE_ANY), (unsigned)flags); g_assert_cmpuint (flags ,==, msgflags[u].flags); mu_msg_unref (msg); } } static void test_mu_msg_umlaut (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/1305664394.2171_402.cthulhu!2,"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Helmut Kröger <hk@testmu.xxx>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Motörhead"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Mü <testmu@testmu.xx>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); mu_msg_unref (msg); } static void test_mu_msg_references (void) { MuMsg *msg; const GSList *refs; msg = get_msg (MU_TESTMAILDIR4 "/1305664394.2171_402.cthulhu!2,"); refs = mu_msg_get_references(msg); g_assert_cmpuint (g_slist_length ((GSList*)refs), ==, 4); g_assert_cmpstr ((char*)refs->data,==, "non-exist-01@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-02@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-03@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-04@msg.id"); refs = g_slist_next (refs); mu_msg_unref (msg); } static void test_mu_msg_references_dups (void) { MuMsg *msg; const GSList *refs; msg = get_msg (MU_TESTMAILDIR4 "/1252168370_3.14675.cthulhu!2,S"); refs = mu_msg_get_references(msg); /* make sure duplicate msg-ids are filtered out */ g_assert_cmpuint (g_slist_length ((GSList*)refs), ==, 6); g_assert_cmpstr ((char*)refs->data,==, "439C1136.90504@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "4399DD94.5070309@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "20051209233303.GA13812@gauss.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "439B41ED.2080402@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "439A1E03.3090604@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "20051211184308.GB13513@gauss.org"); refs = g_slist_next (refs); mu_msg_unref (msg); } static void test_mu_msg_references_many (void) { MuMsg *msg; unsigned u; const GSList *refs, *cur; const char* expt_refs[] = { "e9065dac-13c1-4103-9e31-6974ca232a89@t15g2000prt.googlegroups.com", "87hbblwelr.fsf@sapphire.mobileactivedefense.com", "pql248-4va.ln1@wilbur.25thandClement.com", "ikns6r$li3$1@Iltempo.Update.UU.SE", "8762s0jreh.fsf@sapphire.mobileactivedefense.com", "ikqqp1$jv0$1@Iltempo.Update.UU.SE", "87hbbjc5jt.fsf@sapphire.mobileactivedefense.com", "ikr0na$lru$1@Iltempo.Update.UU.SE", "tO8cp.1228$GE6.370@news.usenetserver.com", "ikr6ks$nlf$1@Iltempo.Update.UU.SE", "8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk" }; msg = get_msg (MU_TESTMAILDIR2 "/bar/cur/181736.eml"); refs = mu_msg_get_references(msg); g_assert_cmpuint (G_N_ELEMENTS(expt_refs), ==, g_slist_length((GSList*)refs)); for (cur = refs, u = 0; cur; cur = g_slist_next(cur), ++u) { if (g_test_verbose()) g_print ("%u. '%s' =? '%s'\n", u, (char*)cur->data, expt_refs[u]); g_assert_cmpstr ((char*)cur->data, ==, expt_refs[u]); } mu_msg_unref (msg); } static void test_mu_msg_tags (void) { MuMsg *msg; const GSList *tags; msg = get_msg (MU_TESTMAILDIR4 "/mail1"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Julius Caesar <jc@example.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Fere libenter homines id quod volunt credunt"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "John Milton <jm@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_HIGH); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1217530645); tags = mu_msg_get_tags (msg); g_assert_cmpstr ((char*)tags->data,==,"Paradise"); g_assert_cmpstr ((char*)tags->next->data,==,"losT"); g_assert_cmpstr ((char*)tags->next->next->data,==,"john"); g_assert_cmpstr ((char*)tags->next->next->next->data,==,"milton"); g_assert (!tags->next->next->next->next); mu_msg_unref (msg); } static void test_mu_msg_comp_unix_programmer (void) { MuMsg *msg; char *refs; msg = get_msg (MU_TESTMAILDIR4 "/181736.eml"); g_assert_cmpstr (mu_msg_get_to(msg), ==, NULL); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Re: Are writes \"atomic\" to readers of the file?"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Jimbo Foobarcuux <jimbo@slp53.sl.home>"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "oktdp.42997$Te.22361@news.usenetserver.com"); refs = mu_str_from_list (mu_msg_get_references(msg), ','); g_assert_cmpstr (refs, ==, "e9065dac-13c1-4103-9e31-6974ca232a89@t15g2000prt" ".googlegroups.com," "87hbblwelr.fsf@sapphire.mobileactivedefense.com," "pql248-4va.ln1@wilbur.25thandClement.com," "ikns6r$li3$1@Iltempo.Update.UU.SE," "8762s0jreh.fsf@sapphire.mobileactivedefense.com," "ikqqp1$jv0$1@Iltempo.Update.UU.SE," "87hbbjc5jt.fsf@sapphire.mobileactivedefense.com," "ikr0na$lru$1@Iltempo.Update.UU.SE," "tO8cp.1228$GE6.370@news.usenetserver.com," "ikr6ks$nlf$1@Iltempo.Update.UU.SE," "8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk"); g_free (refs); //"jimbo@slp53.sl.home (Jimbo Foobarcuux)"; g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1299603860); mu_msg_unref (msg); } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-msg/mu-msg-01", test_mu_msg_01); g_test_add_func ("/mu-msg/mu-msg-02", test_mu_msg_02); g_test_add_func ("/mu-msg/mu-msg-03", test_mu_msg_03); g_test_add_func ("/mu-msg/mu-msg-04", test_mu_msg_04); g_test_add_func ("/mu-msg/mu-msg-multimime", test_mu_msg_multimime); g_test_add_func ("/mu-msg/mu-msg-flags", test_mu_msg_flags); g_test_add_func ("/mu-msg/mu-msg-tags", test_mu_msg_tags); g_test_add_func ("/mu-msg/mu-msg-references", test_mu_msg_references); g_test_add_func ("/mu-msg/mu-msg-references_dups", test_mu_msg_references_dups); g_test_add_func ("/mu-msg/mu-msg-references_many", test_mu_msg_references_many); g_test_add_func ("/mu-msg/mu-msg-umlaut", test_mu_msg_umlaut); g_test_add_func ("/mu-msg/mu-msg-comp-unix-programmer", test_mu_msg_comp_unix_programmer); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-store.c�����������������������������������������������������������������0000664�0000000�0000000�00000012115�12504061031�0017020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-store.h" static void test_mu_store_new_destroy (void) { MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); err = NULL; store = mu_store_new_writable (tmpdir, NULL, FALSE, &err); g_assert_no_error (err); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); mu_store_flush (store); mu_store_unref (store); g_free (tmpdir); } static void test_mu_store_version (void) { MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); err = NULL; store = mu_store_new_writable (tmpdir, NULL, FALSE, &err); g_assert (store); mu_store_unref (store); store = mu_store_new_read_only (tmpdir, &err); g_assert (store); g_assert (err == NULL); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); g_assert_cmpstr (MU_STORE_SCHEMA_VERSION,==, mu_store_version(store)); mu_store_unref (store); g_free (tmpdir); } static void test_mu_store_store_msg_and_count (void) { MuMsg *msg; MuStore *store; gchar* tmpdir; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); store = mu_store_new_writable (tmpdir, NULL, FALSE, NULL); g_assert (store); g_free (tmpdir); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); /* add one */ msg = mu_msg_new_from_file ( MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (1,==,mu_store_count (store, NULL)); g_assert_cmpuint (TRUE,==,mu_store_contains_message (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL)); mu_msg_unref (msg); /* add another one */ msg = mu_msg_new_from_file (MU_TESTMAILDIR2 "/bar/cur/mail3", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (2,==,mu_store_count (store, NULL)); g_assert_cmpuint (TRUE,==, mu_store_contains_message (store, MU_TESTMAILDIR2 "/bar/cur/mail3", NULL)); mu_msg_unref (msg); /* try to add the first one again. count should be 2 still */ msg = mu_msg_new_from_file (MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (2,==,mu_store_count (store, NULL)); mu_msg_unref (msg); mu_store_unref (store); } static void test_mu_store_store_msg_remove_and_count (void) { MuMsg *msg; MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); store = mu_store_new_writable (tmpdir, NULL, FALSE, NULL); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); /* add one */ err = NULL; msg = mu_msg_new_from_file ( MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, &err); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (1,==,mu_store_count (store, NULL)); mu_msg_unref (msg); /* remove one */ mu_store_remove_path (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,"); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); g_assert_cmpuint (FALSE,==,mu_store_contains_message (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL)); g_free (tmpdir); mu_store_unref (store); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_runtime_init/uninit */ g_test_add_func ("/mu-store/mu-store-new-destroy", test_mu_store_new_destroy); g_test_add_func ("/mu-store/mu-store-version", test_mu_store_version); g_test_add_func ("/mu-store/mu-store-store-and-count", test_mu_store_store_msg_and_count); g_test_add_func ("/mu-store/mu-store-store-remove-and-count", test_mu_store_store_msg_remove_and_count); if (!g_test_verbose()) g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/test-mu-str.c�������������������������������������������������������������������0000664�0000000�0000000�00000030737�12504061031�0016506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-str.h" #include "mu-msg-prio.h" static void test_mu_str_size_01 (void) { struct lconv *lc; char *tmp2; lc = localeconv(); tmp2 = g_strdup_printf ("0%s0 kB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (0), ==, tmp2); g_free (tmp2); tmp2 = g_strdup_printf ("100%s0 kB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (100000), ==, tmp2); g_free (tmp2); tmp2 = g_strdup_printf ("1%s1 MB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (1100*1000), ==, tmp2); g_free (tmp2); } static void test_mu_str_size_02 (void) { struct lconv *lc; char *tmp1, *tmp2; lc = localeconv(); tmp2 = g_strdup_printf ("1%s0 MB", lc->decimal_point); tmp1 = mu_str_size (999999); g_assert_cmpstr (tmp1, !=, tmp2); g_free (tmp1); g_free (tmp2); } static void test_mu_str_prio_01 (void) { g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_LOW), ==, "low"); g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_NORMAL), ==, "normal"); g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_HIGH), ==, "high"); } static gboolean ignore_error (const char* log_domain, GLogLevelFlags log_level, const gchar* msg, gpointer user_data) { return FALSE; /* don't abort */ } static void test_mu_str_prio_02 (void) { /* this must fail */ g_test_log_set_fatal_handler ((GTestLogFatalFunc)ignore_error, NULL); g_assert_cmpstr (mu_msg_prio_name(666), ==, NULL); } static void test_mu_str_flatten (void) { int i; struct { const char* word; const char* norm; } words [] = { { "dantès", "dantes"}, { "foo", "foo" }, { "Föö", "foo" }, { "číslo", "cislo" }, { "hÆvý mëÐal ümláõt", "hævy_meðal_umlaot"} }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *str; str = mu_str_process_term (words[i].word); g_assert_cmpstr (str, ==, words[i].norm); g_free (str); } } static void test_parse_arglist (void) { const char *args; GHashTable *hash; GError *err; args = "cmd:find query:\"maildir:\\\"/sent items\\\"\" maxnum:500"; err = NULL; hash = mu_str_parse_arglist (args, &err); g_assert_no_error (err); g_assert (hash); g_assert_cmpstr (g_hash_table_lookup (hash, "cmd"), ==, "find"); g_assert_cmpstr (g_hash_table_lookup (hash, "query"), ==, "maildir:\"/sent items\""); g_assert_cmpstr (g_hash_table_lookup (hash, "maxnum"), ==, "500"); g_hash_table_destroy (hash); } static void test_mu_str_esc_to_list (void) { int i; struct { const char* str; const char* strs[3]; } strings [] = { { "maildir:foo", {"maildir:foo", NULL, NULL}}, { "maildir:sent items", {"maildir:sent", "items", NULL}}, { "\"maildir:sent items\"", {"maildir:sent items", NULL, NULL}}, }; for (i = 0; i != G_N_ELEMENTS(strings); ++i) { GSList *lst, *cur; unsigned u; lst = mu_str_esc_to_list (strings[i].str); for (cur = lst, u = 0; cur; cur = g_slist_next(cur), ++u) g_assert_cmpstr ((const char*)cur->data,==, strings[i].strs[u]); mu_str_free_list (lst); } } static void test_mu_str_process_query_term (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap_noot_mies" }, { "Foo..Bar", "foo__bar" }, { "Foo.Bar", "foo_bar" }, { "Foo Bar", "foo_bar" }, { "\\foo", "_foo" }, { "subject:test@foo", "subject:test_foo" }, { "xxx:test@bar", "xxx:test_bar" }, { "aa$bb$cc", "aa_bb_cc" }, { "date:2010..2012", "date:2010..2012"}, { "d:2010..2012", "d:2010..2012"}, { "size:10..20", "size:10..20"}, { "x:2010..2012", "x:2010__2012"}, { "q:2010..2012", "q:2010__2012"}, { "subject:2010..2012", "subject:2010__2012"}, { "(maildir:foo)", "(maildir:foo)"}, { "Тесла, Никола", "тесла__никола"}, { "Masha@Аркона.ru", "masha_аркона_ru" }, { "foo:ελληνικά", "foo:ελληνικα" }, { "日本語!!", "日本語__" }, { "£", "_" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_query_term (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_process_term (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap_noot_mies" }, { "A&B", "a_b" }, { "Foo..Bar", "foo__bar" }, { "Foo.Bar", "foo_bar" }, { "Foo Bar", "foo_bar" }, { "\\foo", "_foo" }, { "subject:test@foo", "subject_test_foo" }, { "xxx:test@bar", "xxx_test_bar" }, { "aa$bb$cc", "aa_bb_cc" }, { "date:2010..2012", "date_2010__2012"}, { "subject:2010..2012", "subject_2010__2012"}, { "(maildir:foo)", "_maildir_foo_"}, { "Тесла, Никола", "тесла__никола"}, { "Masha@Аркона.ru", "masha_аркона_ru" }, { "foo:ελληνικά", "foo_ελληνικα" }, { "日本語!!", "日本語__" }, { "£", "_" }, /* invalid utf8 */ { "Hello\xC3\x2EWorld", "hello__world" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_term (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_process_text (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap@noot.mies" }, { "A&B", "a&b" }, { "Foo..Bar", "foo..bar" }, { "Foo.Bar", "foo.bar" }, { "Foo Bar", "foo bar" }, { "\\foo", "\\foo" }, { "subject:test@foo", "subject:test@foo" }, { "xxx:test@bar", "xxx:test@bar" }, { "aa$bb$cc", "aa$bb$cc" }, { "date:2010..2012", "date:2010..2012"}, { "subject:2010..2012", "subject:2010..2012"}, { "(maildir:foo)", "(maildir:foo)"}, { "Тесла, Никола", "тесла, никола"}, { "Masha@Аркона.ru", "masha@аркона.ru" }, { "foo:ελληνικά", "foo:ελληνικα" }, { "日本語!!", "日本語!!" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_text (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_display_contact (void) { int i; struct { const char* word; const char* disp; } words [] = { { "\"Foo Bar\" <aap@noot.mies>", "Foo Bar"}, { "Foo Bar <aap@noot.mies>", "Foo Bar" }, { "<aap@noot.mies>", "aap@noot.mies" }, { "foo@bar.nl", "foo@bar.nl" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) g_assert_cmpstr (mu_str_display_contact_s (words[i].word), ==, words[i].disp); } static void assert_cmplst (GSList *lst, const char *items[]) { int i; if (!lst) g_assert (!items); for (i = 0; lst; lst = g_slist_next(lst), ++i) g_assert_cmpstr ((char*)lst->data,==,items[i]); g_assert (items[i] == NULL); } static GSList* create_list (const char *items[]) { GSList *lst; lst = NULL; while (items && *items) { lst = g_slist_prepend (lst, g_strdup(*items)); ++items; } return g_slist_reverse (lst); } static void test_mu_str_from_list (void) { { const char *strs[] = {"aap", "noot", "mies", NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst, ','); g_assert_cmpstr ("aap,noot,mies", ==, str); mu_str_free_list (lst); g_free (str); } { const char *strs[] = {"aap", "no,ot", "mies", NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst, ','); g_assert_cmpstr ("aap,no,ot,mies", ==, str); mu_str_free_list (lst); g_free (str); } { const char *strs[] = {NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst,'@'); g_assert_cmpstr (NULL, ==, str); mu_str_free_list (lst); g_free (str); } } static void test_mu_str_to_list (void) { { const char *items[]= {"foo", "bar ", "cuux", NULL}; GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', FALSE); assert_cmplst (lst, items); mu_str_free_list (lst); } { GSList *lst = mu_str_to_list (NULL,'x',FALSE); g_assert (lst == NULL); mu_str_free_list (lst); } } static void test_mu_str_to_list_strip (void) { const char *items[]= {"foo", "bar", "cuux", NULL}; GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', TRUE); assert_cmplst (lst, items); mu_str_free_list (lst); } static void test_mu_str_subject_normalize (void) { int i; struct { char *src, *exp; } tests[] = { { "test123", "test123" }, { "Re:test123", "test123" }, { "Re: Fwd: test123", "test123" }, { "Re[3]: Fwd: test123", "test123" }, { "operation: mindcrime", "operation: mindcrime" }, /*...*/ { "", "" } }; for (i = 0; i != G_N_ELEMENTS(tests); ++i) g_assert_cmpstr (mu_str_subject_normalize (tests[i].src), ==, tests[i].exp); } static void test_mu_term_fixups (void) { unsigned u; struct { const gchar *expr, *expected; } testcases [] = { { "date:19700101", "date:19700101..19700101" }, { "date:19700101..19700101", "date:19700101..19700101" }, { "(date:20121107))", "(date:20121107..20121107))" }, { "maildir:/somepath", "maildir:/somepath" }, { "([maildir:/somepath]", "([maildir:/somepath]" }, /* add more */ { "({", "({" }, { "({abc", "({abc" }, { "abc)}", "abc)}" }, { "", "" } }; for (u = 0; u != G_N_ELEMENTS(testcases); ++u) { gchar *prep; prep = mu_str_xapian_fixup_terms (testcases[u].expr); g_assert_cmpstr (prep, ==, testcases[u].expected); g_free (prep); } } static void test_mu_str_replace (void) { unsigned u; struct { const char* str; const char* sub; const char *repl; const char *exp; } strings [] = { { "hello", "ll", "xx", "hexxo" }, { "hello", "hello", "hi", "hi" }, { "hello", "foo", "bar", "hello" } }; for (u = 0; u != G_N_ELEMENTS(strings); ++u) { char *res; res = mu_str_replace (strings[u].str, strings[u].sub, strings[u].repl); g_assert_cmpstr (res,==,strings[u].exp); g_free (res); } } int main (int argc, char *argv[]) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); /* mu_str_size */ g_test_add_func ("/mu-str/mu-str-size-01", test_mu_str_size_01); g_test_add_func ("/mu-str/mu-str-size-02", test_mu_str_size_02); /* mu_str_prio */ g_test_add_func ("/mu-str/mu-str-prio-01", test_mu_str_prio_01); g_test_add_func ("/mu-str/mu-str-prio-02", test_mu_str_prio_02); g_test_add_func ("/mu-str/mu-str-flatten", test_mu_str_flatten); g_test_add_func ("/mu-str/process-query-term", test_mu_str_process_query_term); g_test_add_func ("/mu-str/process-term", test_mu_str_process_term); g_test_add_func ("/mu-str/process-text", test_mu_str_process_text); g_test_add_func ("/mu-str/mu-str-display_contact", test_mu_str_display_contact); g_test_add_func ("/mu-str/mu-str-from-list", test_mu_str_from_list); g_test_add_func ("/mu-str/mu-str-to-list", test_mu_str_to_list); g_test_add_func ("/mu-str/mu-str-to-list-strip", test_mu_str_to_list_strip); g_test_add_func ("/mu-str/mu-str-esc-to-list", test_parse_arglist); g_test_add_func ("/mu-str/mu-str-replace", test_mu_str_replace); g_test_add_func ("/mu-str/mu-str-esc-to-list", test_mu_str_esc_to_list); g_test_add_func ("/mu-str/mu_str_subject_normalize", test_mu_str_subject_normalize); /* mu_str_xapian_fixup_terms */ g_test_add_func ("/mu-str/mu_term_fixups", test_mu_term_fixups); /* FIXME: add tests for mu_str_flags; but note the * function simply calls mu_msg_field_str */ g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ���������������������������������mu-0.9.12/lib/tests/test-mu-util.c������������������������������������������������������������������0000664�0000000�0000000�00000014511�12504061031�0016643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include <stdlib.h> #include <unistd.h> #include <limits.h> #include "test-mu-common.h" #include "lib/mu-util.h" static void test_mu_util_dir_expand_00 (void) { #ifdef HAVE_WORDEXP_H gchar *got, *expected; got = mu_util_dir_expand ("~/IProbablyDoNotExist"); expected = g_strdup_printf ("%s%cIProbablyDoNotExist", getenv("HOME"), G_DIR_SEPARATOR); g_assert_cmpstr (got,==,expected); g_free (got); g_free (expected); #endif /*HAVE_WORDEXP_H*/ } static void test_mu_util_dir_expand_01 (void) { /* XXXX: the testcase does not work when using some dir * setups; (see issue #585), although the code should still * work. Turn of the test for now */ return; #ifdef HAVE_WORDEXP_H { gchar *got, *expected; got = mu_util_dir_expand ("~/Desktop"); expected = g_strdup_printf ("%s%cDesktop", getenv("HOME"), G_DIR_SEPARATOR); g_assert_cmpstr (got,==,expected); g_free (got); g_free (expected); } #endif /*HAVE_WORDEXP_H*/ } static void test_mu_util_guess_maildir_01 (void) { char *got; const char *expected; /* skip the test if there's no /tmp */ if (access ("/tmp", F_OK)) return; g_setenv ("MAILDIR", "/tmp", TRUE); got = mu_util_guess_maildir (); expected = "/tmp"; g_assert_cmpstr (got,==,expected); g_free (got); } static void test_mu_util_guess_maildir_02 (void) { char *got, *mdir; g_unsetenv ("MAILDIR"); mdir = g_strdup_printf ("%s%cMaildir", getenv("HOME"), G_DIR_SEPARATOR); got = mu_util_guess_maildir (); if (access (mdir, F_OK) == 0) g_assert_cmpstr (got, ==, mdir); else g_assert_cmpstr (got, == , NULL); g_free (got); g_free (mdir); } static void test_mu_util_check_dir_01 (void) { if (g_access ("/usr/bin", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir ("/usr/bin", TRUE, FALSE) == TRUE, ==, g_access ("/usr/bin", R_OK) == 0); } } static void test_mu_util_check_dir_02 (void) { if (g_access ("/tmp", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir ("/tmp", FALSE, TRUE) == TRUE, ==, g_access ("/tmp", W_OK) == 0); } } static void test_mu_util_check_dir_03 (void) { if (g_access (".", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir (".", TRUE, TRUE) == TRUE, ==, g_access (".", W_OK | R_OK) == 0); } } static void test_mu_util_check_dir_04 (void) { /* not a dir, so it must be false */ g_assert_cmpuint ( mu_util_check_dir ("test-util.c", TRUE, TRUE), ==, FALSE); } static void test_mu_util_str_from_strv_01 (void) { const gchar *strv[] = { "aap", "noot", "mies", NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, "aap noot mies"); g_free (str); } static void test_mu_util_str_from_strv_02 (void) { const gchar *strv[] = { "test", NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, "test"); g_free (str); } static void test_mu_util_str_from_strv_03 (void) { const gchar *strv[] = { NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, ""); g_free (str); } static void test_mu_util_get_dtype_with_lstat (void) { g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR), ==, DT_DIR); g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR2), ==, DT_DIR); g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR2 "/Foo/cur/mail5"), ==, DT_REG); } static void test_mu_util_supports (void) { gboolean has_guile; gchar *path; has_guile = FALSE; #ifdef BUILD_GUILE has_guile = TRUE; #endif /*BUILD_GUILE*/ g_assert_cmpuint (mu_util_supports (MU_FEATURE_GUILE), == ,has_guile); g_assert_cmpuint (mu_util_supports (MU_FEATURE_CRYPTO), == ,TRUE); path = g_find_program_in_path ("gnuplot"); g_free (path); g_assert_cmpuint (mu_util_supports (MU_FEATURE_GNUPLOT),==,path ? TRUE : FALSE); g_assert_cmpuint ( mu_util_supports (MU_FEATURE_GNUPLOT|MU_FEATURE_GUILE|MU_FEATURE_CRYPTO), ==, has_guile && path ? TRUE : FALSE); } static void test_mu_util_program_in_path (void) { g_assert_cmpuint (mu_util_program_in_path("ls"),==,TRUE); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_util_dir_expand */ g_test_add_func ("/mu-util/mu-util-dir-expand-00", test_mu_util_dir_expand_00); g_test_add_func ("/mu-util/mu-util-dir-expand-01", test_mu_util_dir_expand_01); /* mu_util_guess_maildir */ g_test_add_func ("/mu-util/mu-util-guess-maildir-01", test_mu_util_guess_maildir_01); g_test_add_func ("/mu-util/mu-util-guess-maildir-02", test_mu_util_guess_maildir_02); /* mu_util_check_dir */ g_test_add_func ("/mu-util/mu-util-check-dir-01", test_mu_util_check_dir_01); g_test_add_func ("/mu-util/mu-util-check-dir-02", test_mu_util_check_dir_02); g_test_add_func ("/mu-util/mu-util-check-dir-03", test_mu_util_check_dir_03); g_test_add_func ("/mu-util/mu-util-check-dir-04", test_mu_util_check_dir_04); /* test_mu_util_str_from_strv */ g_test_add_func ("/mu-util/mu-util-str-from-strv-01", test_mu_util_str_from_strv_01); g_test_add_func ("/mu-util/mu-util-str-from-strv-02", test_mu_util_str_from_strv_02); g_test_add_func ("/mu-util/mu-util-str-from-strv-03", test_mu_util_str_from_strv_03); g_test_add_func ("/mu-util/mu-util-get-dtype-with-lstat", test_mu_util_get_dtype_with_lstat); g_test_add_func ("/mu-util/mu-util-supports", test_mu_util_supports); g_test_add_func ("/mu-util/mu-util-program-in-path", test_mu_util_program_in_path); g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG| G_LOG_LEVEL_MESSAGE| G_LOG_LEVEL_INFO, (GLogFunc)black_hole, NULL); return g_test_run (); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0015602�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0016373�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863042.12663_1.mindcrime!2,S������������������������������������0000664�0000000�0000000�00000014325�12504061031�0022566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-4.9 required=3.0 tests=BAYES_00,DATE_IN_PAST_96_XX, RCVD_IN_DNSWL_MED autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 5123469CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:19 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:19 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs39272wfh; Wed, 6 Aug 2008 20:15:17 -0700 (PDT) Received: by 10.65.133.8 with SMTP id k8mr2071878qbn.7.1218078916289; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received: from sourceware.org (sourceware.org [209.132.176.174]) by mx.google.com with SMTP id 28si7904461qbw.0.2008.08.06.20.15.15; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received-SPF: neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) client-ip=209.132.176.174; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) smtp.mail=gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org Received: (qmail 13493 invoked by alias); 7 Aug 2008 03:15:13 -0000 Received: (qmail 13485 invoked by uid 22791); 7 Aug 2008 03:15:12 -0000 Received: from mailgw1a.lmco.com (HELO mailgw1a.lmco.com) (192.31.106.7) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 07 Aug 2008 03:14:27 +0000 Received: from emss07g01.ems.lmco.com (relay5.ems.lmco.com [166.29.2.16])by mailgw1a.lmco.com (LM-6) with ESMTP id m773EPZH014730for <gcc-help@gcc.gnu.org>; Wed, 6 Aug 2008 21:14:25 -0600 (MDT) Received: from CONVERSION2-DAEMON.lmco.com by lmco.com (PMDF V6.3-x14 #31428) id <0K5700601NO18J@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:25 -0600 (MDT) Received: from EMSS04I00.us.lmco.com ([166.17.13.135]) by lmco.com (PMDF V6.3-x14 #31428) with ESMTP id <0K5700H5MNNWGX@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:20 -0600 (MDT) Received: from EMSS35M06.us.lmco.com ([158.187.107.143]) by EMSS04I00.us.lmco.com with Microsoft SMTPSVC(5.0.2195.6713); Wed, 06 Aug 2008 23:14:20 -0400 Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Mickey Mouse" <anon@example.com> Subject: gcc include search order To: "Donald Duck" <gcc-help@gcc.gnu.org> Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Content-class: urn:content-classes:message Mailing-List: contact gcc-help-help@gcc.gnu.org; run by ezmlm Precedence: klub List-Id: <gcc-help.gcc.gnu.org> List-Unsubscribe: <mailto:gcc-help-unsubscribe-xxxx.klub=gmail.com@gcc.gnu.org> List-Archive: <http://gcc.gnu.org/ml/gcc-help/> List-Post: <mailto:gcc-help@gcc.gnu.org> List-Help: <mailto:gcc-help-help@gcc.gnu.org> Sender: gcc-help-owner@gcc.gnu.org Delivered-To: mailing list gcc-help@gcc.gnu.org Content-Length: 3024 Hi. In my unit testing I need to change some header files (target is vxWorks, which supports some things that the sun does not). So, what I do is fetch the development tree, and then in a new unit test directory I attempt to compile the unit under test. Since this is NOT vxworks, I use sed to change some of the .h files and put them in a ./changed directory. When I try to compile the file, it is still using the .h file from the original location, even though I have listed the include path for ./changed before the include path for the development tree. Here is a partial output from gcc using the -v option GNU CPP version 3.1 (cpplib) (sparc ELF) GNU C++ version 3.1 (sparc-sun-solaris2.8) compiled by GNU C version 3.1. ignoring nonexistent directory "NONE/include" #include "..." search starts here: #include <...> search starts here: . changed /export/home4/xxx/yyyy/builds/int_rel5_latest/src/mp/interface /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/interface /usr/local/include/g++-v3 /usr/local/include/g++-v3/sparc-sun-solaris2.8 /usr/local/include/g++-v3/backward /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.8/3.1/include /usr/local/sparc-sun-solaris2.8/include /usr/include End of search list. I know the changed file is correct and that the include is not working as expected, because when I copy the file from ./changed, back into the development tree, the compilation works as expected. One more bit of information. The source that I cam compiling is in /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app And it is including files from /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common These include files should be including the files from ./changed (when they exist) but they are ignoring the .h files in the ./changed directory and are instead using other, unchanged files in the /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common directory. The gcc command line is something like TEST_DIR="." CHANGED_DIR_NAME=changed CHANGED_FILES_DIR=${TEST_DIR}/${CHANGED_DIR_NAME} CICU_HEADER_FILES="-I ${AP_INTERFACE_FILES} -I ${AP_APP_FILES} -I ${SHARED_COMMON_FILES} -I ${SHARED_INTERFACE_FILES}" HEADERS="-I ./ -I ${CHANGED_FILES_DIR} ${CICU_HEADER_FILES}" DEFINES="-DSUNRUN -DA10_DEBUG -DJOETEST" CFLAGS="-v -c -g -O1 -pipe -Wformat -Wunused -Wuninitialized -Wshadow -Wmissing-prototypes -Wmissing-declarations" printf "Compiling the UUT File\n" gcc -fprofile-arcs -ftest-coverage ${CFLAGS} ${HEADERS} ${DEFINES} ${AP_APP_FILES}/unitUnderTest.cpp I hope this explanation is clear. If anyone knows how to fix the command line so that it gets the .h files in the "changed" directory are used instead of files in the other include directories. Thanks Joe ---------------------------------------------------- Time Flies like an Arrow. Fruit Flies like a Banana �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863060.12663_3.mindcrime!2,S������������������������������������0000664�0000000�0000000�00000027073�12504061031�0022574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <sqlite-dev-bounces@sqlite.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00,HTML_MESSAGE autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D724F6963B for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:27 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Mon, 04 Aug 2008 21:49:27 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs86537wfy; Mon, 4 Aug 2008 00:38:51 -0700 (PDT) Received: by 10.151.113.5 with SMTP id q5mr272266ybm.37.1217835529913; Mon, 04 Aug 2008 00:38:49 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 5si5754915ywd.8.2008.08.04.00.38.30; Mon, 04 Aug 2008 00:38:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 765A511C46; Mon, 4 Aug 2008 03:38:27 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from ik-out-1112.google.com (ik-out-1112.google.com [66.249.90.176]) by sqlite.org (Postfix) with ESMTP id 4C59511C41 for <sqlite-dev@sqlite.org>; Mon, 4 Aug 2008 03:38:23 -0400 (EDT) Received: by ik-out-1112.google.com with SMTP id b32so2163423ika.0 for <sqlite-dev@sqlite.org>; Mon, 04 Aug 2008 00:38:23 -0700 (PDT) Received: by 10.210.54.19 with SMTP id c19mr14589042eba.107.1217835502549; Mon, 04 Aug 2008 00:38:22 -0700 (PDT) Received: by 10.210.115.10 with HTTP; Mon, 4 Aug 2008 00:38:22 -0700 (PDT) Message-ID: <477821040808040038s381bf382p7411451e3c1a2e4e@mail.gmail.com> Date: Mon, 4 Aug 2008 10:38:22 +0300 From: anon@example.com To: sqlite-dev@sqlite.org In-Reply-To: <73d4fc50808030747g303a170ieac567723c2d4f24@mail.gmail.com> MIME-Version: 1.0 References: <477821040808030533y41f1501dq32447b568b6e6ca5@mail.gmail.com> <73d4fc50808030747g303a170ieac567723c2d4f24@mail.gmail.com> Subject: Re: [sqlite-dev] SQLite exception A&B X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Priority: normal Reply-To: sqlite-dev@sqlite.org List-Id: <sqlite-dev.sqlite.org> List-Unsubscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=unsubscribe> List-Archive: <http://sqlite.org:8080/cgi-bin/mailman/private/sqlite-dev> List-Post: <mailto:sqlite-dev@sqlite.org> List-Help: <mailto:sqlite-dev-request@sqlite.org?subject=help> List-Subscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=subscribe> Content-Type: multipart/mixed; boundary="===============2123623832==" Mime-version: 1.0 Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 8475 --===============2123623832== Content-Type: multipart/alternative; boundary="----=_Part_29556_25702991.1217835502493" ------=_Part_29556_25702991.1217835502493 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline Hi Grant, Thanks for your reply. I am using a different session for each thread, whenever a thread wishes to access the database it gets a session from the session pool and works with that session until its work is done. Most of the actions the threads are doing on the database are quite complicated and are required to be fully committed or completely ignored, so yes, I am (most of the time) explicitly beginning and committing my transactions. Regarding the SQLiteStatementImpl, I believe the Poco manual explains that sessions and statements for that matter cannot be shared between threads, therefore if you are using a session via one thread only it should work fine. My first impression was that the problem was in the Poco infrastructure (I have found several Poco related bugs in the past), but the problem ALWAYS occurs when I perform the "BEGIN IMMEDIATE" action, if it were a Poco related bug, I would expect to see it here and there without any relation to this specific statement, but that is not the case. None the less, I will also post my question on the Poco forums. Nadav. On Sun, Aug 3, 2008 at 5:47 PM, Grant Gatchel <grant.gatchel@gmail.com>wrote: > Are you using the same Poco::Session for every thread or does each call > create a new session/handle to the database? > > Are you explicitly BEGINning and COMMITting your transactions? > > In looking at the 1.3.2 branch of Poco::Data::SQLite, there appears to be a > race condition in the SQLiteStatementImpl::next() method in which the member > _nextResponse is being accessed before the SQLiteStatementImpl::hasNext() > method has a chance to interpret that value and throw an exception. > > This question might be more suitable in the Poco forums or mailinglist. > > - Grant > > On Sun, Aug 3, 2008 at 8:33 AM, nadav g <nadav.gr@gmail.com> wrote: > >> Hi All, >> >> I have been using SQLite with Poco (www.appinf.com) as my infrastructure. >> The program is running several threads that access this database very >> often and are synchronized by SQLite itself. >> Everything seems to work just fine most of time (usually days - weeks) but >> I do get an occasional exception: >> >> Exception: SQL error or missing database: Iterator Error: trying to check >> if there is a next value >> >> The backtrace leads to this statement: >> *"BEGIN IMMEDIATE"* >> >> This specific code runs numerous times before an exception occurs (if >> occurs at all) and I cannot think of any reason for it to fail later rather >> than sooner. >> It is pretty obvious that this situation occurs due to some rare thread >> state, but I could not find any information that gives me any hint as to >> what this state might be. >> >> So what I am asking is: >> 1) Does anyone know why this sort of exception occurs? >> 2) Can anyone think of a reason for such an exception to occur in the >> situation I have described? >> >> Thanks in advance, >> Nadav. >> >> >> _______________________________________________ >> sqlite-dev mailing list >> sqlite-dev@sqlite.org >> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev >> >> > > _______________________________________________ > sqlite-dev mailing list > sqlite-dev@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev > > ------=_Part_29556_25702991.1217835502493 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline <div dir="ltr">Hi Grant,<br><br>Thanks for your reply.<br>I am using a different session for each thread, whenever a thread wishes to access the database it gets a session from the session pool and works with that session until its work is done.<br> <br>Most of the actions the threads are doing on the database are quite complicated and are required to be fully committed or completely ignored, so yes, I am (most of the time) explicitly beginning and committing my transactions.<br> <br>Regarding the SQLiteStatementImpl, I believe the Poco manual explains that sessions and statements for that matter cannot be shared between threads, therefore if you are using a session via one thread only it should work fine.<br> <br>My first impression was that the problem was in the Poco infrastructure (I have found several Poco related bugs in the past), but the problem ALWAYS occurs when I perform the "BEGIN IMMEDIATE" action, if it were a Poco related bug, I would expect to see it here and there without any relation to this specific statement, but that is not the case.<br> <br>None the less, I will also post my question on the Poco forums.<br><br>Nadav.<br><br><div class="gmail_quote">On Sun, Aug 3, 2008 at 5:47 PM, Grant Gatchel <span dir="ltr"><<a href="mailto:grant.gatchel@gmail.com">grant.gatchel@gmail.com</a>></span> wrote:<br> <blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div dir="ltr">Are you using the same Poco::Session for every thread or does each call create a new session/handle to the database?<br> <br>Are you explicitly BEGINning and COMMITting your transactions?<br><br>In looking at the 1.3.2 branch of Poco::Data::SQLite, there appears to be a race condition in the SQLiteStatementImpl::next() method in which the member _nextResponse is being accessed before the SQLiteStatementImpl::hasNext() method has a chance to interpret that value and throw an exception.<br> <br>This question might be more suitable in the Poco forums or mailinglist.<br><br>- Grant<br> <br><div class="gmail_quote"><div><div></div><div class="Wj3C7c"> On Sun, Aug 3, 2008 at 8:33 AM, nadav g <span dir="ltr"><<a href="http://nadav.gr" target="_blank">nadav.gr</a>@<a href="http://gmail.com" target="_blank">gmail.com</a>></span> wrote:<br></div></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"> <div><div></div><div class="Wj3C7c"> <div dir="ltr">Hi All,<br><br>I have been using SQLite with Poco (<a href="http://www.appinf.com" target="_blank">www.appinf.com</a>) as my infrastructure.<br>The program is running several threads that access this database very often and are synchronized by SQLite itself.<br> Everything seems to work just fine most of time (usually days - weeks) but I do get an occasional exception:<br><br>Exception: SQL error or missing database: Iterator Error: trying to check if there is a next value<br><br> The backtrace leads to this statement:<br><b>"BEGIN IMMEDIATE"</b><br><br>This specific code runs numerous times before an exception occurs (if occurs at all) and I cannot think of any reason for it to fail later rather than sooner.<br> It is pretty obvious that this situation occurs due to some rare thread state, but I could not find any information that gives me any hint as to what this state might be.<br><br>So what I am asking is:<br>1) Does anyone know why this sort of exception occurs?<br> 2) Can anyone think of a reason for such an exception to occur in the situation I have described?<br><br>Thanks in advance,<br>Nadav.<br><br></div> <br></div></div>_______________________________________________<br> sqlite-dev mailing list<br> <a href="mailto:sqlite-dev@sqlite.org" target="_blank">sqlite-dev@sqlite.org</a><br> <a href="http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev" target="_blank">http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev</a><br> <br></blockquote></div><br></div> <br>_______________________________________________<br> sqlite-dev mailing list<br> <a href="mailto:sqlite-dev@sqlite.org">sqlite-dev@sqlite.org</a><br> <a href="http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev" target="_blank">http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev</a><br> <br></blockquote></div><br></div> ------=_Part_29556_25702991.1217835502493-- --===============2123623832== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --===============2123623832==-- ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863087.12663_15.mindcrime!2,PS����������������������������������0000664�0000000�0000000�00000014643�12504061031�0023007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS,WHOIS_NETSOLPR autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 1A6CD69CB6 for <xxxx@localhost>; Tue, 12 Aug 2008 21:42:38 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Tue, 12 Aug 2008 21:42:38 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs123119wfh; Sun, 10 Aug 2008 22:06:31 -0700 (PDT) Received: by 10.100.166.10 with SMTP id o10mr9327844ane.0.1218431190107; Sun, 10 Aug 2008 22:06:30 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id c29si10110392anc.13.2008.08.10.22.06.29; Sun, 10 Aug 2008 22:06:30 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:45637 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSPbx-0006dj-96 for xxxx.klub@gmail.com; Mon, 11 Aug 2008 01:06:29 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KSPbE-0006cQ-Nd for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:44 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KSPbD-0006bs-Px for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:44 -0400 Received: from [199.232.76.173] (port=37426 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSPbD-0006bk-HT for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:43 -0400 Received: from main.gmane.org ([80.91.229.2]:46446 helo=ciao.gmane.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from <geh-help-gnu-emacs@m.gmane.org>) id 1KSPbD-0003Kl-CA for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:43 -0400 Received: from list by ciao.gmane.org with local (Exim 4.43) id 1KSPb9-00080r-CX for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 05:05:39 +0000 Received: from bas2-toronto63-1088792724.dsl.bell.ca ([64.229.168.148]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for <help-gnu-emacs@gnu.org>; Mon, 11 Aug 2008 05:05:39 +0000 Received: from cpchan by bas2-toronto63-1088792724.dsl.bell.ca with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for <help-gnu-emacs@gnu.org>; Mon, 11 Aug 2008 05:05:39 +0000 X-Injected-Via-Gmane: http://gmane.org/ To: help-gnu-emacs@gnu.org From: anon@example.com Date: Mon, 11 Aug 2008 01:03:22 -0400 Organization: Linux Private Site Message-ID: <87bq00nnxh.fsf@MagnumOpus.Mercurius> References: <877iav5s49.fsf@163.com> <86hc9yc5sj.fsf@timbral.net> <877iat7udd.fsf@163.com> <87fxphcsxi.fsf@lion.rapttech.com.au> <8504ddd4-5e3b-4ed5-bf77-aa9cce81b59a@1g2000pre.googlegroups.com> <87k5es59we.fsf@lion.rapttech.com.au> <63c824e3-62b1-4a93-8fa8-2813e1f9397f@v13g2000pro.googlegroups.com> <874p5vsgg8.fsf@nonospaz.fatphil.org> <8250972e-1886-4021-80bc-376e34881c80@v39g2000pro.googlegroups.com> <87zlnnqvvs.fsf@nonospaz.fatphil.org> <57add0e0-b39d-4c71-8d2c-d3b9ddfaa1a9@1g2000pre.googlegroups.com> <87sktfnz5p.fsf@atthis.clsnet.nl> <562e1111-d9e7-4b6a-b661-3f9af13fea17@b30g2000prf.googlegroups.com> <87d4khoq97.fsf@atthis.clsnet.nl> <0fe404c5-cab8-4692-8a27-532e737a7813@i24g2000prf.googlegroups.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" X-Complaints-To: usenet@ger.gmane.org X-Gmane-NNTP-Posting-Host: bas2-toronto63-1088792724.dsl.bell.ca X-Face: G; Z,`sm>)4t4LB/GUrgH$W`!AmfHMj,LG)Z}X0ax@s9:0>0)B&@vcm{v-le)wng)?|o]D<V6&ay<F=H{M5?$T%p!dPdJeF,au\E@TA"v22K!Zl\\mzpU4]6$ZnAI3_L)h; fpd}mn2py/7gv^|*85-D_f:07cT>\Z}0:6X User-Agent: Gnus/5.110011 (No Gnus v0.11) Emacs/23.0.60 (gnu/linux) Cancel-Lock: sha1:IKyfrl5drOw6HllHFSmWHAKEeC8= X-detected-kernel: by monty-python.gnu.org: Linux 2.6, seldom 2.4 (older, 4) Subject: Re: Can anybody tell me how to send HTML-format mail in gnus X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 1229 Lines: 36 --=-=-= Content-Type: text/plain Xah <xahlee@gmail.com> writes: > So, i was reading about it in Wikipedia. Although i don't have a TV, > and haven't had since 2000, but i still enjoyed the festive spirits > anyhow. After all, i'm Chinese by blood. So, in my wandering, i ran > into this welcome song on youtube: > > http://www.youtube.com/watch?v=1HEndNYVhZo What is your point? Your email is in plain text and I can click on the link just fine- it is not exactly rocket science to implement parsing of URL's to workable links in an Email program (a lot of programs does that, including Gnus). Images can be included inline if you want. Also mail markups such as *this*, **this** and _this_ have been around since the Usenet days and displayed appropriately by a number of mailers. Like others have said, most html messages that I have seen either contains useless information, or are plain spam and can introduce a host of security problems in some mailers. Charles --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.4-svn0 (GNU/Linux) iD8DBQFIn8gm3epPyyKbwPYRApbvAKDRirXwzMzI+NHV77+QcP3EgTPaCgCfb/6m GtNVKdYAeftaYm1nwRVoCDA= =ULo3 -----END PGP SIGNATURE----- --=-=-=-- ���������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863087.12663_19.mindcrime!2,S�����������������������������������0000664�0000000�0000000�00000007136�12504061031�0022672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C4D6569CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:08 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:08 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34794wfh; Wed, 6 Aug 2008 13:40:29 -0700 (PDT) Received: by 10.100.33.13 with SMTP id g13mr1093301ang.79.1218055228418; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d19si15908789and.17.2008.08.06.13.40.27; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:56316 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpo3-0007Pc-Qk for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:40:27 -0400 From: anon@example.com Newsgroups: gnu.emacs.help Date: Wed, 6 Aug 2008 20:38:35 +0100 Message-ID: <r6bpm5-6n6.ln1@news.ducksburg.com> References: <55dbm5-qcl.ln1@news.ducksburg.com> <mailman.15710.1217599959.18990.help-gnu-emacs@gnu.org> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Trace: individual.net bABVU1hcJwWAuRwe/097AAoOXnGGeYR8G1In635iFGIyfDLPUv X-Orig-Path: news.ducksburg.com!news Cancel-Lock: sha1:wK7dsPRpNiVxpL/SfvmNzlvUR94= sha1:oepBoM0tJBLN52DotWmBBvW5wbg= User-Agent: slrn/pre0.9.9-120/mm/ao (Ubuntu Hardy) Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!feeder.erje.net!proxad.net!feeder1-2.proxad.net!feed.ac-versailles.fr!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Xref: news.stanford.edu gnu.emacs.help:160868 To: help-gnu-emacs@gnu.org Subject: Re: Learning LISP; Scheme vs elisp. X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 417 Lines: 11 On 2008-08-01, Thien-Thi Nguyen wrote: > warriors attack, felling foe after foe, > few growing old til they realize: to know > what deceit is worth deflection; > such receipt reversed rejection! > then their heavy arms, e'er transformed to shields: > balanced hooked charms, ploughed deep, rich yields. Aha: the exercise for the reader is to place the parens correctly. Might take me a while to solve this puzzle. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863087.12663_5.mindcrime!2,S������������������������������������0000664�0000000�0000000�00000007165�12504061031�0022607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <sqlite-dev-bounces@sqlite.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 32F276963F for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:34 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Mon, 04 Aug 2008 21:49:34 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs89397wfy; Mon, 4 Aug 2008 02:41:16 -0700 (PDT) Received: by 10.150.156.20 with SMTP id d20mr963580ybe.104.1217842875596; Mon, 04 Aug 2008 02:41:15 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 6si3605185ywi.1.2008.08.04.02.40.57; Mon, 04 Aug 2008 02:41:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 7147F11C45; Mon, 4 Aug 2008 05:40:55 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from relay00.pair.com (relay00.pair.com [209.68.5.9]) by sqlite.org (Postfix) with SMTP id B5F901192C for <sqlite-dev@sqlite.org>; Mon, 4 Aug 2008 05:40:52 -0400 (EDT) Received: (qmail 59961 invoked from network); 4 Aug 2008 09:40:50 -0000 Received: from unknown (HELO ?192.168.0.17?) (unknown) by unknown with SMTP; 4 Aug 2008 09:40:50 -0000 X-pair-Authenticated: 87.13.75.164 Message-Id: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> From: anon@example.com To: sqlite-dev@sqlite.org Mime-Version: 1.0 (Apple Message framework v926) Date: Mon, 4 Aug 2008 11:40:49 +0200 X-Mailer: Apple Mail (2.926) Subject: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: sqlite-dev@sqlite.org List-Id: <sqlite-dev.sqlite.org> List-Unsubscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=unsubscribe> List-Archive: <http://sqlite.org:8080/cgi-bin/mailman/private/sqlite-dev> List-Post: <mailto:sqlite-dev@sqlite.org> List-Help: <mailto:sqlite-dev-request@sqlite.org?subject=help> List-Subscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=subscribe> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 639 Inside sqlite3VdbeExec there is a very big switch statement. In order to increase performance with few modifications to the original code, why not use this technique ? http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html With a properly defined "instructions" array, instead of the switch statement you can use something like: goto * instructions[pOp->opcode]; --- Marco Bambini http://www.sqlabs.net http://www.sqlabs.net/blog/ http://www.sqlabs.net/realsqlserver/ _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1220863087.12663_7.mindcrime!2,RS�����������������������������������0000664�0000000�0000000�00000012626�12504061031�0022731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <sqlite-dev-bounces@sqlite.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 3EBAB6963B for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:35 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Mon, 04 Aug 2008 21:49:35 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs89536wfy; Mon, 4 Aug 2008 02:48:56 -0700 (PDT) Received: by 10.150.134.21 with SMTP id h21mr7950048ybd.181.1217843335665; Mon, 04 Aug 2008 02:48:55 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 6si5897081ywi.1.2008.08.04.02.48.35; Mon, 04 Aug 2008 02:48:55 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id ED01611C4E; Mon, 4 Aug 2008 05:48:31 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from mx0.security.ro (mx0.security.ro [80.96.72.194]) by sqlite.org (Postfix) with ESMTP id EB3F51192C for <sqlite-dev@sqlite.org>; Mon, 4 Aug 2008 05:48:28 -0400 (EDT) Received: (qmail 348 invoked from network); 4 Aug 2008 12:48:03 +0300 Received: from dev.security.ro (HELO ?192.168.1.70?) (192.168.1.70) by mx0.security.ro with SMTP; 4 Aug 2008 12:48:03 +0300 Message-ID: <4896D06A.8000901@security.ro> Date: Mon, 04 Aug 2008 12:48:26 +0300 From: anon@example.com User-Agent: Thunderbird 2.0.0.16 (Windows/20080708) MIME-Version: 1.0 To: sqlite-dev@sqlite.org References: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> In-Reply-To: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> Content-Type: multipart/mixed; boundary="------------000207070200050102060301" X-BitDefender-Scanner: Clean, Agent: BitDefender qmail 2.0.0 on mx0.security.ro X-BitDefender-Spam: No (0) X-BitDefender-SpamStamp: v1, whitelisted, total: 0 Subject: Re: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: high Reply-To: sqlite-dev@sqlite.org List-Id: <sqlite-dev.sqlite.org> List-Unsubscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=unsubscribe> List-Archive: <http://sqlite.org:8080/cgi-bin/mailman/private/sqlite-dev> List-Post: <mailto:sqlite-dev@sqlite.org> List-Help: <mailto:sqlite-dev-request@sqlite.org?subject=help> List-Subscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=subscribe> Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 2212 This is a multi-part message in MIME format. --------------000207070200050102060301 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Marco Bambini wrote: > Inside sqlite3VdbeExec there is a very big switch statement. > In order to increase performance with few modifications to the > original code, why not use this technique ? > http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html > > With a properly defined "instructions" array, instead of the switch > statement you can use something like: > goto * instructions[pOp->opcode]; > --- > Marco Bambini > http://www.sqlabs.net > http://www.sqlabs.net/blog/ > http://www.sqlabs.net/realsqlserver/ > > > > _______________________________________________ > sqlite-dev mailing list > sqlite-dev@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev > All the world's not a VAX. This technique is GCC-specific. The SQLite source must be as portable as possible thus tying it to a specific compiler is out of the question. While one could conceivably use some preprocessor magic to provide alternate implementations, that would be impractical considering the sheer size of the code affected. On the other hand - perhaps you could benchmark the change and provide some data on whether this actually improves performance? --------------000207070200050102060301 Content-Type: text/x-vcard; charset=utf-8; name="mihailim.vcf" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="mihailim.vcf" begin:vcard fn:Mihai Limbasan n:Limbasan;Mihai org:SC SECPRAL COM SRL adr:;;str. Actorului nr. 9;Cluj-Napoca;Cluj;400441;Romania email;internet:mihailim@security.ro title:SoftwareDeveloper tel;work:+40 264 449579 tel;fax:+40 264 418594 tel;cell:+40 729 038302 url:http://secpral.ro/ version:2.1 end:vcard --------------000207070200050102060301 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --------------000207070200050102060301-- ����������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1252168370_3.14675.cthulhu!2,S��������������������������������������0000664�0000000�0000000�00000001563�12504061031�0022311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <dfgh@floppydisk.nl> X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mindcrime X-Spam-Level: Delivered-To: dfgh@floppydisk.nl Message-ID: <43A09C49.9040902@euler.org> Date: Wed, 14 Dec 2005 23:27:21 +0100 From: Fred Flintstone <fred@euler.org> User-Agent: Mozilla Thunderbird 1.0.7 (X11/20051010) X-Accept-Language: nl-NL, nl, en MIME-Version: 1.0 To: dfgh@floppydisk.nl Subject: Re: xyz References: <439C1136.90504@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439B41ED.2080402@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439A1E03.3090604@euler.org> <20051211184308.GB13513@gauss.org> In-Reply-To: <20051211184308.GB13513@gauss.org> X-Enigmail-Version: 0.92.0.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-UIDL: T<?"!%LG"!cAK"!_j(#! Content-Length: 1879 Test 123. ���������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/1283599333.1840_11.cthulhu!2,���������������������������������������0000664�0000000�0000000�00000000752�12504061031�0022165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: Frodo Baggins <frodo@example.com> To: Bilbo Baggins <bilbo@anotherexample.com> Subject: Greetings from =?UTF-8?B?TG90aGzDs3JpZW4=?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent Organization: The Fellowship of the Ring MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <abcd$efgh@example.com> Let's write some fünkÿ text using umlauts. Foo. ����������������������mu-0.9.12/lib/tests/testdir/cur/1305664394.2171_402.cthulhu!2,��������������������������������������0000664�0000000�0000000�00000001156�12504061031�0022241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: =?UTF-8?B?TcO8?= <testmu@testmu.xx> To: Helmut =?UTF-8?B?S3LDtmdlcg==?= <hk@testmu.xxx> Subject: =?UTF-8?B?TW90w7ZyaGVhZA==?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) References: <non-exist-01@msg.id> <non-exist-02@msg.id> <non-exist-03@msg.id> <non-exist-04@msg.id> 1n-Reply-To: <non-exist-04@msg.id> MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test for issue #38, where apparently searching for accented words in subject, to etc. fails. What about here? Queensrÿche. Mötley Crüe. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/encrypted!2,S�������������������������������������������������������0000664�0000000�0000000�00000004630�12504061031�0020640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: peter@example.com Delivery-date: Fri, 11 May 2012 16:22:03 +0300 Received: from localhost.example.com ([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-Ux for djcb@localhost; Fri, 11 May 2012 16:21:58 +0300 Delivered-To: peter@example.com From: Brian <brian@example.com> To: Peter <peter@example.com> Subject: encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:21:42 +0300 Message-ID: <!&!AAAAAAAAAYAAAAAAAAAOH1+8mkk+lLn7Gg5fke7FbCgAAAEAAAAJ7eBDgcactKhXL6r8cEnJ8BAAAAAA==@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA//YXkUB4Dy09ngCRyHWbXmV3XBjuKTr8xrak5ML1kwurav gyagOHKLMU+5CKvObChiKtXhtgU0od7IC8o+ALlHevQ0XXcqNYA2KUfX8R7akq7d Xx9mA6D8P7Y/P8juUCLBpfrCi2GC42DtvPZSUu3bL/ctUJ3InPHIfHibKF2HMm7/ gUHAKY8VPJF39dLP8GLcfki6qFdeWbxgtzmuyzHfCBCLnDL0J9vpEQBpGDFMcc4v cCbmMJaiPOmRb6U4WOuRVnuXuTztLiIn0jMslzOSFDcLTVBAsrC01r71O+XZKfN4 mIfcpcWJYKM2NQW8Jwf+8Hr84uznBqs8uTTlrmppjkAHZGqGMjiQDxLhDVaCQzMy O8PSV4xT6HPlKXOwV1OLc+vm0A0RAdSBctgZg40oFn4XdB1ur8edwAkLvc0hJKaz gyTQiPaXm2Uh2cDeEx4xNgXmwCKasqc9jAlnDC2QwA33+pw3OqgZT5h1obn0fAeR mgB+iW1503DIi/96p8HLZcr2EswLEH9ViHIEaFj/vlR5BaOncsLB0SsNV/MHRvym Xg5GUjzPIiyBZ3KaR9OIBiZ5eXw+bSrPAo/CAs0Zwxag7W3CH//oK39Qo1GnkYpc 4IQxhx4IwkzqtCnripltV/kfpGu0yA/OdK8lOjkUqCwvL97o73utXIxm21Zd3mEP /iLNrduZjMCq+goz1pDAQa9Dez6VjwRuRPTqeAac8Fx/nzrVzIoIEAt36hpuaH1l KpbmHpKgsUWcrE5iYT0RRlRRtRF4PfJg8PUmP1hvw8TaEmNfT+0HgzcJB/gRsVdy gTzkzUDzGZLhRcpmM5eW4BkuUmIO7625pM6Jd3HOGyfCGSXyEZGYYeVKzv8xbzYf QM6YYKooRN9Ya2jdcWguW0sCSJO/RZ9eaORpTeOba2+Fp6w5L7lga+XM9GLfgref Cf39XX1RsmRBsrJTw0z5COf4bT8G3/IfQP0QyKWIFITiFjGmpZhLsKQ3KT4vSe/d gTY1xViVhkjvMFn3cgSOSrvktQpAhsXx0IRazN0T7pTU33a5K0SrZajY9ynFDIw9 we7XYyVwZzYEXjGih5mTH1PhWYK5fZZEKKqaz5TyYv9SeWJ+8FrHeXUKD38SQEHM qkpl9Iv17RF4Qy9uASWwRoobhKO+GykTaBSTyw8R8ctG/hfAlnaZxQ3TwNyHWyvU 9SVJsp27ulv/W9MLZtGpEMK0ckAR164Vyou1KOn200BqxbC2tJpegNeD2TP5ZtdY HIcxkgKr0haYcDnVEf1ulSxv23pZWIexbgvVCG7dRL0eB+6O28f9CWehle10MDyM 0AYyw8Da2cu7PONMovqt4nayScyGTacFBp7c2KXR9DGZ0mcBwOjL/mGRKcVWN3MG 2auCrwn2KVWmKZI3Jp0T8KhfGBnFs9lUElpDTOiED1/2bKz6Yoc385QtWx99DFMZ IWiH5wMxkWFpzjE+GHiJ09vSbTTL4JY9eu2n5nxQmtjYMBVxQm7S7qwH =0Paa -----END PGP MESSAGE----- --=-=-=-- ��������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/multimime!2,FS������������������������������������������������������0000664�0000000�0000000�00000001160�12504061031�0020746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: djcb@localhost Delivery-date: Sun, 20 May 2012 09:59:51 +0300 From: Steve Jobs <jobs@example.com> To: Bill Gates <bg@example.com> Subject: multimime User-agent: mu4e 0.9.8.4; emacs 23.3.1 Date: Sat, 19 May 2012 20:57:56 +0100 Message-ID: <m2fwaw2baz.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain abc --=-=-= Content-Type: application/octet-stream Content-Disposition: attachment; filename="test1.C" Content-Transfer-Encoding: base64 aGVyZSBpcyBhIHNpbXBsZSB0ZXN0IGZpbGUuCg== --=-=-= Content-Type: text/plain def --=-=-=-- ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/signed!2,S����������������������������������������������������������0000664�0000000�0000000�00000001644�12504061031�0020116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 Delivered-To: diggler@gmail.com From: Skipio <skipio@roma.net> To: Hannibal <hanni@carthago.net> Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- ��������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/signed-encrypted!2,S������������������������������������������������0000664�0000000�0000000�00000004401�12504061031�0022103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: karjala@localhost Delivery-date: Fri, 11 May 2012 16:37:57 +0300 From: karjala@example.com To: lapinkulta@example.com Subject: signed + encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:36:08 +0300 Message-ID: <874nrm96wn.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA/+K4kSpMa7zk+qihUkQnHSq28xYxisNQx6X5DVNjA/Qx16 uZj/40ae+PoSMTVfklP+B2S/IomuTW6dwVqS7aQ3u4MTzi+YOi11k1lEMD7hR0Wb L0i48o3/iCPuCTpnOsaLZvRL06g+oTi0BF2pgz/YdsgsBTGrTb3pkDGSlLIhvh/J P8eE3OuzkXS6d8ymJKx2S2wQJrc1AFf1BgJfgc5T0iAvcV+zIMG+PIYcVd04zVpj cORFEfvGgfxWkeX+Ks3tu/l5PA1EesnoqFdNFZm+RKBg3RFsOm8tBlJ46xJjfeHg zLgifeSLy3tOX7CvWYs9torrx7s7UOI2gV8kzBqz+a7diyCMezceeQ9l0nIRybwW C9Egp8Bpfb02iXTOGdE/vRiNItQH14GKmXf4nCSwdtQUm3yzaqY9yL3xBxAlW53e YOFfPMESt+E7IlPn0c7llWGrcdrhJbUEoGOIPezES7kdeNPzi8G1lLtvT04/SSZJ QxPH5FNzSFaYFAQSdI7TR69P7L7vtLL8ndkjY49HfLFXochQQzsqrzVxzRCruHxA zbZSRptNf9SuXEaX9buO1vlFHheGvrCKzEWa6O7JD/DiyrE/zqy4jdlh9abMCouQ GWGSbn8jk6SMTQQ2Yv/VOyFqifHZp0UJD59tyIdenpxoYu5M0lwHLNVDlRjLEwUQ AIDz1tbLoM7lxs2FOKGr8QqbKIeMfL+NUmbvVIDc4mJrOlRnHh+cZYm4Z49iTl1v bYNMYgR5nY7W6rqh0ae7ZOW0h2NzpkAwTzuf1YrSjNavd9KBwOCFtAoZhRwfwFVx ju+ByHFNnf7g/R6DekHS0pSiatM0cPDJT05atEZb+13CRHHznonmLHi+VahXjrpg cIUA8Lhjdfm6Fsabo7gNZnTTRxNBqUXKK2vJF/XLbNrH5K2BH2dCCmUNtm3yFWiM DOzaw3665Y3S6MvZdyKpatbNrVoJdBpRgPxJ1YCSEituFUqHJBStay+aRb5fVkQR w3+9hWw+Ob0+2EumKbgfQ7iMwTZBCZP4VOxkoqdHvs9aWm4N7wHtXsyCew3icbJx lyUWsDx/FI+HlQRfOqeAMxmp8kKybmHNw8oGiw+uPPUHSD1NFYVm2DtwhYll3Fvs YY7r5s3yP1ZnwxMqWI3OsExVUXs8MS4UTAgO+cggO7YidPcANbBDihBFP8mTXtni Oo5n5v+/eRoLfHmnsGcaK8EkKsfFHpbqn4gxXGcBuHaTTJ/ZhbW6bi1WWZA9ExaJ IeTDtp5Bks1pJvTjCDacvgwl3rEBM6yaeIvB7575Y/GPMTOZhawhfOxV1smMmTKI JOWYb3+PuN2cvWetkjFgH8re4sRXq22DKBZHJEWYU8sH0sACAePnIr+pkrOtGeJB t1zBqZUnrupH6ptk9n/AjbQ+XSMTEKu55gSjYLAYx1EHApx52QLkdh+ej5xCIVeY 6wS1Iipkoc6/r6F7CKctupXurNY2AlD4uQIOfD6kQgkqK4PY3hsRHQA+Zqj6oRfr kxysFJZvhgt26IeBVapFs10WuYt9iHfpbPUBQUIZCLyPAh08UdVW64Uc2DvUPy+I C+3RrmTHQPP/YNKgDQaZ3ySVEDkqjaDPmXr5K0Ibaib2dtPCLcA= =pv03 -----END PGP MESSAGE----- --=-=-=-- ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/cur/special!2,Sabc������������������������������������������������������0000664�0000000�0000000�00000000536�12504061031�0020732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Date: Thu, 1 Jun 2012 14:57:25 -0200 From: "Rocky Balboa" <rocky@example.com> To: "Ivan Drago" <ivan@example.com> Subject: currying and tail optimization Message-id: <3BE9E653ef345@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Test 123. I'm a special message with special flags. ������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/new/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�12504061031�0016373�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/new/1220863087.12663_21.mindcrime���������������������������������������0000664�0000000�0000000�00000012701�12504061031�0022333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 6389969CB2 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:07 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:07 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34769wfh; Wed, 6 Aug 2008 13:38:53 -0700 (PDT) Received: by 10.100.6.13 with SMTP id 13mr4103508anf.83.1218055131215; Wed, 06 Aug 2008 13:38:51 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id b32si10199298ana.34.2008.08.06.13.38.49; Wed, 06 Aug 2008 13:38:51 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; DomainKey-Status: good (test mode) Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org; domainkeys=pass (test mode) header.From=juanma_bellon@yahoo.es Received: from localhost ([127.0.0.1]:55648 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpmT-0005W9-AQ for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:38:49 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KQplz-0005U5-Pk for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:19 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KQplw-0005Nw-OG for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:19 -0400 Received: from [199.232.76.173] (port=45465 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQplw-0005NX-I6 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:16 -0400 Received: from n74a.bullet.mail.sp1.yahoo.com ([98.136.45.21]:29868) by monty-python.gnu.org with smtp (Exim 4.60) (envelope-from <juanma_bellon@yahoo.es>) id 1KQplw-0007EF-7Z for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:16 -0400 Received: from [216.252.122.216] by n74.bullet.mail.sp1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [68.142.237.89] by t1.bullet.sp1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [69.147.75.180] by t5.bullet.re3.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [127.0.0.1] by omp101.mail.re1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 X-Yahoo-Newman-Id: 778995.62909.bm@omp101.mail.re1.yahoo.com Received: (qmail 43643 invoked from network); 6 Aug 2008 20:38:14 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.es; h=Received:X-YMail-OSG:X-Yahoo-Newman-Property:From:To:Subject:Date:User-Agent:References:In-Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-Disposition:Message-Id; b=ThdHlND5CNUsLPGuk+XhCWkdUA9w7lg4hiAgx8F8egsmQteMpwUlV/Y5tfe6K3O2jzHjtsklkzWqm7WY3VAcxxD/QgxLnianK5ZQHoelDAiGaFRqu8Y42XMZso2ccCBFWUQaKo9C+KIfa3e3ci73qehVxTtmr7bxLjurcSYEBPo= ; Received: from unknown (HELO 212251170160.customer.cdi.no) (juanma_bellon@212.251.170.160 with plain) by smtp109.plus.mail.re1.yahoo.com with SMTP; 6 Aug 2008 20:38:14 -0000 X-YMail-OSG: k86L54kVM1kiZbUlYx7gayoBrCLYMFIRDL.KJLBKetNucAbwU4RjeeE1vhjw33hREaUig0CCjG7BTwIfbeZZpRmUcHbxl6gR0z6Sd3lYqA-- X-Yahoo-Newman-Property: ymail-3 From: anon@example.com To: help-gnu-emacs@gnu.org Date: Wed, 6 Aug 2008 22:38:15 +0200 User-Agent: KMail/1.9.6 (enterprise 0.20070907.709405) References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> In-Reply-To: <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Message-Id: <200808062238.15634.juanma_bellon@yahoo.es> X-detected-kernel: by monty-python.gnu.org: FreeBSD 6.x (1) Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 361 On Thursday 31 July 2008, Xah wrote: > what's the logic of =E2=80=9COK=E2=80=9D? =46or all I know, it comes from "0 Knock-outs" (from USA civil war times, IIRC), i.e., all went really well. But this is really off-topic. =2D-=20 Juanma "Having a smoking section in a restaurant is like having a peeing section in a swimming pool." -- Edward Burr ���������������������������������������������������������������mu-0.9.12/lib/tests/testdir/new/1220863087.12663_23.mindcrime���������������������������������������0000664�0000000�0000000�00000012340�12504061031�0022334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C3EF069CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:10 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:10 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs35153wfh; Wed, 6 Aug 2008 13:58:17 -0700 (PDT) Received: by 10.100.166.10 with SMTP id o10mr4182182ane.0.1218056296101; Wed, 06 Aug 2008 13:58:16 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d34si13875743and.3.2008.08.06.13.58.14; Wed, 06 Aug 2008 13:58:16 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org; dkim=pass (test mode) header.i=@gmail.com Received: from localhost ([127.0.0.1]:33418 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQq5G-0001aY-Cr for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:58:14 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KQq4n-0001Z9-06 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:45 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KQq4l-0001V8-6c for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:44 -0400 Received: from [199.232.76.173] (port=46438 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQq4k-0001Un-V2 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:42 -0400 Received: from ik-out-1112.google.com ([66.249.90.180]:17562) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from <lekktu@gmail.com>) id 1KQq4k-0001fk-OW for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:42 -0400 Received: by ik-out-1112.google.com with SMTP id c21so94956ika.2 for <help-gnu-emacs@gnu.org>; Wed, 06 Aug 2008 13:57:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from:to :subject:cc:in-reply-to:mime-version:content-type :content-transfer-encoding:content-disposition:references; bh=TTNY9749hpg1+TXOwdaCr+zbQGhBUt3IvsjLWp+pxp0=; b=BOfudUT/SiW9V4e9+k3dXDzwm+ogdrq4m5OlO+f1H+oE6OAYGIm8dbdqDAOwUewBoS jRpfZo07YamP9rkko79SeFdQnf7UAPFAw9x7DFCm3x6muSlCcJBR7vYs1rgHOSINAn2B vQx2//lKR4fXfKNURNu+B30KrvoEmw6m2C8dI= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:cc:in-reply-to:mime-version :content-type:content-transfer-encoding:content-disposition :references; b=UMDBulH/LwxDywEH0pfK3DbJ4u2kIZCVDLIM++PqrdcR82HjcS/O3Jhf5OFrf7Fnyj GH76xmc7zkTG/3aQy2WY6DeWCJaFarEItmhxy3h/xS+kUKeDARzNox0OzK6lIv/u9bdy f2LnFlYRJ7Q5vy3lxpxAWB4v0qCwtF9LjWFg4= Received: by 10.210.47.7 with SMTP id u7mr3100239ebu.30.1218056261587; Wed, 06 Aug 2008 13:57:41 -0700 (PDT) Received: by 10.210.71.14 with HTTP; Wed, 6 Aug 2008 13:57:41 -0700 (PDT) Message-ID: <f7ccd24b0808061357t453f5962w8b61f9a453b684d0@mail.gmail.com> Date: Wed, 6 Aug 2008 22:57:41 +0200 From: anon@example.com To: Juanma <juanma_bellon@yahoo.es> In-Reply-To: <200808062238.15634.juanma_bellon@yahoo.es> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Content-Disposition: inline References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> X-detected-kernel: by monty-python.gnu.org: Linux 2.6 (newer, 2) Cc: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 309 On Wed, Aug 6, 2008 at 22:38, Juanma <juanma_bellon@yahoo.es> wrote: > For all I know, it comes from "0 Knock-outs" (from USA civil war times, > IIRC), i.e., all went really well. See http://en.wikipedia.org/wiki/Okay#Etymology "0 knock-outs" is among the "Improbable or refuted etymologies". Juanma ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/new/1220863087.12663_25.mindcrime���������������������������������������0000664�0000000�0000000�00000010173�12504061031�0022340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D68E769CB5 for <xxxx@localhost>; Fri, 8 Aug 2008 20:56:25 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Fri, 08 Aug 2008 20:56:25 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs71287wfh; Fri, 8 Aug 2008 07:40:46 -0700 (PDT) Received: by 10.100.122.8 with SMTP id u8mr3824321anc.77.1218206446062; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d35si2718351and.38.2008.08.08.07.40.45; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:47349 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KRT93-0006Po-A3 for xxxx.klub@gmail.com; Fri, 08 Aug 2008 10:40:45 -0400 Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!news-out.readnews.com!news-xxxfer.readnews.com!panix!not-for-mail From: anon@example.com Newsgroups: gnu.emacs.help Date: Fri, 08 Aug 2008 10:07:30 -0400 Organization: PANIX Public Access Internet and UNIX, NYC Message-ID: <uwsireh25.fsf@one.dot.net> References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> <mailman.15958.1218056266.18990.help-gnu-emacs@gnu.org> NNTP-Posting-Host: panix5.panix.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: reader1.panix.com 1218204439 22850 166.84.1.5 (8 Aug 2008 14:07:19 GMT) X-Complaints-To: abuse@panix.com NNTP-Posting-Date: Fri, 8 Aug 2008 14:07:19 +0000 (UTC) User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (windows-nt) Cancel-Lock: sha1:Ckkp5oJPIMuAVgEHGnS/9MkZsEs= Xref: news.stanford.edu gnu.emacs.help:160963 To: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 710 Lines: 27 I seem to remember from my early school days it was a campaign slogan for someone nick-named Kinderhook that went something like Old Kinderhook is OK - Chris "Juanma Barranquero" <lekktu@gmail.com> writes: > On Wed, Aug 6, 2008 at 22:38, Juanma <juanma_bellon@yahoo.es> wrote: > >> For all I know, it comes from "0 Knock-outs" (from USA civil war times, >> IIRC), i.e., all went really well. > > See http://en.wikipedia.org/wiki/Okay#Etymology > > "0 knock-outs" is among the "Improbable or refuted etymologies". > > Juanma > > -- (. .) =ooO=(_)=Ooo===================================== Chris McMahan | first_initiallastname@one.dot.net ================================================= �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.12/lib/tests/testdir/new/1220863087.12663_9.mindcrime����������������������������������������0000664�0000000�0000000�00000021747�12504061031�0022273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <sqlite-dev-bounces@sqlite.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-1.2 required=3.0 tests=BAYES_00,HTML_MESSAGE, MIME_QP_LONG_LINE autolearn=no version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 4E3CF6963B for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:37 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Mon, 04 Aug 2008 21:49:37 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs94317wfy; Mon, 4 Aug 2008 05:48:28 -0700 (PDT) Received: by 10.150.152.17 with SMTP id z17mr1245909ybd.194.1217854107583; Mon, 04 Aug 2008 05:48:27 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 9si6334793yws.5.2008.08.04.05.47.57; Mon, 04 Aug 2008 05:48:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 4FBC111C6F; Mon, 4 Aug 2008 08:47:54 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from cpsmtpo-eml02.kpnxchange.com (cpsmtpo-eml02.kpnxchange.com [213.75.38.151]) by sqlite.org (Postfix) with ESMTP id AA4F111C10 for <sqlite-dev@sqlite.org>; Mon, 4 Aug 2008 08:47:51 -0400 (EDT) Received: from hpsmtp-eml21.kpnxchange.com ([213.75.38.121]) by cpsmtpo-eml02.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from cpbrm-eml13.kpnsp.local ([195.121.247.250]) by hpsmtp-eml21.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from hpsmtp-eml30.kpnxchange.com ([10.94.53.250]) by cpbrm-eml13.kpnsp.local with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from localhost ([10.94.53.250]) by hpsmtp-eml30.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:49 +0200 Content-class: urn:content-classes:message MIME-Version: 1.0 X-MimeOLE: Produced By Microsoft Exchange V6.5 Date: Mon, 4 Aug 2008 14:46:06 +0200 Message-ID: <F687EC042917A94E8BB4B0902946453AE17D6C@CPEXBE-EML18.kpnsp.local> X-MS-Has-Attach: X-MS-TNEF-Correlator: Thread-Topic: [sqlite-dev] VM optimization inside sqlite3VdbeExec Thread-Index: Acj2FjkWvteFtLHTTYeVz4ES7E2ggAAGRxeI References: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> From: anon@example.com To: <sqlite-dev@sqlite.org> X-OriginalArrivalTime: 04 Aug 2008 12:47:49.0650 (UTC) FILETIME=[4D577720:01C8F630] Subject: Re: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: sqlite-dev@sqlite.org List-Id: <sqlite-dev.sqlite.org> List-Unsubscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=unsubscribe> List-Archive: <http://sqlite.org:8080/cgi-bin/mailman/private/sqlite-dev> List-Post: <mailto:sqlite-dev@sqlite.org> List-Help: <mailto:sqlite-dev-request@sqlite.org?subject=help> List-Subscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=subscribe> Content-Type: multipart/mixed; boundary="===============1911358387==" Mime-version: 1.0 Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 5318 This is a multi-part message in MIME format. --===============1911358387== Content-class: urn:content-classes:message Content-Type: multipart/alternative; boundary="----_=_NextPart_001_01C8F630.0FC2EC1E" This is a multi-part message in MIME format. ------_=_NextPart_001_01C8F630.0FC2EC1E Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Actually, almost every C compiler will already do what you suggest: if = the range of case labels is compact, the switch will be compiled using a = jump table. Only if the range is limited and/or sparse other techniques = will be used, such as linear search and binary search. =20 I'm pretty sure, if you perform the tests suggested by Mihai, that you = will find zero performance difference, neither better, nor worse. =20 Paul =20 ________________________________ From: anon@example.com Sent: Mon 8/4/2008 11:40 AM To: sqlite-dev@sqlite.org Subject: [sqlite-dev] VM optimization inside sqlite3VdbeExec Inside sqlite3VdbeExec there is a very big switch statement. In order to increase performance with few modifications to the=20 original code, why not use this technique ? http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html = <http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html>=20 With a properly defined "instructions" array, instead of the switch=20 statement you can use something like: goto * instructions[pOp->opcode]; --- Marco Bambini http://www.sqlabs.net <http://www.sqlabs.net/>=20 http://www.sqlabs.net/blog/ <http://www.sqlabs.net/blog/>=20 http://www.sqlabs.net/realsqlserver/ = <http://www.sqlabs.net/realsqlserver/>=20 _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev = <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>=20 ------_=_NextPart_001_01C8F630.0FC2EC1E Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable <HTML dir=3Dltr><HEAD><TITLE>[sqlite-dev] VM optimization inside = sqlite3VdbeExec=0A= =0A= =0A= =0A=
=0A=
Actually, = almost every C compiler will already do what you suggest: if the range = of case labels is compact, the switch will be compiled using a jump = table. Only if the range is limited and/or sparse other techniques will = be used, such as linear search and binary search.
=0A=
 
=0A=
I'm pretty sure, if you = perform the tests suggested by Mihai, that you will find zero = performance difference, neither better, nor worse.
=0A=
 
=0A=
Paul
=0A=
 
=0A=
=0A=
=0A=
=0A=
From: = sqlite-dev-bounces@sqlite.org on behalf of Marco Bambini
Sent: = Mon 8/4/2008 11:40 AM
To: = sqlite-dev@sqlite.org
Subject: [sqlite-dev] VM optimization = inside sqlite3VdbeExec

=0A=
=0A=

Inside sqlite3VdbeExec there is a very = big switch statement.
In order to increase performance with few = modifications to the 
original code, why not use this technique = ?
= http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html<= /FONT>

With a properly defined = "instructions" array, instead of the switch 
statement you can = use something like:
goto * = instructions[pOp->opcode];
---
Marco Bambini
http://www.sqlabs.net
http://www.sqlabs.net/blog/
http://www.sqlabs.net/realsqlserver/



<= FONT face=3DArial = size=3D2>_______________________________________________
sqlite-dev = mailing list
sqlite-dev@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev

------_=_NextPart_001_01C8F630.0FC2EC1E-- --===============1911358387== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --===============1911358387==-- mu-0.9.12/lib/tests/testdir/tmp/000077500000000000000000000000001250406103100164025ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir/tmp/1220863087.12663.ignore000066400000000000000000000101731250406103100213550ustar00rootroot00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D68E769CB5 for ; Fri, 8 Aug 2008 20:56:25 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Fri, 08 Aug 2008 20:56:25 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs71287wfh; Fri, 8 Aug 2008 07:40:46 -0700 (PDT) Received: by 10.100.122.8 with SMTP id u8mr3824321anc.77.1218206446062; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d35si2718351and.38.2008.08.08.07.40.45; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:47349 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KRT93-0006Po-A3 for xxxx.klub@gmail.com; Fri, 08 Aug 2008 10:40:45 -0400 Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!news-out.readnews.com!news-xxxfer.readnews.com!panix!not-for-mail From: anon@example.com Newsgroups: gnu.emacs.help Date: Fri, 08 Aug 2008 10:07:30 -0400 Organization: PANIX Public Access Internet and UNIX, NYC Message-ID: References: <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> NNTP-Posting-Host: panix5.panix.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: reader1.panix.com 1218204439 22850 166.84.1.5 (8 Aug 2008 14:07:19 GMT) X-Complaints-To: abuse@panix.com NNTP-Posting-Date: Fri, 8 Aug 2008 14:07:19 +0000 (UTC) User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (windows-nt) Cancel-Lock: sha1:Ckkp5oJPIMuAVgEHGnS/9MkZsEs= Xref: news.stanford.edu gnu.emacs.help:160963 To: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 710 Lines: 27 I seem to remember from my early school days it was a campaign slogan for someone nick-named Kinderhook that went something like Old Kinderhook is OK - Chris "Juanma Barranquero" writes: > On Wed, Aug 6, 2008 at 22:38, Juanma wrote: > >> For all I know, it comes from "0 Knock-outs" (from USA civil war times, >> IIRC), i.e., all went really well. > > See http://en.wikipedia.org/wiki/Okay#Etymology > > "0 knock-outs" is among the "Improbable or refuted etymologies". > > Juanma > > -- (. .) =ooO=(_)=Ooo===================================== Chris McMahan | first_initiallastname@one.dot.net ================================================= mu-0.9.12/lib/tests/testdir2/000077500000000000000000000000001250406103100156645ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/000077500000000000000000000000001250406103100164075ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/cur/000077500000000000000000000000001250406103100172005ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/cur/arto.eml000066400000000000000000000561701250406103100206550ustar00rootroot00000000000000Return-Path: <> X-Original-To: f00f@localhost Delivered-To: f00f@localhost Received: from puppet (puppet [127.0.0.1]) by f00fmachines.nl (Postfix) with ESMTP id A534D39C7F1 for ; Mon, 23 May 2011 20:30:05 +0300 (EEST) Delivered-To: diggler@gmail.com Received: from ew-in-f109.1e100.net [174.15.27.101] by puppet with POP3 (fetchmail-6.3.18) for (single-drop); Mon, 23 May 2011 20:30:05 +0300 (EEST) Received: by 10.142.147.13 with SMTP id u13cs87252wfd; Mon, 23 May 2011 01:54:10 -0700 (PDT) Received: by 10.204.7.74 with SMTP id c10mr1984197bkc.104.1306140849326; Mon, 23 May 2011 01:54:09 -0700 (PDT) Received: from MTX4.mbn1.net (mtx4.mbn1.net [213.188.129.252]) by mx.google.com with ESMTP id e6si18117551bkw.39.2011.05.23.01.54.07; Mon, 23 May 2011 01:54:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of MTX4.mbn1.net designates 213.188.129.252 as permitted sender) client-ip=213.188.129.252; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of MTX4.mbn1.net designates 213.188.129.252 as permitted sender) smtp.mail= Resent-From: X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=192.168.10.123; From: ArtOlive To: "f00f@f00fmachines.nl" Reply-To: Date: Mon, 23 May 2011 10:53:45 +0200 Subject: NIEUWSBRIEF ART OLIVE | juni exposite in galerie ArtOlive MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d" X-Mailer: aspNetEmail ver 3.5.2.10 X-Sender: 87.93.13.24 X-RemoteIP: 87.93.13.24 Originating-IP: 87.93.13.24 X-MAILINGLIJST-ID: <10374608.109906.11909.2011523105345.MSGID@mailinglijst.nl> Message-ID: <10374608.109906.11909.2011523105345.MSGID@mailinglijst.nl> X-Authenticated-User: guest@mailinglijst.eu X-STA-Metric: 0 (engine=030) X-STA-NotSpam: geinformeerd vormen spec:usig:3.8.2 twee samen X-STA-Spam: 2011 &bull • e-mailing subject:juni X-BTI-AntiSpam: score:0,sta:0/030,dnsbl:passed,sw:passed,bsn:10/passed,spf:off,bsctr:passed/1,dk:off,pbmf:none,ipr:0/3,trusted:no,ts:no,bs:no,ubl:passed X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply Resent-Message-Id: <19740414233016.EB6835A132F5FCF4@MTX4.mbn1.net> Resent-Date: Mon, 23 May 2011 10:54:07 +0200 (CEST) --_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable ART-O-NEWS; juni 2011 Westergasfabriekterrein Polonceaukade 17 10= 14 DA Amsterdam tel: 020-6758504 info@artolive.nl www.artolive.nlJuni= expositie bij ArtOlive: Peter van den Akker en Marinel Vieleers Zondag 5 juni Elke maand vindt er in de galerie van ArtOlive een expositie plaats. We = lichten enkele kunstenaars uit (die je misschien al kent van onze website= ), waarbij we een spannende mix van materiaal en techniek presenteren. Ti= jdens de expositie staan we klaar om elke vraag over ons kunstaanbod te b= eantwoorden.=20 De exposities zijn te bezoeken van maandag t/m vrijdag, tussen 10:00 en 1= 7:00 uur. De opening is altijd op de eerste zondag van de maand. Dit valt= samen met de Sunday Market die elke maand op het Cultuurpark Westergasfa= briek georganiseerd wordt. De Sunday Market is gratis te bezoeken en staa= t vol met kunst, design, mode en heerlijke hapjes, en er hangt altijd een= vrolijke sfeer. Een ideaal moment dus om in te haken en deze maand twee = kunstenaars te presenteren: Peter van den Akker en Marinel Vieleers. We verwelkomen je graag op zondag 5 juni 2011, van 12:00 t/m 17:00 uur op= de Polonceaukade 17 van het Cultuurpark Westergasfabriek in Amsterdam!=20= bekijk meer werk op www.artolive.nl... Peter van den Akker "In mijn beelden en schilderijen staat het mensbeeld centraal; niet als i= ndividu maar als universele gestalte, waarbij ik op transparante wijze ti= jdsbeelden en gelaagdheid in het menselijke handelen naar voren breng. Ve= rhoudingen tussen mensen, verschuivingen in wereldculturen en verandering= en in techniek, architectuur, natuur en mensbeeld vormen mijn inspiratieb= ronnen. Het zijn allemaal beelden en sferen die naast en met elkaar besta= an. Mijn werkwijze omvat vele technieken in verschillende materialen: sch= ilderijen, gemengde technieken op papier/collages, zeefdrukken, beelden i= n cortenstaal, keramische objecten." Peter van den Akker exposeert regelmatig in binnen- en buitenland bij gal= erie=EBn en musea en is in verschillende kunstinstellingen en bedrijfscol= lecties opgenomen. lees meer over Peter... Marinel Vieleers Marinel Vieleers probeert het menselijke in de bouwwerken - en ook vaak i= ets van het karakter van de bouwer of bewoner - te laten zien. Het zijn m= aar subtiele details die dat alles weergeven. De 'tand des tijds' of invloed van mensen op de gebouwen spelen vaak mee = in het werk. Koper, cement, lood en andere materialen worden in haar nieu= we werk gebruikt. Op deze manier kan ze gemakkelijker improviseren en nog= directer op haar gevoel afgaan. Marinel is gefascineerd door de schoonheid van het imperfecte. De gelaagd= heid van ouderdom, het verval. De imperfectie die ontstaat door toevallig= e omstandigheden maakt een huis, een muur, een schutting, hout of steen b= oeiend. Het is doorleefd en het toont een stukje van zijn geschiedenis. lees meer over Marinel... =20 ZONDAG 5 MEI - Juni expositie in de galerie van ArtOlive met Marinel Viel= eers en Peter van den Akker Opening op zondag 5 mei, tijdens de Sunday Market op het Cultuurpark West= ergasfabriek in Amsterdam. Je bent van harte uitgenodigd om tussen 12:00 = en 17:00 uur langs te komen! Daarna is de expositie te zien op werkdagen (ma - vrij) tussen 10:00 en 1= 7:00. De expositie duurt tot 24 juni 2011. wil je niet langer door artolive ge=EFnformeerd worden? Klik dan hier om= je af te melden.=20 kreeg je dit mailtje doorgestuurd en wil je voortaan zelf ook graag de n= ieuwsbrief ontvangen?=20 klik dan hier om je aan te melden.=20 Deze e-mailing is verzorgd met MailingLijst --_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d Content-Type: text/html; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable Artolive
3D"artolive"
ART-O-NEWS • juni 2011
Westergasfabriekterrein   Polonceau= kade 17 1014 DA Amsterdam   tel: 020-6758504  info@artolive.nl<= /a>  www.artolive.nl
Juni= expositie bij ArtOlive: Peter van den Akker en Marinel Vieleers

Zondag 5 juni
Elke maand vindt er in de galerie van Art= Olive een expositie plaats. We lichten enkele kunstenaars uit (die je mis= schien al kent van onze website), waarbij we een spannende mix van materi= aal en techniek presenteren. Tijdens de expositie staan we klaar om elke = vraag over ons kunstaanbod te beantwoorden.

De exposities zijn te bezoeken van maa= ndag t/m vrijdag, tussen 10:00 en 17:00 uur. De opening is altijd op de e= erste zondag van de maand. Dit valt samen met de Sunday Market die elke m= aand op het Cultuurpark Westergasfabriek georganiseerd wordt. De Sunday M= arket is gratis te bezoeken en staat vol met kunst, design, mode en heerl= ijke hapjes, en er hangt altijd een vrolijke sfeer. Een ideaal moment dus= om in te haken en deze maand twee kunstenaars te presenteren: Peter van = den Akker en Marinel Vieleers.

We verwelkomen je graag op zondag 5 ju= ni 2011, van 12:00 t/m 17:00 uur op de Polonceaukade 17 van het Cultuurpa= rk Westergasfabriek in Amsterdam!


3D""  = bekijk= meer werk op www.artolive.nl...   

=
3D""
<= a target=3D"_blank" href=3D"http://www.mailinglijst.eu/redirect.aspx?l=3D= 154043&a=3D10374608&t=3DH"> <= /td> Peter van den Akker

"In mijn beelden en schild= erijen staat het mensbeeld centraal; niet als individu maar als universel= e gestalte, waarbij ik op transparante wijze tijdsbeelden en gelaagdheid = in het menselijke handelen naar voren breng. Verhoudingen tussen mensen, = verschuivingen in wereldculturen en veranderingen in techniek, architectu= ur, natuur en mensbeeld vormen mijn inspiratiebronnen. Het zijn allemaal = beelden en sferen die naast en met elkaar bestaan. Mijn werkwijze omvat v= ele technieken in verschillende materialen: schilderijen, gemengde techni= eken op papier/collages, zeefdrukken, beelden in cortenstaal, keramische = objecten.”

Peter van den Akker expose= ert regelmatig in binnen- en buitenland bij galerieën en musea en is= in verschillende kunstinstellingen en bedrijfscollecties opgenomen.


3D""  = lees meer over Peter...   

= Marinel Vieleers

Marinel Vieleers probeert = het menselijke in de bouwwerken - en ook vaak iets van het karakter van d= e bouwer of bewoner - te laten zien. Het zijn maar subtiele details die d= at alles weergeven.

De ‘tand des tijds&r= squo; of invloed van mensen op de gebouwen spelen vaak mee in het werk. K= oper, cement, lood en andere materialen worden in haar nieuwe werk gebrui= kt. Op deze manier kan ze gemakkelijker improviseren en nog directer op h= aar gevoel afgaan.

Marinel is gefascineerd do= or de schoonheid van het imperfecte. De gelaagdheid van ouderdom, het ver= val. De imperfectie die ontstaat door toevallige omstandigheden maakt een= huis, een muur, een schutting, hout of steen boeiend. Het is doorleefd e= n het toont een stukje van zijn geschiedenis.


3D""  = lees meer ov= er Marinel...   

3D""
3D""

ZONDAG 5 MEI - Juni expositie in de galerie van ArtOlive met = Marinel Vieleers en Peter van den Akker

Opening op zondag 5 mei, = tijdens de Sunday Market op het Cultuurpark Westergasfabriek in Amsterdam= . Je bent van harte uitgenodigd om tussen 12:00 en 17:00 uur langs te kom= en!

Daarna is de expositie te zien op werkdagen (ma - vrij= ) tussen 10:00 en 17:00. De expositie duurt tot 24 juni 2011.
3D"Kunst wil je niet langer door artolive geïnformeerd worden? Klik= dan hier om je af te melden.
kreeg je dit mailtje doorgestuurd en wil je voortaan = zelf ook graag de nieuwsbrief ontvangen?
klik dan hier om= je aan te melden.

<= HR SIZE=3D1 STYLE=3D"COLOR:#d3d3d3" SIZE=3D1>Deze e-mailing is verzorgd m= et MailingLijst

--_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d-- mu-0.9.12/lib/tests/testdir2/Foo/cur/fraiche.eml000066400000000000000000000005201250406103100212750ustar00rootroot00000000000000From: Sender To: Recip Subject: search accents Date: 2012-12-08 00:48 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit line 1: Глокая куздра штеко будланула бокра и курдячит бокрёнка line 2: crème fraîche mu-0.9.12/lib/tests/testdir2/Foo/cur/mail5000066400000000000000000001323441250406103100201410ustar00rootroot00000000000000From: Sitting Bull To: George Custer Subject: pics for you Mail-Reply-To: djcb@djcbsoftware.nl User-Agent: Hunkpapa/2.15.9 (Almost Unreal) Message-Id: CAHSaMxZ9rk5ASjqsbXizjTQuSk583=M6TORHz=bfogtmbGGs5A@mail.gmail.com Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: multipart/mixed; boundary="Multipart_Sun_Oct_17_10:37:40_2010-1" --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: text/plain; charset=US-ASCII Dude! Here are some pics! --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="sittingbull.jpg" Content-Transfer-Encoding: base64 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 KD//2Q== --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="custer.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4Q1kRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAAyAAAABsBBQABAAAAbgAAACgBAwABAAAAAgAAADEBAgAOAAAAdgAAADIBAgAUAAAAhAAAABMC CQABAAAAAQAAAGmHBAABAAAAmAAAAOYAAADIAAAAAQAAAGd0aHVtYiAyLjExLjMAMjAwNTowMTox MCAwMDo1NzowMwAGAACQBwAEAAAAMDIyMQGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA //8AAAKgCQABAAAAyAAAAAOgCQABAAAA9gAAAAAAAAAGAAMBAwABAAAABgAAABoBCQABAAAASAAA ABsBCQABAAAASAAAACgBCQABAAAAAgAAAAECBAABAAAANAEAAAICBAABAAAAJwwAAAAAAAD/2P/g ABBKRklGAAEBAAABAAEAAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAk LicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAIAAaAMBIgACEQED EQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0B AgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpD REVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEB AQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFR B2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVW V1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrC 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 pAznLa7juUCSEJMvr3qZwydRx6isi4ty3zJw1TWksjrs3kOOoJpiL0twwXlCwHoKxri6WFj8pG/k A1fe6vIODtKmu1+Gc9vcazNHeRxyZh+RXjVuc8nJ5oA4zwxp91rmpSW1nGGnZMZbgAZ5JP5V6Cvw 213Jb7TYgbcKPMbr/wB816pGsESARpGi+iqAKT7VCPlMi5+tAHisPwc8SLJk39hH/tpI+R/47XWe FvBeqeGtHnhlENxO8hlYRSH5vQDIHP1xXoSzow4ZT9DSvIO1AHiepeMNUWaWCBPsgDFWDrl8jsc9 PpXMajqdxe3BuLt90zAAsABkDp0rq/ixpTWeuwanbDC3kZDr/trwT+IIrziS6uUcJcqGWgDpPDOo pb+ILctJxITGQffp+uKKyNFtjc67YxqdyNMrfgDk/wAqKljR64vB5rK8Q6DaapCtxI0qXC7I0Kth eXAGR3+8fzrTDYNQavKRol465DpEZFI6gryP1FJMbPMEcqxRhytNkwrLInDDriqST/ay3z4lB3A+ tSrIWQq3DjqDVEl9h9oiDIC2e1V4H1KxvEuIGe2KHPmZxtqkbuazYtC5FVJ767vG/eyEr6dKAPQr P4k6tZRHcy3KKeGlXJI98VuQ+OdM1EB3nubN3IASWM4Y+gIzXksN4YcB49yjsank1L7Q4ZvlVfuj 3oC57To2vQX8jJBdJujO0hnCnP0ODXWxC6dBuLL6ZFeP2/izwp4hsIodejmtNREflvdRICjnszY5 OepGOtcvc6vqeh3Ij0jXbnyifl8mZth+maLDPU/i9Cf+EOt53YB4rtcHvypBx+leILdgDbICwrqL 7TvHPiVo7fUTdTxp8y+dIAg9+uM/rXS6D8PbDTQs+pst5cjkJ/yzU/T+L8fyp3AyfAmiXHn/ANrz I0duqkQhurk8Z+mM0V6BI+FCqAFHAA6CiobA/9kA/+EMRWh0dHA6Ly9ucy5hZG9iZS5jb20veGFw LzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQi Pz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUg NC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5 LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgog ICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6dGlm Zj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8v bnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUu Y29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50 cy8xLjEvIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAwNS0wMS0xMFQwMDowNzoyNyswMTowMCIKICAg eG1wOk1vZGlmeURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpNZXRhZGF0 YURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpDcmVhdG9yVG9vbD0iQWRv YmUgUGhvdG9zaG9wIENTIFdpbmRvd3MiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHRpZmY6 WFJlc29sdXRpb249IjIwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIyMDAvMSIKICAgdGlmZjpS ZXNvbHV0aW9uVW5pdD0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSI0Mjk0OTY3Mjk1IgogICBleGlm OlBpeGVsWERpbWVuc2lvbj0iNzU1IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iOTMwIgogICB4 bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6Zjg2ZTcwZTQtNjI5OC0xMWQ5 LTllM2YtZDQyZjM0NjM5ZGJiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ1dWlkOmY4NmU3MGU1LTYy OTgtMTFkOS05ZTNmLWQ0MmYzNDYzOWRiYiIKICAgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIi8+CiA8 L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg ICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7/2wBDAAUDBAQEAwUEBAQFBQUG BwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh7/wAARCAD2AMgDASIAAhEBAxEB/8QAHQAAAAcBAQEAAAAAAAAAAAAAAAIDBAUGBwEICf/E AEIQAAIBAwIEBAQDBgUBCAMBAAECAwAEEQUhBhIxQQcTUWEicYGRFDKhI0JSscHRCBVicoLwFiQl M5KissJDU9Lh/8QAGQEBAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAGhEBAQEBAQEBAAAAAAAAAAAA AAERAjEhEv/aAAwDAQACEQMRAD8Am8Hl60dEGN+tAbnalEBIr5+PUIoGcmllXPSgkW9LxxntVBET 2pVY/Y0tFEe+KcpFRDYRDHSjrD/pp4sdHWIUwMHhwOlJcpXr0qVeEEd6aXCxxRtLK6xou7MxwB8z TDSSpjelAtQFxxvwjaSmGXW7dnH/AOtWkH3UEUeLjjhGXATV0z7wyD/61cNTTDbG1AKcdKjBxXww RznWbUA9Mkj+lSem3+n6lE0mnXtvdopwzQyBgp9DjpTDXGQ0mY6fFD6Vzy8DpTDTMR7UOT3p0U9q KF3oaasm1F5PSnTr3pMAb1FJrGwGRRuX1o64xvRtqYG5X2oyp7UpgV1agCKAQSKFKZHLQozUEoNL Rj3oBd+lLxJsKrTsaEmnccW+d65Ehz0pzGp9KuI5HHv0pdU7Yrsa+1LqmSBitSAsabdKrXiLxfb8 IabG6xR3V/O4EVsXweXuxx2/maX4w410PhmJknlWe7A2gRhkf7j2/nWCaxxK9/qs+ohea6mbJuJd 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 Cheerio! --Multipart_Sun_Oct_17_10:37:40_2010-1-- mu-0.9.12/lib/tests/testdir2/Foo/new/000077500000000000000000000000001250406103100172005ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/new/.noindex000066400000000000000000000000001250406103100206330ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/tmp/000077500000000000000000000000001250406103100172075ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/Foo/tmp/.noindex000066400000000000000000000000001250406103100206420ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/000077500000000000000000000000001250406103100164305ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/cur/000077500000000000000000000000001250406103100172215ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/cur/181736.eml000066400000000000000000000040741250406103100204760ustar00rootroot00000000000000Path: uutiset.elisa.fi!feeder2.news.elisa.fi!feeder.erje.net!newsfeed.kamp.net!newsfeed0.kamp.net!nx02.iad01.newshosting.com!newshosting.com!post01.iad!not-for-mail X-newsreader: xrn 9.03-beta-14-64bit Sender: jimbo@lews (Jimbo Foobarcuux) From: jimbo@slp53.sl.home (Jimbo Foobarcuux) Reply-To: slp53@pacbell.net Subject: Re: Are writes "atomic" to readers of the file? Newsgroups: comp.unix.programmer References: <87hbblwelr.fsf@sapphire.mobileactivedefense.com> <8762s0jreh.fsf@sapphire.mobileactivedefense.com> <87hbbjc5jt.fsf@sapphire.mobileactivedefense.com> <8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk> Organization: UseNetServer - www.usenetserver.com X-Complaints-To: abuse@usenetserver.com Message-ID: Date: 08 Mar 2011 17:04:20 GMT Lines: 27 Xref: uutiset.elisa.fi comp.unix.programmer:181736 John Denver writes: >Eric the Red wrote: > >>> There _IS_ a requirement that all reads and writes to regular files >>> be atomic. There is also an ordering guarantee. Any implementation >>> that doesn't provide both atomicity and ordering guarantees is broken. >> >> But where is it specified? > >The place where it is stated most explicitly is in XSH7 2.9.7 >Thread Interactions with Regular File Operations: > > All of the following functions shall be atomic with respect to each > other in the effects specified in POSIX.1-2008 when they operate on > regular files or symbolic links: > > [List of functions that includes read() and write()] > > If two threads each call one of these functions, each call shall > either see all of the specified effects of the other call, or none > of them. > And, for the purposes of this paragraph, the two threads need not be part of the same process. jimbo mu-0.9.12/lib/tests/testdir2/bar/cur/mail1000066400000000000000000000027721250406103100201570ustar00rootroot00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "John Milton" Subject: Fere libenter homines id quod volunt credunt To: "Julius Caesar" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 x-label: Paradise losT X-Keywords: milton,john Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high OF Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, With loss of Eden, till one greater Man Restore us, and regain the blissful Seat, [ 5 ] Sing Heav'nly Muse,that on the secret top Of Oreb, or of Sinai, didst inspire That Shepherd, who first taught the chosen Seed, In the Beginning how the Heav'ns and Earth Rose out of Chaos: Or if Sion Hill [ 10 ] Delight thee more, and Siloa's Brook that flow'd Fast by the Oracle of God; I thence Invoke thy aid to my adventrous Song, That with no middle flight intends to soar Above th' Aonian Mount, while it pursues [ 15 ] Things unattempted yet in Prose or Rhime. And chiefly Thou O Spirit, that dost prefer Before all Temples th' upright heart and pure, Instruct me, for Thou know'st; Thou from the first Wast present, and with mighty wings outspread [ 20 ] Dove-like satst brooding on the vast Abyss And mad'st it pregnant: What in me is dark Illumin, what is low raise and support; That to the highth of this great Argument I may assert Eternal Providence, [ 25 ] And justifie the wayes of God to men. mu-0.9.12/lib/tests/testdir2/bar/cur/mail2000066400000000000000000000006461250406103100201560ustar00rootroot00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Socrates" Subject: cool stuff To: "Alcibiades" Message-id: <3BE9E6535E0D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high The hour of departure has arrived, and we go our ways—I to die, and you to live. Which is better God only knows. http-emacs mu-0.9.12/lib/tests/testdir2/bar/cur/mail3000066400000000000000000000036131250406103100201540ustar00rootroot00000000000000From: Napoleon Bonaparte To: Edmond =?UTF-8?B?RGFudMOocw==?= Subject: rock on dude User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le 24 février 1815, la vigie de Notre-Dame de la Garde signala le trois-mâts le Pharaon, venant de Smyrne, Trieste et Naples. Comme d'habitude, un pilote côtier partit aussitôt du port, rasa le château d'If, et alla aborder le navire entre le cap de Morgion et l'île de Rion. Aussitôt, comme d'habitude encore, la plate-forme du fort Saint-Jean s'était couverte de curieux; car c'est toujours une grande affaire à Marseille que l'arrivée d'un bâtiment, surtout quand ce bâtiment, comme le Pharaon, a été construit, gréé, arrimé sur les chantiers de la vieille Phocée, et appartient à un armateur de la ville. Cependant ce bâtiment s'avançait; il avait heureusement franchi le détroit que quelque secousse volcanique a creusé entre l'île de Calasareigne et l'île de Jaros; il avait doublé Pomègue, et il s'avançait sous ses trois huniers, son grand foc et sa brigantine, mais si lentement et d'une allure si triste, que les curieux, avec cet instinct qui pressent un malheur, se demandaient quel accident pouvait être arrivé à bord. Néanmoins les experts en navigation reconnaissaient que si un accident était arrivé, ce ne pouvait être au bâtiment lui-même; car il s'avançait dans toutes les conditions d'un navire parfaitement gouverné: son ancre était en mouillage, ses haubans de beaupré décrochés; et près du pilote, qui s'apprêtait à diriger le Pharaon par l'étroite entrée du port de Marseille, était un jeune homme au geste rapide et à l'œil actif, qui surveillait chaque mouvement du navire et répétait chaque ordre du pilote. mu-0.9.12/lib/tests/testdir2/bar/cur/mail4000066400000000000000000000020561250406103100201550ustar00rootroot00000000000000Return-Path: Delivered-To: foo@example.com Received: from [128.88.204.56] by freemailng0304.web.de with HTTP; Mon, 07 May 2005 00:27:52 +0200 Date: Mon, 07 May 2005 00:27:52 +0200 Message-Id: <293847329847@web.de> MIME-Version: 1.0 From: =?iso-8859-1?Q? "=F6tzi" ?= To: foo@example.com Subject: =?iso-8859-1?Q?Re:=20der=20b=E4r=20und=20das=20m=E4dchen?= Precedence: fm-user Organization: http://freemail.web.de/ Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from quoted-printable to 8bit by mailhost6.ladot.com id j48MScQ30791 X-Label: \backslash X-UIDL: 93h!!\i 123 mu-0.9.12/lib/tests/testdir2/bar/cur/mail6000066400000000000000000000010621250406103100201530ustar00rootroot00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Geoff Tate" Subject: eyes of a stranger To: "Enrico Fermi" Message-id: <3BE9E6535E302944823E7A1A20D852173@msg.id> MIME-version: 1.0 X-label: @NextActions operation:mindcrime Queensrÿche Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high And I raise my head and stare Into the eyes of a stranger I've always known that the mirror never lies People always turn away From the eyes of a stranger Afraid to know what Lies behind the stare mu-0.9.12/lib/tests/testdir2/bar/new/000077500000000000000000000000001250406103100172215ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/new/.noindex000066400000000000000000000000001250406103100206540ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/tmp/000077500000000000000000000000001250406103100172305ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/bar/tmp/.noindex000066400000000000000000000000001250406103100206630ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/wom_bat/000077500000000000000000000000001250406103100173145ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/wom_bat/cur/000077500000000000000000000000001250406103100201055ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir2/wom_bat/cur/atomic000066400000000000000000000016601250406103100213070ustar00rootroot00000000000000Date: Sat, 12 Nov 2011 12:06:23 -0400 From: "Richard P. Feynman" Subject: atoms To: "Democritus" Message-id: <3BE9E6535E302944823E7A1A20D852173@msg.id> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high If, in some cataclysm, all scientific knowledge were to be destroyed, and only one sentence passed on to the next generation of creatures, what statement would contain the most information in the fewest words? I believe it is the atomic hypothesis (or atomic fact, or whatever you wish to call it) that all things are made of atoms — little particles that move around in perpetual motion, attracting each other when they are a little distance apart, but repelling upon being squeezed into one another. In that one sentence you will see an enormous amount of information about the world, if just a little imagination and thinking are applied. mu-0.9.12/lib/tests/testdir2/wom_bat/cur/rfc822.1000066400000000000000000000032451250406103100212010ustar00rootroot00000000000000Return-Path: Subject: Fwd: rfc822 From: foobar To: martin Content-Type: multipart/mixed; boundary="=-XHhVx/BCC6tJB87HLPqF" Message-Id: <1077300332.871.27.camel@example.com> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.4.5 Date: Fri, 20 Feb 2004 19:05:33 +0100 --=-XHhVx/BCC6tJB87HLPqF Content-Type: text/plain Content-Transfer-Encoding: 7bit Hello world, forwarding some RFC822 message --=-XHhVx/BCC6tJB87HLPqF Content-Disposition: inline Content-Type: message/rfc822 Return-Path: Message-ID: <9A01B19D0D605D478E8B72E1367C66340141B9C5@example.com> From: frob@example.com To: foo@example.com Subject: hopjesvla Date: Sat, 13 Dec 2003 19:35:56 +0100 MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 7bit The ship drew on and had safely passed the strait, which some volcanic shock has made between the Calasareigne and Jaros islands; had doubled Pomegue, and approached the harbor under topsails, jib, and spanker, but so slowly and sedately that the idlers, with that instinct which is the forerunner of evil, asked one another what misfortune could have happened on board. However, those experienced in navigation saw plainly that if any accident had occurred, it was not to the vessel herself, for she bore down with all the evidence of being skilfully handled, the anchor a-cockbill, the jib-boom guys already eased off, and standing by the side of the pilot, who was steering the Pharaon towards the narrow entrance of the inner port, was a young man, who, with activity and vigilant eye, watched every motion of the ship, and repeated each direction of the pilot. --=-XHhVx/BCC6tJB87HLPqF-- mu-0.9.12/lib/tests/testdir2/wom_bat/cur/rfc822.2000066400000000000000000000026611250406103100212030ustar00rootroot00000000000000From: dwarf@siblings.net To: root@eruditorum.org Subject: Fwd: test abc References: <8639ddr9wu.fsf@cthulhu.djcbsoftware> User-agent: mu 0.98pre; emacs 24.0.91.9 Date: Thu, 24 Nov 2011 14:24:00 +0200 Message-ID: <861usxr9nj.fsf@cthulhu.djcbsoftware> Content-Type: multipart/mixed; boundary="=-=-=" MIME-Version: 1.0 --=-=-= Content-Type: text/plain Saw the website. Am willing to stipulate that you are not RIST 9E03. Suspect that you are the Dentist, who yearns for honest exchange of views. Anonymous, digitally signed e-mail is the only safe vehicle for same. If you want me to believe you are not the Dentist, provide plausible explanation for your question regarding why we are building the Crypt. Yours truly, --=-=-= Content-Type: message/rfc822 Content-Disposition: inline; filename= "1322137188_3.11919.foo:2,S" Content-Description: rfc822 From: dwarf@siblings.net To: root@eruditorum.org Subject: test abc User-agent: mu 0.98pre; emacs 24.0.91.9 Date: Thu, 24 Nov 2011 14:18:25 +0200 Message-ID: <8639ddr9wu.fsf@cthulhu.djcbsoftware> Content-Type: text/plain MIME-Version: 1.0 As I stepped on this unknown middle-aged Filipina's feet during an ill-advised ballroom dancing foray, she leaned close to me and uttered some latitude and longitude figures with a conspicuously large number of significant digits of precision, implying a maximum positional error on the order of the size of a dinner plate. Gosh, was I ever curious! --=-=-=-- mu-0.9.12/lib/tests/testdir3/000077500000000000000000000000001250406103100156655ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/000077500000000000000000000000001250406103100167645ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/cur/000077500000000000000000000000001250406103100175555ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/cur/cycle0000066400000000000000000000002001250406103100206470ustar00rootroot00000000000000From: foo@example.com To: bar@example.com Subject: cycle0 Message-Id: Date: Tue, 21 Jun 2011 11:00 +0000 def mu-0.9.12/lib/tests/testdir3/cycle/cur/cycle0.0000066400000000000000000000002401250406103100210110ustar00rootroot00000000000000From: foo@example.com To: bar@example.com Subject: cycle0.0 Message-Id: References: Date: Tue, 21 Jun 2011 12:00 +0000 def mu-0.9.12/lib/tests/testdir3/cycle/cur/cycle0.0.0000066400000000000000000000002661250406103100211570ustar00rootroot00000000000000From: foo@example.com To: bar@example.com Subject: cycle0.0.0 Message-Id: References: Date: Tue, 21 Jun 2011 13:00 +0000 def mu-0.9.12/lib/tests/testdir3/cycle/cur/rogue0000066400000000000000000000002571250406103100207050ustar00rootroot00000000000000From: foo@example.com To: bar@example.com Subject: rogue0 Message-Id: References: Date: Tue, 21 Jun 2011 15:00 +0000 def mu-0.9.12/lib/tests/testdir3/cycle/new/000077500000000000000000000000001250406103100175555ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/new/.noindex000066400000000000000000000000001250406103100212100ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/tmp/000077500000000000000000000000001250406103100175645ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/cycle/tmp/.noindex000066400000000000000000000000001250406103100212170ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/000077500000000000000000000000001250406103100166545ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/000077500000000000000000000000001250406103100235575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/000077500000000000000000000000001250406103100243505ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A000066400000000000000000000001751250406103100244560ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: A Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 A mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B000066400000000000000000000001751250406103100244570ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: B Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 B mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C000066400000000000000000000001751250406103100244600ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: C Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 C mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D000066400000000000000000000002541250406103100244570ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: D Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 D mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/new/000077500000000000000000000000001250406103100243505ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/new/.noindex000066400000000000000000000000001250406103100260030ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/tmp/000077500000000000000000000000001250406103100243575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/1st-child-promotes-thread/tmp/.noindex000066400000000000000000000000001250406103100260120ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/000077500000000000000000000000001250406103100235335ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/000077500000000000000000000000001250406103100243245ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A000066400000000000000000000001751250406103100244320ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: A Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 A mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B000066400000000000000000000001751250406103100244330ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: B Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 B mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C000066400000000000000000000002541250406103100244320ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: C Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 C mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D000066400000000000000000000001751250406103100244350ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: D Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 D mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E000066400000000000000000000002541250406103100244340ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: E Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 E mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/new/000077500000000000000000000000001250406103100243245ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/new/.noindex000066400000000000000000000000001250406103100257570ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/tmp/000077500000000000000000000000001250406103100243335ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/2nd-child-promotes-thread/tmp/.noindex000066400000000000000000000000001250406103100257660ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/000077500000000000000000000000001250406103100244155ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/000077500000000000000000000000001250406103100252065ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A000066400000000000000000000002541250406103100253120ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: A Message-Id: References: In-reply-to: Aate: Sat, 17 May 2014 10:00:00 +0000 A mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X000066400000000000000000000001751250406103100253430ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: X Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 X mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y000066400000000000000000000001751250406103100253440ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Y Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 Y mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z000066400000000000000000000001751250406103100253450ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Z Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 Z mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/new/000077500000000000000000000000001250406103100252065ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/new/.noindex000066400000000000000000000000001250406103100266410ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/tmp/000077500000000000000000000000001250406103100252155ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/child-does-not-promote-thread/tmp/.noindex000066400000000000000000000000001250406103100266500ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/000077500000000000000000000000001250406103100255575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/000077500000000000000000000000001250406103100263505ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A000066400000000000000000000001751250406103100264560ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: A Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 A mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B000066400000000000000000000001751250406103100264570ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: B Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 B mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C000066400000000000000000000002541250406103100264560ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: C Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 C mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D000066400000000000000000000002541250406103100264570ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: D Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 D mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E000066400000000000000000000002541250406103100264600ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: E Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 E mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F000066400000000000000000000002671250406103100264650ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: F Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 F mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G000066400000000000000000000001751250406103100264640ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: G Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 G mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/new/000077500000000000000000000000001250406103100263505ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/new/.noindex000066400000000000000000000000001250406103100300030ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/tmp/000077500000000000000000000000001250406103100263575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/tmp/.noindex000066400000000000000000000000001250406103100300120ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/000077500000000000000000000000001250406103100240665ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/000077500000000000000000000000001250406103100246575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A000066400000000000000000000001751250406103100247650ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: A Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 A mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B000066400000000000000000000001751250406103100247660ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: B Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 B mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C000066400000000000000000000002541250406103100247650ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: C Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 C mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D000066400000000000000000000001751250406103100247700ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: D Message-Id: Date: Sat, 17 May 2014 10:00:00 +0000 D mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E000066400000000000000000000002671250406103100247730ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: E Message-Id: References: In-reply-to: Date: Sat, 17 May 2014 10:00:00 +0000 E mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/new/000077500000000000000000000000001250406103100246575ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/new/.noindex000066400000000000000000000000001250406103100263120ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/tmp/000077500000000000000000000000001250406103100246665ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/sort/grandchild-promotes-thread/tmp/.noindex000066400000000000000000000000001250406103100263210ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/000077500000000000000000000000001250406103100166245ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/cur/000077500000000000000000000000001250406103100174155ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/cur/child0.0000066400000000000000000000003101250406103100206330ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 0.0 Message-Id: References: In-reply-to: Date: Tue, 21 Jun 2011 11:10 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/child0.1000066400000000000000000000003101250406103100206340ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 0.1 Message-Id: References: In-reply-to: Date: Tue, 21 Jun 2011 11:20 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/child0.1.0000066400000000000000000000003401250406103100207750ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 0.1.0 Message-Id: References: In-Reply-To: Date: Tue, 21 Jun 2011 11:22 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/child2.0.0000066400000000000000000000005071250406103100210030ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 2.0.0 Message-Id: References: In-Reply-To: Date: Tue, 21 Jun 2011 15:02 +0000 abc note, there's no message for 'nonexistant@msg.id', so this msg should be promoted to level 2.0 mu-0.9.12/lib/tests/testdir3/tree/cur/child3.0.0.0.0000066400000000000000000000004421250406103100212760ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 3.0.0.0 Message-Id: References: 1n-Reply-To: Date: Wed, 22 Jun 2011 16:33 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/child4.0000066400000000000000000000003341250406103100206450ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 4.0 Message-Id: References: In-reply-to: Date: Tue, 24 Jun 2011 11:10 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/child4.1000066400000000000000000000003331250406103100206450ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: Re: child 4.1 Message-Id: References: In-reply-to: Date: Tue, 24 Jun 2011 11:20 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/root0000066400000000000000000000002051250406103100204000ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: root0 Message-Id: Date: Tue, 21 Jun 2011 11:00 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/root1000066400000000000000000000002051250406103100204010ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: root1 Message-Id: Date: Tue, 21 Jun 2011 12:00 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/cur/root2000066400000000000000000000002051250406103100204020ustar00rootroot00000000000000From: testfrom@example.com To: testto@example.com Subject: root2 Message-Id: Date: Tue, 21 Jun 2011 13:00 +0000 abc mu-0.9.12/lib/tests/testdir3/tree/new/000077500000000000000000000000001250406103100174155ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/new/.noindex000066400000000000000000000000001250406103100210500ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/tmp/000077500000000000000000000000001250406103100174245ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir3/tree/tmp/.noindex000066400000000000000000000000001250406103100210570ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir4/000077500000000000000000000000001250406103100156665ustar00rootroot00000000000000mu-0.9.12/lib/tests/testdir4/1220863042.12663_1.mindcrime!2,S000066400000000000000000000143251250406103100220610ustar00rootroot00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-4.9 required=3.0 tests=BAYES_00,DATE_IN_PAST_96_XX, RCVD_IN_DNSWL_MED autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 5123469CB3 for ; Thu, 7 Aug 2008 08:10:19 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Thu, 07 Aug 2008 08:10:19 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs39272wfh; Wed, 6 Aug 2008 20:15:17 -0700 (PDT) Received: by 10.65.133.8 with SMTP id k8mr2071878qbn.7.1218078916289; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received: from sourceware.org (sourceware.org [209.132.176.174]) by mx.google.com with SMTP id 28si7904461qbw.0.2008.08.06.20.15.15; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received-SPF: neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) client-ip=209.132.176.174; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) smtp.mail=gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org Received: (qmail 13493 invoked by alias); 7 Aug 2008 03:15:13 -0000 Received: (qmail 13485 invoked by uid 22791); 7 Aug 2008 03:15:12 -0000 Received: from mailgw1a.lmco.com (HELO mailgw1a.lmco.com) (192.31.106.7) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 07 Aug 2008 03:14:27 +0000 Received: from emss07g01.ems.lmco.com (relay5.ems.lmco.com [166.29.2.16])by mailgw1a.lmco.com (LM-6) with ESMTP id m773EPZH014730for ; Wed, 6 Aug 2008 21:14:25 -0600 (MDT) Received: from CONVERSION2-DAEMON.lmco.com by lmco.com (PMDF V6.3-x14 #31428) id <0K5700601NO18J@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:25 -0600 (MDT) Received: from EMSS04I00.us.lmco.com ([166.17.13.135]) by lmco.com (PMDF V6.3-x14 #31428) with ESMTP id <0K5700H5MNNWGX@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:20 -0600 (MDT) Received: from EMSS35M06.us.lmco.com ([158.187.107.143]) by EMSS04I00.us.lmco.com with Microsoft SMTPSVC(5.0.2195.6713); Wed, 06 Aug 2008 23:14:20 -0400 Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Mickey Mouse" Subject: gcc include search order To: "Donald Duck" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Content-class: urn:content-classes:message Mailing-List: contact gcc-help-help@gcc.gnu.org; run by ezmlm Precedence: klub List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-help-owner@gcc.gnu.org Delivered-To: mailing list gcc-help@gcc.gnu.org Content-Length: 3024 Hi. In my unit testing I need to change some header files (target is vxWorks, which supports some things that the sun does not). So, what I do is fetch the development tree, and then in a new unit test directory I attempt to compile the unit under test. Since this is NOT vxworks, I use sed to change some of the .h files and put them in a ./changed directory. When I try to compile the file, it is still using the .h file from the original location, even though I have listed the include path for ./changed before the include path for the development tree. Here is a partial output from gcc using the -v option GNU CPP version 3.1 (cpplib) (sparc ELF) GNU C++ version 3.1 (sparc-sun-solaris2.8) compiled by GNU C version 3.1. ignoring nonexistent directory "NONE/include" #include "..." search starts here: #include <...> search starts here: . changed /export/home4/xxx/yyyy/builds/int_rel5_latest/src/mp/interface /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/interface /usr/local/include/g++-v3 /usr/local/include/g++-v3/sparc-sun-solaris2.8 /usr/local/include/g++-v3/backward /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.8/3.1/include /usr/local/sparc-sun-solaris2.8/include /usr/include End of search list. I know the changed file is correct and that the include is not working as expected, because when I copy the file from ./changed, back into the development tree, the compilation works as expected. One more bit of information. The source that I cam compiling is in /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app And it is including files from /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common These include files should be including the files from ./changed (when they exist) but they are ignoring the .h files in the ./changed directory and are instead using other, unchanged files in the /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common directory. The gcc command line is something like TEST_DIR="." CHANGED_DIR_NAME=changed CHANGED_FILES_DIR=${TEST_DIR}/${CHANGED_DIR_NAME} CICU_HEADER_FILES="-I ${AP_INTERFACE_FILES} -I ${AP_APP_FILES} -I ${SHARED_COMMON_FILES} -I ${SHARED_INTERFACE_FILES}" HEADERS="-I ./ -I ${CHANGED_FILES_DIR} ${CICU_HEADER_FILES}" DEFINES="-DSUNRUN -DA10_DEBUG -DJOETEST" CFLAGS="-v -c -g -O1 -pipe -Wformat -Wunused -Wuninitialized -Wshadow -Wmissing-prototypes -Wmissing-declarations" printf "Compiling the UUT File\n" gcc -fprofile-arcs -ftest-coverage ${CFLAGS} ${HEADERS} ${DEFINES} ${AP_APP_FILES}/unitUnderTest.cpp I hope this explanation is clear. If anyone knows how to fix the command line so that it gets the .h files in the "changed" directory are used instead of files in the other include directories. Thanks Joe ---------------------------------------------------- Time Flies like an Arrow. Fruit Flies like a Banana mu-0.9.12/lib/tests/testdir4/1220863087.12663_19.mindcrime!2,S000066400000000000000000000071361250406103100221650ustar00rootroot00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C4D6569CB3 for ; Thu, 7 Aug 2008 08:10:08 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Thu, 07 Aug 2008 08:10:08 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34794wfh; Wed, 6 Aug 2008 13:40:29 -0700 (PDT) Received: by 10.100.33.13 with SMTP id g13mr1093301ang.79.1218055228418; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d19si15908789and.17.2008.08.06.13.40.27; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:56316 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpo3-0007Pc-Qk for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:40:27 -0400 From: anon@example.com Newsgroups: gnu.emacs.help Date: Wed, 6 Aug 2008 20:38:35 +0100 Message-ID: References: <55dbm5-qcl.ln1@news.ducksburg.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Trace: individual.net bABVU1hcJwWAuRwe/097AAoOXnGGeYR8G1In635iFGIyfDLPUv X-Orig-Path: news.ducksburg.com!news Cancel-Lock: sha1:wK7dsPRpNiVxpL/SfvmNzlvUR94= sha1:oepBoM0tJBLN52DotWmBBvW5wbg= User-Agent: slrn/pre0.9.9-120/mm/ao (Ubuntu Hardy) Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!feeder.erje.net!proxad.net!feeder1-2.proxad.net!feed.ac-versailles.fr!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Xref: news.stanford.edu gnu.emacs.help:160868 To: help-gnu-emacs@gnu.org Subject: Re: Learning LISP; Scheme vs elisp. X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 417 Lines: 11 On 2008-08-01, Thien-Thi Nguyen wrote: > warriors attack, felling foe after foe, > few growing old til they realize: to know > what deceit is worth deflection; > such receipt reversed rejection! > then their heavy arms, e'er transformed to shields: > balanced hooked charms, ploughed deep, rich yields. Aha: the exercise for the reader is to place the parens correctly. Might take me a while to solve this puzzle. mu-0.9.12/lib/tests/testdir4/1252168370_3.14675.cthulhu!2,S000066400000000000000000000015631250406103100216040ustar00rootroot00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mindcrime X-Spam-Level: Delivered-To: dfgh@floppydisk.nl Message-ID: <43A09C49.9040902@euler.org> Date: Wed, 14 Dec 2005 23:27:21 +0100 From: Fred Flintstone User-Agent: Mozilla Thunderbird 1.0.7 (X11/20051010) X-Accept-Language: nl-NL, nl, en MIME-Version: 1.0 To: dfgh@floppydisk.nl Subject: Re: xyz References: <439C1136.90504@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439B41ED.2080402@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439A1E03.3090604@euler.org> <20051211184308.GB13513@gauss.org> In-Reply-To: <20051211184308.GB13513@gauss.org> X-Enigmail-Version: 0.92.0.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-UIDL: T To: Bilbo Baggins Subject: Greetings from =?UTF-8?B?TG90aGzDs3JpZW4=?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent Organization: The Fellowship of the Ring MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's write some fünkÿ text using umlauts. Foo. mu-0.9.12/lib/tests/testdir4/1305664394.2171_402.cthulhu!2,000066400000000000000000000011561250406103100215340ustar00rootroot00000000000000From: =?UTF-8?B?TcO8?= To: Helmut =?UTF-8?B?S3LDtmdlcg==?= Subject: =?UTF-8?B?TW90w7ZyaGVhZA==?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) References: 1n-Reply-To: MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test for issue #38, where apparently searching for accented words in subject, to etc. fails. What about here? Queensrÿche. Mötley Crüe. mu-0.9.12/lib/tests/testdir4/181736.eml000066400000000000000000000040741250406103100171430ustar00rootroot00000000000000Path: uutiset.elisa.fi!feeder2.news.elisa.fi!feeder.erje.net!newsfeed.kamp.net!newsfeed0.kamp.net!nx02.iad01.newshosting.com!newshosting.com!post01.iad!not-for-mail X-newsreader: xrn 9.03-beta-14-64bit Sender: jimbo@lews (Jimbo Foobarcuux) From: jimbo@slp53.sl.home (Jimbo Foobarcuux) Reply-To: slp53@pacbell.net Subject: Re: Are writes "atomic" to readers of the file? Newsgroups: comp.unix.programmer References: <87hbblwelr.fsf@sapphire.mobileactivedefense.com> <8762s0jreh.fsf@sapphire.mobileactivedefense.com> <87hbbjc5jt.fsf@sapphire.mobileactivedefense.com> <8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk> Organization: UseNetServer - www.usenetserver.com X-Complaints-To: abuse@usenetserver.com Message-ID: Date: 08 Mar 2011 17:04:20 GMT Lines: 27 Xref: uutiset.elisa.fi comp.unix.programmer:181736 John Denver writes: >Eric the Red wrote: > >>> There _IS_ a requirement that all reads and writes to regular files >>> be atomic. There is also an ordering guarantee. Any implementation >>> that doesn't provide both atomicity and ordering guarantees is broken. >> >> But where is it specified? > >The place where it is stated most explicitly is in XSH7 2.9.7 >Thread Interactions with Regular File Operations: > > All of the following functions shall be atomic with respect to each > other in the effects specified in POSIX.1-2008 when they operate on > regular files or symbolic links: > > [List of functions that includes read() and write()] > > If two threads each call one of these functions, each call shall > either see all of the specified effects of the other call, or none > of them. > And, for the purposes of this paragraph, the two threads need not be part of the same process. jimbo mu-0.9.12/lib/tests/testdir4/encrypted!2,S000066400000000000000000000045231250406103100201340ustar00rootroot00000000000000Return-path: <> Envelope-to: peter@example.com Delivery-date: Fri, 11 May 2012 16:22:03 +0300 Received: from localhost.example.com ([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-Ux for djcb@localhost; Fri, 11 May 2012 16:21:58 +0300 Delivered-To: peter@example.com From: Brian To: Peter Subject: encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:21:42 +0300 Message-ID: <877gwi97kp.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA//YXkUB4Dy09ngCRyHWbXmV3XBjuKTr8xrak5ML1kwurav gyagOHKLMU+5CKvObChiKtXhtgU0od7IC8o+ALlHevQ0XXcqNYA2KUfX8R7akq7d Xx9mA6D8P7Y/P8juUCLBpfrCi2GC42DtvPZSUu3bL/ctUJ3InPHIfHibKF2HMm7/ gUHAKY8VPJF39dLP8GLcfki6qFdeWbxgtzmuyzHfCBCLnDL0J9vpEQBpGDFMcc4v cCbmMJaiPOmRb6U4WOuRVnuXuTztLiIn0jMslzOSFDcLTVBAsrC01r71O+XZKfN4 mIfcpcWJYKM2NQW8Jwf+8Hr84uznBqs8uTTlrmppjkAHZGqGMjiQDxLhDVaCQzMy O8PSV4xT6HPlKXOwV1OLc+vm0A0RAdSBctgZg40oFn4XdB1ur8edwAkLvc0hJKaz gyTQiPaXm2Uh2cDeEx4xNgXmwCKasqc9jAlnDC2QwA33+pw3OqgZT5h1obn0fAeR mgB+iW1503DIi/96p8HLZcr2EswLEH9ViHIEaFj/vlR5BaOncsLB0SsNV/MHRvym Xg5GUjzPIiyBZ3KaR9OIBiZ5eXw+bSrPAo/CAs0Zwxag7W3CH//oK39Qo1GnkYpc 4IQxhx4IwkzqtCnripltV/kfpGu0yA/OdK8lOjkUqCwvL97o73utXIxm21Zd3mEP /iLNrduZjMCq+goz1pDAQa9Dez6VjwRuRPTqeAac8Fx/nzrVzIoIEAt36hpuaH1l KpbmHpKgsUWcrE5iYT0RRlRRtRF4PfJg8PUmP1hvw8TaEmNfT+0HgzcJB/gRsVdy gTzkzUDzGZLhRcpmM5eW4BkuUmIO7625pM6Jd3HOGyfCGSXyEZGYYeVKzv8xbzYf QM6YYKooRN9Ya2jdcWguW0sCSJO/RZ9eaORpTeOba2+Fp6w5L7lga+XM9GLfgref Cf39XX1RsmRBsrJTw0z5COf4bT8G3/IfQP0QyKWIFITiFjGmpZhLsKQ3KT4vSe/d gTY1xViVhkjvMFn3cgSOSrvktQpAhsXx0IRazN0T7pTU33a5K0SrZajY9ynFDIw9 we7XYyVwZzYEXjGih5mTH1PhWYK5fZZEKKqaz5TyYv9SeWJ+8FrHeXUKD38SQEHM qkpl9Iv17RF4Qy9uASWwRoobhKO+GykTaBSTyw8R8ctG/hfAlnaZxQ3TwNyHWyvU 9SVJsp27ulv/W9MLZtGpEMK0ckAR164Vyou1KOn200BqxbC2tJpegNeD2TP5ZtdY HIcxkgKr0haYcDnVEf1ulSxv23pZWIexbgvVCG7dRL0eB+6O28f9CWehle10MDyM 0AYyw8Da2cu7PONMovqt4nayScyGTacFBp7c2KXR9DGZ0mcBwOjL/mGRKcVWN3MG 2auCrwn2KVWmKZI3Jp0T8KhfGBnFs9lUElpDTOiED1/2bKz6Yoc385QtWx99DFMZ IWiH5wMxkWFpzjE+GHiJ09vSbTTL4JY9eu2n5nxQmtjYMBVxQm7S7qwH =0Paa -----END PGP MESSAGE----- --=-=-=-- mu-0.9.12/lib/tests/testdir4/mail1000066400000000000000000000027721250406103100166240ustar00rootroot00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "John Milton" Subject: Fere libenter homines id quod volunt credunt To: "Julius Caesar" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 x-label: Paradise losT X-keywords: john, milton Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high OF Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, With loss of Eden, till one greater Man Restore us, and regain the blissful Seat, [ 5 ] Sing Heav'nly Muse,that on the secret top Of Oreb, or of Sinai, didst inspire That Shepherd, who first taught the chosen Seed, In the Beginning how the Heav'ns and Earth Rose out of Chaos: Or if Sion Hill [ 10 ] Delight thee more, and Siloa's Brook that flow'd Fast by the Oracle of God; I thence Invoke thy aid to my adventrous Song, That with no middle flight intends to soar Above th' Aonian Mount, while it pursues [ 15 ] Things unattempted yet in Prose or Rhime. And chiefly Thou O Spirit, that dost prefer Before all Temples th' upright heart and pure, Instruct me, for Thou know'st; Thou from the first Wast present, and with mighty wings outspread [ 20 ] Dove-like satst brooding on the vast Abyss And mad'st it pregnant: What in me is dark Illumin, what is low raise and support; That to the highth of this great Argument I may assert Eternal Providence, [ 25 ] And justifie the wayes of God to men. mu-0.9.12/lib/tests/testdir4/mail5000066400000000000000000001322261250406103100166260ustar00rootroot00000000000000From: Sitting Bull To: George Custer Subject: pics for you Mail-Reply-To: djcb@djcbsoftware.nl User-Agent: Hunkpapa/2.15.9 (Almost Unreal) Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: multipart/mixed; boundary="Multipart_Sun_Oct_17_10:37:40_2010-1" --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: text/plain; charset=US-ASCII Dude! Here are some pics! --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="sittingbull.jpg" Content-Transfer-Encoding: base64 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 KD//2Q== --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="custer.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4Q1kRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAAyAAAABsBBQABAAAAbgAAACgBAwABAAAAAgAAADEBAgAOAAAAdgAAADIBAgAUAAAAhAAAABMC CQABAAAAAQAAAGmHBAABAAAAmAAAAOYAAADIAAAAAQAAAGd0aHVtYiAyLjExLjMAMjAwNTowMTox MCAwMDo1NzowMwAGAACQBwAEAAAAMDIyMQGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA //8AAAKgCQABAAAAyAAAAAOgCQABAAAA9gAAAAAAAAAGAAMBAwABAAAABgAAABoBCQABAAAASAAA ABsBCQABAAAASAAAACgBCQABAAAAAgAAAAECBAABAAAANAEAAAICBAABAAAAJwwAAAAAAAD/2P/g ABBKRklGAAEBAAABAAEAAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAk LicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAIAAaAMBIgACEQED EQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0B AgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpD REVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEB AQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFR B2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVW V1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrC 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 pAznLa7juUCSEJMvr3qZwydRx6isi4ty3zJw1TWksjrs3kOOoJpiL0twwXlCwHoKxri6WFj8pG/k A1fe6vIODtKmu1+Gc9vcazNHeRxyZh+RXjVuc8nJ5oA4zwxp91rmpSW1nGGnZMZbgAZ5JP5V6Cvw 213Jb7TYgbcKPMbr/wB816pGsESARpGi+iqAKT7VCPlMi5+tAHisPwc8SLJk39hH/tpI+R/47XWe FvBeqeGtHnhlENxO8hlYRSH5vQDIHP1xXoSzow4ZT9DSvIO1AHiepeMNUWaWCBPsgDFWDrl8jsc9 PpXMajqdxe3BuLt90zAAsABkDp0rq/ixpTWeuwanbDC3kZDr/trwT+IIrziS6uUcJcqGWgDpPDOo pb+ILctJxITGQffp+uKKyNFtjc67YxqdyNMrfgDk/wAqKljR64vB5rK8Q6DaapCtxI0qXC7I0Kth eXAGR3+8fzrTDYNQavKRol465DpEZFI6gryP1FJMbPMEcqxRhytNkwrLInDDriqST/ay3z4lB3A+ tSrIWQq3DjqDVEl9h9oiDIC2e1V4H1KxvEuIGe2KHPmZxtqkbuazYtC5FVJ767vG/eyEr6dKAPQr P4k6tZRHcy3KKeGlXJI98VuQ+OdM1EB3nubN3IASWM4Y+gIzXksN4YcB49yjsank1L7Q4ZvlVfuj 3oC57To2vQX8jJBdJujO0hnCnP0ODXWxC6dBuLL6ZFeP2/izwp4hsIodejmtNREflvdRICjnszY5 OepGOtcvc6vqeh3Ij0jXbnyifl8mZth+maLDPU/i9Cf+EOt53YB4rtcHvypBx+leILdgDbICwrqL 7TvHPiVo7fUTdTxp8y+dIAg9+uM/rXS6D8PbDTQs+pst5cjkJ/yzU/T+L8fyp3AyfAmiXHn/ANrz I0duqkQhurk8Z+mM0V6BI+FCqAFHAA6CiobA/9kA/+EMRWh0dHA6Ly9ucy5hZG9iZS5jb20veGFw LzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQi Pz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUg NC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5 LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgog ICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6dGlm Zj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8v bnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUu Y29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50 cy8xLjEvIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAwNS0wMS0xMFQwMDowNzoyNyswMTowMCIKICAg eG1wOk1vZGlmeURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpNZXRhZGF0 YURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpDcmVhdG9yVG9vbD0iQWRv YmUgUGhvdG9zaG9wIENTIFdpbmRvd3MiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHRpZmY6 WFJlc29sdXRpb249IjIwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIyMDAvMSIKICAgdGlmZjpS ZXNvbHV0aW9uVW5pdD0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSI0Mjk0OTY3Mjk1IgogICBleGlm OlBpeGVsWERpbWVuc2lvbj0iNzU1IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iOTMwIgogICB4 bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6Zjg2ZTcwZTQtNjI5OC0xMWQ5 LTllM2YtZDQyZjM0NjM5ZGJiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ1dWlkOmY4NmU3MGU1LTYy OTgtMTFkOS05ZTNmLWQ0MmYzNDYzOWRiYiIKICAgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIi8+CiA8 L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg ICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7/2wBDAAUDBAQEAwUEBAQFBQUG BwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh7/wAARCAD2AMgDASIAAhEBAxEB/8QAHQAAAAcBAQEAAAAAAAAAAAAAAAIDBAUGBwEICf/E AEIQAAIBAwIEBAQDBgUBCAMBAAECAwAEEQUhBhIxQQcTUWEicYGRFDKhI0JSscHRCBVicoLwFiQl M5KissJDU9Lh/8QAGQEBAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAGhEBAQEBAQEBAAAAAAAAAAAA AAERAjEhEv/aAAwDAQACEQMRAD8Am8Hl60dEGN+tAbnalEBIr5+PUIoGcmllXPSgkW9LxxntVBET 2pVY/Y0tFEe+KcpFRDYRDHSjrD/pp4sdHWIUwMHhwOlJcpXr0qVeEEd6aXCxxRtLK6xou7MxwB8z TDSSpjelAtQFxxvwjaSmGXW7dnH/AOtWkH3UEUeLjjhGXATV0z7wyD/61cNTTDbG1AKcdKjBxXww RznWbUA9Mkj+lSem3+n6lE0mnXtvdopwzQyBgp9DjpTDXGQ0mY6fFD6Vzy8DpTDTMR7UOT3p0U9q KF3oaasm1F5PSnTr3pMAb1FJrGwGRRuX1o64xvRtqYG5X2oyp7UpgV1agCKAQSKFKZHLQozUEoNL Rj3oBd+lLxJsKrTsaEmnccW+d65Ehz0pzGp9KuI5HHv0pdU7Yrsa+1LqmSBitSAsabdKrXiLxfb8 IabG6xR3V/O4EVsXweXuxx2/maX4w410PhmJknlWe7A2gRhkf7j2/nWCaxxK9/qs+ohea6mbJuJd 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 Cheerio! --Multipart_Sun_Oct_17_10:37:40_2010-1-- mu-0.9.12/lib/tests/testdir4/multimime!2,FS000066400000000000000000000011601250406103100202410ustar00rootroot00000000000000Return-path: <> Envelope-to: djcb@localhost Delivery-date: Sun, 20 May 2012 09:59:51 +0300 From: Steve Jobs To: Bill Gates Subject: multimime User-agent: mu4e 0.9.8.4; emacs 23.3.1 Date: Sat, 19 May 2012 20:57:56 +0100 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain abc --=-=-= Content-Type: application/octet-stream Content-Disposition: attachment; filename="test1.C" Content-Transfer-Encoding: base64 aGVyZSBpcyBhIHNpbXBsZSB0ZXN0IGZpbGUuCg== --=-=-= Content-Type: text/plain def --=-=-=-- mu-0.9.12/lib/tests/testdir4/signed!2,S000066400000000000000000000016041250406103100174050ustar00rootroot00000000000000Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 From: Skipio To: Hannibal Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- mu-0.9.12/lib/tests/testdir4/signed-bad!2,S000066400000000000000000000016611250406103100201340ustar00rootroot00000000000000Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 From: Skipio To: Hannibal Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! But it's not good because I added this later --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- mu-0.9.12/lib/tests/testdir4/signed-encrypted!2,S000066400000000000000000000044011250406103100213760ustar00rootroot00000000000000Return-path: <> Envelope-to: karjala@localhost Delivery-date: Fri, 11 May 2012 16:37:57 +0300 From: karjala@example.com To: lapinkulta@example.com Subject: signed + encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:36:08 +0300 Message-ID: <874nrm96wn.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA/+K4kSpMa7zk+qihUkQnHSq28xYxisNQx6X5DVNjA/Qx16 uZj/40ae+PoSMTVfklP+B2S/IomuTW6dwVqS7aQ3u4MTzi+YOi11k1lEMD7hR0Wb L0i48o3/iCPuCTpnOsaLZvRL06g+oTi0BF2pgz/YdsgsBTGrTb3pkDGSlLIhvh/J P8eE3OuzkXS6d8ymJKx2S2wQJrc1AFf1BgJfgc5T0iAvcV+zIMG+PIYcVd04zVpj cORFEfvGgfxWkeX+Ks3tu/l5PA1EesnoqFdNFZm+RKBg3RFsOm8tBlJ46xJjfeHg zLgifeSLy3tOX7CvWYs9torrx7s7UOI2gV8kzBqz+a7diyCMezceeQ9l0nIRybwW C9Egp8Bpfb02iXTOGdE/vRiNItQH14GKmXf4nCSwdtQUm3yzaqY9yL3xBxAlW53e YOFfPMESt+E7IlPn0c7llWGrcdrhJbUEoGOIPezES7kdeNPzi8G1lLtvT04/SSZJ QxPH5FNzSFaYFAQSdI7TR69P7L7vtLL8ndkjY49HfLFXochQQzsqrzVxzRCruHxA zbZSRptNf9SuXEaX9buO1vlFHheGvrCKzEWa6O7JD/DiyrE/zqy4jdlh9abMCouQ GWGSbn8jk6SMTQQ2Yv/VOyFqifHZp0UJD59tyIdenpxoYu5M0lwHLNVDlRjLEwUQ AIDz1tbLoM7lxs2FOKGr8QqbKIeMfL+NUmbvVIDc4mJrOlRnHh+cZYm4Z49iTl1v bYNMYgR5nY7W6rqh0ae7ZOW0h2NzpkAwTzuf1YrSjNavd9KBwOCFtAoZhRwfwFVx ju+ByHFNnf7g/R6DekHS0pSiatM0cPDJT05atEZb+13CRHHznonmLHi+VahXjrpg cIUA8Lhjdfm6Fsabo7gNZnTTRxNBqUXKK2vJF/XLbNrH5K2BH2dCCmUNtm3yFWiM DOzaw3665Y3S6MvZdyKpatbNrVoJdBpRgPxJ1YCSEituFUqHJBStay+aRb5fVkQR w3+9hWw+Ob0+2EumKbgfQ7iMwTZBCZP4VOxkoqdHvs9aWm4N7wHtXsyCew3icbJx lyUWsDx/FI+HlQRfOqeAMxmp8kKybmHNw8oGiw+uPPUHSD1NFYVm2DtwhYll3Fvs YY7r5s3yP1ZnwxMqWI3OsExVUXs8MS4UTAgO+cggO7YidPcANbBDihBFP8mTXtni Oo5n5v+/eRoLfHmnsGcaK8EkKsfFHpbqn4gxXGcBuHaTTJ/ZhbW6bi1WWZA9ExaJ IeTDtp5Bks1pJvTjCDacvgwl3rEBM6yaeIvB7575Y/GPMTOZhawhfOxV1smMmTKI JOWYb3+PuN2cvWetkjFgH8re4sRXq22DKBZHJEWYU8sH0sACAePnIr+pkrOtGeJB t1zBqZUnrupH6ptk9n/AjbQ+XSMTEKu55gSjYLAYx1EHApx52QLkdh+ej5xCIVeY 6wS1Iipkoc6/r6F7CKctupXurNY2AlD4uQIOfD6kQgkqK4PY3hsRHQA+Zqj6oRfr kxysFJZvhgt26IeBVapFs10WuYt9iHfpbPUBQUIZCLyPAh08UdVW64Uc2DvUPy+I C+3RrmTHQPP/YNKgDQaZ3ySVEDkqjaDPmXr5K0Ibaib2dtPCLcA= =pv03 -----END PGP MESSAGE----- --=-=-=-- mu-0.9.12/lib/tests/testdir4/special!2,Sabc000066400000000000000000000005361250406103100202250ustar00rootroot00000000000000Date: Thu, 1 Jun 2012 14:57:25 -0200 From: "Rocky Balboa" To: "Ivan Drago" Subject: currying and tail optimization Message-id: <3BE9E653ef345@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Test 123. I'm a special message with special flags. mu-0.9.12/m4/000077500000000000000000000000001250406103100125345ustar00rootroot00000000000000mu-0.9.12/m4/Makefile.am000066400000000000000000000016311250406103100145710ustar00rootroot00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk EXTRA_DIST = \ libtool.m4 \ ltoptions.m4 \ ltversion.m4 \ lt~obsolete.m4 \ ltsugar.m4 mu-0.9.12/man/000077500000000000000000000000001250406103100127675ustar00rootroot00000000000000mu-0.9.12/man/Makefile.am000066400000000000000000000020471250406103100150260ustar00rootroot00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk dist_man_MANS = \ mu-add.1 \ mu-bookmarks.5 \ mu-cfind.1 \ mu-easy.1 \ mu-extract.1 \ mu-find.1 \ mu-help.1 \ mu-index.1 \ mu-mkdir.1 \ mu-remove.1 \ mu-server.1 \ mu-script.1 \ mu-verify.1 \ mu-view.1 \ mu.1 \ mug.1 mu-0.9.12/man/mu-add.1000066400000000000000000000020131250406103100142140ustar00rootroot00000000000000.TH MU ADD 1 "July 2012" "User Manuals" .SH NAME mu add\- add one or more messages to the database .SH SYNOPSIS .B mu add [] .SH DESCRIPTION \fBmu add\fR is the command to add specific message files to the database. Each file must be specified with an absolute path. .SH OPTIONS \fBmu add\fR does not have its own options, but the general options for determining the location of the database (\fI--muhome\fR) are available. See \fBmu-index(1)\fR for more information. .SH RETURN VALUE \fBmu add\fR returns 0 upon success; in general, the following error codes are returned: .nf | code | meaning | |------+-----------------------------------| | 0 | ok | | 1 | general error | | 5 | some database update error | .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) .BR mu-remove(1) mu-0.9.12/man/mu-bookmarks.5000066400000000000000000000022061250406103100154640ustar00rootroot00000000000000.TH MU-BOOKMARKS 5 "May 2011" "User Manuals" .SH NAME bookmarks \- file with bookmarks (shortcuts) for mu search expressions .SH DESCRIPTION Bookmarks are named shortcuts for search queries. They allow using a convenient name for often-used queries. The bookmarks are also visible as shortcuts in the mu experimental user interfaces, \fImug\fR and \fImug2\fR. \fBmu\fR supports bookmarks stored in a file called \fBbookmarks\fR in the mu home directory (typically, this would be \fI~/.mu/bookmarks\fR). The bookmarks file is a typical key=value \fB.ini\fR-file, which is best shown by means of an example: .nf [mu] inbox=maildir:/inbox # inbox oldhat=maildir:/archive subject:hat # archived with subject containing 'hat' .fi The \fB[mu]\fR group header is required. For practical uses of bookmarks, see mu-find(1). .SH LOCATION The bookmarks file is read from \fI/bookmarks\fR. Typically, this would be \fI~/.mu/bookmarks\fR, but this can be influenced using the \fB\-\-muhome\fR parameter for mu-find(1) and mug(1). .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" mu(1) mu-find(1) mu-0.9.12/man/mu-cfind.1000066400000000000000000000100751250406103100145560ustar00rootroot00000000000000.TH MU CFIND 1 "May 2013" "User Manuals" .SH NAME \fBmu cfind\fR is the \fBmu\fR command to find contacts in the \fBmu\fR database and export them for use in other programs. .SH SYNOPSIS .B mu cfind [options] [] .SH DESCRIPTION \fBmu cfind\fR is the \fBmu\fR command for finding \fIcontacts\fR (name and e-mail address of people who were either an e-mail's sender or receiver). There are different output formats available, for importing the contacts into other programs. .SH SEARCHING CONTACTS When you index your messages (see \fBmu index\fR), \fBmu\fR creates a list of unique e-mail addresses found and the accompanying name, and caches this list. In case the same e-mail address is used with different names, the most recent non-empty name is used. \fBmu cfind\fR starts a search for contacts that match a \fIregular expression\fR. For example: .nf $ mu cfind '@gmail\.com' .fi would find all contacts with a gmail-address, while .nf $ mu cfind Mary .fi lists all contacts with Mary in either name or e-mail address. If you do not specify a search expression, \fBmu cfind\fR returns the full list of contacts. Note, \fBmu cfind\fR does not use the database, but uses a cache file with e-mail addresses, which is populated during the indexing process. The regular expressions are Perl-compatible (as per the PCRE-library used by GRegex). .SH OPTIONS .TP \fB\-\-format\fR=\fIplain|mutt-alias|mutt-ab|wl|org-contact|bbdb|csv\fR sets the output format to the given value. The following are available: .nf | --format= | description | |-------------+-----------------------------------| | plain | default, simple list | | mutt-alias | mutt alias-format | | mutt-ab | mutt external address book format | | wl | wanderlust addressbook format | | org-contact | org-mode org-contact format | | bbdb | BBDB format | | csv | comma-separated values (*) | .fi (*) CSV is not really standardized, but \fBmu cfind\fR follows some common practices: any double-quote is replaced by a double-double quote (thus, "hello" become ""hello"", and fields with commas are put in double-quotes. Normally, this should only apply to name fields. .TP \fB\-\-personal\fR only show addresses seen in messages where one of 'my' e-mail addresses was seen in one of the address fields; this is to exclude addresses only seen in mailing-list messages. See the \fB\-\-my-address\fR parameter in \fBmu index\fR. .TP \fB\-\-after=\fR\fI\fR only show addresses last seen after \fI\fR. \fI\fR is a UNIX \fBtime_t\fR value, the number of seconds since 1970-01-01 (in UTC). From the command line, you can use the \fBdate\fR command to get this value. For example, only consider addresses last seen after 2009-06-01, you could specify .nf --after=`date +%s --date='2009-06-01'` .fi .SH RETURN VALUE \fBmu cfind\fR returns 0 upon successful completion -- that is, at least one contact was found. Anything else leads to a non-zero return value: .nf | code | meaning | |------+--------------------------------| | 0 | ok | | 1 | general error | | 2 | no matches (for 'mu cfind') | .fi .SH INTEGRATION WITH MUTT You can use \fBmu cfind\fR as an external address book server for \fBmutt\fR. For this to work, add the following to your \fImuttrc\fR: .nf set query_command = "mu cfind --format=mutt-ab '%s'" .fi Now, in mutt, you can easily search for e-mail addresses using the \fBquery\fR-command, which is (by default) accessible by pressing \fBQ\fR. .SH ENCODING \fBmu cfind\fR output is encoded according to the current locale except for \fI--format=bbdb\fR. This is hard-coded to UTF-8, and as such specified in the output-file, so emacs/bbdb can handle things correctly, without guessing. .SH BUGS Please report bugs if you find them at \fBhttps://github.com/djcb/mu/issues\fR. .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) .BR mu-find(1) .BR pcrepattern(3) mu-0.9.12/man/mu-easy.1000066400000000000000000000223001250406103100144260ustar00rootroot00000000000000.TH MU-EASY 1 "December 2012" "User Manuals" .SH NAME mu easy \- a quick introduction to mu .SH DESCRIPTION \fBmu\fR is a set of tools for dealing with e-mail messages in Maildirs. There are many options, which are all described in the man pages for the various sub-commands. This man pages jumps over all of the details and gives examples of some common use cases. If the use cases described here do not precisely do what you want, please check the more extensive information in the man page about the sub-command you are using -- for example, the mu-index or mu-find man pages. \fBNOTE\fR: the \fBindex\fR command (and therefore, the ones that depend on that, such as \fBfind\fR), require that you store your mail in the Maildir-format. If you don't do so, you can still use the other commands, but you won't be able to index/search your mail. By default, \fBmu\fR uses colorized output when it thinks your terminal is capable of doing so. If you don't like color, you can use the \fB--nocolor\fR command-line option, or set the \fBMU_NOCOLOR\fR environment variable to non-empty. .SH INDEXING YOUR E-MAIL Before you can search e-mails, you'll first need to index them: .nf \fB$ mu index\fR .fi The process can take a few minutes, depending on the amount of mail you have, the speed of your computer, hard drive etc. Usually, indexing should be able to reach a speed of a few hundred messages per second. \fBmu index\fR guesses the top-level Maildir to do its job; if it guesses wrongly, you can use the \fI--maildir\fR option to specify the top-level directory that should be processed. See the \fBmu-index\fR man page for more details. Normally, \fBmu index\fR visits all the directories under the top-level Maildir; however, you can exclude certain directories (say, the 'trash' or 'spam' folders) by creating a file called \fI.noindex\fR in the directory. When \fBmu\fR sees such a file, it will exclude this directory and its sub-directories from indexing. Also see \fB.noupdate\fR in the \fBmu-index\fR manpage. .SH SEARCHING YOUR E-MAIL After you have indexed your mail, you can start searching it. By default, the search results are printed on standard output. Alternatively, the output can take the form of Maildir with symbolic links to the found messages. This enables integration with e-mail clients; see the \fBmu-find\fR man page for details, the syntax of the search parameters and so on. Here, we just give some examples for common cases. First, let's search for all messages sent to Julius (Caesar) regarding fruit: .nf \fB$ mu find t:julius fruit\fR .fi This should return something like: .nf 2008-07-31T21:57:25 EEST John Milton Fere libenter homines id quod volunt credunt .fi This means there is a message to 'julius' with 'fruit' somewhere in the message. In this case, it's a message from John Milton. Note that the date format depends on your the language/locale you are using. How do we know that the message was sent to Julius Caesar? Well, it's not visible from the results above, because the default fields that are shown are date/sender/subject. However, we can change this using the \fI--fields\fR parameter (see the \fBmu-find\fR man page for the details): .nf \fB$ mu find --fields="t s" t:julius fruit\fR .fi In other words, display the 'To:'-field (t) and the subject (s). This should return something like: .nf Julius Caesar Fere libenter homines id quod volunt credunt .fi This is the same message found before, only with some different fields displayed. By default, \fBmu\fR uses the logical AND for the search parameters -- that is, it displays messages that match all the parameters. However, we can use logical OR as well: .nf \fB$ mu find t:julius OR f:socrates\fR .fi In other words, display messages that are either sent to Julius Caesar \fBor\fR are from Socrates. This could return something like: .nf 2008-07-31T21:57:25 EEST Socrates cool stuff 2008-07-31T21:57:25 EEST John Milton Fere libenter homines id quod volunt credunt .fi What if we want to see some of the body of the message? You can get a 'summary' of the first lines of the message using the \fI--summary\fR option, which will 'summarize' the first \fIn\fR lines of the message: .nf \fB$ mu find --summary napoleon m:/archive\fR .fi .nf 1970-01-01T02:00:00 EET Napoleon Bonaparte rock on dude Summary: Le 24 février 1815, la vigie de Notre-Dame de la Garde signala le trois-mâts le Pharaon, venant de Smyrne, Trieste et Naples. Comme d'habitude, un pilote côtier partit aussitôt du port, rasa le château .fi The summary consists of the first n lines of the message with all superfluous whitespace removed. Also note the \fBm:/archive\fR parameter in the query. This means that we only match messages in a maildir called '/archive'. .SH MORE QUERIES Let's list a few more queries that may be interesting; please note that searches for message flags, priority and date ranges are only available in mu version 0.9 or later. Get all important messages which are signed: .nf \fB$ mu find flag:signed prio:high \fR .fi Get all messages from Jim without an attachment: .nf \fB$ mu find from:jim AND NOT flag:attach\fR .fi Get all messages where Jack is in one of the contact fields: .nf \fB$ mu find contact:jack\fR .fi This uses the special contact: pseudo-field which matches (\fBfrom\fR, \fBto\fR, \fBcc\fR and \fBbcc\fR). Get all messages in the Sent Items folder about yoghurt: .nf \fB$mu find maildir:'/Sent Items' yoghurt\fR .fi Note how we need to quote search terms that include spaces. Get all unread messages where the subject mentions Ångström: .nf \fB$ mu find subject:Ångström flag:unread\fR .fi which is equivalent to: .nf \fB$ mu find subject:angstrom flag:unread\fR .fi because does mu is case-insensitive and accent-insensitive. Get all unread messages between March 2002 and August 2003 about some bird (or a Swedish rock band): .nf \fB$ mu find date:20020301..20030831 nightingale flag:unread\fR .fi Get all messages received today: .nf \fB$ mu find date:today..now\fR .fi Get all messages we got in the last two weeks about emacs: .nf \fB$ mu find date:2w..now emacs\fR .fi Another powerful feature (since 0.9.6) are wildcard searches, where you can search for the last \fIn\fR characters in a word. For example, you can search for: .nf \fB$ mu find 'subject:soc*'\fR .fi and get mails about soccer, Socrates, society, and so on. Note, it's important to quote the search query, otherwise the shell will interpret the '*'. You can also search for messages with a certain attachment using their filename, for example: .nf \fB$ mu find 'file:pic*'\fR .fi will get you all messages with an attachment starting with 'pic'. If you want to find attachments with a certain MIME-type, you can use the following: Get all messages with PDF attachments: .nf \fB$ mu find mime:application/pdf\fR .fi or even: Get all messages with image attachments: .nf \fB$ mu find 'mime:image/*'\fR .fi Note that (1) the '*' wildcard can only be used as the rightmost thing in a search query, and (2) that you need to quote the search term, because otherwise your shell will interpret the '*' (expanding it to all files in the current directory -- probably not what you want). .SH DISPLAYING MESSAGES We might also want to display the complete messages instead of the header information. This can be done using \fBmu view\fR command. Note that this command does not use the database; you simply provide it the path to a message. Therefore, if you want to display some message from a search query, you'll need its path. To get the path (think \fBl\fRocation) for our first example we can use: .nf \fB$ mu find --fields="l" t:julius fruit\fR .fi And we'll get someting like: .nf /home/someuser/Maildir/archive/cur/1266188485_0.6850.cthulhu:2, .fi We can now display this message: .nf \fB$ mu view /home/someuser/Maildir/archive/cur/1266188485_0.6850.cthulhu:2,\fR From: John Milton To: Julius Caesar Subject: Fere libenter homines id quod volunt credunt Date: 2008-07-31T21:57:25 EEST OF Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, [...] .fi .SH FINDING CONTACTS While \fBmu find\fR searches for messages, there is also \fBmu cfind\fR to find \fIcontacts\fR, that is, names + addresses. Without any search expression, \fBmu cfind\fR lists all of your contacts. .nf \fB$ mu cfind julius\fR .fi will find all contacts with 'julius' in either name or e-mail address. Note that \fBmu cfind\fR accepts a \fIregular expression\fR. \fBmu cfind\fR also supports a \fI--format=\fR-parameter, which sets the output to some specific format, so the results can be imported into another program. For example, to export your contact information to a \fBmutt\fR address book file, you can use something like: .nf \fB$ mu cfind --format=mutt-alias > ~/mutt-aliases \fR .fi Then, you can use them in \fBmutt\fR if you add something like \fBsource ~/mutt-aliases\fR to your \fImuttrc\fR. .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) .BR mu-find(1) .BR mu-mkdir(1) .BR mu-view(1) .BR mu-extract(1) mu-0.9.12/man/mu-extract.1000066400000000000000000000052471250406103100151520ustar00rootroot00000000000000.TH MU EXTRACT 1 "July 2012" "User Manuals" .SH NAME \fBmu extract\fR is the \fBmu\fR command to display and save message parts (attachments), and open them with other tools. .SH SYNOPSIS .B mu extract [options] .B mu extract [options] .SH DESCRIPTION \fBmu extract\fR is the \fBmu\fR sub-command for extracting MIME-parts (e.g., attachments) from mail messages. The sub-command works on message files, and does not require the message to be indexed in the database. For attachments, the file name used when saving it is the name of the attachment in the message. If there is no such name, or when saving non-attachment MIME-parts, a name is derived from the message-id of the message. If you specify a pattern (a case-insensitive regular expression) as the second argument, all attachments with filenames matching that pattern will be extracted. The regular expressions are Perl-compatible (as per the PCRE-library). Without any options, \fBmu extract\fR simply outputs the list of leaf MIME-parts in the message. Only 'leaf' MIME-parts (including RFC822 attachments) are considered, \fBmultipart/*\fR etc. are ignored. .SH OPTIONS .TP \fB\-a\fR, \fB\-\-save\-attachments\fR save all MIME-parts that look like attachments. .TP \fB\-\-save\-all\fR save all non-multipart MIME-parts. .TP \fB\-\-parts\fR= only consider the following numbered parts (comma-separated list). The numbers for the parts can be seen from running \fBmu extract\fR without any options but only the message file. .TP \fB\-\-target\-dir\fR= save the parts in the target directory rather than the current working directory. .TP \fB\-\-overwrite\fR overwrite existing files with the same name; by default overwriting is not allowed. .TP \fB\-\-play\fR Try to 'play' (open) the attachment with the default application for the particular file type. On MacOS, this uses the \fBopen\fR program, on other platforms it uses \fBxdg-open\fR. You can choose a different program by setting the \fBMU_PLAY_PROGRAM\fR environment variable. .SH EXAMPLES To display information about all the MIME-parts in a message file: .nf $ mu extract msgfile .fi To extract MIME-part 3 and 4 from this message, overwriting existing files with the same name: .nf $ mu extract --parts=3,4 --overwrite msgfile .fi To extract all files ending in '.jpg' (case-insensitive): .nf $ mu extract msgfile '.*\.jpg' .fi To extract an mp3-file, and play it in the default mp3-playing application: .nf $ mu extract --play msgfile 'whoopsididitagain.mp3' .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) mu-0.9.12/man/mu-find.1000066400000000000000000000476521250406103100144260ustar00rootroot00000000000000.TH MU FIND 1 "September 2013" "User Manuals" .SH NAME mu find \- find e-mail messages in the \fBmu\fR database. .SH SYNOPSIS .B mu find [options] .SH DESCRIPTION \fBmu find\fR is the \fBmu\fR command for searching e-mail message that were stored earlier using \fBmu index(1)\fR. .SH SEARCHING MAIL \fBmu find\fR starts a search for messages in the database that match some search pattern. For example: .nf $ mu find subject:snow from:john .fi would find all messages from John with 'snow' in the subject field, something like: .nf 2009-03-05 17:57:33 EET Lucia running in the snow 2009-03-05 18:38:24 EET Marius Re: running in the snow .fi Note, this the default, plain-text output, which is the default, so you don't have to use \fB--format=plain\fR. For other types of output (such as symlinks, XML or s-expressions), see the discussion in the \fBOPTIONS\fR-section below about \fB--format\fR. The search pattern is taken as a command-line parameter. If the search parameter consists of multiple parts (as in the example) they are treated as if there were a logical \fBAND\fR between them. \fBmu\fR relies on the Xapian database for its searching capabilities, so it offers all the search functionality that Xapian offers; for all the details, see: \fIhttp://xapian.org/docs/queryparser.html\fR One special feature of \fBmu\fR is that is does not distinguish between uppercase and lowercase, nor the accented or unaccented versions of characters. All match. In general, \fBmu\fR tries to be 'eager' in matching, as filtering out unwanted results is usually preferable over non matching messages. A wildcard search is a search where a \fB*\fR matches the last \fIn\fR character(s) in some string. The string must always start with one or more characters before the wildcard. \fBmu\fR supports wildcard searches for all fields except maildirs and paths. To get all mails with a subject containing a word starting with \fBcom\fR, you can use: .nf $ mu find 'subject:com*' .fi and get mails about computers, comments, compilation and so on. Note, when running from the command-line it's important to put the query in quotes, otherwise the shell would interpret the '*'. It is equally important to remember that the '*' invokes the wildcard search only when used as the rightmost character of a search term. Furthermore, it is \fBnot\fR a regular expression. The basic way to search a message is to type some words matching it, as you would do in an internet search engine. For example, .nf $ mu find monkey banana .fi will find all messages that contain both 'monkey' and 'banana' in either body or subject or one of the address-fields (to/from/cc). As mentioned, matching is case-insensitive and accent-insensitive; thus .nf $ mu find Mönkey BÄNAÑå .fi yields the same results as the example above. \fBmu\fR also recognizes prefixes for specific fields in a messages; for example: .nf $ mu find subject:penguin .fi to find messages with have the word \fBpenguin\fR in the subject field. You can abbreviate \fBsubject:\fR to just \fBs:\fR. Here is the full table of the search fields and their abbreviations: .nf cc,c Cc (carbon-copy) recipient(s) bcc,h Bcc (blind-carbon-copy) recipient(s) from,f Message sender to,t To: recipient(s) subject,s Message subject maildir,m Maildir msgid,i Message-ID prio,p Message priority ('low', 'normal' or 'high') flag,g Message Flags date,d Date-Range size,z Message size embed,e Search inside embedded text parts (messages, attachments) file,j Attachment filename mime,y MIME-type of one or more message parts tag,x Tags for the message (\fIX-Label\fR and/or \fIX-Keywords\fR) list,v Mailing list (e.g. the List-Id value) .fi There are also the special fields \fBcontact\fR, which matches all contact-fields (\fBfrom\fR, \fBto\fR, \fBcc\fR and \fBbcc\fR), and \fBrecip\fR, which matches all recipient-fields (\fBto\fR, \fBcc\fR and \fBbcc\fR). The meaning of most of the above fields should be clear, but some require some extra discussion. First, the message flags field describes certain properties of the message, as listed in the following table: .nf d,draft Draft Message f,flagged Flagged n,new New message (in new/ Maildir) p,passed Passed ('Handled') r,replied Replied s,seen Seen t,trashed Marked for deletion a,attach Has attachment z,signed Signed message x,encrypted Encrypted message l,list Mailing-list message .fi Using this, we can search e.g. for all signed messages that have an attachment: .nf $ mu find flag:signed flag:attach .fi Encrypted messages may be signed as well, but this is only visible after decrypting, and thus, is invisible to \fBmu\fR. The message-priority has three possible values: low, normal or high. We can match them using \fBprio:\fR - for example, to get all high-priority messages with a subject containing some bird: .nf $ mu find prio:high subject:nightingale .fi The Maildir field describes the directory path starting \fBafter\fR the Maildir-base path, and before the \fI/cur/\fR or \fI/new/\fR part. So for example, if there's a message with the file name \fI~/Maildir/lists/running/cur/1234.213:2,\fR, you could find it (and all the other messages in the same maildir) with: .nf $ mu find maildir:/lists/running .fi Note the starting '/'. If you want to match mails in the 'root' maildir, you can do with a single '/': .nf $ mu find maildir:/ .fi (and of course you can use the \fBm:\fR shortcut instead of \fBmaildir:\fR) The \fBdate:\fR (or \fBd:\fR) search parameter is 'special' in the fact that it takes a range of dates. For now, these dates are in ISO 8601 format (YYYYMMDDHHMM); you can leave out the right part, and mu will add the rest, depending on whether this is the beginning or end of the date interval. For example, for the beginning of the interval "201012" would be interpreted as "20101201010000", or December 1, 2010 at 00:00, while for the end of the interval, this would be interpreted as "20101231122359", or December 31, 2010 at 23:59. If you omit the left part completely, the beginning date is assumed to be January 1, year 0 at 00:00. Likewise, if you omit the right part, the end data is assumed to be to the last second of the year 9999. To get all messages between (inclusive) the 5th of May 2009 and the 2nd of June 2010, you could use: .nf $ mu find date:20090505..20100602 .fi Non-numeric characters are ignored, so the following is equivalent but more readable: .nf $ mu find date:2009-05-05..2010-06-02 .fi Precision is up to the minute and 24-hour notation for times is used, so another example would be: .nf $ mu find date:2009-05-05/12:23..2010-06-02/17:18 .fi \fBmu\fR also understand relative dates, in the form of a positive number followed by h (hour), d (day), w (week), m (30 days) or y (365 days). Some examples to explain this: .nf 5h five hours in the past 2w two weeks in the past 3m three times 30 days in the past 1y 365 days in the past .fi Using this notation, you can for example match messages between two and three weeks old: .nf $ mu find date:3w..2w .fi There are some special keywords for dates, namely 'now', meaning the present moment and 'today' for the beginning of today. So to get all messages sent or received today, you could use: .nf $ mu find date:today..now .fi The \fBsize\fR or \fBz\fR allows you to match \fIsize ranges\fR -- that is, match messages that have a byte-size within a certain range. Units (B (for bytes), K (for 1000 bytes) and M (for 1000 * 1000 bytes) are supported). For example, to get all messages between 10Kb and 2Mb (assuming SI units), you could use: .nf $ mu find size:10K..2M .fi It's important to remember that if a search term includes spaces, you should \fIquote\fr those parts. Thus, when we look at the following examples: .nf $ mu find maildir:/Sent Items yoghurt $ mu find maildir:'/Sent Items' yoghurt .fi The first query searches for messages in the \fI/Sent\fR maildir matching \fIItems\fR and \fIyoghurt\fR, while the second query searches the \fI/Sent Items\fR maildir searching for messages matching \fIyoghurt\fR. You can match \fIall\fR messages using "" (or ''): .nf $ mu find "" .fi .SH OPTIONS Note, some of the important options are described in the \fBmu(1)\fR man-page and not here, as they apply to multiple mu-commands. The \fBfind\fR-command has various options that influence the way \fBmu\fR displays the results. If you don't specify anything, the defaults are \fI\-\-fields="d f s"\fR, \fI\-\-sortfield=date\fR and \fI\-\-reverse\fR. .TP \fB\-f\fR, \fB\-\-fields\fR=\fI\fR specifies a string that determines which fields are shown in the output. This string consists of a number of characters (such as 's' for subject or 'f' for from), which will replace with the actual field in the output. Fields that are not known will be output as-is, allowing for some simple formatting. For example: .nf $ mu find subject:snow --fields "d f s" .fi would list the date, subject and sender of all messages with 'snow' in the their subject. The table of replacement characters is superset of the list mentions for search parameters; the complete list: .nf t \fBt\fRo: recipient c \fBc\fRc: (carbon-copy) recipient h Bcc: (blind carbon-copy, \fBh\fRidden) recipient d Sent \fBd\fRate of the message f Message sender (\fBf\fRrom:) g Message flags (fla\fBg\fRs) l Full path to the message (\fBl\fRocation) p Message \fBp\fRriority (high, normal, low) s Message \fBs\fRubject i Message-\fBi\fRd m \fBm\fRaildir .fi The message flags are the same ones we already saw in the message flags above. Thus, a message which is 'seen', has an attachment and is signed would have 'asz' as its corresponding output string, while an encrypted new message would have 'nx'. .TP \fB\-s\fR, \fB\-\-sortfield\fR \fR=\fI\fR and \fB\-z\fR, \fB\-\-reverse\fR specifies the field to sort the search results by, and the direction (i.e., 'reverse' means that the sort should be reverted - Z-A). The following fields are supported: .nf cc,c Cc (carbon-copy) recipient(s) bcc,h Bcc (blind-carbon-copy) recipient(s) date,d message sent date from,f message sender maildir,m maildir msgid,i message id prio,p message priority subject,s message subject to,t To:-recipient(s) .fi Thus, for example, to sort messages by date, you could specify: .nf $ mu find fahrrad --fields "d f s" --sortfield=date --reverse .fi Note, if you specify a sortfield, by default, messages are sorted in reverse (descending) order (e.g., from lowest to highest). This is usually a good choice, but for dates it may be more useful to sort in the opposite direction. .TP \fB\-n\fR, \fB\-\-maxnum=\fR If > 0, display maximally that number of entries. If not specified, all matching entries are displayed. .TP \fB\-\-summary-len=\fR If > 0, use that number of lines of the message to provide a summary. .TP \fB\-\-format\fR=\fIplain|links|xquery|xml|sexp\fR output results in the specified format. The default is \fBplain\fR, i.e normal output with one line per message. \fBlinks\fR outputs the results as a maildir with symbolic links to the found messages. This enables easy integration with mail-clients (see below for more information). See \fB\-\-linksdir\fR and \fB\-\-clearlinks\fR below. \fBxml\fR formats the search results as XML. \fBsexp\fR formats the search results as an s-expression as used in Lisp programming environments. \fBxquery\fR shows the Xapian query corresponding to your search terms. This is meant for for debugging purposes. .TP \fB\-\-linksdir\fR \fR=\fI\fR and \fB\-c\fR, \fB\-\-clearlinks\fR output the results as a maildir with symbolic links to the found messages. This enables easy integration with mail-clients (see below for more information). \fBmu\fR will create the maildir if it does not exist yet. If you specify \fB\-\-clearlinks\fR, all existing symlinks will be cleared from the target maildir; this allows for re-use of the same directory. An alternative would be to delete the target directory before, but this has a big chance of accidentally removing something that should not be removed. .nf $ mu find grolsch --linksdir=~/Maildir/search --clearlinks .fi will store links to found messages in \fI~/Maildir/search\fR. If the directory does not exist yet, it will be created. Note: when \fBmu\fR creates a Maildir for these links, it automatically inserts a \fI.noindex\fR file, to exclude the directory from \fBmu index\fR. .TP \fB\-\-after=\fR\fI\fR only show messages whose message files were last modified (\fBmtime\fR) after \fI\fR. \fI\fR is a UNIX \fBtime_t\fR value, the number of seconds since 1970-01-01 (in UTC). From the command line, you can use the \fBdate\fR command to get this value. For example, only consider messages modified (or created) in the last 5 minutes, you could specify .nf --after=`date +%s --date='5 min ago'` .fi This is assuming the GNU \fBdate\fR command. .TP \fB\-\-exec\fR=\fI\fR the \fB\-\-exec\fR command causes the \fIcommand\fR to be executed on each matched message; for example, to see the raw text of all messages matching 'milkshake', you could use: .nf $ mu find milkshake --exec='less' .fi which is roughly equivalent to: .nf $ mu find milkshake --fields="l" | xargs less .fi .TP \fB\-b\fR, \fB\-\-bookmark\fR=\fI\fR use a bookmarked search query. Using this option, a query from your bookmark file will be prepended to other search queries. See mu-bookmarks(1) for the details of the bookmarks file. .TP \fB\-\-skip\-dups\fR,\fB-u\fR whenever there are multiple messages with the same name, only show the first one. This is useful if you have copies of the same message, which is a common occurrence when using e.g. Gmail together with \fBofflineimap\fR. .TP \fB\-\-include\-related\fR,\fB-r\fR also include messages being refered to by the matched messages -- i.e.. include messages that are part of the same message thread as some matched messages. This is useful if you want Gmail-style 'conversations'. Note, finding these related messages make searches slower. .TP \fB\-t\fR, \fB\-\-threads\fR show messages in a 'threaded' format -- that is, with indentation and arrows showing the conversation threads in the list of matching messages. Messages in the threaded list are indented based on the depth in the discussion, and are prefix with a kind of arrow with thread-related information about the message, as in the following table: .nf | | normal | orphan | duplicate | |-------------+--------+--------+-----------| | first child | `-> | `*> | `=> | | other | |-> | |*> | |=> | .fi Here, an 'orphan' is a message without a parent message (in the list of matches), and a duplicate is a message whose message-id was already seen before; not this may not really be the same message, if the message-id was copied. The algorithm used for determining the threads is based on Jamie Zawinksi's description: .BR http://www.jwz.org/doc/threading.html .SS Example queries Here are some simple examples of \fBmu\fR search queries; you can make many more complicated queries using various logical operators, parentheses and so on, but in the author's experience, it's usually faster to find a message with a simple query just searching for some words. Find all messages with both 'bee' and 'bird' (in any field) .nf $ mu find bee AND bird .fi or shorter, because \fBAND\fR is implied: .nf $ mu find bee bird .fi Find all messages with either Frodo or Sam: .nf $ mu find 'Frodo OR Sam' .fi Find all messages with the 'wombat' as subject, and 'capibara' anywhere: .nf $ mu find subject:wombat capibara .fi Find all messages in the 'Archive' folder from Fred: .nf $ mu find from:fred maildir:/Archive .fi Find all unread messages with attachments: .nf $ mu find flag:attach flag:unread .fi Find all messages with PDF-attachments: .nf $ mu find mime:application/pdf .fi Find all messages with attached images: .nf $ mu find 'mime:image/*' .fi Note[1]: the argument needs to be quoted, or the shell will interpret the '*' Note[2]: the '*' wild card can only be used as the last (rightmost) part of a search term. Note[3]: non-word characters (such as € or ☺) are ignore in queries; you cannot search for them. .SS Integrating mu find with mail clients .TP \fBmutt\fR For \fBmutt\fR you can use the following in your \fImuttrc\fR; pressing the F8 key will start a search, and F9 will take you to the results. .nf # mutt macros for mu macro index "mu find --clearlinks --format=links --linksdir=~/Maildir/search " \\ "mu find" macro index "~/Maildir/search" \\ "mu find results" .fi .TP \fBWanderlust\fR \fBSam B\fR suggested the following on the \fBmu\fR-mailing list. First add the following to your Wanderlust configuration file: .nf (require 'elmo-search) (elmo-search-register-engine 'mu 'local-file :prog "/usr/local/bin/mu" ;; or wherever you've installed it :args '("find" pattern "--fields" "l") :charset 'utf-8) (setq elmo-search-default-engine 'mu) ;; for when you type "g" in folder or summary. (setq wl-default-spec "[") .fi Now, you can search using the \fBg\fR key binding; you can also create permanent virtual folders when the messages matching some expression by adding something like the following to your \fIfolders\fR file. .nf VFolders { [date:today..now]!mu "Today" [size:1m..100m]!mu "Big" [flag:unread]!mu "Unread" } .fi After restarting Wanderlust, the virtual folders should appear. \fBWanderlust (old)\fR Another way to integrate \fBmu\fR and \fBwanderlust\fR is shown below; the aforementioned method is recommended, but if that does not work for some reason, the below can be an alternative. .nf (defvar mu-wl-mu-program "/usr/local/bin/mu") (defvar mu-wl-search-folder "search") (defun mu-wl-search () "search for messages with `mu', and jump to the results" (let* ((muexpr (read-string "Find messages matching: ")) (sfldr (concat elmo-maildir-folder-path "/" mu-wl-search-folder)) (cmdline (concat mu-wl-mu-program " find " "--clearlinks --format=links --linksdir='" sfldr "' " muexpr)) (rv (shell-command cmdline))) (cond ((= rv 0) (message "Query succeeded")) ((= rv 2) (message "No matches found")) (t (message "Error running query"))) (= rv 0))) (defun mu-wl-search-and-goto () "search and jump to the folder with the results" (interactive) (when (mu-wl-search) (wl-summary-goto-folder-subr (concat "." mu-wl-search-folder) 'force-update nil nil t) (wl-summary-sort-by-date))) ;; querying both in summary and folder (define-key wl-summary-mode-map (kbd "Q") ;; => query '(lambda()(interactive)(mu-wl-search-and-goto))) (define-key wl-folder-mode-map (kbd "Q") ;; => query '(lambda()(interactive)(mu-wl-search-and-goto))) .fi .SH RETURN VALUE \fBmu find\fR returns 0 upon successful completion; if the search was performed, there needs to be a least one match. Anything else leads to a non-zero return value, for example: .nf | code | meaning | |------+--------------------------------| | 0 | ok | | 1 | general error | | 2 | no matches (for 'mu find') | | 4 | database is corrupted | .fi .SH ENCODING \fBmu find\fR output is encoded according the locale for \fI--format=plain\fR (the default), and UTF-8 for all other formats (\fIsexp\fR, \fIxml\fR). .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues If you have specific messages which are not matched correctly, please attach them (appropriately censored if needed). .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) mu-0.9.12/man/mu-help.1000066400000000000000000000010321250406103100144140ustar00rootroot00000000000000.TH MU HELP 1 "July 2012" "User Manuals" .SH NAME \fBmu help\fR is a \fBmu\fR command that gives help information about mu commands. .SH SYNOPSIS .B mu help .SH DESCRIPTION \fBmu help\fR provides help information about mu commands. .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu-index(1) .BR mu-find(1) .BR mu-cfind(1) .BR mu-mkdir(1) .BR mu-view(1) .BR mu-extract(1) .BR mu-easy(1) .BR mu-bookmarks(5) mu-0.9.12/man/mu-index.1000066400000000000000000000210371250406103100146020ustar00rootroot00000000000000.TH MU-INDEX 1 "September 2013" "User Manuals" .SH NAME mu index \- index e-mail messages stored in Maildirs .SH SYNOPSIS .B mu index [options] .SH DESCRIPTION \fBmu index\fR is the \fBmu\fR command for scanning the contents of Maildir directories and storing the results in a Xapian database. The data can then be queried using .BR mu-find(1) \. .B index understands Maildirs as defined by Daniel Bernstein for qmail(7). In addition, it understands recursive Maildirs (Maildirs within Maildirs), Maildir++. It can also deal with VFAT-based Maildirs which use '!' as the separators instead of ':' as used by \fITinymail\fR/\fIModest\fR and some other e-mail programs. E-mail messages which are not stored in something resembling a maildir leaf-directory (\fIcur\fR and \fInew\fR) are ignored, as are the cache directories for \fInotmuch\fR and \fIgnus\fR. Symlinks are not followed. If there is a file called \fI.noindex\fR in a directory, the contents of that directory and all of its subdirectories will be ignored. This can be useful to exclude certain directories from the indexing process, for example directories with spam-messages. If there is a file called \fI.noupdate\fR in a directory, the contents of that directory and all of its subdirectories will be ignored, unless we do a full rebuild (with \fB--rebuild\fR). This can be useful to speed up things you have some maildirs that never change. Note that you can still search for these messages, this only affects updating the database. The first run of \fBmu index\fR may take a few minutes if you have a lot of mail (tens of thousands of messages). Fortunately, such a full scan needs to be done only once; after that it suffices to index the changes, which goes much faster. See the 'Note on performance' below for more information. The optional 'phase two' of the indexing-process is the removal of messages from the database for which there is no longer a corresponding file in the Maildir. If you do not want this, you can use \fB\-n\fR, \fB\-\-nocleanup\fR. When \fBmu index\fR catches one of the signals \fBSIGINT\fR, \fBSIGHUP\fR or \fBSIGTERM\fR (e.g., when you press Ctrl-C during the indexing process), it tries to shutdown gracefully; it tries to save and commit data, and close the database etc. If it receives another signal (e.g., when pressing Ctrl-C once more), \fBmu index\fR will terminate immediately. .SH OPTIONS Note, some of the general options are described in the \fBmu(1)\fR man-page and not here, as they apply to multiple mu commands. .TP \fB\-m\fR, \fB\-\-maildir\fR=\fI\fR starts searching at \fI\fR. By default, \fBmu\fR uses whatever the \fBMAILDIR\fR environment variable is set to; if it is not set, it tries \fI~/Maildir\fR. See the note on mixing sub-maildirs below. .TP \fB\-\-my-address\fR=\fI\fR specifies that some e-mail address is 'my-address' (\fB\-\-my-address\fR can be used multiple times). This is used by \fBmu cfind\fR -- any e-mail address found in the address fields of a message which also has \fI\fR in one of its address fields is considered a \fIpersonal\fR e-mail address. This allows you, for example, to filter out (\fBmu cfind --personal\fR) addresses which were merely seen in mailing list messages. .TP \fB\-\-nocleanup\fR disables the database cleanup that \fBmu\fR does by default after indexing. .TP \fB\-\-rebuild\fR clear all messages from the database before indexing. \fB\-\-rebuild\fR guarantees that after the indexing has finished, there are no 'old' messages in the database anymore, which is not true with \fB\-\-reindex\fR when indexing only a part of messages (using \fB\-\-maildir\fR). For this reason, it is necessary to run \fBmu index \-\-rebuild\fR when there is an upgrade in the database format. \fBmu index\fR will issue a warning about this. .TP \fB\-\-autoupgrade\fR automatically use \fB\-y\fR, \fB\-\-empty\fR when \fBmu\fR notices that the database version is not up-to-date. This option is for use in cron scripts and the like, so they won't require any user interaction, even when mu introduces a new database version. .TP \fB\-\-xbatchsize\fR=\fI\fR set the maximum number of messages to process in a single Xapian transaction. In practice, this option is only useful if you find that \fBmu\fR is running out of memory while indexing; in that case, you can set the batch size to (for example) 1000, which will reduce memory consumption, but also substantially reduce the indexing performance. .TP \fB\-\-max-msg-size\fR=\fI\fR set the maximum size (in bytes) for messages. The default maximum (currently at 50Mb) should be enough in most cases, but if you encounter warnings from \fBmu\fR about ignoring messsage because they are too big, you may want to increase this. Note that the reason for having a maximum size is that big message require big memory allocations, which may lead to problems. .B NOTE: It is not recommended to mix maildirs and sub-maildirs within the hierarchy in the same database; for example, it's better not to index both with \fB\-\-maildir\fR=~/MyMaildir and \fB\-\-maildir\fR=~/MyMaildir/foo, as this may lead to unexpected results when searching with the 'maildir:' search parameter (see below). .SS A note on performance (i) As a non-scientific benchmark, a simple test on the author's machine (a Thinkpad X61s laptop using Linux 2.6.35 and an ext3 file system) with no existing database, and a maildir with 27273 messages: .nf $ sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches' $ time mu index --quiet 66,65s user 6,05s system 27% cpu 4:24,20 total .fi (about 103 messages per second) A second run, which is the more typical use case when there is a database already, goes much faster: .nf $ sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches' $ time mu index --quiet 0,48s user 0,76s system 10% cpu 11,796 total .fi (more than 56818 messages per second) Note that each test flushes the caches first; a more common use case might be to run \fBmu index\fR when new mail has arrived; the cache may stay quite 'warm' in that case: .nf $ time mu index --quiet 0,33s user 0,40s system 80% cpu 0,905 total .fi which is more than 30000 messages per second. .SS A note on performance (ii) As per June 2012, we did the same non-scientific benchmark, this time with an Intel) i5-2500 CPU @ 3.30GHz, an ext4 file system and a maildir with 22589 messages. .nf $ sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches' $ time mu index --quiet 27,79s user 2,17s system 48% cpu 1:01,47 total .fi (about 813 messages per second) A second run, which is the more typical use case when there is a database already, goes much faster: .nf $ sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches' $ time mu index --quiet 0,13s user 0,30s system 19% cpu 2,162 total .fi (more than 173000 messages per second) In general, \fBmu\fR has been getting faster with each release, even with relatively expensive new features such as text-normalization (for case-insensitve/accent-insensitive matching). The profiles are dominated by operations in the Xapian database now. .SH FILES By default, \fBmu index\fR stores its message database in \fI~/.mu/xapian\fR; the database has an embedded version number, and \fBmu\fR will automatically update it when it notices a different version. This allows for automatic updating of \fBmu\fR-versions, without the need to clear out any old databases. However, note that versions of \fBmu\fR before 0.7 used a different scheme, which puts the database in \fI~/.mu/xapian\-\fR. These older databases can safely be deleted. Starting from version 0.7, this manual cleanup should no longer be needed. \fBmu\fR stores logs of its operations and queries in \fI/mu.log\fR (by default, this is \fI~/.mu/mu.log\fR). Upon startup, \fBmu\fR checks the size of this log file. If it exceeds 1 MB, it will be moved to \fI~/.mu/mu.log.old\fR, overwriting any existing file of that name, and start with an empty log file. This scheme allows for continued use of \fBmu\fR without the need for any manual maintenance of log files. .SH ENVIRONMENT \fBmu index\fR uses \fBMAILDIR\fR to find the user's Maildir if it has not been specified explicitly with \fB\-\-maildir\fR=\fI\fR. If \fBMAILDIR\fR is not set, \fBmu index\fR will try \fI~/Maildir\fR. .SH RETURN VALUE \fBmu index\fR return 0 upon successful completion, and any other number greater than 0 signals an error. .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR maildir(5) .BR mu(1) .BR mu-find(1) .BR mu-cfind(1) mu-0.9.12/man/mu-mkdir.1000066400000000000000000000015651250406103100146050ustar00rootroot00000000000000.TH MU MKDIR 1 "July 2012" "User Manuals" .SH NAME mu mkdir\- create a new Maildir .SH SYNOPSIS .B mu mkdir [options] [] .SH DESCRIPTION \fBmu mkdir\fR is the \fBmu\fR command for creating Maildirs. It does \fBnot\fR use the mu database. With the \fBmkdir\fR command, you can create new Maildirs with permissions 0755. For example, .nf mu mkdir tom dick harry .fi creates three maildirs, \fItom\fR, \fIdick\fR and \fIharry\fR. If creation fails for any reason, \fBno\fR attempt is made to remove any parts that were created. This is for safety reasons. .SH OPTIONS .TP \fB\-\-mode\fR= set the file access mode for the new maildir(s) as in \fBchmod(1)\fR. .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR maildir(5) .BR mu(1) .BR chmod(1) mu-0.9.12/man/mu-remove.1000066400000000000000000000021251250406103100147650ustar00rootroot00000000000000.TH MU REMOVE 1 "July 2012" "User Manuals" .SH NAME \fBmu remove\fR is the \fBmu\fR command to remove messages from the database. .SH SYNOPSIS .B mu remove [options] [] .SH DESCRIPTION \fBmu remove\fR removes specific messages from the database, each of them specified by their filename. The files do not have to exist in the file system. .SH OPTIONS \fBmu remove\fR does not have its own options, but the general options for determining the location of the database (\fI--muhome\fR) are available. See \fBmu-index(1)\fR for more information. .SH RETURN VALUE \fBmu remove\fR returns 0 upon success; in general, the following error codes are returned: .nf | code | meaning | |------+-----------------------------------| | 0 | ok | | 1 | general error | | 5 | some database update error | .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) .BR mu-add(1) mu-0.9.12/man/mu-script.1000066400000000000000000000040011250406103100147670ustar00rootroot00000000000000.TH MU SCRIPT 1 "June 2013" "User Manuals" .SH NAME mu script\- show the available mu scripts, and run them. .SH SYNOPSIS .B mu script [options] [] .B mu [] .SH DESCRIPTION \fBmu script\fR is the \fBmu\fR command to list available \fBmu\fR scripts. The scripts are to be implemented in the Guile programming language, and therefore only work if your \fBmu\fR is built with support for Guile. In addition, many scripts require you to have \fBgnuplot\fR installed. Without any parameters, \fBmu script\fR lists the available scripts. If you provide a pattern (a regular expression), only the scripts whose name or one-line description match this pattern are listed. See the examples below. \fBmu\fR ships with a number of scripts. .SH OPTIONS .TP \fB\-\-verbose\fR,\fB\-v\fR when listing the available scripts, show the long descriptions. \fB\-\-\fR all options on the right side of the \fB\-\-\fR are passed to the script. .SH EXAMPLES List all available scripts (one-line descriptions): .nf $ mu script .fi List all available scripts matching \fImonth\fR (long descriptions): .nf $ mu script -v month .fi Run the \fImsgs-per-month\fR script for messages matching 'hello', and pass it the \fI--textonly\fR parameter: .nf $ mu msgs-per-month --query=hello --textonly .fi .SH RETURN VALUE \fBmu script\fR returns 0 when all went well, and returns some non-zero error code when this is not the case. .SH FILES You can make your own Scheme scripts accessible through \fBmu script\fR by putting them in \fI/scripts\fR (which is typically \fI~/.mu/scripts\fR). It is a good idea to document the scripts by using some special comments in the source code: .nf ;; INFO: this is my script -- one-line description ;; INFO: (longer description) ;; INFO: --option1= (describe option1) ;; INFO: etc. .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR guile(1) mu-0.9.12/man/mu-server.1000066400000000000000000000164301250406103100150020ustar00rootroot00000000000000.TH MU-SERVER 1 "September 2012" "User Manuals" .SH NAME mu server \- the mu backend for the mu4e e-mail client .SH SYNOPSIS .B mu server [options] .SH DESCRIPTION \fBmu server\fR starts a simple shell in which one can query and manipulate the mu database. The output of the commands is terms of Lisp symbolic expressions (s-exps). \fBmu server\fR is not meant for use by humans; instead, it is designed specifically for the \fBmu4e\fR e-mail client. In this man-page, we document the commands \fBmu server\fR accepts, as well as their responses. In general, the commands sent to the server are of the form .nf cmd: []* .fi where each of the parameters is prefixed by their name and a colon. For example, to view a certain message, the command would be: .nf cmd:view docid:12345 .fi Parameters can be sent in any order, and parameters not used by a certain command are simply ignored. .SH OUTPUT FORMAT \fBmu server\fR accepts a number of commands, and delivers its results in the form: .nf \\376\\377 .fi \\376 (one byte 0xfe), followed by the length of the s-expression expressed as an hexadecimal number, followed by another \\377 (one byte 0xff), followed by the actual s-expression. By prefixing the expression with its length, it can be processed more efficiently. The \\376 and \\377 were chosen since they never occur in valid UTF-8 (in which the s-expressions are encoded). .SH COMMAND AND RESPONSE .TP .B add Using the \fBadd\fR command, we can add a message to the database. .nf -> cmd:add path: maildir: <- (:info add :path :docid ) .fi .TP .B compose Using the \fBcompose\fR command, we get the (original) message, and tell what to do with it. The user-interface is then expected to pre-process the message, e.g. set the subject, sender and recipient for a reply message. Messages of type 'new' don't use the docid: parameter, the other ones do. .nf -> cmd:compose type: [docid:] <- (:compose :original :include ( is an s-expression describing the attachments to include in the message; this currently only applies to message we are forwarding. This s-expression looks like: .nf (:file-name :mime-type :disposition ) .fi .TP .B contacts Using the \fBcompose\fR command, we can retrieve an s-expression with all known contacts (name + e-mail address). For the details, see \fBmu-cfind(1)\fR. .nf -> cmd:contacts [personal:true|false] [after:] <- (:contacts ((:name abc :mail foo@example.com ...) ...) .fi .TP .B extract Using the \fBextract\fR command we can save and open attachments. .nf -> cmd:extract action: index: [path:] [what: [param:]] .fi If the action is 'save', the path argument is required; the attachment will be saved, and a message .nf <- (:info save :message "... has been saved") .fi is sent. If the action is 'open', the attachment will saved to a temporary file, after which it will be opened with the default handler for this kind of file (see \fBmu-extract(1)\fR), and a message .nf <- (:info open :message "... has been opened") .fi is sent. If the action is 'temp', the arguments 'what' is required. The attachment will saved to a temporary file, and the following message is sent: .nf <- (:temp :what :param cmd:find query:"" [threads:true|false] [sortfield:] [reverse:true|false] [maxnum:] .fi The \fBquery\fR-parameter provides the search query; the \fBthreads\fR-parameter determines whether the results will be returned in threaded fashion or not; the \fBsortfield\fR-parameter (a string, "to", "from", "subject", "date", "size", "prio") sets the search field, the \fBreverse\fR-parameter, if true, set the sorting order Z->A and, finally, the \fBmaxnum\fR-parameter limits the number of results to return (<= 0 means 'unlimited'). First, this will return an 'erase'-sexp, to clear the buffer from possible results from a previous query. .nf <- (:erase t) .fi This will return a series of 0 up to s-expression corresponding to each message found (if there's no maxnum, all results will be returned). The information message s-exps this function returns do not contain the message body; the \fBview\fR command is for that. .nf <- (...) .fi and finally, we receive: .nf <- (:found ) .fi .TP .B guile The \fBguile\fR command is reserved for future use. .TP .B index Using the \fBindex\fR command, we can (re)index the database, similar to what \fBmu find\fR does. The \fBmy-addresses\fR parameter (optionally) registers 'my' email addresses; see the documentation for \fBmu_store_set_my_addresses\fR. .nf -> cmd:index path: [my-addresses:] .fi As a response, it will send (for each 500 messages): .nf (:info index :status running :processed :updated ) .fi and finally: .nf (:info index :status complete :processed :cleaned-up ) .fi .TP .B mkdir Using the \fBmkdir\fR command, we can create a new maildir. .nf -> cmd:mkdir path: <- (:info mkdir :message " has been created") .fi .TP .B move Using the \fBmove\fR command, we can move messages to another maildir or change its flags (which ultimately means it is being move to a different filename), and update the database correspondingly. The function returns an s-exp describing the updated message, so that it can be updated in the user interface. .nf -> cmd:move docid:|msgid: [maildir:] [flags:] <- (:update :move t) .fi One of docid and msgid must be specified to identify the message. At least one of maildir and flags must be specified. .TP .B ping The \fBping\fR command provokes a \fBpong\fR response. It is used for the initial handshake between \fBmu4e\fR and \fBmu server\fR. .nf -> cmd:ping <- (:pong "mu" :version :doccount ) .fi .TP .B remove Using the \fBremove\fR command, we can remove the message from disk, and update the database accordingly. .nf -> cmd:remove docid: <- (:remove ) .fi .TP .B view Using the \fBview\fR command, we can retrieve all information (including the body) of a particular e-mail message. If the optional parameter \fBextract-images\fR is \fBtrue\fR, extract images to temp files, and include links to them in the returned s-exp. If the optional parameter \fBuse-agent\fR is \fBtrue\fR, try to use \fBgpg-agent\fR when verifying PGP/GPG message parts. If the optional parameter \fBauto-retrieve-key\fR is \fBtrue\fR, attempt to retrieve public keys online automatically. .nf -> cmd:view docid:|msgid: [extract-images:true] [use-agent:false] [auto-retrieve-key:false] <- (:view ) .fi or, alternatively: .nf -> cmd:view path: [extract-images:true] [use-agent:false] [auto-retrieve-key:false] <- (:view ) .fi .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) mu-0.9.12/man/mu-verify.1000066400000000000000000000036261250406103100150030ustar00rootroot00000000000000.TH MU VERIFY 1 "September 2012" "User Manuals" .SH NAME mu verify\- verify message signatures and display information about them .SH SYNOPSIS .B mu verify [options] .SH DESCRIPTION \fBmu verify\fR is the \fBmu\fR command for verifying message signatures (such as PGP/GPG signatures) and displaying information about them. The sub-command works on message files, and does not require the message to be indexed in the database. \fBmu verify\fR depends on \fBgpg\fR, and uses the one it finds in your \fBPATH\fR. If you want to use another one, you need to set \fBMU_GPG_PATH\fB to the full path to the desired \fBgpg\fR. .SH OPTIONS .TP \fB\-r\fR, \fB\-\-auto\-retrieve\fR attempt to find keys online (see the \fBauto-key-retrieve\fR option in the \fBgnupg(1)\fR documentation). \" .TP \" \fB\-u\fR, \fB\-\-use\-agent\fR attempt to use the GPG-agent (see the the \" \fBgnupg-agent(1)\fR documentation). Note that GPG-agent is running many \" desktop-evironment; you can check whether this is the case using: \" .nf \" $ env | grep GPG_AGENT_INFO \" .fi .SH EXAMPLES To display aggregated (one-line) information about the signatures in a message: .nf $ mu verify --verbose msgfile .fi To display information about all the signatures: .nf $ mu verify --verbose msgfile .fi If you only want to use the exit code, you can use: .nf $ mu verify --quiet msgfile .fi which does not give any output. .SH RETURN VALUE \fBmu verify\fR returns 0 when all signatures could be verified to be good, and returns some non-zero error code when this is not the case. .nf | code | meaning | |------+--------------------------------| | 0 | ok | | 1 | some non-verified signature(s) | .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR gpg(1) mu-0.9.12/man/mu-view.1000066400000000000000000000024231250406103100144430ustar00rootroot00000000000000.TH MU VIEW 1 "June 2013" "User Manuals" .SH NAME mu view\- display an e-mail message file .SH SYNOPSIS .B mu view [options] [] .SH DESCRIPTION \fBmu view\fR is the \fBmu\fR command for displaying e-mail message files. It works on message files and does \fInot\fR require the message to be indexed in the database. The command shows some common headers (From:, To:, Cc:, Bcc:, Subject: and Date:), the list of attachments and the plain-text body of the message (if any). .SH OPTIONS .TP \fB\-\-summary-len\fR=\fI\fR instead of displaying the full message, output a summary based upon the first \fI\fR lines of the message. .TP \fB\-\-terminate\fR terminate messages with \\f (\fIform-feed\fR) characters when displaying them. This is useful when you want to further process them. .TP \fB\-\-decrypt\fR attempt to decrypt encrypted message bodies. This is only possible if \fBmu\fR was built with crypto-support. Users are strongly recommended to use \fBgpg-agent\fR; however, if needed, \fBmu\fR will request the user password from the console. .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) .BR mu-index(1) .BR gpg(1) .BR gpg-agent(1) mu-0.9.12/man/mu.1000066400000000000000000000150351250406103100134760ustar00rootroot00000000000000.TH MU 1 "March 2013" "User Manuals" .SH NAME mu \- a set of tools to deal with Maildirs and message files, in particular to index and search e-mail messages. .SH SYNOPSIS In alphabetical order: .B mu [options] general mu command .B mu add add specific messages to the database .B mu cfind [options] [] find contacts .B mu extract [options] [] [] extract attachments and other MIME-parts .B mu find [options] find messages .B mu index [options] (re)index the messages in a Maildir .B mu mkdir [options] [] create a new Maildir .B mu remove [options] remove specific messages from the database .B mu script [options] run a mu (Guile) script .B mu server [options] start a server process (for \fBmu4e\fR-internal use) .B mu view [] view a specific message .SH DESCRIPTION \fBmu\fR is a set of tools for dealing with Maildirs and the e-mail messages in them. \fBmu\fR's main function is to enable searching of e-mail messages. It does so by periodically scanning a Maildir directory tree and analyzing the e-mail messages found ('indexing'). The results of this analysis are stored in a database, which can then be queried. In addition to indexing and searching, \fBmu\fR also offers functionality for viewing messages, extracting attachments and creating maildirs, and searching and exporting contact information. \fBmu\fR can be used from the command line or can be integrated with various e-mail clients. This manpage gives a general overview of the available commands (\fBindex\fR, \fBfind\fR, etc.); each \fBmu\fR command has its own man-page as well. .SH COMMANDS \fBmu\fR offers the following commands: .TP \fBindex\fR for indexing (analyzing) the contents of your Maildirs, and storing the information in a database. See .BR mu-index(1) \. .TP \fBfind\fR for finding messages in your database, using certain search parameters. See .BR mu-find(1) \. .TP \fBcfind\fR for finding contacts (names + e-mail addresses) matching a certain expression, and exporting the results in various formats for use in other programs. .BR mu-cfind(1) \. .TP \fBview\fR for displaying e-mail messages. See .BR mu-view(1) \. .TP \fBmkdir\fR for creating Maildirs. See .BR mu-mkdir(1) \. .TP \fBextract\fR for extract MIME-parts (such as attachments) from messages. See .BR mu-extract(1) \. .SH COLORS Some \fBmu\fR sub-commands support colorized output, and do so by default. If you don't want colors, you can use \fI--nocolor\fR. Currently, \fBmu find\fR, \fBmu view\fR, \fBmu cfind\fR and \fBmu extract\fR support colors. .SH ENCODING \fBmu\fR's output is in the current locale, with the exceptions of the output specifically meant for output to UTF8-encoded files. In practice, this means that the output of commands \fBindex\fR, \fBview\fR, \fBextract\fR is always encoded according to the current locale. The same is true for \fBfind\fR and \fBcfind\fR, with some exceptions, where the output is always UTF-8, regardless of the locale. For \fBcfind\fR the exception is \fI--format=bbdb\fR. This is hard-coded to UTF-8, and as such specified in the output-file, so emacs/bbdb can handle it correctly without guessing. For \fBfind\fR the output is encoded according the locale for \fI--format=plain\fR (the default), and UTF-8 for all other formats (\fIjson\fR, \fIsexp\fR, \fIxml\fR). .SH DATABASE AND FILE Commands \fBmu index\fR and \fBfind\fR and \fBcfind\fR work with the database, while the other ones work on invidual mail files. Hence, running \fBview\fR, \fBmkdir\fR and \fBextract\fR does not require the mu database. The various commands are discussed in more detail in their own separate man-pages; here the general options are discussed. .SH OPTIONS \fBmu\fR offers several general options that apply to all commands, including \fBmu\fR without any command. .TP \fB\-\-muhome\fR causes \fBmu\fR to use an alternative directory to store and read its database and logs. By default, \fI~/.mu\fR is used. .TP \fB\-d\fR, \fB\-\-debug\fR makes \fBmu\fR generate extra debug information, useful for debugging the program itself. By default, debug information goes to the log file, \fI~/.mu/log/mu.log\fR. It can safely be deleted when \fBmu\fR is not running. When running with \fB--debug\fR option, the log file can grow rather quickly. See the note on logging below. .TP \fB\-q\fR, \fB\-\-quiet\fR causes \fBmu\fR not to output informational messages and progress information to standard output, but only to the log file. Error messages will still be sent to standard error. Note that \fBmu index\fR is \fBmuch\fR faster with \fB\-\-quiet\fR, so it is recommended you use this option when using \fBmu\fR from scripts etc. .TP \fB\-\-log-stderr\fR causes \fBmu\fR to \fBnot\fR output log messages to standard error, in addition to sending them to the log file. .TP \fB\-v\fR, \fB\-\-version\fR prints \fBmu\fR version and copyright information. .TP \fB\-h\fR, \fB\-\-help\fR lists the various command line options, while \fB\-\-help\-index\fR, \fB\-\-help\-find\fR and \fB\-\-help\-all\fR list only the options for respectively the specified command or for all commands. .SH ERROR CODES The various mu subcommands typically exit with 0 (zero) upon success, and non-zero when some error occurred. The table lists the various error codes. .nf exit code | error ----------+------------------------------------------- 1 | MU_ERROR 2 | MU_ERROR_IN_PARAMETERS 3 | MU_ERROR_INTERNAL 4 | MU_ERROR_NO_MATCHES | 11 | MU_ERROR_XAPIAN | 13 | MU_ERROR_XAPIAN_QUERY 14 | MU_ERROR_XAPIAN_DIR_NOT_ACCESSIBLE 15 | MU_ERROR_XAPIAN_NOT_UP_TO_DATE 16 | MU_ERROR_XAPIAN_MISSING_DATA 17 | MU_ERROR_XAPIAN_CORRUPTION 18 | MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK 30 | MU_ERROR_GMIME | 50 | MU_ERROR_CONTACTS 51 | MU_ERROR_CONTACTS_CANNOT_RETRIEVE | 70 | MU_ERROR_FILE 71 | MU_ERROR_FILE_INVALID_NAME 72 | MU_ERROR_FILE_CANNOT_LINK 73 | MU_ERROR_FILE_CANNOT_OPEN 74 | MU_ERROR_FILE_CANNOT_READ 75 | MU_ERROR_FILE_CANNOT_CREATE 76 | MU_ERROR_FILE_CANNOT_MKDIR 77 | MU_ERROR_FILE_STAT_FAILED 78 | MU_ERROR_FILE_READDIR_FAILED 79 | MU_ERROR_FILE_INVALID_SOURCE .fi .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu-index(1) .BR mu-find(1) .BR mu-cfind(1) .BR mu-mkdir(1) .BR mu-view(1) .BR mu-extract(1) .BR mu-easy(1) .BR mu-bookmarks(5) mu-0.9.12/man/mug.1000066400000000000000000000016311250406103100136420ustar00rootroot00000000000000.TH MUG 1 "November 2010" "User Manuals" .SH NAME mug \- a graphical user interface for \fBmu\fR mail searches .SH SYNOPSIS .B mug [options] .SH DESCRIPTION \fBmug\fR is a program for doing e-mail searches, using the \fBmu\fR-backend for the actual searches and a GTK+ user-interface. \fBmug\fR is considered merely a toy-demonstration of the mu-framework, but can nevertheless be quite useful. If \fBmug\fR finds the \fIbookmarks\fR-file in the mu-home directory, the bookmarks from this file will be shown in mug as a list of buttons in the left toolbar. .SH OPTIONS .TP \fB\-\-muhome\fR causes \fBmug\fR to use an alternative directory to read its database and bookmarks and write its logs. By default, \fI~/.mu\fR is used. .SH BUGS Please report bugs if you find them: .BR https://github.com/djcb/mu/issues .SH AUTHOR Dirk-Jan C. Binnema .SH "SEE ALSO" .BR mu(1) mu-bookmarks(5) mu-0.9.12/mu/000077500000000000000000000000001250406103100126355ustar00rootroot00000000000000mu-0.9.12/mu/Makefile.am000066400000000000000000000037121250406103100146740ustar00rootroot00000000000000## Copyright (C) 2010-2013 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # enforce compiling guile (optionally) first,then this dir first # before decending into tests/ SUBDIRS= . tests AM_CPPFLAGS=-I${top_srcdir}/lib $(GLIB_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement \ -Wno-variadic-macros \ -DMU_SCRIPTS_DIR="\"$(pkgdatadir)/scripts/\"" AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter bin_PROGRAMS= \ mu # note, mu.cc is only '.cc' and not '.c' because libmu must explicitly # be linked as c++, not c. mu_SOURCES= \ mu.cc \ mu-cmd-cfind.c \ mu-config.c \ mu-config.h \ mu-cmd-extract.c \ mu-cmd-find.c \ mu-cmd-index.c \ mu-cmd-server.c \ mu-cmd-script.c \ mu-cmd.c \ mu-cmd.h BUILT_SOURCES= \ mu-help-strings.h mu-help-strings.h: mu-help-strings.txt mu-help-strings.awk $(AWK) -f ${top_srcdir}/mu/mu-help-strings.awk < $< > $@ mu_LDADD= \ ${top_builddir}/lib/libmu.la \ $(GLIB_LIBS) EXTRA_DIST= \ mu-help-strings.awk \ mu-help-strings.txt CLEANFILES=$(BUILT_SOURCES) mu-0.9.12/mu/mu-cmd-cfind.c000066400000000000000000000206371250406103100152540ustar00rootroot00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include "mu-cmd.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-contacts.h" #include "mu-runtime.h" /** * guess the last name for the given name; clearly, * this is just a rough guess for setting an initial value. * * @param name a name * * @return the last name, as a newly allocated string (free with * g_free) */ static gchar* guess_last_name (const char *name) { const gchar *lastsp; if (!name) return g_strdup (""); lastsp = g_strrstr (name, " "); return g_strdup (lastsp ? lastsp + 1 : ""); } /** * guess the first name for the given name; clearly, * this is just a rough guess for setting an initial value. * * @param name a name * * @return the first name, as a newly allocated string (free with * g_free) */ static gchar* guess_first_name (const char *name) { const gchar *lastsp; if (!name) return g_strdup (""); lastsp = g_strrstr (name, " "); if (lastsp) return g_strndup (name, lastsp - name); else return g_strdup (name); } /** * guess some nick name for the given name; if we can determine an * first name, last name, the nick will be first name + the first char * of the last name. otherwise, it's just the first name. clearly, * this is just a rough guess for setting an initial value for nicks. * * @param name a name * * @return the guessed nick, as a newly allocated string (free with g_free) */ static gchar* cleanup_str (const char* str) { gchar *s; const gchar *cur; unsigned i; if (mu_str_is_empty(str)) return g_strdup (""); s = g_new0 (char, strlen(str) + 1); for (cur = str, i = 0; *cur; ++cur) { if (ispunct(*cur) || isspace(*cur)) continue; else s[i++] = *cur; } return s; } static gchar* guess_nick (const char* name) { gchar *fname, *lname, *nick; gchar initial[7]; fname = guess_first_name (name); lname = guess_last_name (name); /* if there's no last name, use first name as the nick */ if (mu_str_is_empty(fname) || mu_str_is_empty(lname)) { g_free (lname); nick = fname; goto leave; } memset (initial, 0, sizeof(initial)); /* couldn't we get an initial for the last name? */ if (g_unichar_to_utf8 (g_utf8_get_char (lname), initial) == 0) { g_free (lname); nick = fname; goto leave; } nick = g_strdup_printf ("%s%s", fname, initial); g_free (fname); g_free (lname); leave: { gchar *tmp; tmp = cleanup_str (nick); g_free (nick); nick = tmp; } return nick; } static void print_header (MuConfigFormat format) { switch (format) { case MU_CONFIG_FORMAT_BBDB: g_print (";; -*-coding: utf-8-emacs;-*-\n" ";;; file-version: 6\n"); break; case MU_CONFIG_FORMAT_MUTT_AB: g_print ("Matching addresses in the mu database:\n"); break; default: break; } } static void each_contact_bbdb (const char *email, const char *name, time_t tstamp) { char *fname, *lname, *now, *timestamp; fname = guess_first_name (name); lname = guess_last_name (name); now = mu_date_str ("%Y-%m-%d", time(NULL)); timestamp = mu_date_str ("%Y-%m-%d", tstamp); g_print ("[\"%s\" \"%s\" nil nil nil nil (\"%s\") " "((creation-date . \"%s\") (time-stamp . \"%s\")) nil]\n", fname, lname, email, now, timestamp); g_free (now); g_free (timestamp); g_free (fname); g_free (lname); } static void each_contact_mutt_alias (const char *email, const char *name) { gchar *nick; if (!name) return; nick = guess_nick (name); mu_util_print_encoded ("alias %s %s <%s>\n", nick, name, email); g_free (nick); } static void each_contact_wl (const char *email, const char *name) { gchar *nick; if (!name) return; nick = guess_nick (name); mu_util_print_encoded ("%s \"%s\" \"%s\"\n", email, nick, name); g_free (nick); } static void each_contact_org_contact (const char *email, const char *name) { if (name) mu_util_print_encoded ( "* %s\n:PROPERTIES:\n:EMAIL: %s\n:END:\n\n", name, email); } static void print_csv_field (const char *str) { char *s; if (!str) return; s = mu_str_replace (str, "\"", "\"\""); if (strchr (s, ',')) mu_util_print_encoded ("\"%s\"", s); else mu_util_print_encoded ("%s", s); g_free (s); } static void each_contact_csv (const char *email, const char *name) { print_csv_field (name); mu_util_print_encoded (","); print_csv_field (email); mu_util_print_encoded ("\n"); } static void print_plain (const char *email, const char *name, gboolean color) { if (name) { if (color) fputs (MU_COLOR_MAGENTA, stdout); mu_util_fputs_encoded (name, stdout); fputs (" ", stdout); } if (color) fputs (MU_COLOR_GREEN, stdout); mu_util_fputs_encoded (email, stdout); if (color) fputs (MU_COLOR_DEFAULT, stdout); fputs ("\n", stdout); } struct _ECData { MuConfigFormat format; gboolean color, personal; time_t after; }; typedef struct _ECData ECData; static void each_contact (const char *email, const char *name, gboolean personal, time_t tstamp, unsigned freq, ECData *ecdata) { if (ecdata->personal && !personal) return; if (tstamp < ecdata->after) return; switch (ecdata->format) { case MU_CONFIG_FORMAT_MUTT_ALIAS: each_contact_mutt_alias (email, name); break; case MU_CONFIG_FORMAT_MUTT_AB: mu_util_print_encoded ("%s\t%s\t\n", email, name ? name : ""); break; case MU_CONFIG_FORMAT_WL: each_contact_wl (email, name); break; case MU_CONFIG_FORMAT_ORG_CONTACT: each_contact_org_contact (email, name); break; case MU_CONFIG_FORMAT_BBDB: each_contact_bbdb (email, name, tstamp); break; case MU_CONFIG_FORMAT_CSV: each_contact_csv (email, name); break; default: print_plain (email, name, ecdata->color); } } static MuError run_cmd_cfind (const char* pattern, gboolean personal, time_t after, MuConfigFormat format, gboolean color, GError **err) { gboolean rv; MuContacts *contacts; size_t num; ECData ecdata; ecdata.personal = personal; ecdata.after = after; ecdata.format = format; ecdata.color = color; contacts = mu_contacts_new (mu_runtime_path(MU_RUNTIME_PATH_CONTACTS)); if (!contacts) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_CONTACTS_CANNOT_RETRIEVE, "could not retrieve contacts"); return MU_ERROR_CONTACTS_CANNOT_RETRIEVE; } print_header (format); rv = mu_contacts_foreach (contacts, (MuContactsForeachFunc)each_contact, &ecdata, pattern, &num); mu_contacts_destroy (contacts); if (num == 0) { g_warning ("no matching contacts found"); return MU_ERROR_NO_MATCHES; } return rv ? MU_OK : MU_ERROR_CONTACTS; } static gboolean cfind_params_valid (MuConfig *opts) { switch (opts->format) { case MU_CONFIG_FORMAT_PLAIN: case MU_CONFIG_FORMAT_MUTT_ALIAS: case MU_CONFIG_FORMAT_MUTT_AB: case MU_CONFIG_FORMAT_WL: case MU_CONFIG_FORMAT_BBDB: case MU_CONFIG_FORMAT_CSV: case MU_CONFIG_FORMAT_ORG_CONTACT: break; default: g_warning ("invalid output format %s", opts->formatstr ? opts->formatstr : ""); return FALSE; } /* only one pattern allowed */ if (opts->params[1] && opts->params[2]) { g_warning ("usage: mu cfind [options] []"); return FALSE; } return TRUE; } MuError mu_cmd_cfind (MuConfig *opts, GError **err) { g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_CFIND, MU_ERROR_INTERNAL); if (!cfind_params_valid (opts)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS, "invalid parameters"); return MU_ERROR_IN_PARAMETERS; } return run_cmd_cfind (opts->params[1], opts->personal, opts->after, opts->format, !opts->nocolor, err); } mu-0.9.12/mu/mu-cmd-extract.c000066400000000000000000000214021250406103100156320ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-cmd.h" #include "mu-util.h" #include "mu-str.h" static gboolean save_part (MuMsg *msg, const char *targetdir, guint partidx, MuConfig *opts) { GError *err; gchar *filepath; gboolean rv; MuMsgOptions msgopts; err = NULL; rv = FALSE; msgopts = mu_config_get_msg_options (opts); filepath = mu_msg_part_get_path (msg, msgopts, targetdir, partidx, &err); if (!filepath) goto exit; if (!mu_msg_part_save (msg, msgopts, filepath, partidx, &err)) goto exit; if (opts->play) rv = mu_util_play (filepath, TRUE, FALSE, &err); else rv = TRUE; exit: if (err) { g_warning ("error with MIME-part: %s", err->message); g_clear_error (&err); } g_free (filepath); return rv; } static gboolean save_numbered_parts (MuMsg *msg, MuConfig *opts) { gboolean rv; char **parts, **cur; parts = g_strsplit (opts->parts, ",", 0); for (rv = TRUE, cur = parts; cur && *cur; ++cur) { unsigned idx; int i; char *endptr; idx = (unsigned)(i = strtol (*cur, &endptr, 10)); if (i < 0 || *cur == endptr) { g_warning ("invalid MIME-part index '%s'", *cur); rv = FALSE; break; } if (!save_part (msg, opts->targetdir, idx, opts)) { g_warning ("failed to save MIME-part %d", idx); rv = FALSE; break; } } g_strfreev (parts); return rv; } static GRegex* anchored_regex (const char* pattern) { GRegex *rx; GError *err; gchar *anchored; anchored = g_strdup_printf ("%s%s%s", pattern[0] == '^' ? "" : "^", pattern, pattern[strlen(pattern)-1] == '$' ? "" : "$"); err = NULL; rx = g_regex_new (anchored, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0, &err); g_free (anchored); if (!rx) { g_warning ("error in regular expression '%s': %s", pattern, err->message ? err->message : "error"); g_error_free (err); return NULL; } return rx; } static gboolean save_part_with_filename (MuMsg *msg, const char *pattern, MuConfig *opts) { GSList *lst, *cur; GRegex *rx; gboolean rv; MuMsgOptions msgopts; msgopts = mu_config_get_msg_options (opts); /* 'anchor' the pattern with '^...$' if not already */ rx = anchored_regex (pattern); if (!rx) return FALSE; lst = mu_msg_find_files (msg, msgopts, rx); g_regex_unref (rx); if (!lst) { g_warning ("no matching attachments found"); return FALSE; } for (cur = lst, rv = TRUE; cur; cur = g_slist_next (cur)) rv = rv && save_part (msg, opts->targetdir, GPOINTER_TO_UINT(cur->data), opts); g_slist_free (lst); return rv; } struct _SaveData { gboolean result; guint saved_num; MuConfig *opts; }; typedef struct _SaveData SaveData; static gboolean ignore_part (MuMsg *msg, MuMsgPart *part, SaveData *sd) { /* something went wrong somewhere; stop */ if (!sd->result) return TRUE; /* only consider leaf parts */ if (!(part->part_type & MU_MSG_PART_TYPE_LEAF)) return TRUE; /* filter out non-attachments? */ if (!sd->opts->save_all && !(mu_msg_part_maybe_attachment (part))) return TRUE; return FALSE; } static void save_part_if (MuMsg *msg, MuMsgPart *part, SaveData *sd) { gchar *filepath; gboolean rv; GError *err; MuMsgOptions msgopts; if (ignore_part (msg, part, sd)) return; rv = FALSE; filepath = NULL; err = NULL; msgopts = mu_config_get_msg_options (sd->opts); filepath = mu_msg_part_get_path (msg, msgopts, sd->opts->targetdir, part->index, &err); if (!filepath) goto exit; if (!mu_msg_part_save (msg, msgopts, filepath, part->index, &err)) goto exit; if (sd->opts->play) rv = mu_util_play (filepath, TRUE, FALSE, &err); else rv = TRUE; ++sd->saved_num; exit: if (err) g_warning ("error saving MIME part: %s", err->message); g_free (filepath); g_clear_error (&err); sd->result = rv; } static gboolean save_certain_parts (MuMsg *msg, MuConfig *opts) { SaveData sd; MuMsgOptions msgopts; sd.result = TRUE; sd.saved_num = 0; sd.opts = opts; msgopts = mu_config_get_msg_options (opts); mu_msg_part_foreach (msg, msgopts, (MuMsgPartForeachFunc)save_part_if, &sd); if (sd.saved_num == 0) { g_warning ("no %s extracted from this message", opts->save_attachments ? "attachments" : "parts"); sd.result = FALSE; } return sd.result; } static gboolean save_parts (const char *path, const char *filename, MuConfig *opts) { MuMsg* msg; gboolean rv; GError *err; err = NULL; msg = mu_msg_new_from_file (path, NULL, &err); if (!msg) { if (err) { g_warning ("error: %s", err->message); g_error_free (err); } return FALSE; } /* note, mu_cmd_extract already checks whether what's in opts * is somewhat, so no need for extensive checking here */ /* should we save some explicit parts? */ if (opts->parts) rv = save_numbered_parts (msg, opts); else if (filename) rv = save_part_with_filename (msg, filename, opts); else rv = save_certain_parts (msg, opts); mu_msg_unref (msg); return rv; } #define color_maybe(C) do{ if (color) fputs ((C),stdout);}while(0) static const char* disp_str (MuMsgPartType ptype) { if (ptype & MU_MSG_PART_TYPE_ATTACHMENT) return "attach"; if (ptype & MU_MSG_PART_TYPE_INLINE) return "inline"; return ""; } static void each_part_show (MuMsg *msg, MuMsgPart *part, gboolean color) { /* index */ g_print (" %u ", part->index); /* filename */ color_maybe (MU_COLOR_GREEN); { gchar *fname; fname = mu_msg_part_get_filename (part, FALSE); mu_util_fputs_encoded (fname ? fname : "", stdout); g_free (fname); } /* content-type */ color_maybe (MU_COLOR_BLUE); mu_util_print_encoded ( " %s/%s ", part->type ? part->type : "", part->subtype ? part->subtype : ""); /* /\* disposition *\/ */ color_maybe (MU_COLOR_MAGENTA); mu_util_print_encoded ("[%s]", disp_str(part->part_type)); /* size */ if (part->size > 0) { color_maybe (MU_COLOR_CYAN); g_print (" (%s)", mu_str_size_s (part->size)); } color_maybe (MU_COLOR_DEFAULT); fputs ("\n", stdout); } static gboolean show_parts (const char* path, MuConfig *opts, GError **err) { MuMsg *msg; MuMsgOptions msgopts; msg = mu_msg_new_from_file (path, NULL, err); if (!msg) return FALSE; msgopts = mu_config_get_msg_options (opts); /* TODO: update this for crypto */ g_print ("MIME-parts in this message:\n"); mu_msg_part_foreach (msg, msgopts, (MuMsgPartForeachFunc)each_part_show, GUINT_TO_POINTER(!opts->nocolor)); mu_msg_unref (msg); return TRUE; } static gboolean check_params (MuConfig *opts, GError **err) { size_t param_num; param_num = mu_config_param_num (opts); if (param_num < 2) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "parameters missing"); return FALSE; } if (opts->save_attachments || opts->save_all) if (opts->parts || param_num == 3) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "--save-attachments and --save-all don't " "accept a filename pattern or --parts"); return FALSE; } if (opts->save_attachments && opts->save_all) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "only one of --save-attachments and" " --save-all is allowed"); return FALSE; } return TRUE; } MuError mu_cmd_extract (MuConfig *opts, GError **err) { int rv; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_EXTRACT, MU_ERROR_INTERNAL); if (!check_params (opts, err)) return MU_ERROR_IN_PARAMETERS; if (!opts->params[2] && !opts->parts && !opts->save_attachments && !opts->save_all) /* show, don't save */ rv = show_parts (opts->params[1], opts, err); else { rv = mu_util_check_dir(opts->targetdir, FALSE, TRUE); if (!rv) mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_WRITE, "target '%s' is not a writable directory", opts->targetdir); else rv = save_parts (opts->params[1], opts->params[2], opts); /* save */ } return rv ? MU_OK : MU_ERROR; } mu-0.9.12/mu/mu-cmd-find.c000066400000000000000000000403321250406103100151030ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include "mu-msg.h" #include "mu-str.h" #include "mu-date.h" #include "mu-maildir.h" #include "mu-index.h" #include "mu-query.h" #include "mu-msg-iter.h" #include "mu-bookmarks.h" #include "mu-runtime.h" #include "mu-util.h" #include "mu-cmd.h" #include "mu-threader.h" typedef gboolean (OutputFunc) (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err); static gboolean print_xapian_query (MuQuery *xapian, const gchar *query, GError **err) { char *querystr; querystr = mu_query_as_string (xapian, query, err); if (!querystr) return FALSE; g_print ("%s\n", querystr); g_free (querystr); return TRUE; } /* returns MU_MSG_FIELD_ID_NONE if there is an error */ static MuMsgFieldId sort_field_from_string (const char* fieldstr, GError **err) { MuMsgFieldId mfid; mfid = mu_msg_field_id_from_name (fieldstr, FALSE); /* not found? try a shortcut */ if (mfid == MU_MSG_FIELD_ID_NONE && strlen(fieldstr) == 1) mfid = mu_msg_field_id_from_shortcut(fieldstr[0], FALSE); if (mfid == MU_MSG_FIELD_ID_NONE) g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS, "not a valid sort field: '%s'\n", fieldstr); return mfid; } static MuMsg* get_message (MuMsgIter *iter, time_t after) { MuMsg *msg; if (mu_msg_iter_is_done (iter)) return NULL; msg = mu_msg_iter_get_msg_floating (iter); if (!msg) return NULL; /* error */ if (!mu_msg_is_readable (msg)) { mu_msg_iter_next (iter); return get_message (iter, after); } if (after != 0 && after > mu_msg_get_timestamp (msg)) { mu_msg_iter_next (iter); return get_message (iter, after); } return msg; } static MuMsgIter* run_query (MuQuery *xapian, const gchar *query, MuConfig *opts, GError **err) { MuMsgIter *iter; MuMsgFieldId sortid; MuQueryFlags qflags; sortid = MU_MSG_FIELD_ID_NONE; if (opts->sortfield) { sortid = sort_field_from_string (opts->sortfield, err); if (sortid == MU_MSG_FIELD_ID_NONE) /* error occured? */ return FALSE; } qflags = MU_QUERY_FLAG_NONE; if (opts->reverse) qflags |= MU_QUERY_FLAG_DESCENDING; if (opts->skip_dups) qflags |= MU_QUERY_FLAG_SKIP_DUPS; if (opts->include_related) qflags |= MU_QUERY_FLAG_INCLUDE_RELATED; if (opts->threads) qflags |= MU_QUERY_FLAG_THREADS; iter = mu_query_run (xapian, query, sortid, opts->maxnum, qflags, err); return iter; } static gboolean exec_cmd (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err) { gint status; char *cmdline, *escpath; gboolean rv; escpath = g_shell_quote (mu_msg_get_path (msg)); cmdline = g_strdup_printf ("%s %s", opts->exec, escpath); rv = g_spawn_command_line_sync (cmdline, NULL, NULL, &status, err); g_free (cmdline); g_free (escpath); return rv; } static gchar* resolve_bookmark (MuConfig *opts, GError **err) { MuBookmarks *bm; char* val; const gchar *bmfile; bmfile = mu_runtime_path (MU_RUNTIME_PATH_BOOKMARKS); bm = mu_bookmarks_new (bmfile); if (!bm) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_OPEN, "failed to open bookmarks file '%s'", bmfile); return FALSE; } val = (gchar*)mu_bookmarks_lookup (bm, opts->bookmark); if (!val) g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_NO_MATCHES, "bookmark '%s' not found", opts->bookmark); else val = g_strdup (val); mu_bookmarks_destroy (bm); return val; } static gchar* get_query (MuConfig *opts, GError **err) { gchar *query, *bookmarkval; /* params[0] is 'find', actual search params start with [1] */ if (!opts->bookmark && !opts->params[1]) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS, "error in parameters"); return NULL; } bookmarkval = NULL; if (opts->bookmark) { bookmarkval = resolve_bookmark (opts, err); if (!bookmarkval) return NULL; } query = mu_str_quoted_from_strv ((const gchar**)&opts->params[1]); if (bookmarkval) { gchar *tmp; tmp = g_strdup_printf ("%s %s", bookmarkval, query); g_free (query); query = tmp; } g_free (bookmarkval); return query; } static MuQuery* get_query_obj (MuStore *store, GError **err) { MuQuery *mquery; unsigned count; count = mu_store_count (store, err); if (count == (unsigned)-1) return NULL; if (count == 0) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_IS_EMPTY, "the database is empty"); return NULL; } if (!mu_store_versions_match (store)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_VERSION_MISMATCH, "the database needs a rebuild"); return NULL; } mquery = mu_query_new (store, err); if (!mquery) return NULL; return mquery; } static gboolean prepare_links (MuConfig *opts, GError **err) { /* note, mu_maildir_mkdir simply ignores whatever part of the * mail dir already exists */ if (!mu_maildir_mkdir (opts->linksdir, 0700, TRUE, err)) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_MKDIR, "error creating %s", opts->linksdir); return FALSE; } if (opts->clearlinks && !mu_maildir_clear_links (opts->linksdir, err)) { mu_util_g_set_error (err, MU_ERROR_FILE, "error clearing links under %s", opts->linksdir); return FALSE; } return TRUE; } static gboolean output_link (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err) { return mu_maildir_link (mu_msg_get_path (msg), opts->linksdir, err); } static void ansi_color_maybe (MuMsgFieldId mfid, gboolean color) { const char* ansi; if (!color) return; /* nothing to do */ switch (mfid) { case MU_MSG_FIELD_ID_FROM: ansi = MU_COLOR_CYAN; break; case MU_MSG_FIELD_ID_TO: case MU_MSG_FIELD_ID_CC: case MU_MSG_FIELD_ID_BCC: ansi = MU_COLOR_BLUE; break; case MU_MSG_FIELD_ID_SUBJECT: ansi = MU_COLOR_GREEN; break; case MU_MSG_FIELD_ID_DATE: ansi = MU_COLOR_MAGENTA; break; default: if (mu_msg_field_type(mfid) == MU_MSG_FIELD_TYPE_STRING) ansi = MU_COLOR_YELLOW; else ansi = MU_COLOR_RED; } fputs (ansi, stdout); } static void ansi_reset_maybe (MuMsgFieldId mfid, gboolean color) { if (!color) return; /* nothing to do */ fputs (MU_COLOR_DEFAULT, stdout); } static const char* field_string_list (MuMsg *msg, MuMsgFieldId mfid) { char *str; const GSList *lst; static char buf[80]; lst = mu_msg_get_field_string_list (msg, mfid); if (!lst) return NULL; str = mu_str_from_list (lst, ','); if (str) { strncpy (buf, str, sizeof(buf)); g_free (str); return buf; } return NULL; } static const char* display_field (MuMsg *msg, MuMsgFieldId mfid) { gint64 val; switch (mu_msg_field_type(mfid)) { case MU_MSG_FIELD_TYPE_STRING: { const gchar *str; str = mu_msg_get_field_string (msg, mfid); return str ? str : ""; } case MU_MSG_FIELD_TYPE_INT: if (mfid == MU_MSG_FIELD_ID_PRIO) { val = mu_msg_get_field_numeric (msg, mfid); return mu_msg_prio_name ((MuMsgPrio)val); } else if (mfid == MU_MSG_FIELD_ID_FLAGS) { val = mu_msg_get_field_numeric (msg, mfid); return mu_str_flags_s ((MuFlags)val); } else /* as string */ return mu_msg_get_field_string (msg, mfid); case MU_MSG_FIELD_TYPE_TIME_T: val = mu_msg_get_field_numeric (msg, mfid); return mu_date_str_s ("%c", (time_t)val); case MU_MSG_FIELD_TYPE_BYTESIZE: val = mu_msg_get_field_numeric (msg, mfid); return mu_str_size_s ((unsigned)val); case MU_MSG_FIELD_TYPE_STRING_LIST: { const char *str; str = field_string_list (msg, mfid); return str ? str : ""; } default: g_return_val_if_reached (NULL); } } static void print_summary (MuMsg *msg, MuConfig *opts) { const char* body; char *summ; MuMsgOptions msgopts; msgopts = mu_config_get_msg_options (opts); body = mu_msg_get_body_text(msg, msgopts); if (body) summ = mu_str_summarize (body, (unsigned)opts->summary_len); else summ = NULL; g_print ("Summary: "); mu_util_fputs_encoded (summ ? summ : "", stdout); g_print ("\n"); g_free (summ); } static void thread_indent (MuMsgIter *iter) { const MuMsgIterThreadInfo *ti; const char* threadpath; int i; gboolean is_root, first_child, empty_parent, is_dup; ti = mu_msg_iter_get_thread_info (iter); if (!ti) { g_warning ("cannot get thread-info for message %u", mu_msg_iter_get_docid (iter)); return; } threadpath = ti->threadpath; /* fputs (threadpath, stdout); */ /* fputs (" ", stdout); */ is_root = ti->prop & MU_MSG_ITER_THREAD_PROP_ROOT; first_child = ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD; empty_parent = ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT; is_dup = ti->prop & MU_MSG_ITER_THREAD_PROP_DUP; /* FIXME: count the colons... */ for (i = 0; *threadpath; ++threadpath) i += (*threadpath == ':') ? 1 : 0; /* indent */ while (i --> 0) fputs (" ", stdout); if (!is_root) { fputs (first_child ? "`" : "|", stdout); fputs (empty_parent ? "*> " : is_dup ? "=> " : "-> ", stdout); } } static void output_plain_fields (MuMsg *msg, const char *fields, gboolean color, gboolean threads) { const char* myfields; int nonempty; g_return_if_fail (fields); for (myfields = fields, nonempty = 0; *myfields; ++myfields) { MuMsgFieldId mfid; mfid = mu_msg_field_id_from_shortcut (*myfields, FALSE); if (mfid == MU_MSG_FIELD_ID_NONE || (!mu_msg_field_xapian_value (mfid) && !mu_msg_field_xapian_contact (mfid))) nonempty += printf ("%c", *myfields); else { ansi_color_maybe (mfid, color); nonempty += mu_util_fputs_encoded (display_field (msg, mfid), stdout); ansi_reset_maybe (mfid, color); } } if (nonempty) fputs ("\n", stdout); } static gboolean output_plain (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err) { /* we reuse the color (whatever that may be) * for message-priority for threads, too */ ansi_color_maybe (MU_MSG_FIELD_ID_PRIO, !opts->nocolor); if (opts->threads) thread_indent (iter); output_plain_fields (msg, opts->fields, !opts->nocolor, opts->threads); if (opts->summary_len > 0) print_summary (msg, opts); return TRUE; } static gboolean output_sexp (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err) { char *sexp; const MuMsgIterThreadInfo *ti; ti = opts->threads ? mu_msg_iter_get_thread_info (iter) : NULL; sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter), ti, MU_MSG_OPTION_HEADERS_ONLY); fputs (sexp, stdout); g_free (sexp); return TRUE; } static void print_attr_xml (const char* elm, const char *str) { gchar *esc; if (mu_str_is_empty(str)) return; /* empty: don't include */ esc = g_markup_escape_text (str, -1); g_print ("\t\t<%s>%s\n", elm, esc, elm); g_free (esc); } static gboolean output_xml (MuMsg *msg, MuMsgIter *iter, MuConfig *opts, GError **err) { g_print ("\t\n"); print_attr_xml ("from", mu_msg_get_from (msg)); print_attr_xml ("to", mu_msg_get_to (msg)); print_attr_xml ("cc", mu_msg_get_cc (msg)); print_attr_xml ("subject", mu_msg_get_subject (msg)); g_print ("\t\t%u\n", (unsigned)mu_msg_get_date (msg)); g_print ("\t\t%u\n", (unsigned)mu_msg_get_size (msg)); print_attr_xml ("msgid", mu_msg_get_msgid (msg)); print_attr_xml ("path", mu_msg_get_path (msg)); print_attr_xml ("maildir", mu_msg_get_maildir (msg)); g_print ("\t\n"); return TRUE; } static OutputFunc* output_prepare (MuConfig *opts, GError **err) { switch (opts->format) { case MU_CONFIG_FORMAT_EXEC: return exec_cmd; case MU_CONFIG_FORMAT_LINKS: if (!prepare_links (opts, err)) return NULL; else return output_link; case MU_CONFIG_FORMAT_PLAIN: return output_plain; case MU_CONFIG_FORMAT_XML: g_print ("\n"); g_print ("\n"); return output_xml; case MU_CONFIG_FORMAT_SEXP: return output_sexp; default: g_return_val_if_reached (NULL); return NULL; } } static void output_finish (MuConfig *opts) { if (opts->format == MU_CONFIG_FORMAT_XML) g_print ("\n"); } static gboolean output_query_results (MuMsgIter *iter, MuConfig *opts, GError **err) { int count; gboolean rv; OutputFunc *output_func; output_func = output_prepare (opts, err); if (!output_func) return FALSE; for (count = 0, rv = TRUE; !mu_msg_iter_is_done(iter); mu_msg_iter_next (iter)) { MuMsg *msg; if (count == opts->maxnum) break; msg = get_message (iter, opts->after); if (!msg) break; /* { */ /* const char* thread_id; */ /* thread_id = mu_msg_iter_get_thread_id (iter); */ /* g_print ("%s ", thread_id ? thread_id : ""); */ /* } */ rv = output_func (msg, iter, opts, err); if (!rv) break; else ++count; } output_finish (opts); if (rv && count == 0) { mu_util_g_set_error (err, MU_ERROR_NO_MATCHES, "no matches for search expression"); return FALSE; } return rv; } static gboolean process_query (MuQuery *xapian, const gchar *query, MuConfig *opts, GError **err) { MuMsgIter *iter; gboolean rv; iter = run_query (xapian, query, opts, err); if (!iter) return FALSE; rv = output_query_results (iter, opts, err); mu_msg_iter_destroy (iter); return rv; } static gboolean execute_find (MuStore *store, MuConfig *opts, GError **err) { char *query_str; MuQuery *oracle; gboolean rv; oracle = get_query_obj(store, err); if (!oracle) return FALSE; query_str = get_query (opts, err); if (!query_str) { mu_query_destroy (oracle); return FALSE; } if (opts->format == MU_CONFIG_FORMAT_XQUERY) rv = print_xapian_query (oracle, query_str, err); else rv = process_query (oracle, query_str, opts, err); mu_query_destroy (oracle); g_free (query_str); return rv; } static gboolean format_params_valid (MuConfig *opts, GError **err) { switch (opts->format) { case MU_CONFIG_FORMAT_EXEC: break; case MU_CONFIG_FORMAT_PLAIN: case MU_CONFIG_FORMAT_SEXP: case MU_CONFIG_FORMAT_LINKS: case MU_CONFIG_FORMAT_XML: case MU_CONFIG_FORMAT_XQUERY: if (opts->exec) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "--exec and --format cannot be combined"); return FALSE; } break; default: mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "invalid output format %s", opts->formatstr ? opts->formatstr : ""); return FALSE; } if (opts->format == MU_CONFIG_FORMAT_LINKS && !opts->linksdir) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing --linksdir argument"); return FALSE; } if (opts->linksdir && opts->format != MU_CONFIG_FORMAT_LINKS) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "--linksdir is only valid with --format=links"); return FALSE; } return TRUE; } static gboolean query_params_valid (MuConfig *opts, GError **err) { const gchar *xpath; if (!opts->params[1]) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing query"); return FALSE; } xpath = mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB); if (mu_util_check_dir (xpath, TRUE, FALSE)) return TRUE; mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_READ, "'%s' is not a readable Xapian directory", xpath); return FALSE; } MuError mu_cmd_find (MuStore *store, MuConfig *opts, GError **err) { g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_FIND, MU_ERROR_INTERNAL); if (opts->exec) opts->format = MU_CONFIG_FORMAT_EXEC; /* pseudo format */ if (!query_params_valid (opts, err) || !format_params_valid(opts, err)) return MU_G_ERROR_CODE (err); if (!execute_find (store, opts, err)) return MU_G_ERROR_CODE(err); else return MU_OK; } mu-0.9.12/mu/mu-cmd-index.c000066400000000000000000000221721250406103100152740ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "mu-cmd.h" #include #include #include #include #include #include "mu-util.h" #include "mu-msg.h" #include "mu-index.h" #include "mu-store.h" #include "mu-runtime.h" #include "mu-log.h" static gboolean MU_CAUGHT_SIGNAL; static void sig_handler (int sig) { if (!MU_CAUGHT_SIGNAL && sig == SIGINT) { /* Ctrl-C */ g_print ("\n"); g_warning ("shutting down gracefully, " "press again to kill immediately"); } MU_CAUGHT_SIGNAL = TRUE; } static void install_sig_handler (void) { struct sigaction action; int i, sigs[] = { SIGINT, SIGHUP, SIGTERM }; MU_CAUGHT_SIGNAL = FALSE; action.sa_handler = sig_handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_RESETHAND; for (i = 0; i != G_N_ELEMENTS(sigs); ++i) if (sigaction (sigs[i], &action, NULL) != 0) g_critical ("set sigaction for %d failed: %s", sigs[i], strerror (errno));; } static gboolean check_params (MuConfig *opts, GError **err) { /* param[0] == 'index' there should be no param[1] */ if (opts->params[1]) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "unexpected parameter"); return FALSE; } if (opts->xbatchsize < 0) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "the batch size must be >= 0"); return FALSE; } if (opts->max_msg_size < 0) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "the maximum message size must >= 0"); return FALSE; } return TRUE; } static gboolean check_maildir (const char *maildir, GError **err) { if (!maildir) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "no maildir to work on; use --maildir="); return FALSE; } if (!g_path_is_absolute (maildir)) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "maildir path '%s' is not absolute", maildir); return FALSE; } if (!mu_util_check_dir (maildir, TRUE, FALSE)) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "not a valid Maildir: %s", maildir); return FALSE; } return TRUE; } static MuError index_msg_silent_cb (MuIndexStats* stats, void *user_data) { return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK; } static void print_stats (MuIndexStats* stats, gboolean clear, gboolean color) { const char *kars="-\\|/"; char output[120]; static unsigned i = 0; if (clear) fputs ("\r", stdout); if (color) snprintf (output, sizeof(output), MU_COLOR_YELLOW "%c " MU_COLOR_DEFAULT "processing mail; " "processed: " MU_COLOR_GREEN "%u; " MU_COLOR_DEFAULT "updated/new: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT ", cleaned-up: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT, (unsigned)kars[++i % 4], (unsigned)stats->_processed, (unsigned)stats->_updated, (unsigned)stats->_cleaned_up); else snprintf (output, sizeof(output), "%c processing mail; processed: %u; " "updated/new: %u, cleaned-up: %u", (unsigned)kars[++i % 4], (unsigned)stats->_processed, (unsigned)stats->_updated, (unsigned)stats->_cleaned_up); fputs (output, stdout); fflush (stdout); } struct _IndexData { gboolean color; }; typedef struct _IndexData IndexData; static MuError index_msg_cb (MuIndexStats* stats, IndexData *idata) { if (stats->_processed % 75) return MU_OK; print_stats (stats, TRUE, idata->color); return MU_CAUGHT_SIGNAL ? MU_STOP: MU_OK; } static gboolean database_version_check_and_update (MuStore *store, MuConfig *opts, GError **err) { if (mu_store_count (store, err) == 0) return TRUE; /* when rebuilding, we empty the database before doing * anything */ if (opts->rebuild) { g_debug ("clearing database"); g_debug ("clearing contacts-cache"); return mu_store_clear (store, err); } if (mu_store_versions_match (store)) return TRUE; /* ok, nothing to do */ /* ok, database is not up to date */ if (opts->autoupgrade) { g_debug ("auto-upgrade: clearing old database and cache"); return mu_store_clear (store, err); } return FALSE; } static void show_time (unsigned t, unsigned processed, gboolean color) { if (color) { if (t) g_print ("elapsed: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT " second(s), ~ " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT " msg/s", t, processed/t); else g_print ("elapsed: " MU_COLOR_GREEN "%u" MU_COLOR_DEFAULT " second(s)", t); } else { if (t) g_print ("elapsed: %u second(s), ~ %u msg/s", t, processed/t); else g_print ("elapsed: %u second(s)", t); } g_print ("\n"); } /* when logging to console, print a newline before doing so; this * makes it more clear when something happens during the * indexing/cleanup progress output */ #define newline_before_on() \ mu_log_options_set(mu_log_options_get() | MU_LOG_OPTIONS_NEWLINE) #define newline_before_off() \ mu_log_options_set(mu_log_options_get() & ~MU_LOG_OPTIONS_NEWLINE) static MuError cleanup_missing (MuIndex *midx, MuConfig *opts, MuIndexStats *stats, GError **err) { MuError rv; time_t t; IndexData idata; gboolean show_progress; if (!opts->quiet) g_print ("cleaning up messages [%s]\n", mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB)); show_progress = !opts->quiet && isatty(fileno(stdout)); mu_index_stats_clear (stats); t = time (NULL); idata.color = !opts->nocolor; newline_before_on(); rv = mu_index_cleanup (midx, stats, show_progress ? (MuIndexCleanupDeleteCallback)index_msg_cb : (MuIndexCleanupDeleteCallback)index_msg_silent_cb, &idata, err); newline_before_off(); if (!opts->quiet) { print_stats (stats, TRUE, !opts->nocolor); g_print ("\n"); show_time ((unsigned)(time(NULL)-t),stats->_processed, !opts->nocolor); } return (rv == MU_OK || rv == MU_STOP) ? MU_OK: MU_G_ERROR_CODE(err); } static void index_title (const char* maildir, const char* xapiandir, gboolean color) { if (color) g_print ("indexing messages under " MU_COLOR_BLUE "%s" MU_COLOR_DEFAULT " [" MU_COLOR_BLUE "%s" MU_COLOR_DEFAULT "]\n", maildir, xapiandir); else g_print ("indexing messages under %s [%s]\n", maildir, xapiandir); } static MuError cmd_index (MuIndex *midx, MuConfig *opts, MuIndexStats *stats, GError **err) { IndexData idata; MuError rv; gboolean show_progress; if (!opts->quiet) index_title (opts->maildir, mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), !opts->nocolor); show_progress = !opts->quiet && isatty(fileno(stdout)); idata.color = !opts->nocolor; newline_before_on(); rv = mu_index_run (midx, opts->maildir, opts->rebuild, stats, show_progress ? (MuIndexMsgCallback)index_msg_cb : (MuIndexMsgCallback)index_msg_silent_cb, NULL, &idata); newline_before_off(); if (rv == MU_OK || rv == MU_STOP) { MU_WRITE_LOG ("index: processed: %u; updated/new: %u", stats->_processed, stats->_updated); } else mu_util_g_set_error (err, rv, "error while indexing"); return rv; } static MuIndex* init_mu_index (MuStore *store, MuConfig *opts, GError **err) { MuIndex *midx; if (!check_params (opts, err)) return NULL; if (!database_version_check_and_update(store, opts, err)) return NULL; if (!check_maildir (opts->maildir, err)) return NULL; midx = mu_index_new (store, err); if (!midx) return NULL; mu_index_set_max_msg_size (midx, opts->max_msg_size); mu_index_set_xbatch_size (midx, opts->xbatchsize); return midx; } MuError mu_cmd_index (MuStore *store, MuConfig *opts, GError **err) { MuIndex *midx; MuIndexStats stats; gboolean rv; time_t t; g_return_val_if_fail (opts, FALSE); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_INDEX, FALSE); /* create, and do error handling if needed */ midx = init_mu_index (store, opts, err); if (!midx) return MU_G_ERROR_CODE(err); mu_index_stats_clear (&stats); install_sig_handler (); t = time (NULL); rv = cmd_index (midx, opts, &stats, err); if (rv == MU_OK && !opts->nocleanup) { if (!opts->quiet) g_print ("\n"); rv = cleanup_missing (midx, opts, &stats, err); } if (!opts->quiet) { print_stats (&stats, TRUE, !opts->nocolor); g_print ("\n"); show_time ((unsigned)(time(NULL)-t), stats._processed, !opts->nocolor); } mu_index_destroy (midx); return rv; } mu-0.9.12/mu/mu-cmd-script.c000066400000000000000000000115011250406103100154630ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "mu-cmd.h" #include "mu-util.h" #include "mu-str.h" #include "mu-script.h" #define MU_GUILE_EXT ".scm" #define MU_GUILE_DESCR_PREFIX ";; INFO: " #define COL(C) ((color)?C:"") static void print_script (const char *name, const char *oneline, const char *descr, gboolean color, gboolean verbose) { g_print ("%s%s%s%s%s%s%s%s", verbose ? "\n" : " * ", COL(MU_COLOR_GREEN),name,COL(MU_COLOR_DEFAULT), oneline ? ": " : "", COL(MU_COLOR_BLUE),oneline ? oneline :"",MU_COLOR_DEFAULT); if (verbose && descr) g_print ("%s%s%s", COL(MU_COLOR_MAGENTA),descr,COL(MU_COLOR_DEFAULT)); } static gboolean print_scripts (GSList *scripts, gboolean color, gboolean verbose, const char *rxstr, GError **err) { GSList *cur; const char *verb; if (!scripts) { g_print ("No scripts available\n"); return TRUE; /* not an error */ } verb = verbose ? "" : " (use --verbose for details)"; if (rxstr) g_print ("Available scripts matching '%s'%s:\n", rxstr, verb); else g_print ("Available scripts%s:\n", verb); for (cur = scripts; cur; cur = g_slist_next (cur)) { MuScriptInfo *msi; const char* descr, *oneline, *name; msi = (MuScriptInfo*)cur->data; name = mu_script_info_name (msi); oneline = mu_script_info_one_line (msi); descr = mu_script_info_description (msi); /* if rxstr is provide, only consider matching scriptinfos */ if (rxstr && !mu_script_info_matches_regex (msi, rxstr, err)) { if (err && *err) return FALSE; continue; } print_script (name, oneline, descr, color, verbose); } return TRUE; } static GSList* get_script_info_list (const char *muhome, GError **err) { GSList *scripts, *userscripts, *last; char *userpath; scripts = mu_script_get_script_info_list (MU_SCRIPTS_DIR, MU_GUILE_EXT, MU_GUILE_DESCR_PREFIX, err); if (err && *err) return NULL; userpath = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, "scripts"); /* is there are userdir for scripts? */ if (!mu_util_check_dir (userpath, TRUE, FALSE)) { g_free (userpath); return scripts; } /* append it to the list we already have */ userscripts = mu_script_get_script_info_list (userpath, MU_GUILE_EXT, MU_GUILE_DESCR_PREFIX, err); g_free (userpath); /* some error, return nothing */ if (err && *err) { mu_script_info_list_destroy (userscripts); mu_script_info_list_destroy (scripts); return NULL; } /* append the user scripts */ last = g_slist_last (scripts); if (last) { last->next = userscripts; return scripts; } else return userscripts; /* apparently, scripts was NULL */ } static gboolean check_params (MuConfig *opts, GError **err) { if (!mu_util_supports (MU_FEATURE_GUILE | MU_FEATURE_GNUPLOT)) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "the 'script' command is not available " "in this version of mu"); return FALSE; } return TRUE; } MuError mu_cmd_script (MuConfig *opts, GError **err) { MuScriptInfo *msi; GSList *scripts; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_SCRIPT, MU_ERROR_INTERNAL); if (!check_params (opts, err)) return MU_ERROR; scripts = get_script_info_list (opts->muhome, err); if (err && *err) goto leave; if (g_strcmp0 (opts->cmdstr, "script") == 0) { print_scripts (scripts, !opts->nocolor, opts->verbose, opts->script_params[0], err); goto leave; } msi = mu_script_find_script_with_name (scripts, opts->script); if (!msi) { mu_util_g_set_error (err, MU_ERROR_SCRIPT_NOT_FOUND, "command or script not found"); goto leave; } /* do it! */ mu_script_guile_run (msi, opts->muhome, opts->script_params, err); leave: /* this won't be reached, unless there is some error */ mu_script_info_list_destroy (scripts); return (err && *err) ? MU_ERROR : MU_OK; } mu-0.9.12/mu/mu-cmd-server.c000066400000000000000000001145111250406103100154720ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include /* hopefully, the should get us a sane PATH_MAX */ #include /* not all systems provide PATH_MAX in limits.h */ #ifndef PATH_MAX #include #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif /*!PATH_MAX */ #endif /*PATH_MAX */ #include "mu-runtime.h" #include "mu-str.h" #include "mu-cmd.h" #include "mu-maildir.h" #include "mu-query.h" #include "mu-index.h" #include "mu-msg-part.h" #include "mu-contacts.h" /* signal handling *****************************************************/ /* * when we receive SIGINT, SIGHUP, SIGTERM, set MU_CAUGHT_SIGNAL to * TRUE * */ static gboolean MU_TERMINATE; static void sig_handler (int sig) { MU_TERMINATE = TRUE; } static void install_sig_handler (void) { struct sigaction action; int i, sigs[] = { SIGINT, SIGHUP, SIGTERM, SIGPIPE }; MU_TERMINATE = FALSE; action.sa_handler = sig_handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_RESETHAND; for (i = 0; i != G_N_ELEMENTS(sigs); ++i) if (sigaction (sigs[i], &action, NULL) != 0) g_critical ("set sigaction for %d failed: %s", sigs[i], strerror (errno));; } /************************************************************************/ /* * Markers for/after the lenght cookie that precedes the expression we * write to output. We use octal 376, 377 (ie, 0xfe, 0xff) as they * will never occur in utf8 */ #define COOKIE_PRE '\376' #define COOKIE_POST '\377' static void G_GNUC_PRINTF(1, 2) print_expr (const char* frm, ...) { char *expr; va_list ap; ssize_t rv; size_t exprlen, lenlen; char cookie[16]; static int outfd = 0; if (outfd == 0) outfd = fileno (stdout); expr = NULL; va_start (ap, frm); exprlen = g_vasprintf (&expr, frm, ap); va_end (ap); /* this cookie tells the frontend where to expect the next * expression */ cookie[0] = COOKIE_PRE; lenlen = sprintf(cookie + 1, "%x", (unsigned)exprlen + 1); /* + 1 for \n */ cookie[lenlen + 1] = COOKIE_POST; /* write the cookie, ie. * COOKIE_PRE COOKIE_POST */ rv = write (outfd, cookie, lenlen + 2); if (rv != -1) { rv = write (outfd, expr, exprlen); g_free (expr); } if (rv != -1) rv = write (outfd, "\n", 1); if (rv == -1) { g_critical ("%s: write() failed: %s", __FUNCTION__, strerror(errno)); /* terminate ourselves */ raise (SIGTERM); } } static MuError print_error (MuError errcode, const char *msg) { char *str; str = mu_str_escape_c_literal (msg, TRUE); print_expr ("(:error %u :message %s)", errcode, str); g_free (str); return errcode; } static MuError print_and_clear_g_error (GError **err) { MuError rv; if (err && *err) rv = print_error ((*err)->code, (*err)->message); else rv = print_error (MU_ERROR_INTERNAL, "unknown error"); g_clear_error (err); return rv; } static GHashTable* read_command_line (GError **err) { char *line; GHashTable *hash; GString *gstr; line = NULL; gstr = g_string_sized_new (512); fputs (";; mu> ", stdout); do { int kar; kar = fgetc (stdin); if (kar == '\n' || kar == EOF) break; else gstr = g_string_append_c (gstr, (char)kar); } while (1); line = g_string_free (gstr, FALSE); if (!mu_str_is_empty (line)) hash = mu_str_parse_arglist (line, err); else hash = NULL; g_free (line); return hash; } static const char* get_string_from_args (GHashTable *args, const char *param, gboolean optional, GError **err) { const char *str; str = g_hash_table_lookup (args, param); if (!str && !optional) mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "parameter '%s' not found", param); return str; } static gboolean get_bool_from_args (GHashTable *args, const char *param, gboolean optional, GError **err) { const char *val; val = get_string_from_args (args, param, optional, err); if (err && (*err != NULL)) return FALSE; if (g_strcmp0 (val, "true") == 0) return TRUE; if (!val || g_strcmp0 (val, "false") == 0) return FALSE; mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "invalid value for parameter '%s'", param); return FALSE; } #define GET_STRING_OR_ERROR_RETURN(ARGS,PARAM,SP,E) \ do { \ *(SP) = get_string_from_args ((ARGS),(PARAM),FALSE,(E)); \ if (!(*(SP))) \ return MU_G_ERROR_CODE((E)); \ } while (0); /* NOTE: this assumes there is only _one_ docid (message) for the * particular message id */ static unsigned get_docid_from_msgid (MuQuery *query, const char *str, GError **err) { gchar *querystr; unsigned docid; MuMsgIter *iter; querystr = g_strdup_printf ("msgid:\"%s\"", str); iter = mu_query_run (query, querystr, MU_MSG_FIELD_ID_NONE, 1, MU_QUERY_FLAG_NONE, err); g_free (querystr); docid = MU_STORE_INVALID_DOCID; if (!iter || mu_msg_iter_is_done (iter)) mu_util_g_set_error (err, MU_ERROR_NO_MATCHES, "could not find message %s", str); else { MuMsg *msg; msg = mu_msg_iter_get_msg_floating (iter); if (!mu_msg_is_readable(msg)) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_READ, "'%s' is not readable", mu_msg_get_path(msg)); } else docid = mu_msg_iter_get_docid (iter); mu_msg_iter_destroy (iter); } return docid; } /* get a *list* of all messages with the given message id */ static GSList* get_docids_from_msgids (MuQuery *query, const char *str, GError **err) { gchar *querystr; MuMsgIter *iter; GSList *lst; querystr = g_strdup_printf ("msgid:\"%s\"", str); iter = mu_query_run (query, querystr, MU_MSG_FIELD_ID_NONE, -1 /*unlimited*/, MU_QUERY_FLAG_NONE, err); g_free (querystr); if (!iter || mu_msg_iter_is_done (iter)) { mu_util_g_set_error (err, MU_ERROR_NO_MATCHES, "could not find message %s", str); return NULL; } lst = NULL; do { lst = g_slist_prepend (lst, GSIZE_TO_POINTER(mu_msg_iter_get_docid (iter))); } while (mu_msg_iter_next (iter)); mu_msg_iter_destroy (iter); return lst; } /* the string contains either a number (docid) or a message-id if it * doesn't look like a number, and the query param is non-nil, try to * locale the message with message-id in the database, and return its * docid */ static unsigned determine_docid (MuQuery *query, GHashTable *args, GError **err) { const char* docidstr, *msgidstr; docidstr = get_string_from_args (args, "docid", TRUE, err); if (docidstr) return atoi (docidstr); /* no docid: param; use msgid: instead */ msgidstr = get_string_from_args (args, "msgid", TRUE, err); if (!msgidstr) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "neither docid nor msgid specified"); return MU_STORE_INVALID_DOCID; } return get_docid_from_msgid (query, msgidstr, err); } #define DOCID_VALID_OR_ERROR_RETURN(DOCID,E) \ if ((DOCID)==MU_STORE_INVALID_DOCID) { \ mu_util_g_set_error((E),MU_ERROR_IN_PARAMETERS, \ "invalid docid"); \ return MU_G_ERROR_CODE((E)); \ } #define EQSTR(S1,S2) (g_strcmp0((S1),(S2))==0) struct _ServerContext { MuStore *store; MuQuery *query; }; typedef struct _ServerContext ServerContext; /*************************************************************************/ /* implementation for the commands -- for each command , there is a * dedicated function cmd_. These function all are of the type CmdFunc * * these functions return errors only if they don't handle them * themselves, where 'handling' is defined as 'report it using * print_and_clear_g_error' * * if function return non-MU_OK, the repl will print the error instead */ typedef MuError (*CmdFunc) (ServerContext*,GHashTable*,GError**); /* 'add' adds a message to the database, and takes two parameters: * 'path', which is the full path to the message, and 'maildir', which * is the maildir this message lives in (e.g. "/inbox"). response with * an (:info ...) message with information about the newly added * message (details: see code below) */ static MuError cmd_add (ServerContext *ctx, GHashTable *args, GError **err) { unsigned docid; const char *maildir, *path; MuMsg *msg; gchar *sexp; GET_STRING_OR_ERROR_RETURN (args, "path", &path, err); GET_STRING_OR_ERROR_RETURN (args, "maildir", &maildir, err); docid = mu_store_add_path (ctx->store, path, maildir, err); if (docid == MU_STORE_INVALID_DOCID) print_and_clear_g_error (err); else { gchar *escpath; escpath = mu_str_escape_c_literal (path, TRUE); print_expr ("(:info add :path %s :docid %u)", escpath, docid); msg = mu_store_get_msg (ctx->store, docid, err); if (msg) { sexp = mu_msg_to_sexp (msg, docid, NULL, MU_MSG_OPTION_VERIFY); print_expr ("(:update %s :move nil)", sexp); mu_msg_unref(msg); g_free (sexp); } g_free (escpath); } return MU_OK; } struct _PartInfo { GSList *attlist; MuMsgOptions opts; }; typedef struct _PartInfo PartInfo; static void each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) { char *att, *cachefile; GError *err; /* exclude things that don't look like proper attachments, * unless they're images */ if (!mu_msg_part_maybe_attachment(part)) return; err = NULL; cachefile = mu_msg_part_save_temp (msg, pinfo->opts|MU_MSG_OPTION_OVERWRITE, part->index, &err); if (!cachefile) { print_and_clear_g_error (&err); return; } att = g_strdup_printf ( "(:file-name \"%s\" :mime-type \"%s/%s\")", cachefile, part->type, part->subtype); pinfo->attlist = g_slist_append (pinfo->attlist, att); g_free (cachefile); } /* take the attachments of msg, save them as tmp files, and return * as sexp (as a string) describing them * * ((:name :mime-type :disposition * ) ... ) * */ static gchar* include_attachments (MuMsg *msg, MuMsgOptions opts) { GSList *cur; GString *gstr; PartInfo pinfo; pinfo.attlist = NULL; pinfo.opts = opts; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part, &pinfo); gstr = g_string_sized_new (512); gstr = g_string_append_c (gstr, '('); for (cur = pinfo.attlist; cur; cur = g_slist_next (cur)) g_string_append (gstr, (gchar*)cur->data); gstr = g_string_append_c (gstr, ')'); mu_str_free_list (pinfo.attlist); return g_string_free (gstr, FALSE); } static MuMsgOptions get_encrypted_msg_opts (GHashTable *args) { MuMsgOptions opts; opts = MU_MSG_OPTION_NONE; if (get_bool_from_args (args, "use-agent", FALSE, NULL)) opts |= MU_MSG_OPTION_USE_AGENT; if (get_bool_from_args (args, "extract-encrypted", FALSE, NULL)) opts |= MU_MSG_OPTION_DECRYPT; return opts; } enum { NEW, REPLY, FORWARD, EDIT, INVALID_TYPE }; static unsigned compose_type (const char *typestr) { if (EQSTR (typestr, "reply")) return REPLY; else if (EQSTR (typestr, "forward")) return FORWARD; else if (EQSTR (typestr, "edit")) return EDIT; else if (EQSTR (typestr, "new")) return NEW; else return INVALID_TYPE; } /* 'compose' produces the un-changed *original* message sexp (ie., the * message to reply to, forward or edit) for a new message to * compose). It takes two parameters: 'type' with the compose type * (either reply, forward or edit), and 'docid' for the message to * reply to. Note, type:new does not have an original message, and * therefore does not need a docid * * In returns a (:compose [:original ] [:include] ) * message (detals: see code below) * * Note ':include' t or nil determines whether to include attachments */ static MuError cmd_compose (ServerContext *ctx, GHashTable *args, GError **err) { const gchar *typestr; char *sexp, *atts; unsigned ctype; MuMsgOptions opts; opts = get_encrypted_msg_opts (args); GET_STRING_OR_ERROR_RETURN (args, "type", &typestr, err); ctype = compose_type (typestr); if (ctype == INVALID_TYPE) { print_error (MU_ERROR_IN_PARAMETERS, "invalid type to compose"); return MU_OK; } if (ctype == REPLY || ctype == FORWARD || ctype == EDIT) { MuMsg *msg; const char *docidstr; GET_STRING_OR_ERROR_RETURN (args, "docid", &docidstr, err); msg = mu_store_get_msg (ctx->store, atoi(docidstr), err); if (!msg) { print_and_clear_g_error (err); return MU_OK; } sexp = mu_msg_to_sexp (msg, atoi(docidstr), NULL, opts); atts = (ctype == FORWARD) ? include_attachments (msg, opts) : NULL; mu_msg_unref (msg); } else atts = sexp = NULL; print_expr ("(:compose %s :original %s :include %s)", typestr, sexp ? sexp : "nil", atts ? atts : "nil"); g_free (sexp); g_free (atts); return MU_OK; } struct _SexpData { GString *gstr; gboolean personal; time_t after; }; typedef struct _SexpData SexpData; static void each_contact_sexp (const char *email, const char *name, gboolean personal, time_t tstamp, unsigned freq, SexpData *sdata) { char *escmail; /* (maybe) only include 'personal' contacts */ if (sdata->personal && !personal) return; /* only include newer-than-x contacts */ if (tstamp < sdata->after) return; /* only include *real* e-mail addresses (ignore local * addresses... there's little to complete there anyway...) */ if (!email || !strstr (email, "@")) return; escmail = mu_str_escape_c_literal (email, TRUE); if (name) { char *escname; escname = mu_str_escape_c_literal (name, TRUE); g_string_append_printf (sdata->gstr, "(:name %s :mail %s :tstamp %u :freq %u)\n", escname, escmail, (unsigned)tstamp, freq); g_free (escname); } else g_string_append_printf (sdata->gstr, "(:mail %s :tstamp %u :freq %u)\n", escmail, (unsigned)tstamp, freq); g_free (escmail); } /** * get all contacts as an s-expression * * @param self contacts object * @param personal_only whether to restrict the list to 'personal' email addresses * * @return the sexp */ static char* contacts_to_sexp (MuContacts *contacts, gboolean personal, time_t after) { SexpData sdata; g_return_val_if_fail (contacts, NULL); sdata.personal = personal; sdata.after = after; /* make a guess for the initial size */ sdata.gstr = g_string_sized_new (mu_contacts_count(contacts) * 128); sdata.gstr = g_string_append (sdata.gstr, "(:contacts ("); mu_contacts_foreach (contacts, (MuContactsForeachFunc)each_contact_sexp, &sdata, NULL, NULL); sdata.gstr = g_string_append (sdata.gstr, "))"); return g_string_free (sdata.gstr, FALSE); } static MuError cmd_contacts (ServerContext *ctx, GHashTable *args, GError **err) { MuContacts *contacts; char *sexp; gboolean personal; time_t after; const char *str; personal = get_bool_from_args (args, "personal", TRUE, NULL); str = get_string_from_args (args, "after", TRUE, NULL); after = str ? (time_t)atoi(str) : 0; contacts = mu_contacts_new (mu_runtime_path (MU_RUNTIME_PATH_CONTACTS)); if (!contacts) { print_error (MU_ERROR_INTERNAL, "failed to open contacts cache"); return MU_OK; } /* dump the contacts cache as a giant sexp */ sexp = contacts_to_sexp (contacts, personal, after); print_expr ("%s\n", sexp); g_free (sexp); mu_contacts_destroy (contacts); return MU_OK; } static unsigned print_sexps (MuMsgIter *iter, unsigned maxnum) { unsigned u; u = 0; while (!mu_msg_iter_is_done (iter) && u < maxnum && !MU_TERMINATE) { MuMsg *msg; msg = mu_msg_iter_get_msg_floating (iter); if (mu_msg_is_readable (msg)) { char *sexp; const MuMsgIterThreadInfo* ti; ti = mu_msg_iter_get_thread_info (iter); sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter), ti, MU_MSG_OPTION_HEADERS_ONLY); print_expr ("%s", sexp); g_free (sexp); ++u; } mu_msg_iter_next (iter); } return u; } static MuError save_part (MuMsg *msg, unsigned docid, unsigned index, MuMsgOptions opts, GHashTable *args, GError **err) { gboolean rv; const gchar *path; gchar *escpath; GET_STRING_OR_ERROR_RETURN (args, "path", &path, err); rv = mu_msg_part_save (msg, opts | MU_MSG_OPTION_OVERWRITE, path, index, err); if (!rv) { print_and_clear_g_error (err); return MU_OK; } escpath = mu_str_escape_c_literal (path, FALSE); print_expr ("(:info save :message \"%s has been saved\")", escpath); g_free (escpath); return MU_OK; } static MuError open_part (MuMsg *msg, unsigned docid, unsigned index, MuMsgOptions opts, GError **err) { char *targetpath; gboolean rv; targetpath = mu_msg_part_get_cache_path (msg, opts, index, err); if (!targetpath) return print_and_clear_g_error (err); rv = mu_msg_part_save (msg, opts | MU_MSG_OPTION_USE_EXISTING, targetpath, index, err); if (!rv) { print_and_clear_g_error (err); goto leave; } rv = mu_util_play (targetpath, TRUE,/*allow local*/ FALSE/*allow remote*/, err); if (!rv) print_and_clear_g_error (err); else { gchar *path; path = mu_str_escape_c_literal (targetpath, FALSE); print_expr ("(:info open :message \"%s has been opened\")", path); g_free (path); } leave: g_free (targetpath); return MU_OK; } static MuError temp_part (MuMsg *msg, unsigned docid, unsigned index, MuMsgOptions opts, GHashTable *args, GError **err) { const char *what, *param; char *path; GET_STRING_OR_ERROR_RETURN (args, "what", &what, err); param = get_string_from_args (args, "param", TRUE, NULL); path = mu_msg_part_get_cache_path (msg, opts, index, err); if (!path) print_and_clear_g_error (err); else if (!mu_msg_part_save (msg, opts | MU_MSG_OPTION_USE_EXISTING, path, index, err)) print_and_clear_g_error (err); else { gchar *escpath; escpath = mu_str_escape_c_literal (path, FALSE); if (param) { char *escparam; escparam = mu_str_escape_c_literal (param, FALSE); print_expr ("(:temp \"%s\"" " :what \"%s\"" " :docid %u" " :param \"%s\""")", escpath, what, docid, escparam); g_free (escparam); } else print_expr ("(:temp \"%s\" :what \"%s\" :docid %u)", escpath, what, docid); g_free (escpath); } g_free (path); return MU_OK; } enum { SAVE, OPEN, TEMP, INVALID_ACTION }; static int action_type (const char *actionstr) { if (EQSTR (actionstr, "save")) return SAVE; else if (EQSTR (actionstr, "open")) return OPEN; else if (EQSTR (actionstr, "temp")) return TEMP; else return INVALID_ACTION; } /* 'extract' extracts some mime part from a message */ static MuError cmd_extract (ServerContext *ctx, GHashTable *args, GError **err) { MuMsg *msg; int docid, index, action; MuError rv; MuMsgOptions opts; const char* actionstr, *indexstr; opts = get_encrypted_msg_opts (args); rv = MU_ERROR; /* read parameters */ GET_STRING_OR_ERROR_RETURN (args, "action", &actionstr, err); GET_STRING_OR_ERROR_RETURN (args, "index", &indexstr, err); index = atoi (indexstr); docid = determine_docid (ctx->query, args, err); if (docid == MU_STORE_INVALID_DOCID) { print_and_clear_g_error (err); return MU_OK; } if ((action = action_type (actionstr)) == INVALID_ACTION) { print_error (MU_ERROR_IN_PARAMETERS, "invalid action"); return MU_OK; } msg = mu_store_get_msg (ctx->store, docid, err); if (!msg) { print_error (MU_ERROR, "failed to get message"); return MU_OK; } switch (action) { case SAVE: rv = save_part (msg, docid, index, opts, args, err); break; case OPEN: rv = open_part (msg, docid, index, opts, err); break; case TEMP: rv = temp_part (msg, docid, index, opts, args, err); break; default: print_error (MU_ERROR_INTERNAL, "unknown action"); } if (rv != MU_OK) print_and_clear_g_error (err); mu_msg_unref (msg); return MU_OK; } #define MAX_NUM_DEFAULT 500 /* parse the find parameters, and return the values as out params */ static MuError get_find_params (GHashTable *args, MuMsgFieldId *sortfield, int *maxnum, MuQueryFlags *qflags, GError **err) { const char *maxnumstr, *sortfieldstr; /* defaults */ *maxnum = MAX_NUM_DEFAULT; *qflags = MU_QUERY_FLAG_NONE; *sortfield = MU_MSG_FIELD_ID_NONE; /* field to sort by */ sortfieldstr = get_string_from_args (args, "sortfield", TRUE, NULL); if (sortfieldstr) { *sortfield = mu_msg_field_id_from_name (sortfieldstr, FALSE); /* note: shortcuts are not allowed here */ if (*sortfield == MU_MSG_FIELD_ID_NONE) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "not a valid sort field: '%s'", sortfield); return MU_G_ERROR_CODE(err); } } else *sortfield = MU_MSG_FIELD_ID_DATE; /* maximum number of results */ maxnumstr = get_string_from_args (args, "maxnum", TRUE, NULL); if (maxnumstr) *maxnum = atoi (maxnumstr); if (get_bool_from_args (args, "reverse", TRUE, NULL)) *qflags |= MU_QUERY_FLAG_DESCENDING; if (get_bool_from_args (args, "skip-dups", TRUE, NULL)) *qflags |= MU_QUERY_FLAG_SKIP_DUPS; if (get_bool_from_args (args, "include-related", TRUE, NULL)) *qflags |= MU_QUERY_FLAG_INCLUDE_RELATED; if (get_bool_from_args (args, "include-related", TRUE, NULL)) *qflags |= MU_QUERY_FLAG_INCLUDE_RELATED; if (get_bool_from_args (args, "threads", TRUE, NULL)) *qflags |= MU_QUERY_FLAG_THREADS; return MU_OK; } /* * 'find' finds a list of messages matching some query, and takes a * parameter 'query' with the search query, and (optionally) a * parameter 'maxnum' with the maximum number of messages to return. * * returns: * => list of s-expressions, each describing a message => * (:found ) */ static MuError cmd_find (ServerContext *ctx, GHashTable *args, GError **err) { MuMsgIter *iter; unsigned foundnum; int maxnum; MuMsgFieldId sortfield; const char *querystr; MuQueryFlags qflags; GET_STRING_OR_ERROR_RETURN (args, "query", &querystr, err); if (get_find_params (args, &sortfield, &maxnum, &qflags, err) != MU_OK) { print_and_clear_g_error (err); return MU_OK; } /* note: when we're threading, we get *all* matching messages, * and then only return maxnum; this is so that we maximimize * the change of all messages in a thread showing up */ iter = mu_query_run (ctx->query, querystr, sortfield, maxnum, qflags, err); if (!iter) { print_and_clear_g_error (err); return MU_OK; } /* before sending new results, send an 'erase' message, so the * frontend knows it should erase the headers buffer. this * will ensure that the output of two finds will not be * mixed. */ print_expr ("(:erase t)"); foundnum = print_sexps (iter, maxnum); print_expr ("(:found %u)", foundnum); mu_msg_iter_destroy (iter); return MU_OK; } /* static gpointer */ /* start_guile (GuileData *data) */ /* { */ /* g_print ("starting guile"); */ /* sleep (10); */ /* } */ #ifdef BUILD_GUILE static MuError cmd_guile (ServerContext *ctx, GHashTable *args, GError **err) { const char *script, *file; script = get_string_from_args (args, "script", TRUE, NULL); file = get_string_from_args (args, "file", TRUE, NULL); if (!script == !file) { print_error (MU_ERROR_IN_PARAMETERS, "guile: must provide one of 'script', 'file'"); return MU_OK; } return MU_OK; } #else /*!BUILD_GUILE*/ static MuError cmd_guile (ServerContext *ctx, GHashTable *args, GError **err) { print_error (MU_ERROR_INTERNAL, "this mu does not have guile support"); return MU_OK; } #endif /*BUILD_GUILE*/ static MuError index_msg_cb (MuIndexStats *stats, void *user_data) { if (MU_TERMINATE) return MU_STOP; if (stats->_processed % 1000) return MU_OK; print_expr ("(:info index :status running " ":processed %u :updated %u)", stats->_processed, stats->_updated); return MU_OK; } static void set_my_addresses (MuStore *store, const char *addrstr) { char **my_addresses; if (!addrstr) return; my_addresses = g_strsplit (addrstr, ",", -1); mu_store_set_my_addresses (store, (const char**)my_addresses); g_strfreev (my_addresses); } static char* get_checked_path (const char *path) { char *cpath; cpath = mu_util_dir_expand(path); if (!cpath || !mu_util_check_dir (cpath, TRUE, FALSE)) { print_error (MU_ERROR_IN_PARAMETERS, "not a readable dir: '%s'"); g_free (cpath); return NULL; } return cpath; } static MuError index_and_cleanup (MuIndex *index, const char *path, GError **err) { MuError rv; MuIndexStats stats, stats2; mu_index_stats_clear (&stats); rv = mu_index_run (index, path, FALSE, &stats, index_msg_cb, NULL, NULL); if (rv != MU_OK && rv != MU_STOP) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "indexing failed"); return rv; } mu_index_stats_clear (&stats2); rv = mu_index_cleanup (index, &stats2, NULL, NULL, err); if (rv != MU_OK && rv != MU_STOP) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "cleanup failed"); return rv; } print_expr ("(:info index :status complete " ":processed %u :updated %u :cleaned-up %u)", stats._processed, stats._updated, stats2._cleaned_up); return rv; } /* * 'index' (re)indexs maildir at path:, and responds with (:info * index ... ) messages while doing so (see the code) */ static MuError cmd_index (ServerContext *ctx, GHashTable *args, GError **err) { MuIndex *index; const char *argpath; char *path; index = NULL; GET_STRING_OR_ERROR_RETURN (args, "path", &argpath, err); if (!(path = get_checked_path (argpath))) goto leave; set_my_addresses (ctx->store, get_string_from_args (args, "my-addresses", TRUE, NULL)); if (!(index = mu_index_new (ctx->store, err))) goto leave; index_and_cleanup (index, path, err); leave: g_free (path); if (err && *err) print_and_clear_g_error (err); mu_index_destroy (index); return MU_OK; } /* 'mkdir' attempts to create a maildir directory at 'path:'; sends an * (:info mkdir ...) message when it succeeds */ static MuError cmd_mkdir (ServerContext *ctx, GHashTable *args, GError **err) { const char *path; GET_STRING_OR_ERROR_RETURN (args, "path", &path, err); if (!mu_maildir_mkdir (path, 0755, FALSE, err)) print_and_clear_g_error (err); else print_expr ("(:info mkdir :message \"%s has been created\")", path); return MU_OK; } static MuFlags get_flags (const char *path, const char *flagstr) { if (!flagstr) return MU_FLAG_NONE; /* ie., ignore flags */ else { /* if there's a '+' or '-' sign in the string, it must * be a flag-delta */ if (strstr (flagstr, "+") || strstr (flagstr, "-")) { MuFlags oldflags; oldflags = mu_maildir_get_flags_from_path (path); return mu_flags_from_str_delta (flagstr, oldflags, MU_FLAG_TYPE_ANY); } else return mu_flags_from_str (flagstr, MU_FLAG_TYPE_ANY, TRUE /*ignore invalid*/); } } static MuError do_move (MuStore *store, unsigned docid, MuMsg *msg, const char *maildir, MuFlags flags, gboolean new_name, GError **err) { unsigned rv; gchar *sexp; gboolean different_mdir; if (!maildir) { maildir = mu_msg_get_maildir (msg); different_mdir = FALSE; } else /* are we moving to a different mdir, or is it just flags? */ different_mdir = (g_strcmp0 (maildir, mu_msg_get_maildir(msg)) != 0); if (!mu_msg_move_to_maildir (msg, maildir, flags, TRUE, new_name, err)) return MU_G_ERROR_CODE (err); /* after mu_msg_move_to_maildir, path will be the *new* path, * and flags and maildir fields will be updated as wel */ rv = mu_store_update_msg (store, docid, msg, err); if (rv == MU_STORE_INVALID_DOCID) { mu_util_g_set_error (err, MU_ERROR_XAPIAN, "failed to store updated message"); print_and_clear_g_error (err); } sexp = mu_msg_to_sexp (msg, docid, NULL, MU_MSG_OPTION_VERIFY); /* note, the :move t thing is a hint to the frontend that it * could remove the particular header */ print_expr ("(:update %s :move %s)", sexp, different_mdir ? "t" : "nil"); g_free (sexp); return MU_OK; } static MuError move_docid (MuStore *store, unsigned docid, const char* flagstr, gboolean new_name, GError **err) { MuMsg *msg; MuError rv; MuFlags flags; rv = MU_ERROR; msg = mu_store_get_msg (store, docid, err); if (!msg) goto leave; flags = flagstr ? get_flags (mu_msg_get_path(msg), flagstr) : mu_msg_get_flags (msg); if (flags == MU_FLAG_INVALID) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "invalid flags"); goto leave; } rv = do_move (store, docid, msg, NULL, flags, new_name, err); leave: if (msg) mu_msg_unref (msg); if (rv != MU_OK) print_and_clear_g_error (err); return rv; } /* when called with a msgid, we need to take care of possibly multiple * messages with this message id. this is a common case when sending * messages to ourselves (maybe through a mailing list), where there * would a message in the inbox and sent folders with the same id. we * set the flag on both */ static gboolean move_msgid_maybe (ServerContext *ctx, GHashTable *args, GError **err) { GSList *docids, *cur; const char* maildir, *msgid, *flagstr; gboolean new_name; maildir = get_string_from_args (args, "maildir", TRUE, err); msgid = get_string_from_args (args, "msgid", TRUE, err); flagstr = get_string_from_args (args, "flags", TRUE, err); new_name = get_bool_from_args (args, "newname", TRUE, err); /* you cannot use 'maildir' for multiple messages at once */ if (!msgid || !flagstr || maildir) return FALSE; if (!(docids = get_docids_from_msgids (ctx->query, msgid, err))) { print_and_clear_g_error (err); return TRUE; } for (cur = docids; cur; cur = g_slist_next(cur)) if (move_docid (ctx->store, GPOINTER_TO_SIZE(cur->data), flagstr, new_name, err) != MU_OK) break; g_slist_free (docids); return TRUE; } /* * 'move' moves a message to a different maildir and/or changes its * flags. parameters are *either* a 'docid:' or 'msgid:' pointing to * the message, a 'maildir:' for the target maildir, and a 'flags:' * parameter for the new flags. * * returns an (:update ) * */ static MuError cmd_move (ServerContext *ctx, GHashTable *args, GError **err) { unsigned docid; MuMsg *msg; MuFlags flags; const char *maildir, *flagstr; gboolean new_name; /* check if the move is based on the message id; if so, handle * it in move_msgid_maybe */ if (move_msgid_maybe (ctx, args, err)) return MU_OK; maildir = get_string_from_args (args, "maildir", TRUE, err); flagstr = get_string_from_args (args, "flags", TRUE, err); new_name = get_bool_from_args (args, "newname", TRUE, err); docid = determine_docid (ctx->query, args, err); if (docid == MU_STORE_INVALID_DOCID || !(msg = mu_store_get_msg (ctx->store, docid, err))) { print_and_clear_g_error (err); return MU_OK; } /* if maildir was not specified, take the current one */ if (!maildir) maildir = mu_msg_get_maildir (msg); /* determine the real target flags, which come from the * flags-parameter we received (ie., flagstr), if any, plus * the existing message flags. */ if (flagstr) flags = get_flags (mu_msg_get_path(msg), flagstr); else flags = mu_msg_get_flags (msg); if (flags == MU_FLAG_INVALID) { print_error (MU_ERROR_IN_PARAMETERS, "invalid flags"); goto leave; } if ((do_move (ctx->store, docid, msg, maildir, flags, new_name, err) != MU_OK)) print_and_clear_g_error (err); leave: mu_msg_unref (msg); return MU_OK; } /* 'ping' takes no parameters, and provides information about this mu * server using a (:pong ...) message (details: see code below) */ static MuError cmd_ping (ServerContext *ctx, GHashTable *args, GError **err) { unsigned doccount; doccount = mu_store_count (ctx->store, err); if (doccount == (unsigned)-1) return print_and_clear_g_error (err); print_expr ("(:pong \"" PACKAGE_NAME "\" " " :props (:crypto %s :guile %s " " :version \"" VERSION "\" " " :doccount %u))", mu_util_supports (MU_FEATURE_CRYPTO) ? "t" : "nil", mu_util_supports (MU_FEATURE_GUILE|MU_FEATURE_GNUPLOT) ? "t" : "nil", doccount); return MU_OK; } /* 'quit' takes no parameters, terminates this mu server */ static MuError cmd_quit (ServerContext *ctx, GHashTable *args , GError **err) { print_expr (";; quiting"); return MU_STOP; } /* * creating a message object just to get a path seems a bit excessive * maybe mu_store_get_path could be added if this turns out to be a * problem * * NOTE: not re-entrant. */ static const char* get_path_from_docid (MuStore *store, unsigned docid, GError **err) { MuMsg *msg; const char* msgpath; static char path[PATH_MAX + 1]; msg = mu_store_get_msg (store, docid, err); if (!msg) return NULL; msgpath = mu_msg_get_path (msg); if (!msgpath) { mu_msg_unref (msg); return NULL; } strncpy (path, msgpath, sizeof(path)); mu_msg_unref (msg); return path; } /* 'remove' removes the message with either docid: or msgid:, sends a * (:remove ...) message when it succeeds */ static MuError cmd_remove (ServerContext *ctx, GHashTable *args, GError **err) { unsigned docid; const char *path; docid = determine_docid (ctx->query, args, err); if (docid == MU_STORE_INVALID_DOCID) { print_and_clear_g_error (err); return MU_OK; } path = get_path_from_docid (ctx->store, docid, err); if (!path) { print_and_clear_g_error (err); return MU_OK; } if (unlink (path) != 0) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_UNLINK, "%s", strerror (errno)); print_and_clear_g_error (err); return MU_OK; } if (!mu_store_remove_path (ctx->store, path)) { print_error (MU_ERROR_XAPIAN_REMOVE_FAILED, "failed to remove from database"); return MU_OK; } print_expr ("(:remove %u)", docid); return MU_OK; } /* 'add' adds a message to the database, and takes two parameters: * 'path', which is the full path to the message, and 'maildir', which * is the maildir this message lives in (e.g. "/inbox"). response with * an (:info ...) message with information about the newly added * message (details: see code below) */ static MuError cmd_sent (ServerContext *ctx, GHashTable *args, GError **err) { unsigned docid; const char *maildir, *path; GET_STRING_OR_ERROR_RETURN (args, "path", &path, err); GET_STRING_OR_ERROR_RETURN (args, "maildir", &maildir, err); docid = mu_store_add_path (ctx->store, path, maildir, err); if (docid == MU_STORE_INVALID_DOCID) print_and_clear_g_error (err); else { gchar *escpath; escpath = mu_str_escape_c_literal (path, TRUE); print_expr ("(:sent t :path %s :docid %u)", escpath, docid); g_free (escpath); } return MU_OK; } static MuMsgOptions get_view_msg_opts (GHashTable *args) { MuMsgOptions opts; opts = MU_MSG_OPTION_VERIFY; if (get_bool_from_args (args, "extract-images", FALSE, NULL)) opts |= MU_MSG_OPTION_EXTRACT_IMAGES; if (get_bool_from_args (args, "use-agent", FALSE, NULL)) opts |= MU_MSG_OPTION_USE_AGENT; if (get_bool_from_args (args, "auto-retrieve-key", FALSE, NULL)) opts |= MU_MSG_OPTION_AUTO_RETRIEVE; if (get_bool_from_args (args, "extract-encrypted", FALSE, NULL)) opts |= MU_MSG_OPTION_DECRYPT; return opts; } /* 'view' gets a full (including body etc.) sexp for some message, * identified by either docid: or msgid:; return a (:view ) */ static MuError cmd_view (ServerContext *ctx, GHashTable *args, GError **err) { MuMsg *msg; const gchar *path; char *sexp; MuMsgOptions opts; unsigned docid; opts = get_view_msg_opts (args); /* when 'path' is specified, get the message at path */ path = get_string_from_args (args, "path", FALSE, NULL); if (path) { docid = 0; msg = mu_msg_new_from_file (path, NULL, err); } else { docid = determine_docid (ctx->query, args, err); if (docid == MU_STORE_INVALID_DOCID) { print_and_clear_g_error (err); return MU_OK; } msg = mu_store_get_msg (ctx->store, docid, err); } if (!msg) { print_and_clear_g_error (err); return MU_OK; } sexp = mu_msg_to_sexp (msg, docid, NULL, opts); mu_msg_unref (msg); print_expr ("(:view %s)\n", sexp); g_free (sexp); return MU_OK; } /*************************************************************************/ static MuError handle_args (ServerContext *ctx, GHashTable *args, GError **err) { unsigned u; const char *cmd; struct { const char *cmd; CmdFunc func; } cmd_map[] = { { "add", cmd_add }, { "compose", cmd_compose }, { "contacts", cmd_contacts }, { "extract", cmd_extract }, { "find", cmd_find }, { "guile", cmd_guile }, { "index", cmd_index }, { "mkdir", cmd_mkdir }, { "move", cmd_move }, { "ping", cmd_ping }, { "quit", cmd_quit }, { "remove", cmd_remove }, { "sent", cmd_sent }, { "view", cmd_view } }; cmd = g_hash_table_lookup (args, "cmd"); /* ignore empty */ if (mu_str_is_empty (cmd)) return MU_OK; for (u = 0; u != G_N_ELEMENTS (cmd_map); ++u) if (g_strcmp0(cmd, cmd_map[u].cmd) == 0) return cmd_map[u].func (ctx, args, err); mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "unknown command '%s'", cmd ? cmd : ""); return MU_G_ERROR_CODE (err); } MuError mu_cmd_server (MuStore *store, MuConfig *opts/*unused*/, GError **err) { ServerContext ctx; gboolean do_quit; g_return_val_if_fail (store, MU_ERROR_INTERNAL); ctx.store = store; ctx.query = mu_query_new (store, err); if (!ctx.query) return MU_G_ERROR_CODE (err); srand (time(NULL)*getpid()); install_sig_handler (); g_print (";; welcome to " PACKAGE_STRING "\n"); /* the main REPL */ do_quit = FALSE; while (!MU_TERMINATE && !do_quit) { GHashTable *args; GError *my_err; /* args will receive a the command as a list of * strings. returning NULL indicates an error */ my_err = NULL; args = read_command_line (&my_err); if ((!args || g_hash_table_size(args) == 0) && !my_err) { if (args) g_hash_table_destroy (args); continue; } else if (my_err) { print_and_clear_g_error (&my_err); continue; } switch (handle_args (&ctx, args, &my_err)) { case MU_OK: break; case MU_STOP: do_quit = TRUE; break; default: /* some error occurred */ print_and_clear_g_error (&my_err); } g_hash_table_destroy (args); } mu_store_flush (ctx.store); mu_query_destroy (ctx.query); return MU_OK; } mu-0.9.12/mu/mu-cmd.c000066400000000000000000000335731250406103100141760ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-cmd.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-maildir.h" #include "mu-contacts.h" #include "mu-runtime.h" #include "mu-flags.h" #include "mu-store.h" #include "mu-log.h" #define VIEW_TERMINATOR '\f' /* form-feed */ static gboolean view_msg_sexp (MuMsg *msg, MuConfig *opts) { char *sexp; sexp = mu_msg_to_sexp (msg, 0, NULL, mu_config_get_msg_options(opts)); fputs (sexp, stdout); g_free (sexp); return TRUE; } static void each_part (MuMsg *msg, MuMsgPart *part, gchar **attach) { char *fname, *tmp; if (!mu_msg_part_maybe_attachment (part)) return; fname = mu_msg_part_get_filename (part, FALSE); if (!fname) return; tmp = *attach; *attach = g_strdup_printf ("%s%s'%s'", *attach ? *attach : "", *attach ? ", " : "", fname); g_free (tmp); } /* return comma-sep'd list of attachments */ static gchar * get_attach_str (MuMsg *msg, MuConfig *opts) { gchar *attach; attach = NULL; mu_msg_part_foreach (msg, mu_config_get_msg_options(opts), (MuMsgPartForeachFunc)each_part, &attach); return attach; } #define color_maybe(C) do { if(color) fputs ((C),stdout);} while(0) static void print_field (const char* field, const char *val, gboolean color) { if (!val) return; color_maybe (MU_COLOR_MAGENTA); mu_util_fputs_encoded (field, stdout); color_maybe (MU_COLOR_DEFAULT); fputs (": ", stdout); if (val) { color_maybe (MU_COLOR_GREEN); mu_util_fputs_encoded (val, stdout); } color_maybe (MU_COLOR_DEFAULT); fputs ("\n", stdout); } /* a summary_len of 0 mean 'don't show summary, show body */ static void body_or_summary (MuMsg *msg, MuConfig *opts) { const char *body; gboolean color; color = !opts->nocolor; body = mu_msg_get_body_text (msg, mu_config_get_msg_options(opts) | MU_MSG_OPTION_CONSOLE_PASSWORD); if (!body) { if (mu_msg_get_flags (msg) & MU_FLAG_ENCRYPTED) { color_maybe (MU_COLOR_CYAN); g_print ("[No body found; " "message has encrypted parts]\n"); } else { color_maybe (MU_COLOR_MAGENTA); g_print ("[No body found]\n"); } color_maybe (MU_COLOR_DEFAULT); return; } if (opts->summary_len != 0) { gchar *summ; summ = mu_str_summarize (body, opts->summary_len); print_field ("Summary", summ, color); g_free (summ); } else { mu_util_print_encoded ("%s", body); if (!g_str_has_suffix (body, "\n")) g_print ("\n"); } } /* we ignore fields for now */ /* summary_len == 0 means "no summary */ static gboolean view_msg_plain (MuMsg *msg, MuConfig *opts) { gchar *attachs; time_t date; const GSList *lst; gboolean color; color = !opts->nocolor; print_field ("From", mu_msg_get_from (msg), color); print_field ("To", mu_msg_get_to (msg), color); print_field ("Cc", mu_msg_get_cc (msg), color); print_field ("Bcc", mu_msg_get_bcc (msg), color); print_field ("Subject", mu_msg_get_subject (msg), color); if ((date = mu_msg_get_date (msg))) print_field ("Date", mu_date_str_s ("%c", date), color); if ((lst = mu_msg_get_tags (msg))) { gchar *tags; tags = mu_str_from_list (lst,','); print_field ("Tags", tags, color); g_free (tags); } if ((attachs = get_attach_str (msg, opts))) { print_field ("Attachments", attachs, color); g_free (attachs); } body_or_summary (msg, opts); return TRUE; } static gboolean handle_msg (const char *fname, MuConfig *opts, GError **err) { MuMsg *msg; gboolean rv; err = NULL; msg = mu_msg_new_from_file (fname, NULL, err); if (!msg) return FALSE; switch (opts->format) { case MU_CONFIG_FORMAT_PLAIN: rv = view_msg_plain (msg, opts); break; case MU_CONFIG_FORMAT_SEXP: rv = view_msg_sexp (msg, opts); break; default: g_critical ("bug: should not be reached"); rv = FALSE; } mu_msg_unref (msg); return rv; } static gboolean view_params_valid (MuConfig *opts, GError **err) { /* note: params[0] will be 'view' */ if (!opts->params[0] || !opts->params[1]) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "error in parameters"); return FALSE; } switch (opts->format) { case MU_CONFIG_FORMAT_PLAIN: case MU_CONFIG_FORMAT_SEXP: break; default: mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "invalid output format"); return FALSE; } return TRUE; } MuError mu_cmd_view (MuConfig *opts, GError **err) { int i; gboolean rv; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VIEW, MU_ERROR_INTERNAL); rv = view_params_valid (opts, err); if (!rv) goto leave; for (i = 1; opts->params[i]; ++i) { rv = handle_msg (opts->params[i], opts, err); if (!rv) break; /* add a separator between two messages? */ if (opts->terminator) g_print ("%c", VIEW_TERMINATOR); } leave: if (!rv) return err && *err ? (*err)->code : MU_ERROR; return MU_OK; } MuError mu_cmd_mkdir (MuConfig *opts, GError **err) { int i; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_MKDIR, MU_ERROR_INTERNAL); if (!opts->params[1]) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing directory parameter"); return MU_ERROR_IN_PARAMETERS; } for (i = 1; opts->params[i]; ++i) if (!mu_maildir_mkdir (opts->params[i], opts->dirmode, FALSE, err)) return err && *err ? (*err)->code : MU_ERROR_FILE_CANNOT_MKDIR; return MU_OK; } static gboolean check_file_okay (const char *path, gboolean cmd_add) { if (!g_path_is_absolute (path)) { g_warning ("path is not absolute: %s", path); return FALSE; } if (cmd_add && access(path, R_OK) != 0) { g_warning ("path is not readable: %s: %s", path, strerror (errno)); return FALSE; } return TRUE; } MuError mu_cmd_add (MuStore *store, MuConfig *opts, GError **err) { gboolean allok; int i; g_return_val_if_fail (store, MU_ERROR_INTERNAL); g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_ADD, MU_ERROR_INTERNAL); /* note: params[0] will be 'add' */ if (!opts->params[0] || !opts->params[1]) { g_print ("usage: mu add []\n"); mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing source and/or target"); return MU_ERROR_IN_PARAMETERS; } for (i = 1, allok = TRUE; opts->params[i]; ++i) { const char* src; src = opts->params[i]; if (!check_file_okay (src, TRUE) || mu_store_add_path (store, src, NULL, err) == MU_STORE_INVALID_DOCID) { MU_WRITE_LOG ("failed to add %s", src); allok = FALSE; } } if (!allok) { mu_util_g_set_error (err, MU_ERROR_XAPIAN_STORE_FAILED, "store failed for some message(s)"); return MU_ERROR_XAPIAN_STORE_FAILED; } return MU_OK; } MuError mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err) { gboolean allok; int i; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_REMOVE, MU_ERROR_INTERNAL); /* note: params[0] will be 'remove' */ if (!opts->params[0] || !opts->params[1]) { g_warning ("usage: mu remove []"); mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing source and/or target"); return MU_ERROR_IN_PARAMETERS; } for (i = 1, allok = TRUE; opts->params[i]; ++i) { const char* src; src = opts->params[i]; if (!check_file_okay (src, FALSE) || !mu_store_remove_path (store, src)) { allok = FALSE; MU_WRITE_LOG ("failed to remove %s", src); } } if (!allok) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_STORE_FAILED, "remove failed for some message(s)"); return MU_ERROR_XAPIAN_REMOVE_FAILED; } return MU_OK; } struct _VData { MuMsgPartSigStatus combined_status; char *report; gboolean oneline; }; typedef struct _VData VData; static void each_sig (MuMsg *msg, MuMsgPart *part, VData *vdata) { MuMsgPartSigStatusReport *report; report = part->sig_status_report; if (!report) return; if (vdata->oneline) vdata->report = g_strdup_printf ("%s%s%s", vdata->report ? vdata->report : "", vdata->report ? "; " : "", report->report); else vdata->report = g_strdup_printf ("%s%s\t%s", vdata->report ? vdata->report : "", vdata->report ? "\n" : "", report->report); if (vdata->combined_status == MU_MSG_PART_SIG_STATUS_BAD || vdata->combined_status == MU_MSG_PART_SIG_STATUS_ERROR) return; vdata->combined_status = report->verdict; } static void print_verdict (VData *vdata, gboolean color) { g_print ("verdict: "); switch (vdata->combined_status) { case MU_MSG_PART_SIG_STATUS_UNSIGNED: g_print ("%s", "no signature found"); break; case MU_MSG_PART_SIG_STATUS_GOOD: color_maybe (MU_COLOR_GREEN); g_print ("%s", "signature(s) verified"); break; case MU_MSG_PART_SIG_STATUS_BAD: color_maybe (MU_COLOR_RED); g_print ("%s", "bad signature"); break; case MU_MSG_PART_SIG_STATUS_ERROR: color_maybe (MU_COLOR_RED); g_print ("%s", "verification failed"); break; case MU_MSG_PART_SIG_STATUS_FAIL: color_maybe(MU_COLOR_RED); g_print ("%s", "error in verification process"); break; default: g_return_if_reached (); } color_maybe (MU_COLOR_DEFAULT); if (vdata->report) g_print ("%s%s\n", (vdata->oneline) ? ";" : "\n", vdata->report); } MuError mu_cmd_verify (MuConfig *opts, GError **err) { MuMsg *msg; MuMsgOptions msgopts; VData vdata; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY, MU_ERROR_INTERNAL); if (!opts->params[1]) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "missing message-file parameter"); return MU_ERROR_IN_PARAMETERS; } msg = mu_msg_new_from_file (opts->params[1], NULL, err); if (!msg) return MU_ERROR; msgopts = mu_config_get_msg_options (opts) | MU_MSG_OPTION_VERIFY; vdata.report = NULL; vdata.combined_status = MU_MSG_PART_SIG_STATUS_UNSIGNED; vdata.oneline = FALSE; mu_msg_part_foreach (msg, msgopts, (MuMsgPartForeachFunc)each_sig, &vdata); if (!opts->quiet) print_verdict (&vdata, !opts->nocolor); mu_msg_unref (msg); g_free (vdata.report); return vdata.combined_status == MU_MSG_PART_SIG_STATUS_GOOD ? MU_OK : MU_ERROR; } static void show_usage (void) { g_print ("usage: mu command [options] [parameters]\n"); g_print ("where command is one of index, find, cfind, view, mkdir, " "extract, add, remove, script, verify or server\n"); g_print ("see the mu, mu- or mu-easy manpages for " "more information\n"); } typedef MuError (*store_func) (MuStore *, MuConfig *, GError **err); static MuError with_store (store_func func, MuConfig *opts, gboolean read_only, GError **err) { MuStore *store; MuError merr; if (read_only) store = mu_store_new_read_only (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), err); else store = mu_store_new_writable (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), mu_runtime_path(MU_RUNTIME_PATH_CONTACTS), opts->rebuild, err); if (!store) return MU_G_ERROR_CODE(err); mu_store_set_my_addresses (store, (const char**)opts->my_addresses); merr = func (store, opts, err); mu_store_unref (store); return merr; } static gboolean check_params (MuConfig *opts, GError **err) { if (!opts->params||!opts->params[0]) {/* no command? */ show_usage (); mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "error in parameters"); return FALSE; } return TRUE; } static void set_log_options (MuConfig *opts) { MuLogOptions logopts; logopts = MU_LOG_OPTIONS_NONE; if (opts->quiet) logopts |= MU_LOG_OPTIONS_QUIET; if (!opts->nocolor) logopts |= MU_LOG_OPTIONS_COLOR; if (opts->log_stderr) logopts |= MU_LOG_OPTIONS_STDERR; if (opts->debug) logopts |= MU_LOG_OPTIONS_DEBUG; } MuError mu_cmd_execute (MuConfig *opts, GError **err) { MuError merr; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); if (!check_params(opts, err)) return MU_G_ERROR_CODE(err); set_log_options (opts); switch (opts->cmd) { /* already handled in mu-config.c */ case MU_CONFIG_CMD_HELP: return MU_OK; case MU_CONFIG_CMD_CFIND: merr = mu_cmd_cfind (opts, err); break; case MU_CONFIG_CMD_MKDIR: merr = mu_cmd_mkdir (opts, err); break; case MU_CONFIG_CMD_SCRIPT: merr = mu_cmd_script (opts, err); break; case MU_CONFIG_CMD_VIEW: merr = mu_cmd_view (opts, err); break; case MU_CONFIG_CMD_VERIFY: merr = mu_cmd_verify (opts, err); break; case MU_CONFIG_CMD_EXTRACT: merr = mu_cmd_extract (opts, err); break; case MU_CONFIG_CMD_FIND: merr = with_store (mu_cmd_find, opts, TRUE, err); break; case MU_CONFIG_CMD_INDEX: merr = with_store (mu_cmd_index, opts, FALSE, err); break; case MU_CONFIG_CMD_ADD: merr = with_store (mu_cmd_add, opts, FALSE, err); break; case MU_CONFIG_CMD_REMOVE: merr = with_store (mu_cmd_remove, opts, FALSE, err); break; case MU_CONFIG_CMD_SERVER: merr = with_store (mu_cmd_server, opts, FALSE, err); break; default: merr = MU_ERROR_IN_PARAMETERS; break; } return merr; } mu-0.9.12/mu/mu-cmd.h000066400000000000000000000112671250406103100141770ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_CMD_H__ #define __MU_CMD_H__ #include #include #include G_BEGIN_DECLS /** * execute the 'mkdir' command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeded, * some error code otherwise */ MuError mu_cmd_mkdir (MuConfig *opts, GError **err); /** * execute the 'view' command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeded, * some error code otherwise */ MuError mu_cmd_view (MuConfig *opts, GError **err); /** * execute the 'index' command * * @param store store object to use * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeded, * some error code otherwise */ MuError mu_cmd_index (MuStore *store, MuConfig *opt, GError **err); /** * execute the 'find' command * * @param store store object to use * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds and * >MU_OK (0) results, MU_EXITCODE_NO_MATCHES if the command * succeeds but there no matches, some error code for all other errors */ MuError mu_cmd_find (MuStore *store, MuConfig *opts, GError **err); /** * execute the 'extract' command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_extract (MuConfig *opts, GError **err); /** * execute the 'mv' command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_mv (MuConfig *opts, GError **err); /** * execute the 'script' command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_script (MuConfig *opts, GError **err); /** * execute the cfind command * * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_cfind (MuConfig *opts, GError **err); /** * execute the add command * * @param store store object to use * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_add (MuStore *store, MuConfig *opts, GError **err); /** * execute the remove command * * @param store store object to use * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err); /** * execute the server command * @param store store object to use * @param opts configuration options * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_server (MuStore *store, MuConfig *opts, GError**/*unused*/); /** * execute the verify command (to verify signatures) * @param store store object to use * @param opts configuration options * @param err receives error information, or NULL * * @return MU_OK (0) if the command succeeds, * some error code otherwise */ MuError mu_cmd_verify (MuConfig *opts, GError **err); /** * execute some mu command, based on 'opts' * * @param opts configuration option * @param err receives error information, or NULL * * @return MU_OK if all went wall, some error code otherwise */ MuError mu_cmd_execute (MuConfig *opts, GError **err); G_END_DECLS #endif /*__MU_CMD_H__*/ mu-0.9.12/mu/mu-config.c000066400000000000000000000502561250406103100146750ustar00rootroot00000000000000/* -*-Mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include /* memset */ #include #include #include "mu-util.h" #include "mu-config.h" #include "mu-cmd.h" static MuConfig MU_CONFIG; static MuConfigFormat get_output_format (const char *formatstr) { int i; struct { const char* name; MuConfigFormat format; } formats [] = { {"mutt-alias", MU_CONFIG_FORMAT_MUTT_ALIAS}, {"mutt-ab", MU_CONFIG_FORMAT_MUTT_AB}, {"wl", MU_CONFIG_FORMAT_WL}, {"csv", MU_CONFIG_FORMAT_CSV}, {"org-contact", MU_CONFIG_FORMAT_ORG_CONTACT}, {"bbdb", MU_CONFIG_FORMAT_BBDB}, {"links", MU_CONFIG_FORMAT_LINKS}, {"plain", MU_CONFIG_FORMAT_PLAIN}, {"sexp", MU_CONFIG_FORMAT_SEXP}, {"xml", MU_CONFIG_FORMAT_XML}, {"xquery", MU_CONFIG_FORMAT_XQUERY} }; for (i = 0; i != G_N_ELEMENTS(formats); i++) if (strcmp (formats[i].name, formatstr) == 0) return formats[i].format; return MU_CONFIG_FORMAT_UNKNOWN; } static void set_group_mu_defaults (void) { gchar *exp; if (!MU_CONFIG.muhome) MU_CONFIG.muhome = mu_util_guess_mu_homedir(); exp = mu_util_dir_expand(MU_CONFIG.muhome); if (exp) { g_free(MU_CONFIG.muhome); MU_CONFIG.muhome = exp; } /* check for the MU_NOCOLOR env var; but in any case don't * use colors unless we're writing to a tty */ if (g_getenv (MU_NOCOLOR) != NULL) MU_CONFIG.nocolor = TRUE; if (!isatty(fileno(stdout)) || !isatty(fileno(stderr))) MU_CONFIG.nocolor = TRUE; } static GOptionGroup* config_options_group_mu (void) { GOptionGroup *og; GOptionEntry entries[] = { {"debug", 'd', 0, G_OPTION_ARG_NONE, &MU_CONFIG.debug, "print debug output to standard error (false)", NULL}, {"quiet", 'q', 0, G_OPTION_ARG_NONE, &MU_CONFIG.quiet, "don't give any progress information (false)", NULL}, {"version", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.version, "display version and copyright information (false)", NULL}, {"muhome", 0, 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.muhome, "specify an alternative mu directory", ""}, {"log-stderr", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.log_stderr, "log to standard error (false)", NULL}, {"nocolor", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.nocolor, "don't use ANSI-colors in output (false)", NULL}, {"verbose", 'v', 0, G_OPTION_ARG_NONE, &MU_CONFIG.verbose, "verbose output (false)", NULL}, {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &MU_CONFIG.params, "parameters", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("mu", "general mu options", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } #define expand_dir(D) \ if ((D)) { \ char *exp; \ exp = mu_util_dir_expand((D)); \ if (exp) { \ g_free((D)); \ (D) = exp; \ } \ } static void set_group_index_defaults (void) { if (!MU_CONFIG.maildir) MU_CONFIG.maildir = mu_util_guess_maildir (); expand_dir (MU_CONFIG.maildir); } static GOptionGroup* config_options_group_index (void) { GOptionGroup *og; GOptionEntry entries[] = { {"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir, "top of the maildir", ""}, {"rebuild", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.rebuild, "rebuild the database from scratch (false)", NULL}, {"my-address", 0, 0, G_OPTION_ARG_STRING_ARRAY, &MU_CONFIG.my_addresses, "my e-mail address (regexp); can be used multiple times", "
"}, {"autoupgrade", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.autoupgrade, "auto-upgrade the database with new mu versions (false)", NULL}, {"nocleanup", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.nocleanup, "don't clean up the database after indexing (false)", NULL}, {"xbatchsize", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.xbatchsize, "set transaction batchsize for xapian commits (0)", NULL}, {"max-msg-size", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.max_msg_size, "set the maximum size for message files", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("index", "Options for the 'index' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static void set_group_find_defaults (void) { /* note, when no fields are specified, we use * date-from-subject, and sort descending by date. If fields * *are* specified, we sort in ascending order. */ if (!MU_CONFIG.fields || !*MU_CONFIG.fields) { MU_CONFIG.fields = g_strdup ("d f s"); if (!MU_CONFIG.sortfield) MU_CONFIG.sortfield = g_strdup ("d"); } if (!MU_CONFIG.formatstr) /* by default, use plain output */ MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN; else MU_CONFIG.format = get_output_format (MU_CONFIG.formatstr); expand_dir (MU_CONFIG.linksdir); } static GOptionGroup* config_options_group_find (void) { GOptionGroup *og; GOptionEntry entries[] = { {"fields", 'f', 0, G_OPTION_ARG_STRING, &MU_CONFIG.fields, "fields to display in the output", ""}, {"sortfield", 's', 0, G_OPTION_ARG_STRING, &MU_CONFIG.sortfield, "field to sort on", ""}, {"maxnum", 'n', 0, G_OPTION_ARG_INT, &MU_CONFIG.maxnum, "number of entries to display in the output", ""}, {"threads", 't', 0, G_OPTION_ARG_NONE, &MU_CONFIG.threads, "show message threads", NULL}, {"bookmark", 'b', 0, G_OPTION_ARG_STRING, &MU_CONFIG.bookmark, "use a bookmarked query", ""}, {"reverse", 'z', 0, G_OPTION_ARG_NONE, &MU_CONFIG.reverse, "sort in reverse (descending) order (z -> a)", NULL}, {"skip-dups", 'u', 0, G_OPTION_ARG_NONE, &MU_CONFIG.skip_dups, "show only the first of messages duplicates (false)", NULL}, {"include-related", 'r', 0, G_OPTION_ARG_NONE, &MU_CONFIG.include_related, "include related messages in results (false)", NULL}, {"linksdir", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.linksdir, "output as symbolic links to a target maildir", ""}, {"clearlinks", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.clearlinks, "clear old links before filling a linksdir (false)", NULL}, {"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr, "output format ('plain'(*), 'links', 'xml'," "'sexp', 'xquery')", ""}, {"summary-len", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.summary_len, "use up to lines for the summary, or 0 for none (0)", ""}, {"exec", 'e', 0, G_OPTION_ARG_STRING, &MU_CONFIG.exec, "execute command on each match message", ""}, {"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after, "only show messages whose m_time > T (t_time)", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("find", "Options for the 'find' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static GOptionGroup * config_options_group_mkdir (void) { GOptionGroup *og; GOptionEntry entries[] = { {"mode", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.dirmode, "set the mode (as in chmod), in octal notation", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; /* set dirmode before, because '0000' is a valid mode */ MU_CONFIG.dirmode = 0755; og = g_option_group_new("mkdir", "Options for the 'mkdir' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static void set_group_cfind_defaults (void) { if (!MU_CONFIG.formatstr) /* by default, use plain output */ MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN; else MU_CONFIG.format = get_output_format (MU_CONFIG.formatstr); } static GOptionGroup * config_options_group_cfind (void) { GOptionGroup *og; GOptionEntry entries[] = { {"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr, "output format (plain(*), mutt-alias, mutt-ab, wl, " "org-contact, bbdb, csv)", ""}, {"personal", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.personal, "whether to only get 'personal' contacts", NULL}, {"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after, "only get addresses last seen after T", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("cfind", "Options for the 'cfind' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static GOptionGroup * config_options_group_script (void) { GOptionGroup *og; GOptionEntry entries[] = { {G_OPTION_REMAINING, 0,0, G_OPTION_ARG_STRING_ARRAY, &MU_CONFIG.params, "script parameters", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("script", "Options for the 'script' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static void set_group_view_defaults (void) { if (!MU_CONFIG.formatstr) /* by default, use plain output */ MU_CONFIG.format = MU_CONFIG_FORMAT_PLAIN; else MU_CONFIG.format = get_output_format (MU_CONFIG.formatstr); } /* crypto options are used in a few different commands */ static GOptionEntry* crypto_option_entries (void) { static GOptionEntry entries[] = { {"auto-retrieve", 'r', 0, G_OPTION_ARG_NONE, &MU_CONFIG.auto_retrieve, "attempt to retrieve keys online (false)", NULL}, {"use-agent", 'a', 0, G_OPTION_ARG_NONE, &MU_CONFIG.use_agent, "attempt to use the GPG agent (false)", NULL}, {"decrypt", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.decrypt, "attempt to decrypt the message", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; return entries; } static GOptionGroup * config_options_group_view (void) { GOptionGroup *og; GOptionEntry entries[] = { {"summary-len", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.summary_len, "use up to lines for the summary, or 0 for none (0)", ""}, {"terminate", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.terminator, "terminate messages with ascii-0x07 (\\f, form-feed)", ""}, {"format", 'o', 0, G_OPTION_ARG_STRING, &MU_CONFIG.formatstr, "output format ('plain'(*), 'sexp')", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("view", "Options for the 'view' command", "", NULL, NULL); g_option_group_add_entries(og, entries); g_option_group_add_entries(og, crypto_option_entries()); return og; } static void set_group_extract_defaults (void) { if (!MU_CONFIG.targetdir) MU_CONFIG.targetdir = g_strdup ("."); expand_dir (MU_CONFIG.targetdir); } static GOptionGroup* config_options_group_extract (void) { GOptionGroup *og; GOptionEntry entries[] = { {"save-attachments", 'a', 0, G_OPTION_ARG_NONE, &MU_CONFIG.save_attachments, "save all attachments (false)", NULL}, {"save-all", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.save_all, "save all parts (incl. non-attachments) (false)", NULL}, {"parts", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.parts, "save specific parts (comma-separated list)", ""}, {"target-dir", 0, 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.targetdir, "target directory for saving", ""}, {"overwrite", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.overwrite, "overwrite existing files (false)", NULL}, {"play", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.play, "try to 'play' (open) the extracted parts", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("extract", "Options for the 'extract' command", "", NULL, NULL); g_option_group_add_entries(og, entries); g_option_group_add_entries(og, crypto_option_entries()); return og; } static GOptionGroup* config_options_group_verify (void) { GOptionGroup *og; og = g_option_group_new("verify", "Options for the 'verify' command", "", NULL, NULL); g_option_group_add_entries(og, crypto_option_entries()); return og; } static GOptionGroup* config_options_group_server (void) { GOptionGroup *og; GOptionEntry entries[] = { {"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir, "top of the maildir", ""}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("server", "Options for the 'server' command", "", NULL, NULL); g_option_group_add_entries(og, entries); return og; } static MuConfigCmd cmd_from_string (const char *str) { int i; struct { const gchar* name; MuConfigCmd cmd; } cmd_map[] = { { "add", MU_CONFIG_CMD_ADD }, { "cfind", MU_CONFIG_CMD_CFIND }, { "extract", MU_CONFIG_CMD_EXTRACT }, { "find", MU_CONFIG_CMD_FIND }, { "help", MU_CONFIG_CMD_HELP }, { "index", MU_CONFIG_CMD_INDEX }, { "mkdir", MU_CONFIG_CMD_MKDIR }, { "remove", MU_CONFIG_CMD_REMOVE }, { "script", MU_CONFIG_CMD_SCRIPT }, { "server", MU_CONFIG_CMD_SERVER }, { "verify", MU_CONFIG_CMD_VERIFY }, { "view", MU_CONFIG_CMD_VIEW } }; if (!str) return MU_CONFIG_CMD_UNKNOWN; for (i = 0; i != G_N_ELEMENTS(cmd_map); ++i) if (strcmp (str, cmd_map[i].name) == 0) return cmd_map[i].cmd; #ifdef BUILD_GUILE /* if we don't recognize it and it's not an option, it may be * some script */ if (str[0] != '-') return MU_CONFIG_CMD_SCRIPT; #endif /*BUILD_GUILE*/ return MU_CONFIG_CMD_UNKNOWN; } static gboolean parse_cmd (int *argcp, char ***argvp, GError **err) { MU_CONFIG.cmd = MU_CONFIG_CMD_NONE; MU_CONFIG.cmdstr = NULL; if (*argcp < 2) /* no command found at all */ return TRUE; else if ((**argvp)[1] == '-') /* if the first param starts with '-', there is no * command, just some option (like --version, --help * etc.)*/ return TRUE; MU_CONFIG.cmdstr = g_strdup ((*argvp)[1]); MU_CONFIG.cmd = cmd_from_string (MU_CONFIG.cmdstr); #ifndef BUILD_GUILE if (MU_CONFIG.cmd == MU_CONFIG_CMD_SCRIPT) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "command 'script' not supported"); return FALSE; } #endif /*!BUILD_GUILE*/ if (MU_CONFIG.cmdstr && MU_CONFIG.cmdstr[0] != '-' && MU_CONFIG.cmd == MU_CONFIG_CMD_UNKNOWN) { mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, "unknown command '%s'", MU_CONFIG.cmdstr); return FALSE; } return TRUE; } static GOptionGroup* get_option_group (MuConfigCmd cmd) { switch (cmd) { case MU_CONFIG_CMD_CFIND: return config_options_group_cfind(); case MU_CONFIG_CMD_EXTRACT: return config_options_group_extract(); case MU_CONFIG_CMD_FIND: return config_options_group_find(); case MU_CONFIG_CMD_INDEX: return config_options_group_index(); case MU_CONFIG_CMD_MKDIR: return config_options_group_mkdir(); case MU_CONFIG_CMD_SERVER: return config_options_group_server(); case MU_CONFIG_CMD_SCRIPT: return config_options_group_script(); case MU_CONFIG_CMD_VERIFY: return config_options_group_verify(); case MU_CONFIG_CMD_VIEW: return config_options_group_view(); default: return NULL; /* no group to add */ } } /* ugh yuck massaging the GOption text output; glib prepares some text * which has a 'Usage:' for the 'help' commmand. However, we need the * help for the command we're asking help for. So, we remove the Usage: * from what glib generates. :-( */ static gchar* massage_help (const char *help) { GRegex *rx; char *str; rx = g_regex_new ("^Usage:.*\n.*\n", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL); str = g_regex_replace (rx, help, -1, 0, "", G_REGEX_MATCH_NEWLINE_ANY, NULL); g_regex_unref (rx); return str; } static const gchar* get_help_string (MuConfigCmd cmd, gboolean long_help) { unsigned u; /* this include gets us MU_HELP_STRINGS */ #include "mu-help-strings.h" for (u = 0; u != G_N_ELEMENTS(MU_HELP_STRINGS); ++u) if (cmd == MU_HELP_STRINGS[u].cmd) { if (long_help) return MU_HELP_STRINGS[u].long_help; else return MU_HELP_STRINGS[u].usage ; } g_return_val_if_reached (""); return ""; } void mu_config_show_help (MuConfigCmd cmd) { GOptionContext *ctx; GOptionGroup *group; char *cleanhelp; g_return_if_fail (mu_config_cmd_is_valid(cmd)); ctx = g_option_context_new ("- mu help"); g_option_context_set_main_group (ctx, config_options_group_mu()); group = get_option_group (cmd); if (group) g_option_context_add_group (ctx, group); g_option_context_set_description (ctx, get_help_string (cmd, TRUE)); cleanhelp = massage_help (g_option_context_get_help (ctx, TRUE, group)); g_print ("usage:\n\t%s\n%s", get_help_string (cmd, FALSE), cleanhelp); g_free (cleanhelp); } static gboolean cmd_help (void) { MuConfigCmd cmd; if (!MU_CONFIG.params) cmd = MU_CONFIG_CMD_UNKNOWN; else cmd = cmd_from_string (MU_CONFIG.params[1]); if (cmd == MU_CONFIG_CMD_UNKNOWN) { mu_config_show_help (MU_CONFIG_CMD_HELP); return TRUE; } mu_config_show_help (cmd); return TRUE; } static void show_usage (void) { g_print ("usage: mu [options] [parameters]\n"); g_print ("try 'mu help ', or "); g_print ("see the mu, mu- or mu-easy manpages.\n"); } static gboolean parse_params (int *argcp, char ***argvp, GError **err) { GOptionContext *context; GOptionGroup *group; gboolean rv; context = g_option_context_new("- mu general options"); g_option_context_set_help_enabled (context, TRUE); rv = TRUE; g_option_context_set_main_group(context, config_options_group_mu()); g_option_context_set_ignore_unknown_options (context, FALSE); switch (MU_CONFIG.cmd) { case MU_CONFIG_CMD_NONE: show_usage(); break; case MU_CONFIG_CMD_HELP: /* 'help' is special; sucks in the options of the * command after it */ rv = g_option_context_parse (context, argcp, argvp, err) && cmd_help (); break; case MU_CONFIG_CMD_SCRIPT: /* all unknown commands are passed to 'script' */ g_option_context_set_ignore_unknown_options (context, TRUE); group = get_option_group (MU_CONFIG.cmd); g_option_context_add_group (context, group); rv = g_option_context_parse (context, argcp, argvp, err); MU_CONFIG.script = g_strdup (MU_CONFIG.cmdstr); /* argvp contains the script parameters */ MU_CONFIG.script_params = (const char**)&((*argvp)[1]); break; default: group = get_option_group (MU_CONFIG.cmd); if (group) g_option_context_add_group (context, group); rv = g_option_context_parse (context, argcp, argvp, err); break; } g_option_context_free (context); return rv ? TRUE : FALSE; } MuConfig* mu_config_init (int *argcp, char ***argvp, GError **err) { g_return_val_if_fail (argcp && argvp, NULL); memset (&MU_CONFIG, 0, sizeof(MU_CONFIG)); MU_CONFIG.maxnum = -1; /* By default, output all matching entries. */ if (!parse_cmd (argcp, argvp, err)) goto errexit; if (!parse_params(argcp, argvp, err)) goto errexit; /* fill in the defaults if user did not specify */ set_group_mu_defaults(); set_group_index_defaults(); set_group_find_defaults(); set_group_cfind_defaults(); set_group_view_defaults(); set_group_extract_defaults(); /* set_group_mkdir_defaults (config); */ return &MU_CONFIG; errexit: mu_config_uninit (&MU_CONFIG); return NULL; } void mu_config_uninit (MuConfig *opts) { if (!opts) return; g_free (opts->cmdstr); g_free (opts->muhome); g_free (opts->maildir); g_free (opts->fields); g_free (opts->sortfield); g_free (opts->bookmark); g_free (opts->formatstr); g_free (opts->exec); g_free (opts->linksdir); g_free (opts->targetdir); g_free (opts->parts); g_free (opts->script); g_strfreev (opts->params); memset (opts, 0, sizeof(MU_CONFIG)); } size_t mu_config_param_num (MuConfig *opts) { size_t n; g_return_val_if_fail (opts && opts->params, 0); for (n = 0; opts->params[n]; ++n); return n; } MuMsgOptions mu_config_get_msg_options (MuConfig *muopts) { MuMsgOptions opts; opts = MU_MSG_OPTION_NONE; if (muopts->decrypt) opts |= MU_MSG_OPTION_DECRYPT; if (muopts->verify) opts |= MU_MSG_OPTION_VERIFY; if (muopts->use_agent) opts |= MU_MSG_OPTION_USE_AGENT; if (muopts->auto_retrieve) opts |= MU_MSG_OPTION_AUTO_RETRIEVE; if (muopts->overwrite) opts |= MU_MSG_OPTION_OVERWRITE; return opts; } mu-0.9.12/mu/mu-config.h000066400000000000000000000172261250406103100147020ustar00rootroot00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_CONFIG_H__ #define __MU_CONFIG_H__ #include #include /* for mode_t */ #include #include #include G_BEGIN_DECLS /* env var; if non-empty, color are disabled */ #define MU_NOCOLOR "MU_NOCOLOR" enum _MuConfigFormat { MU_CONFIG_FORMAT_UNKNOWN = 0, /* for cfind, find, view */ MU_CONFIG_FORMAT_PLAIN, /* plain output */ /* for cfind */ MU_CONFIG_FORMAT_MUTT_ALIAS, /* mutt alias style */ MU_CONFIG_FORMAT_MUTT_AB, /* mutt ext abook */ MU_CONFIG_FORMAT_WL, /* Wanderlust abook */ MU_CONFIG_FORMAT_CSV, /* comma-sep'd values */ MU_CONFIG_FORMAT_ORG_CONTACT, /* org-contact */ MU_CONFIG_FORMAT_BBDB, /* BBDB */ /* for find, view */ MU_CONFIG_FORMAT_SEXP, /* output sexps (emacs) */ /* for find */ MU_CONFIG_FORMAT_LINKS, /* output as symlinks */ MU_CONFIG_FORMAT_XML, /* output xml */ MU_CONFIG_FORMAT_XQUERY, /* output the xapian query */ MU_CONFIG_FORMAT_EXEC /* execute some command */ }; typedef enum _MuConfigFormat MuConfigFormat; enum _MuConfigCmd { MU_CONFIG_CMD_UNKNOWN = 0, MU_CONFIG_CMD_ADD, MU_CONFIG_CMD_CFIND, MU_CONFIG_CMD_EXTRACT, MU_CONFIG_CMD_FIND, MU_CONFIG_CMD_HELP, MU_CONFIG_CMD_INDEX, MU_CONFIG_CMD_MKDIR, MU_CONFIG_CMD_REMOVE, MU_CONFIG_CMD_SCRIPT, MU_CONFIG_CMD_SERVER, MU_CONFIG_CMD_VERIFY, MU_CONFIG_CMD_VIEW, MU_CONFIG_CMD_NONE }; typedef enum _MuConfigCmd MuConfigCmd; #define mu_config_cmd_is_valid(C) \ ((C) > MU_CONFIG_CMD_UNKNOWN && (C) < MU_CONFIG_CMD_NONE) /* struct with all configuration options for mu; it will be filled * from the config file, and/or command line arguments */ struct _MuConfig { MuConfigCmd cmd; /* the command, or * MU_CONFIG_CMD_NONE */ char *cmdstr; /* cmd string, for user * info */ /* general options */ gboolean quiet; /* don't give any output */ gboolean debug; /* spew out debug info */ gchar *muhome; /* the House of Mu */ gboolean version; /* request mu version */ gboolean log_stderr; /* log to stderr (not logfile) */ gchar** params; /* parameters (for querying) */ gboolean nocolor; /* don't use use ansi-colors * in some output */ gboolean verbose; /* verbose output */ /* options for indexing */ gchar *maildir; /* where the mails are */ gboolean nocleanup; /* don't cleanup del'd mails from db */ gboolean rebuild; /* empty the database before indexing */ gboolean autoupgrade; /* automatically upgrade db * when needed */ int xbatchsize; /* batchsize for xapian * commits, or 0 for * default */ int max_msg_size; /* maximum size for message files */ char** my_addresses; /* 'my e-mail address', for mu * cfind; can be use multiple * times */ /* options for querying 'find' (and view-> 'summary') */ gchar *fields; /* fields to show in output */ gchar *sortfield; /* field to sort by (string) */ int maxnum; /* max # of entries to print */ gboolean reverse; /* sort in revers order (z->a) */ gboolean threads; /* show message threads */ gboolean summary; /* OBSOLETE: use summary_len */ int summary_len; /* max # of lines for summary */ gchar *bookmark; /* use bookmark */ gchar *formatstr; /* output type for find * (plain,links,xml,json,sexp) * and view (plain, sexp) and cfind */ MuConfigFormat format; /* the decoded formatstr */ gchar *exec; /* command to execute on the * files for the matched * messages */ gboolean skip_dups; /* if there are multiple * messages with the same * msgid, show only the first * one */ gboolean include_related; /* included related messages * in results */ /* for find and cind */ time_t after; /* only show messages or * adresses last seen after * T */ /* options for crypto * ie, 'view', 'extract' */ gboolean auto_retrieve; /* assume we're online */ gboolean use_agent; /* attempt to use the gpg-agent */ gboolean decrypt; /* try to decrypt the * message body, if any */ gboolean verify; /* try to crypto-verify the * message */ /* options for view */ gboolean terminator; /* add separator \f between * multiple messages in mu * view */ /* options for cfind (and 'find' --> "after") */ gboolean personal; /* only show 'personal' addresses */ /* also 'after' --> see above */ /* output to a maildir with symlinks */ gchar *linksdir; /* maildir to output symlinks */ gboolean clearlinks; /* clear a linksdir before filling */ mode_t dirmode; /* mode for the created maildir */ /* options for extracting parts */ gboolean save_all; /* extract all parts */ gboolean save_attachments; /* extract all attachment parts */ gchar *parts; /* comma-sep'd list of parts * to save / open */ gchar *targetdir; /* where to save the attachments */ gboolean overwrite; /* should we overwrite same-named files */ gboolean play; /* after saving, try to 'play' * (open) the attmnt using xdgopen */ /* options for mu-script */ gchar *script; /* script to run */ const char **script_params; /* parameters for scripts */ }; typedef struct _MuConfig MuConfig; /** * initialize a mu config object * * set default values for the configuration options; when you call * mu_config_init, you should also call mu_config_uninit when the data * is no longer needed. * * Note that this is _static_ data, ie., mu_config_init will always * return the same pointer * * @param argcp: pointer to argc * @param argvp: pointer to argv * @param err: receives error information */ MuConfig *mu_config_init (int *argcp, char ***argvp, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * free the MuConfig structure * * @param opts a MuConfig struct, or NULL */ void mu_config_uninit (MuConfig *conf); /** * execute the command / options in this config * * @param opts a MuConfig struct * * @return a value denoting the success/failure of the execution; * MU_ERROR_NONE (0) for success, non-zero for a failure. This is to used for * the exit code of the process * */ MuError mu_config_execute (MuConfig *conf); /** * count the number of non-option parameters * * @param opts a MuConfig struct * * @return the number of non-option parameters, or 0 in case of error */ size_t mu_config_param_num (MuConfig *conf); /** * determine MuMsgOptions for command line args * * @param opts a MuConfig struct * * @return the corresponding MuMsgOptions */ MuMsgOptions mu_config_get_msg_options (MuConfig *opts); /** * print help text for the current command * * @param cmd the command to show help for */ void mu_config_show_help (MuConfigCmd cmd); G_END_DECLS #endif /*__MU_CONFIG_H__*/ mu-0.9.12/mu/mu-help-strings.awk000066400000000000000000000027771250406103100164140ustar00rootroot00000000000000## Copyright (C) 2012 Dirk-Jan C. Binnema ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ## convert text blobs statements into c-strings BEGIN { in_def=0; in_string=0; # srand(); # guard=int(100000*rand()); # print "#ifndef __" guard "__" print "/* Do not edit - auto-generated. */" print "static const struct {" print "\tMuConfigCmd cmd;" print "\tconst char *usage;" print "\tconst char *long_help;" print "} MU_HELP_STRINGS[] = {" } /^#BEGIN/ { print "\t{ " $2 "," # e.g., MU_CONFIG_CMD_ADD in_def=1 } /^#STRING/ { if (in_def== 1) { if (in_string==1) { print ","; } in_string=1 } } /^#END/ { if (in_string==1) { in_string=0; } in_def=0; print "\n\t},\n" } !/^#/ { if (in_string==1) { printf "\n\t\"" $0 "\\n\"" } } END { print "};" # print "#endif /*" guard "*/" print "/* the end */" } mu-0.9.12/mu/mu-help-strings.txt000066400000000000000000000120721250406103100164360ustar00rootroot00000000000000#-*-mode:org-*- # # Copyright (C) 2012-2013 Dirk-Jan C. Binnema # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #BEGIN MU_CONFIG_CMD_ADD #STRING mu add [] #STRING mu add is the command to add specific measage files to the database. Each of the files must be specified with an absolute path. #END #BEGIN MU_CONFIG_CMD_CFIND #STRING mu cfind [options] [--format=] [--personal] [--after=] [] #STRING mu cfind is the mu command to find contacts in the mu database and export them for use in other programs. is one of: plain mutt-alias mutt-ab wl csv org-contact bbdb 'plain' is the default. If you specify '--personal', only addresses that were found in mails that include 'my' e-mail addres will be listed - so to exclude e.g. mailing-list posts. Use the --my-address= option in 'mu index' to specify what addresses are considered 'my' address. With '--after=T' you can tell mu to only show addresses that were seen after T. T is a Unix timestamp. For example, to get only addresses seen after the beginning of 2012, you could use --after=`date +%%s -d 2012-01-01` #END #BEGIN MU_CONFIG_CMD_EXTRACT #STRING mu extract [options] #STRING mu extract is the mu command to display and save message parts (attachments), and open them with other tools. #END #BEGIN MU_CONFIG_CMD_FIND #STRING mu find [options] #STRING mu find is the mu command for searching e-mail message that were stored earlier using mu index(1). Some examples: # get all messages with 'bananas' in body, subject or recipient fields: $ mu find bananas # get all messages regarding bananas from John with an attachment: $ mu find from:john flag:attach bananas # get all messages with subject wombat in June 2009 $ mu find subject:wombat date:20090601..20090630 See the `mu-find' and `mu-easy' man-pages for more information. #END #BEGIN MU_CONFIG_CMD_HELP #STRING mu help #STRING mu find is the mu command to get help about , where is one of: add - add message to database cfind - find a contact extract - extract parts/attachments from messages find - query the message database help - get help index - index messages mkdir - create a maildir remove - remove a message from the database script - run a script (available only when mu was built with guile-support) server - start mu server verify - verify signatures of a message view - view a specific message #END #BEGIN MU_CONFIG_CMD_INDEX #STRING mu index [options] #STRING mu index is the mu command for scanning the contents of Maildir directories and storing the results in a Xapian database.The data can then be queried using mu-find(1). #END #BEGIN MU_CONFIG_CMD_MKDIR #STRING mu mkdir [options] [] #STRING mu mkdir is the mu command for creating Maildirs.It does not use the mu database. #END #BEGIN MU_CONFIG_CMD_REMOVE #STRING mu remove [options] [] #STRING mu remove is the mu command to remove messages from the database. #END #BEGIN MU_CONFIG_CMD_SERVER #STRING mu server [options] #STRING mu server starts a simple shell in which one can query and manipulate the mu database.The output of the commands is terms of Lisp symbolic expressions (s-exps).mu server is not meant for use by humans; instead, it is designed specificallyfor the mu4e e-mail client. #END #BEGIN MU_CONFIG_CMD_SCRIPT #STRING mu script [] [-v] mu [ #+end_html mu-0.9.12/www/graph01.png000066400000000000000000000207761250406103100150240ustar00rootroot00000000000000PNG  IHDRy9XsRGB pHYs  tIME 36QtEXtCommentCreated with GIMPW IDATxTS' !`B$/e-?Pu+VZmլ+ kX?ΌJgδ0S!T7 (((_kAAA+<|\.1GH)]fdbGր5y9[.\/\S^써͛7{ݸqcxxx\\+>ԩSϗH$%%%Rԗu~zDRΜ9seoDݔRgq.~~wyoyJ%H֯_6RJm6[tt={va+L[###O|_J 6o{ӹ|H{ưX,P(T*3u%I}}=bwA4- N H^$/ y@h4H$̖BH}}Rd[؃m6VdZfq y}xK.=sEEElիW-z^VwvvTRO_zaƍgϞu5^tiGGG||lo[R6W3gΡC\:`0Bs^wb*B0""6;Nԉ:S˗{R:449+n7o{ɓ ]BE)eG-"##'BV;-/:Q'gR[[ۤ]\zRUUUPPPYYl21`0  F0cLlCZZ=ߧFBȯkBVe088e˖˗yٵ$VZj* HfXɖ4-^0ԉ:Q sZ^"**6`:b1l0ۙL&\.fw^Y9s]_ |ؘN":nlllXH^6p?>!!6Ƶ^1'/\w?ec}w {PoeɔqNKOO7LKqȟoOKKO~Z7|pBͫ-**oyB۷oÆ 봜W_}X {ցPyӟ56tشiӜ9sbbbFK/tw׳RZnmmu_OJҴ4LX,w_pp004qIIIPPCN͛=IMtiJ%JzGܛL&J+ݟe͚5lיL5kxw{[d6!ɧNox8 !vȑ#"C`,##s%tɒ%ÇJKKfff샽r8ǏOII{}+<V{A?w>gqnQmmoqNۺuk]]t̥6ILBBBccRWXo[[ɴ622R^^O>d<::|r{Bo:K G J{aʓ¬0cC؟07 S҂0Cl 00@ H^$/1yFcJJ {Bfj2Ll6[jll?}ţ]v{TpUBF1 6l0 ǖ魯\|xoҿaKKK{ryyy9{U-[={;<{믿c Ez:ǼǎY"qIAdXp$m$/ H^$/ y@ y $/H^@ y@X}}RH$'N 444644Bl6VdZfq yǑFEEE+W m۶ZjuggJ*--l1' L&{#77w˖-BAxBHSSl_n]vv6g w|u:` TVV.]t`` <<ѣ>\N彽-),,tݍ)//k ~V\\e_k6u:ݦM !+Vطo<O?>**'>>'::.445l28ȻNr^ >拊QVUU{h4lkh Æ  0H{; +3$x''''''{Klllss[f I@ .GNW?ƋH^V*  H^$/ y@ N^i6lJq ?~|8IOȽ{dddn*[v??]1'\O^ټ`PH~ &7y|ɭ[:>A}}RH$'N 8m۶%%% B6m6VdZfq̺pBNN!aq%h_zuQQ!wٷo(;qjuggJ*--l9orrr !vzGutt̝;RzwP((]]]-"##p!%?ԩVNJ%H~A" h `h4-nw TTTl۶---͗=nppf=C(V5//O*YVy1Řc3xXDhhh__X,rҥKcccS"**o:~KO'|,,7;iikT]"%9ΐ24Abu!!!fhhhϞ= } p |Mޚw}722wew/1׽222N>ߘ)55500ݽ0cާ~z999/q>믗,YO:`ғRX^xhݺu8[8GYf͚55yqw'/U;DU:U K ߰L|1/aw B÷m0Onݺg)_^p!''0Lxx8:`ғ733'%KJR"dff8q/f-l6VdZfq̺䭬ܵkWhhhuuuyyh4^m<}tee1z^VwvvTRKGGܹs)CCCjرcgW(]]]ҮxwtzYǟkTyZEDu&%ΐ \jkk Owwwt:gW\߾>\Na-),,tݍ0`BC1폔Oϻm۶ vvv89UUUTVV.[mq:j4000`04 g ٽ{ ?qxAboXּEZZcaN'[p=m@ y $/NzR)H233{l6V+ɴZfl@#yFcի8[zZTT-3sr(.JiWWW||)y֙:CB`WO788hZRi^^j҂y^bG{Hf؛8 $/H^@ y@ H^$/&_ Dͻ""ŋH^FFoa!6 MZRSSCBBRSS!6Md2Vk8[RJ[V\i0m۶j*B^W՝*`Ƙ} ;&Bfsxxu벳9[f ǀt\OtɥK =z4;;;88ʕ+"httT*^zջ}maaa52tBnwt:o%.v\T YtUָ8 _ƺz{C:?3'gu:1%8" }YbLwŊ{xGEEDGGB[y46 f5 `栓ߟ|AJj˓JyyyV`fhkkP(\$!#++t.XUaFFFYYٕ+WʲT~%KTWW:t(-- u].]3g@DD˗J-##axS{zfll'q~Y)))~.uVkX4 ;: R@p8#O:QS(}}}FUD"tX,fF.3 3zٙRaCǥ 55ܹsxza:%:z=<\h``Ծ!o߹so966& YlܟwW^X,NmT(Gڱc&bs~رĽQT/J'{キav1USoC=Dy ;]CCC|| |~d_wX̆2f&{T*c)~lllxxM\1Mtjimm?멽;… 2l r| !?]"vΜ9R$y2qۻ|y߸qcxxx\\+n:TꟂq0{H H^@ yt_IDAT $/.iBBBRSS_{:.YRjϞ=3B`y&D"vڵ{n<PJ͛^ p[cccw~g|O?4%%W $/W_{{Ν999턐-[lݺwu֡ p[vzjʕlyZ333;::rssi0  (((11qŊ? #+html: #+options: skip t * Welcome to mu! With the /enormous/ amounts of e-mail many people gather and the importance of e-mail messages in our daily work-flow, it is very important to be able to quickly deal with all that - in particular, to instantly find that one important e-mail you need right now. For that, *mu* was created. *mu* is a tool for dealing with e-mail messages stored in the [[http://en.wikipedia.org/wiki/Maildir][Maildir]]-format, on Unix-like systems. *mu*'s main purpose is to help you to find the messages you need, quickly; in addition, it allows you to view messages, extract attachments, create new maildirs, ... See the [[file:cheatsheet.org][mu cheatsheet]] for some examples. Mu's source code is available [[https://github.com/djcb/mu][in github]], and there is the [[http://groups.google.com/group/mu-discuss][mu-discuss]] mailing list. *mu* includes an emacs-based e-mail client (=mu4e=), a simple GUI (=mug=) and bindings for the Guile/Scheme programming language. ** Features - fast indexing for [[http://en.wikipedia.org/wiki/Maildir][Maildir]], Maildir+ and Maildir-on-VFAT - search for messages based on the sender, receiver, subject, date-range, size, priority, words in message, flags (signed, encrypted, new, replied, has-attachment,...), message-id, maildir, tags, attachment (name, mime-type, text) and more - support for encrypted and signed messages - command-line tools for indexing, searching, viewing, adding/removing messages, extracting attachments, exporting/searching address lists, creating maildirs, ... - accent/case normalization - so /angstrom/ matches /Ångström/ - can be integrated with other e-mail clients such as [[http://www.mutt.org/][mutt]] and [[http://www.emacswiki.org/emacs/WanderLust][Wanderlust]]. - [[file:mu4e.html][mu4e]], an emacs-based e-mail client based on =mu= (see screenshot). - [[file:mu-guile.html][mu-guile]]: [[http://www.gnu.org/software/guile/][guile 2.0]] bindings that allow for scripting, advanced processing of your data, and doing all kinds of statistics - fully documented (man pages, info pages) ** News - 2013-03-30: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.9.5.tar.gz][mu-0.9.9.5]]; full with new features and bug fixes – see the download link for some of the details. Many thanks to all who contributed! - 2012-10-14: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.9.tar.gz][mu-0.9.9]]; a new barrage of fixes and improvements – check the link and [[https://github.com/djcb/mu/blob/master/NEWS][NEWS]]. Also, note the [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu4e-manual-0.9.9.pdf][mu4e-manual]] (PDF). - 2012-07-01: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.8.5.tar.gz][mu-0.9.8.5]]; more fixes, improvements (see the link). - 2012-05-08: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.8.4.tar.gz][mu-0.9.8.4]] with even more improvements (the link has all the details) - 2012-04-06: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.8.3.tar.gz][mu-0.9.8.3]], with many improvements, fixes. See the link for details. *NOTE*: existing =mu= and =mu4e= users are recommended to execute =mu index --rebuild= after installation. - 2012-03-11: released [[http://code.google.com/p/mu0/downloads/detail?name=mu-0.9.8.2.tar.gz][mu-0.9.8.2]], with a number of fixes and improvements, see the link for the details. - 2012-02-17: released [[http://code.google.com/p/mu0/downloads/detail?name%3Dmu-0.9.8.1.tar.gz][mu-0.9.8.1]], which has a number of improvements to the 0.9.8 release: add mark as read/unread, colorize cited message parts, better handling of text-based message parts, documentation fixes, documentation updates and a few fixes here and there - 2012-02-09: moved the mu source code repository [[https://github.com/djcb/mu][to Github]]. - 2012-01-31: finally, [[http://mu0.googlecode.com/files/mu-0.9.8.tar.gz][mu-0.9.8]] is available. It comes with an emacs-based e-mail client, [[file:mu4e.html][mu4e]], and much improved [[file:mu-guile.html][guile bindings]]. Furthermore, It adds search for attachment mime type and search inside any text part of a message, more tests, improvements in many parts of the code. - 2011-09-03: mu 0.9.7 is now available; compared to the -pre version there are a few small changes; the most important one is a fix specifically for running mu on MacOS. - [[file:old-news.org][Old news]] ** Development & download #+html: Some Linux-distributions already provide pre-built mu packages; if there's no packagage for your distribution, or if you want the latest release, you can [[http://code.google.com/p/mu0/downloads/list][download mu source packages]] from Google Code. In case you find a bug, or have a feature requests, please use the [[https://github.com/djcb/mu/issues][issue tracker]]. If you'd like to work with the mu source code, you can find it [[https://github.com/djcb/mu][in Github]]; also, see the notes on [[https://github.com/djcb/mu/blob/master/HACKING][HACKING]] the mu source code. There's also a [[http://groups.google.com/group/mu-discuss][mailing list]]. ** License & Copyright *mu* was designed and implemented by Dirk-Jan C. Binnema, and is Free Software, licensed under the GNU GPLv3 #+html:
© 2011-2012 Dirk-Jan C. Binnema
#+begin_html #+end_html mu-0.9.12/www/mu-guile.org000066400000000000000000000143051250406103100153000ustar00rootroot00000000000000#+title: mu-guile: guile-bindings for mu #+style: #+options: skip t Starting from version 0.9.7, [[http://www.djcbsoftware.nl/code/mu][mu]] had experimental bindings for the [[http://www.gnu.org/software/guile/][GNU/Guile]] programming language, which is a version of the [[http://en.wikipedia.org/wiki/Scheme_(programming_language)][Scheme]] programming language, specifically designed for extending existing programs. =mu= version 0.9.8 has much improved bindings, and they are [[file:mu-guile/index.html][documented]], with many examples. You can find more examples in the =guile/examples= directory of the =mu= source package. It must be said that Scheme (and in general, languages from the Lisp-family) initially may look a bit 'strange' -- all these parentheses etc.; so please bear with us -- you will get used to it. ** Some examples Here are some examples; we don't provide too much explanation /how/ they do what they do, but the [[file:mu-guile/index.html][manual]] takes you through that, step-by-step. *NOTE (1)*: if you get errors like =ERROR: no code for module (mu)=, ~guile~ cannot find the ~mu~ modules. To solve this, you need to set the ~GUILE_LOAD_PATH~ to the directory with the installed ~mu.scm~, e.g. #+begin_src sh export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.0" #+end_src (you need to adapt this if you installed =mu= in some non-standard place; but it's always the directory with the installed ~mu.scm~). *NOTE (2)*: for the graphs (below) to work, you will need to have the =gnuplot= program installed. *NOTE (3)*: the examples below assume that you have your messages indexed already using =mu=; see the man pages, or the [[http://www.djcbsoftware.nl/code/mu/cheatsheet.html][mu cheat sheet]]. *** Messages per weekday #+begin_src scheme #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu message) (mu stats) (mu plot)) (mu:initialize) ;; create a list like (("Mon" . 13) ("Tue" . 23) ...) (define weekday-table (mu:weekday-numbers->names (sort (mu:tabulate-messages (lambda (msg) (tm:wday (localtime (mu:date msg))))) (lambda (a b) (< (car a) (car b)))))) (for-each (lambda (elm) (format #t "~a: ~a\n" (car elm) (cdr elm))) weekday-table) #+end_src Which outputs something like: #+begin_example Sun: 2278 Mon: 2991 Tue: 3077 Wed: 2734 Thu: 2796 Fri: 2343 Sat: 1856 #+end_example The numbers may be a bit different though... In my case, Saturday seems a particularly slow day for e-mail. *** Drawing graphs We can also draw graphs from this, by adding the following to the script: #+begin_src scheme ;; plain-text graph (mu:plot (weekday-table) "Messages per weekday" "Day" "Messages" #t) ;; GUI graph (mu:plot (weekday-table) "Messages per weekday" "Day" "Messages") #+end_src scheme This gives us the following: **** plain text graph #+begin_example Messages per weekday Messages 3200 ++---+--------+---------+--------+---------+---------+--------+---++ | + + "/tmp/filel8NGRf" using 2:xticlabels(1) ****** | 3000 ++ * * ++ | *********** * | | * ** * | 2800 ++ * ** * ********* ++ | * ** ************ * | 2600 ++ * ** ** ** * ++ | * ** ** ** * | | * ** ** ** * | 2400 ++ * ** ** ** *********** ++ *********** ** ** ** ** * | 2200 *+ ** ** ** ** ** * ++ * ** ** ** ** ** * | * ** ** ** ** ** * | 2000 *+ ** ** ** ** ** * ++ * + ** + ** + ** + ** + ** + *********** 1800 ******************************************************************** Sun Mon Tue Wed Thu Fri Sat Day #+end_example **** GUI graph [[file:graph01.png]] *** Export contacts to =mutt= =mu= provides =mu cfind= to get contact information from the database; it's fast, since it uses cached contact data. But sometimes, we may want to get a bit more advanced. For examples, suppose I want a list of names and e-mail addresses of people that were seen at least 20 times since 2010, in the =mutt= address book format. We could get such a list with something like the following: #+begin_src scheme #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu message) (mu contact)) (mu:initialize) ;; Get a list of contacts that were seen at least 20 times since 2010 (define (selected-contacts) (let ((addrs '()) (start (car (mktime (car (strptime "%F" "2010-01-01"))))) (minfreq 20)) (mu:for-each-contact (lambda (contact) (if (and (mu:email contact) (>= (mu:frequency contact) minfreq) (>= (mu:last-seen contact) start)) (set! addrs (cons contact addrs))))) addrs)) (for-each (lambda (contact) (format #t "~a\n" (mu:contact->string contact "mutt-alias"))) (selected-contacts)) #+end_src ** License & Copyright *mu-guile* was designed and implemented by Dirk-Jan C. Binnema, and is Free Software, licensed under the GNU GPLv3 #+html:
© 2011-2012 Dirk-Jan C. Binnema
#+begin_html #+end_html mu-0.9.12/www/mu-small.png000066400000000000000000000147251250406103100153060ustar00rootroot00000000000000PNG  IHDR]3sRGBbKGD pHYs  tIME  $p ZUIDATx՜{]}?s{].EQdʢ%Yrl9c6lu9M&A i Mk(&E EPMI'h:m4j'JlQEIQ"_}93ٳw)^ܻw|;s(|{_r |_~o?wv?w~.0;; !4@|0&!KOʂ(ɧ>)׀;bmq T=Y "܎5p$|>+\Y\buu`ߧg/* ~9_w죡!ݭi+c~nhV|O H: _$;! t)5[D讯s ZXD{엞yn5ʿO IXeegV76(a<O}YDW5ֻt!x|px9G{l#wɫ]$7{GZ fbg౷|9;| L |Rf!I /r֢JNl|b:ypK`M>l|?~]wr"ZviLO똞s|8gqi>g:v6tU[y}gLwl Fw}|@k c۷5#No<އGMai3:Sdf\wuy> F{iQg5fr1b860VWnpS Y&o=z.\J20c*8u'jysXkp>{q XK?W6f 6F[ ia,!5Xk7~'ѷ<죰A㝧n(;Zx"5\zF 'Os[⬭:nDC:62L Q A~7 un܇!}W7HgT"&SDڒqg-s9Kˋ }Xj6!/-09G= }zAcvn'!x.ZVWWp˗"5eLoK? )(*_]7kc1P819񫟶1ohG֘t&gcY 'OCGx"21.>4N.&amA;g VV{hb:l cdxm@^ݻ,`VWWxY c>{zRy"ӛ%1 W/pD˯__Gu@(SgαixǑ~7FwкԔϰy)vϕ`޻Gh5x~ i53z>vu546- ԣ1֢*V1 %KzKQ%5v_E9N-9Xj,B( /y#Z=3vt&ڄA1ENQ |,J)t)ZDy!%(_4-i *"g"a(AoT$Ue.Eb`l7!q<Š D6j,ƫ7lMZKL~zɦa^OqD@kT̷`h2;&v1&w/a݈hWRژ7Lmi9D b8:<˕W^0E14)'q#7qn5cFjSxԮfiF_4{Μ Xkɴp߽wK"u @7ڵE^rgyl&Ceͣ7suFޖ/,9bh'ZہAt?|bGȃ٪֦c&:S5iq{N>ǎeZqߑC|i1E)L'OوWa6t'+F>J6F&`mu B%'XUw )AGz7*zP(kI RMr:a G ^;"s(%8afz1xIo}=8ҫY4nc҆FbXgfQsΥ~1|˜RZ5O*^11n>1ͣb^v(S.'_|,[?@06dH*j(m[ 0E67Ɣ4k*Kkk,(s DM5.TLQ~zzߺˈ)qɖ[ݰ#N  al{u zkn3.n7)@)nbdzt/ԣ>,:( ҩLN)!d^ c@ !SI-[c˭wҡT V6K`Ca#CNJqP3KŚqs_6xTJq*K>9p5;!sT:J-֕hU9ă&.ƹ{Y78z RA"(b%6hQ%(/ Vd /{"ӛA25tIQ"(-@dT)K+(.]S*nֈ(μzrW',G7\gLiP:r#_2]/t*`~B}ݜ,~ʝ;&vkVdJE ?"قŨ*VEљV" *'z<©o]hed&8N~kfCǦ9WΔϋ&lNMK%'2EJ`QO#jcJ 2ZZgdgɡ-%fkVR'ң$2`ziFCs}qn$y`,c-+k,jfB`FƩ'^0PGȴQ2f:wjHgM֐dCTd|ڸҴm#7k~6JψRPjݕZ|uӇ?c.^}6%` ˩/lh2〗ԼzJ'V)Z+HPDD֡Ʋao19NFQ;`{f#{,c nTtn!%:l #o4$U:J*J {_jV~2dJٗ^b'.^~A+!Ӛ^8eΞs r"KK+z"c&J)WVgVx%ٖQ_2m:Q[Vqxr&cgDv5v!*71=eJS #y;?#l!U(PϨn3M}t[gg&~4Ըbr$wi r"#sLgf3C74n?'/L9ed^JUg#7NsiΩ."2=fzrBڲsKWWrnb2٨2Hя5yǾLqAqy(5wNdxw>FR##ÖVDQ|7:`yu'_zذG/]rpIMWOJIJ)E5YhdLk M#SZwpfCKtE("/BA(=%@qaZ>#|Q:F@8ؽ{0ހZ*]T ʲƦ`HZ6`eeWY\^%^ u%RD$a ]}y}_0KY7 odQBJHK o'ذU^B[MoPʹ׹4صkO<>uv Ws;nu\X¥}~lC?oo- b*7LTD62[;(1rIRJj @+Z'pFFW,qlx+{#ZNpy&&hdY6 `}zNoevC^X._[diFʵGall+EVx!k(E,J!%. @E^Hdo373_g^pSMpbRJ:mfg&7bk]vk=m4tgnz>OLLq90>֦aqe g~6nίsjJ)Xɱ2oxH=+7s)Luؽs20-hU`nG3宽8c:z0v_kT=od1asl{n\z^[pkg72ɻL-VzO?2Au:fh3?ۡ3ъ6-n77 FaYIߥIwL07](3HZ, )硲l> &ʿ6w=7kϼTA I9~o%w- ~;]B)9Ǻxez;|<%),]\°Hߞzg_{2;)JbqYOqsLhT.U5SMyAWFb녇7>Ru/#nZȈUOZ+Ry|㜋;_nlZz/k׾xŚ &CӚze{PjzT6Ft&7LUQ]x^U|MW~U*Ӫ]ߠ7Q^vD:>m=GU'x/U̸ lgj7FF]s mM ȶYhu]xύܿ y ,V_o # }-urOĨ ?!7I(IENDB`mu-0.9.12/www/mu.css000066400000000000000000000036261250406103100142020ustar00rootroot00000000000000/* stylesheet for mu website */ body { background:#ffffff; margin: 50px; padding: 10px; font-family: arial, Helvetica, 'Bitstream Vera Sans', 'Luxi Sans', Verdana, Sans-Serif; font-size: 12px; } .content { margin:30px; background: #5fb6de; } a.menu {font-weight: bold} a.menu:link {color: #ffffff; text-decoration: none; } a.menu:active {color: #ff0000; text-decoration: none; } a.menu:visited {color: #ffffff; text-decoration: none; } a.menu:hover {color: #ff0000; text-decoration: underline; } .mine {color: #ffffff; font-weight: bold} /* emacs-code -----------------------*/ /* zenburnesque code blocks in for html-exported org mode */ pre.src { background: #dddddd; color: #555555; } .org-preprocessor { color: #8cd0d3; } .org-variable-name { color: #0084C8; font-weight: bold; } .org-string { color: #4E9A06; } .org-type { color: #2F8B58; font-weight: bold; } .org-function-name { color: #00578E; font-weight: bold } .org-keyword { color: #A52A2A; font-weight: bold; } .org-comment { color: #204A87; } .org-doc { color: #afd8af; } .org-comment-delimiter { color: #708070; } .org-constant { color: #F5666D; } .org-builtin { color: #A020F0; } .warning, .org-warning { color: yellow; font-weight: bold } /* emacs other stuff --------------------- */ .org-date, .org-org-date { /* org-date */ color: #00ffff; text-decoration: underline; } .org-hide, .org-org-hide { /* org-hide */ color: #000000; } .org-level-1, .org-org-level-1 { /* org-level-1 */ color: #356da0; } .org-level-2,.org-org-level-2 { /* org-level-2 */ color: #7685de; } .org-todo,.org-org-todo { /* org-todo */ color: #ffc0cb; font-weight: bold; } mu-0.9.12/www/mu.jpg000066400000000000000000000465361250406103100142010ustar00rootroot00000000000000JFIFHHExifII* z(2izCanonCanon DIGITAL IXUS 30HH2008:09:10 21:00:444<0220DXl t|   |$0100 nD,4<G 2005:07:23 13:42:382005:07:23 13:42:38? t !.n" *<t|[  U V$\@Ct!Dz?EE q.qqq///IMG:DIGITAL IXUS 30 JPEGFirmware Version 1.00\&& I\ I3$)$)6>o$5m$8% 1364402@pR980100(0JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?a)=Hmj|T/5&9JsT^f9IQcEiLRi͓֠3bgY; )&vɤ&H1 j":ĐYҥIH HI(X) Vs4˲v4}IrK,+CVjUnj\I<'ֳ1+drED4 aja4iCJ4P۲i;TӺ֔ &)€).L-Έڜ9}\A{2ﴳt{z4Z[6IBz+CsZ|RjQ62;_Rd;Fway7H23{U:&܆RWeț ? NiDq"+9㜣I=k_+k@~,XĠ0 Tll 2I&ʌ:z5—C fYm.7Q?$pG2Jzz ]  6T+u5I<;}G<5Kc,?>6w){g vD =1YEեu<(Lƀ)~]SHOHqr'7eǙ">o](|IG`֓bINkiwƹ#ֻ 90#‹5T1\@lr?jتZAf#q~JlZz^E!ee8$j|UJmJ@XA{#>nEgtḣMMm:ֺh-E[aPvr}AO]̗`UuطKj~0e~5%8)2Z3RUAs &O1ЀϤ? C'5(믲g{ SZ0zRqQ4-1kUK0m6rnp{@,wOU~tRmS ZMuFdl8Q+i$ z;6wX9mt5a{.rA#8d=wSdT($`4_B}v*FUFI8fqۏC^9M]WW#\[}v6Q[#@ވ쁛+M09$zzF7sJp+Kro5Kj#FkZ54a_oi _nE"O#zv UMZYnz[ڢTBW;i0&Zϭq 2éuʷڷCYGr\m͕ũ"XGoβwG]Fz8 Y Kt:V$5rT_]s09xnۇWǘ?~dTI1H1q{W<摿bi#Gc /ӥLy;z}ӈS簭]—&uG?a׮xs,),8>T?JXZ"/iYҠPz55jhghpiDfJ`R8DmPwRIadG9jMRFҸr EicG)Lpq^s&%r ?a;ƥ O+Ϗ''@sjIGb6&tKEۿXxzhGZ'QʻϔxѣU#StX٢ƵFVhAjt-1IINM"CžمY_' zWJwJ]ҫȓa+ ?ixL̃Mf 9kʏz?Q[^*#( bqTlL0*_΢Y%JvՎ3I@YA&PՊNMjuB<}v]cI\fk>$ 5v*́j)c $Y.bhBm,q}~)!WtUV ӜRJ+uV";/sZlz6d[^?ss怱SY ʤ2qmnmm2!NC0sRW-o3YN@PRèNsIY'򮎘mx+xQSm<}k ϧI'އ[P([iqwW#8ǥ_*djW*v~=j`$HvT[I P0K* @܌9NǍ8ֳנ9$Dc7ËEM/+k:# p[j7K+>RsnVT Q~3Ԗm{y?O.1z}k+ök:-i19u0J,EEo·tuR¸Rx>)ݔdx,*߆No Ljn5sF`u'զh(G@8#Be⪲ZQtr Tg UhlԊJ캨9Ħ32HXTplNƇóNQE) `ǻKLVabSN(IFYAcM9I{/i %${GDP#!YTMMX7DPY h ܒ.HEIJaF9"d[I Ĉ,CzU`hi26HӚz6_z3t.I"A1pB]k"(ZTqʢz!٫^cnWH+KܖiV{dM䘖:6w$Dtee#U\mΊWΩ"3XDF+rLtAJtyXh֣%`V>`H]z$$,ҸM#)4Ws@o %@Ā4I,P#I0dVv.RnMŴ%1d.: CbsG p.9!9=:/:iwt@Z,/|86fa.~dnDS^ZQ[%𶶚 Hukp_ "렟Z8xDBw/i@v0B$n9ddAR FrI."őUR<@4b Z3=MAT642M;!RI0x񷳌EJOEJ-7zϩs pӆ*.q$ߪHYwF=w!) kִt\*L iO~_.MB@jPls Q =d@٭gҊ%q&V5Hǽ|Pvcl,>Bω4u2?$Z 'lkceH fgw D2F-?fhbǕtwP,y| 9 w;Ժm>0 I' &⫊81Iq eSF`x~:#s^ !kgM:jp#{s3@ b+'Da=BJ>˸΄Cz (s.n:+YEki A-h &c^F} f l-(UaE}"OP9!"ቶ7*[$p*DpE]Suw"ە$x->p+iA^}/$60BS0H*$1by'1b6쁧aUI=3(Vˢ]< =k&z1l TUs7V#{l͜ӑ/䡨eDJk}ǹ%*:067qvw͜ ũ0};UNOO(,u69:e;VC\\YtFN$KKNʱmp=(Fʃoq(54ַڬ/=ɧ/ `TQAGw(j#sT8Qy(i*]m/Éz5.. rT\2J/rS f-v~,okee۬R_"BϣR߲ p,ӣst'K# x$UUx]ob98s\fʭv{r @$/o^p!ːwnGU) `\otk"iug(dfDJl=& Bț&XxtRk$ YF|Y0[tEaJ&؄Zt)ARg[$ۄ$ԦtI.R~ԛ?xU){qFxZ>ڮI lrN)7 sâ@ktU3 ,52VVIQ!ۢTz K5 &=ѰL".Fy\<=<\02J;V"Dn[F 81WVJf-scs>qx->7Y*WvFdr>qȟES7L x*X"i3x.9D*)YF攗9ks**QNͤ]V2b5sd9fI ҾvK^ȅX@jbRFع++dc\l.}+){N 05rMħİJz 1،eqCCx-6sG@f| Pnz >gc_AO<2 XWL"gcRSbtFF3bV% >:Ѳv9܁ZMjNI}9ћfW8FZgf i7E3 &ֽ2$߸UF$QR 7.ѡj:ic]P/umU&Egg4$Ym=OqU ]^)QǶsLjv!%3i)#G˲fkqkQ^ _ WS/M3Hz9-.g+~}aT$p <>&(2.$} KЮq! [qgj0b,vZCkK9Xg(m`ij]|/spAs[i*1gaz]ۇ4:|u<1^Qd4:tΜ1=v-Nw5Ж04]=_Y5UC窔WrztQ_#$.{)*9jq>Y\lѧzMNjƆ. .q@t&2V㍎y qGg|,p 5Ťh\1[yg7W8D˚؇V:"lS xdhޜPR𗙦ݤҸX5ܔL)j_&5V$gZxHy窣wn{k>̏uH5MUӲjw2X}b:%٩+m4Xf9ds$s,Fcqf3ߔwܮ(kvi28:J) Fٷ=x9.;F\fNzwY -`{ sR!Z dW Mܩ[$74fBdW4dc{h#nqC$ -(vhAK$"g77wcc}4|;xqkoewx.{&252V޷i΄|AλPh8A!?(Y[xn2}8H75ӉߨU$2.g$$QaF64#G<@wG/RԊYkAYCۙb6:yD\|Ȭ&9c2$pd ݑ4ޢR|8^Fݧߌ&Ǥ<:Rŀ`j%u9W8k>GlqJ X8.|VbJh]K i]`t5CaXe[!6h-x-6vԫeM}Y0'ӗUTOY(ohDı,7Ց͏s=ʃ 5UU0[{Ϲj'P-LJՆҐzo@j};WVql NlK~G/Xg㸅vEr$Zg KeR,|bEt&L:QenP~&X x{ۮNI9iDDM8l`d-ysOEnal6gh6he@CC~31|=kǛaخ/@py dX|4-gvf&7K+u^M .Zkri|eLudM8 s17)hHJ`2d*fl?,bo`}jQ8f29rm",dp#C> F4l$-p ظi,K,g8q::0iܝN}EL{ @0NX=fzOG{XfOrĩ)0"کbZcH)udq GUV-tpkz9=qDآcYErɭR2wmaQ;cUUGU2K'gP0\ZT7i SV:,2p+nq2RVGF[Sqñ(}+#Kx8jQ^  o.<netMqPAĶum3d }>70bf5TeI Z\֋-c%;N 1TMxmԸg1r#O6iu]Oy-UMUQG,hmaFwwgX\i-Ouxdy<7[T08G|@%UҲ\Ej\L1Q{߼'gSY^OuYcYZ;kf2\ (90ˠڜ~&Nr^u}ed]kC\=CD4Ij INAHl~wtT0E{ 1A;c7`埭]1\3EK1'6BjNWuU4(@#'%h)0I+\(挈2ν, iB؊Yb'Kʊ6 q f_[GZF5ɶ.Ww`Πُ.?b9Sa)#dQ1XgDi]b/BRned뤋#pH2Y&:9R[+ r P\rfk6-\S>I Ξ]NB(sIK.7)Ьڰ {%aq%7sDFwOjYGPۄonU+F`Lh]R5.L8W7+nnG^UL Jf~^ih˗ok`^%0Jpg \~:J&ЍR>dGkUR7 5$lS=xZ3z.g76υJM#=@:SRj5 9'[űM%e#nM 9Q :ne{fmeiJnY$fO6lm][K)Zr/#~ r8lpMH=U#T8u ܲ{؆qN $h=˸.$6hy%Y(O6jU[Lۤ8Ӧ1ꪅd=NJ1912G%( - a(\+h" L$ )uƆɶ'r[,H3|qVgz \n>+9+ݥЎÙ4Ʋ\o6 n&<^v*c0:;H}Bq ZE(~BVseϴi C-S uGz/Em^ վ~"r_crۃz,(tgHʰq*;ԒphUnV#ֳ;T/e6+a l6Rٴ +s!Rvd[-~>9}|:bF4C,8ׁHǩ7E42S(pSI'+*8OS,8cs`.V Cq躖⾲64gUP/(Y˛Y.v_cuDNNLƗZj{^ ޶Ecq-ҋu.cGȯMmTG e}GpZq{{4yy2#>êOΉJ[aZ#29kA#^|4Bu9ÒoYdc. !)L) l(ѧ$LxժdX !#^v]nQ#7+"p' 󹧢;,jʹoШ/EњFS8o`WnyrmMYsiP~tU#'6--;1lUx5%Dd0!573??!5}~lk9HO<Йl6MhϬ οqx)3I,ж#Пu6OR?Bjp;,^yV oJDv'?T) G oۊ.￶-Ug˪MgkdKa2cEhښQBO4h>3b;/KG4p7Ķ)pjeybЏGoFcūi 钱-) yvzQSUP?=B'lP׷~j6Z͔ 5.=n~i$S0NXR6Wi{QZje`9j/ov /?xN O{Mp/g6ͣЧhCō0#A+#4dIH$f)da@E˹!4R}Be@?n|mڣC:FwxNlX3v FT/$ ͦҰ|ze_~]8"f0 fVd'&da]"Sa$m" ؉vA=@&$%E4ZvC.Jcw;YPF˛cpSo2~zh~c\?\JA)x55n[?kX4JD)A 4g !B<~C臹~K55I¶YD0N٤Tnls`_Rs` ;ֱ ޣniji04Bb QaF@@Q[eB[ $M,w ɻOJ׫ws v:y8Է%fkF@$k$y!.SHvJ`K$L-l|% dGyΗ??Z~aKY$t3(KfHA 'mCb;"4u٭a\ד(u,@G,V_ҽD3/&2_,߳>An 9.Bp3FH`\oSep#bnӳ>t躪yGtMAS?nx 99O&TI-m>!12^d\&tihj^*Hm4<NOڳʥuٰ޵^c KE 4|Ğ{MͮMj=k*YMKRMPE\HHeM=5.)[K$.$)h3ᶖR1~?RmLS&S!nNuW'5u_\8/ r\/tf-US{xd~ns]{yUf?տI; s永4 ȱ^[EIemmpvn-TF?B1:V5渌#03} eM#3R-8ܘTYjGSHM7ݞv&[$߿Q}-gzgp*WWM9ZYlVK6C$1a-@ Y=_b k+詪vkOu’CE>CtIvcNZȨ0 =m5#^n M`qRPL$A}?Foһ_|%?MۡŲڻnv8Yo4#Ь[rw%X L^|+1zsN\f-ZL~Q\'@AAA: E  (" @A: UCe+̻'hd~ ڶl ?%uϱME~Ua'I"RFSI X&a (և:$R/4\#;6؜ RkYlOb4y>6o{ օڔv{z'KWj.>D@w]CSx~ʡeozSKY# <_v 0)%{fohۂN;Iv8u5LJ .\"24M bHHʻ wF.E.ѻbMS[ێ86W&DE>̴[BlEYItS ' :% u魯;dYmԟҧ hJ]C{в?T1|^0Ifl(0_v?AWn{Ǐs= .ڜ0G do10gI@_,GhglX2^op3۷ Ív=c ,w;?U6g?q+&3g=#=wfHϩ;bՋ0%Ir6"6*((VPJ1uf(Cub+^ 4@O+of0{>x m/fc@ϓXipd0TR1+((Hs&2s 30;W>0k?Ps$J//}YS$]i'ܿA "uμ)5Obl_n4&H3L17sR lfNK*VG9|R=?0gX$I`pxjPdfKr ,!8}g f?Ͽ{#6Լb>20`rȶ] TedlaǶxq!\ܞG10ŧK!*#U;X$+Š`ZvJ<}kԼA=OV ۶nr&1MRkDSk3J,-weYe0 _kn5L5$I4W&O4>5lmg.lpkFW]7xbzXtxd&Vwhv 3Ys}LC)9`hhVqT\չ} (K,wz:]aNZffy2Ó$\+%XAJv9˨ P ^&;"믿˝>nn^fgk^ !esYMsXt15Is\؁fMos>4pIJNNnndc L$ 0_1 6ҩFCabD\С76׵@ۊxҪni78NNxָ?^&NcjjCI,<)Sz2PT%q`3I0ߤN9R5MP# tߴ7 kvu sݵKgTjtb@N(MR>6ymPU:O{eN6'2. hskJJ@ P_v٥B_A驷yU%KfVl͜_3l2i Dgqv +^FC*AYMìS)]2I0_Iny!hEGӧO,*)wށ`0(0Rswap\- yy{Y^ve9} eQ82K)Q tFTYGx$I̵19JbZQ |* q:0Pߟ7x˛ބEUsM~e;,LrY yS2f7mѹ3w@U>@6Ws]+eyEY!0t.)$I.(Uȹb>h(P⯿Ŧx()i<*WT,JcNSc&7+V`i8Cߋ9&<+&( HJ+P#l|ŨDUUR|a?$k̈́ }%Gdڿ C =,9ozS̱ o3Z,EUdMXQ1I1<l3D1#H^GPDBA%% _ (~ԊyHYabbo6T0f.dЬsvk>Oǝ0+Bs|f ~70nWt!(8T0QI^U.6ӵ8M;fkhNIńj6'ك H\;(2ziX=, 6@3l֔Uf&V`x !pH %x7h_VS fo}LNihH\uη>cVXlfx199N^u)5E|hh5AU{N4sH[X]uCz3EidJqTbv~{Llw Aq|D_l%<'.F?WJi5!>7_ƛtBTزu+uQ@%ڭ^ð^UEп^ EO`T*~W^z:7llOkۢtܫR} i| 0B2!Ef1&!Ogp] ߩQwA>Ȫr\e9nM( DtvYVرmdb^OBzunz~s6, _q*:dq7XY&<aǎ U,f/1a2?frXځAFt^geERJ1>%GܓQY ,o3+گx PA[Jӳ sؿUe|$?iX&Js *"o$o_~ݨ Y#*)KL tcozӚ*XE]Lϙ)HlvzM4A^xD2miȽ{C54GxEn@1quWc4dZZbd@8r88ťeP^}}-_Jz<\fCGNXZ\6|'`S ӚaxQ3#7 -4̿ˁgN޽/⪫v-V z#c JUU.n>vJ]6gHrkd9E;Ur麯"{ cmTRBͨxT Pkd*x@d(}_sWEN, ߄gBLzdOԑޠbu^;",GSL1ৰi~:pm%.wШpӶVz:b"3>7&U?/{?JKiy)5c(ٰyMIy}uQ '$GI,166ѦJZ kw]$_QL[R7yΌv S"à4ry™x091TWJ:.9vsS55]qk <qyZԠDQ(q`ZxUkE%-bC/0U/bL3nzs\C,Bg(=4ʢEl7K]b=.++PfNlփ*۶Q!j澼凙ffJl>mf*XkJk7: / EeW;x,#ͺ@k_*=A1_y^ǗLs u1πƄ1Uv5CCk LL=_ܸj1xym;[A**yGeUWqHK.YSag'L@ۉ4gݼ n߼!LL r5̨m؂Z쐀05\R 24+3֩F:ܜB%EG0Z2+ NfJG[îfz @vGh~!̔dbC/L)*¿J-VqBpAf3O>3 g0'flFgn701>( Y&n}'A*Y/gw`„ ̓*ǎ⥃Gp-‰oi6ܭO(= 0%K[l8YvLIUwJ$ޠoTRbpu=ͥiك/6YHfmw8І.zWar"3LX^ZĽ}V(ʘ~*VP5b 娔ޚk`lChLnfnwL&Lf7p 6ɡTf"zibT }S֔aaR_l/9LgMq6հj1BDYTfzL{;P'1cffC "ȪBYeh[Nϟ:[,TɏG楒 h4h4vfLЌ} mWZBkZTWic-ŗ _nJ)Unga1&=жp0Fez+%kPSLqN"TS&6`CJ[0W!D0Wj(znpm׀4ae[\DA﫲BwxeҀ ՖO6#r,+b N&I=|c/90jC=dpk9pBOS:RH!'!b>?Rz3\UaC2*in. `/) cfLO( aAtE&p`p2n5s'8p&ȥj?+i9h9&N ʘp~@mRFtJ !,LպB'IIRUf ڥS PvKrDaݾm%<MF z (jNEurdk5|1wAG Э*6^ x'P Y%0n]qť/߻S5rVIO_Ǭ^]c&Ʀ!f#5CBr=NFm!TXv/Q$!؃kA}1Y{!y#bGe.`4̐tfx?JlQPb| 0%`Jb+x9,G_\7٧FG1H@#P{]Kv4 }J"2îvmϯcVR9'b4 lYVD=Dq?jii?$ye* sVDӈH=rЮbMVܑ.kd׬L7Ї 2qF I.*N=FYQ~eP^~.LDLT%<Z \&۹V'nt#z%Ï]CJ ]o4ʲJɔ'DḌ?L)A;f0?#P#2Cj\jDJI^kj0r #ۛ2_s-{,}: IDATcAYps@UtTO5 ルB$Zc+DBk9o%<f\&g^ms_MIΨqPX?o{a;LhÈDbo7oV"T@e7\s=\f؆6Z7\s;`u㔚4 f`0Eĉػ ϘhejM&*ޔ5fVIϯq5w[> ɘp4 *w((KBn 0uKʔv#ԁ3UBh8Z_ sMFL.\T96V\cS_.-H O>,4ORD&r=.^+mWImz ~*柭6Th زe3ʲCR+(%YƃnMtf&0ۚ K8jɘyg ߨ4F+7B+)%Zc-ܲ*< d!V󚋶m?z+ϿNZfS :{ .N=fS73D:v8|HR8:prY*]ЬG.Hk|zF {^ٺJbƥPS{.O)+KyjLp"rrR peQB0 <6&2_:K/Bc8V)کFu]}B .)t[?VIk?L)Ħ͛ JVѮaֱ<܋( mJAs|a< „ a&ŵ[lVb ⸼kVѥt'[އ0AP*\{%AiH-}maǮOǫ51SQAgƥvFEڧNZ@)#i6M7\/AdJgl2w}GX-LX3Ǡ*iA P1#J %e< ) Y08P d9||OUYu.Oke\ʯª"!|5iדR:d('%aB"ۅJi2rM"ZY\dU.aW]mlҚ{aȪ4V(LM@gs<*%IXf#xQVaK/b$"ZcG@ߋʙb 4=F0#OA|ypjayL2Y>8LrPtDـdpNyoB0.F"ܾ1$Bϖ6tg1%fj| 0ϱi(0J,uCۺ4/ 7oy&pN^t:D#pM|?* wĆ`b}6@P3!df SKA YILNYv>YUf0==ޠT/Ú]nǞޫKz)_`>ك'uA&)T}kn<Җ`O򭏣~IkYV#UƻRaQz@r.1wR&UR8I_[8$|#2-q݆<|/ۯ+D8t8_ &L 5HxHy=[g IVH#JP}:i&,(a֪`婼~˟:ť.&[V'.r"|}O LNIVRkÖ-U*f0Zm`PE=~Z %LX MA-j##eDsK=Ld@aFFRf l$@Q.%}ԁft鳾}u/4# d@l6^`3?ne 98@uA'1k\sNm2 e3"E[t5S(JD̲*̞CD溆Mf[=Bn(HW+BaV~hdX\xaΗ7kFlEE/, u/KؘjP 6}$  aÀA7}&̿k%4=u(\%Mp}]̌qlLpRaRE[j6!k0B13ƌ,%~LD/RK(@ ,L[0I!k& k!mMS"r!\KY{3svw |"րhz1um%<G~z,қ$܆^cAi7LG"_x!8OiW1cSj ԍjN"{5%6D ('1|P7V%ؼ@>p"M>ǚ>y6jS˲R_p hO-?UN5Fĭa*W_B`S в?(Kt jJE q[/>sKӠBR̅U&5d%:&ȼLrbw,2Ajҿ=uS,rgIafDhd2>b<ז $ ld%z 7%'aPTxكQ垍>lK0hYib$ wmq\ jc eAa5V-Gr^{4kK=?j."4 M5+H-Ɛ̱\T_o#!֌0y P-A B A̠"Èhc0!pH͆'LQA 23&'pǭW_< Ky( J#>MD 2k@A"I($D|ͳCUJNh͵, <ԋ.H>:{j`9Jig"p9PqaKǨ u능XM3 +Bz| fV+ ϲf 怈‰k%Pð/!a."8,ͯi_F3ÉQ%XL`.VVnw-9 5]װȆwv6oI.@3xPnZ@\$8$~~HDEK.;P+ %RPLs0AQ38vb1L^"Õ06S"P}0wh DNqԂLccNC47f50"gXl WX߯ UL`||E)zmњ I~`5LDEh V WDP9<QWeD`P}ž[bc3&mT|Y5{SA=4}U/,v=ǫPdn Nje ¶l/6꥞Q_̮N@a`33vTW4*@(?;@eǃ2np2Y VX$;`V Q .Q {Nx,2GDe>*0zk"05vMŶ[ \X8LOODK8ǏpuI\x!0AㅪaTY; &WCFa%T?CrF8~n4#)\!Lٚ yz8<Ek(gDqHQ0Oy隯ՑtX]'y.033>n5  ;I#a-V`fzp|PI ƟZuj(6GIF^D$m%,E?8i5 a$v:{|zP+*x2>3ʆ={_&C@חUOUȱ}ˬ_՟m#XmZo_wipc NMbp(C9rbz 5/,M!> ]R:i,D!"W?o%h6p)E&7yf-=r.1F Ʌa;lAS]țz-L_H?XsϛA~n;C$Wl0û˜dųٸ A!Z>t4cy/&L7g7oEհ]#:~LtZA_crIMژd2a^cW/NuZ;/9`bR$kKp~c_/$ 3]¢^i=deGͣ\DW +=C}&u^XrJ(jµF1(p"^:| y.~Խ~9(+!XJ=QO„%n7,%ֱA*NR4,SZ \wpmON^j5kB6|~rtc&9a%; qнV}>}-QfG>n/q26"Kڇ߭0!+lp 342ؽ&HM1aDʝ*T\vXI,_ Hf4 }l.x3J9-&j:kVwD dy-,^a~lu`mùVJjPg)H8Mj 1"@~<jnߵ8>1]_ 4]:n۸+3l~oW]';Y'([_z  M@$gCk̨ JQu" k-XEK{ .̣,LrkYApܻ_ɂnCׯ /_⻞p[}aa R2+HPRA)ӟ\M4 49ڍ \@)^Z @?x^FhQ%oi +<$1_. O!KRS}(.1o"#\wh6rٸMsSo߾c #@s%M笁=%N&`jj f+R*IrH5hRIl n#)6V94RzQl(T|@G>.;<Q!sC&WD4ahY: ,?HV7!"`xvlƎms( ^Ġ(1T( E)QVU%! r'(#LYw&yL=gĆXJ^_όu`8uf 3B>ěo>48#7)^zK(x"37~=ӗ\ʎ`9&'}LMMbYX%Aŋ0~Xƀ΋f011C!}{6) B>z݁g`*@I4 Lr=arܱE^Ta ?o}p5, 2 JyA\1yjVKր|Rjб3e@o 4LDlά$0<(%o?=]u| ޟArڹq[1=3Kv^^Qh2=,ayYOdwE#k#B7(fLr=MO//hrPՀktm_*UDЍظ?OD(:? 5  (L= uc0 y89zg휯d>WK8ujRysJ w?~ {5W\=Zk j%ordgZU5ŦhA9MF¯2Tm X Jۊ̹.i6s=','8w,Pvbܮ;|A)ܜhsI2+2t?y [6"o)6Qk<Sqg"~K+|Ͼc~C{W1͝~G O |HfSĮ s r=ֶҨVPz_D)ۗ砉8~U 4 xnxS &ApDY|G>5ZPuA~|@,10Q{ͣeޓNVuct aNJ#R`Ďjmq1VfPBؼy>Y9nkR׵h6Ͻ,Rb(73T֜7v/yU2p!)|qjr,llf٥$pu`DG gcml4r0]+P}!RڍO 9hm"5/{gȇ:_ýhXJF%EVp[9_'hn|<8>wS&m4Z;hXZd~ ` Mev#0D!k"y:%Wx=⮨"~o毾Dt7{g^:th B.pZ<#?da䨙$tw ZHM*RJor2E$_lFG7 -'Sٞz 2aG>=^zkrD C(0mW0 2t&y=8#ZoJc,;Wu[6'ccK" HQd)ȏN "Ȇd@mq"LǓd[f<=wG-zuOO/3}>;rYOk!RR㮲B"ix0uZ)xqMǐ$}.Ssw7*J (mp>xgAJre`IMT@$T"LMqLNp115ƽNp iJ↨XOT%>K#Wm_- 6o4EP,J"Wu&zڨLs5k`iLU@kj[kMM+VTé,Vrl0S 3潤5ku|DDeR[ /@YhmdZ@*V^x7x=H$TލݭE R%z1|3C:M7: 6:dp?|CH0{i 9nciRcV=SV*vh @[q睷_3^J4H Փ՞$A$huRtuR >LNOP ⹬%mc-,.JqxwLTEGl WYVzҰWHȲRJ&C)0S.BdazՉ"Gj !^f3Åұ!igTgN zlЅN7zPbKjJ>J~gN O|GDղnS/y9^CRWVXތv+}\ܥ V2E:inI178z=KT%yt$!&ښ"㑧1=1vB$K&tW “ј5a%rNlHPŅrnB|;1Mkz\9r9ƖT3Rض&panS_BADʺ[ehd1'oo)fcCv*3.'z Lbbc=DQJ8(䥨A(17a:^"2dʫ !Q.X@2YJe%v u5m0eEI!%F ܅ՅD݅64Lj8?S sYEĐY!l^ս-zDŽ'\"c<+Obv&P-i-t(K]2HXJi@kE~<+׿u xYQ"/4 Q z @:c":FA@"6a iYZ~rm+ȣWū9Wr2^dXu(:k;@䴐dc#u6DԔwTbD6k5ҍ&Yo3}Up" 0!HcCh&]w]Z%Zs $%FZ0"+EXE%@╅6խӸ||owۭԉ1:5hGONS?ĉgyn~ @u1IC|CVS#yA ẜ%1?S_C.-qp{y`LEnN(:g*2$j72^lP5ԣ:nԚn P/0u\oȄ8 8y\4ohS&IG$E|=6# H ={3s $>. cZuħi'RvLju3¿>xކ[n>qLMNiI:d[ÿ /rW0?>KxS_ctGM&(KU$8s<.^Z%DRI]ӡy fq9^D족5k[$IAFX?w_<tZ 6֚@v^D D"7gqkd+6;10F@jL}p2 XǍخ12~{OLHRKa*!/ IDAT-=<..-K-hmppmooE(UTs #d\~3@b|MAzg j#h/|_8WM5S؇cL"1 e` p)=4wfJ,r',6'4XTVTnl #6ZҎ#C4X YV`\B)#}}Wߦ1;ӿ ?wD1@jRL Ɂ LMt<+gfqLcq$}v[h ڭBcc?ss9= s$6GLuY9XXZዯ<;O`d7>X+Fz*:B3M$0\UqE+D_xGMo#k&w0/eV 6Vk@ +%-}gP\cW TxU(Jvr –j`na0Ll;7',X`޽ݿ/vPF@|R7tJiKP*rSk,ڥVR_'ۤ4q|ǿ|9JszO`p{:Jrr6| +X[wX/ DMi*i[hiUE矚[z}{J[mLhk?KEf]L/M$XyX33n4#MX'$kFK[U*R@e=JeO,.Wyо JJ1ϡj%֌Q0 eƑB="Mfk^ϷO߇JTONņS؁ƴ6y ֮mM9z~L =WiLl*4ST#{뗏=)T(y(JMbo a=R#rKճOϢNM;2=w4IY_FRt^&> Y3U^ }66-v>1ӽ| f}/0E&0̟5PoZ"k uۜ\vc:j͖{?y[rݻ{c_RR:J ˒TėCjOVa=gW^ +Qߣ\rЖPQ|>I悊Q|pLI&ʫ2zŞ{ˠ:Z}Xs9Fn똮L=Lr;Mݞ& G!!2mmLKk#4VYPZJ\i=Ȳb_X.-gR&5$+yt!/Ǯ a6`ǟց@l~Qeda0{'*oo<}(vІn'}k5$3baۼ˽2M`"=&u7 (Ϧxik"2bg7k>`ӓ>^DribN!V[^S1sH6ЦkI&5f e*G6<(l<}S flXCi?vUc7:bezom~1UiVcob_k>r>hW&Br/N`(1sf<1\QjÞ܇blz' LnO1kl< j`uM4Xk͚=.L2m/8b8ly3PMޓmʽ8 Ml`+4 >5G Vl1bF=(aWX׾C{o/ `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `l'y^V\yIENDB`mu-0.9.12/www/mu4e-1.png000066400000000000000000001360361250406103100145670ustar00rootroot00000000000000PNG  IHDR`V{sBIT|dtEXtSoftwaregnome-screenshot> IDATxwS^&" v%1h,ƒĨ`!F+XAF]}i?޾Ǯ~?{{澙3g=#ƺu6le0 !@  M,b۟9rSӧO$>QFM2e v}pE@ ȶmX|9s̉iK.#0eee~vO7o^?:W\'²,Lӌ@ x#IŴMY,+ƺmY)**"??+VoTK'LF@ Oe#YYYSkjj~sƏeY1=@  ȲsCQQj;''Dݎ1=@  m'R4V :}?5oCPDHRq+/>-DzL/q VzvqCɽ媕+}>F_ݔ@O0ƭ(2*t+T?N@W70P#4{q=׏ * }e0{w IR̓(iQ=ݷbŊ~( &MBeill$991c`&W*Ws+*&,zf8┋ÑJaj(6yG#7mGj!\?_)SU$Ĵ,8\)OgcI%r0N\1oWΟz[`䙜wN|ꢫছ3e/]m-^Ͼ}g [=+G0,ǐ$EIo;qz++GR ,a܆m7Zu˲e1cưc"4OL,K,b\6/~>årY,~~H0$-k)|>z<.7s]OSu4/K_OcY_֗p7Gw'ZLBC$XѯDwh{Z=\(}vwC;fi7_gSSoG]v'@ Yގqew,$EX.i3}YzmMxw#+**F7׸LrX@04yw$;}]^whY_rB$4ϚNeϿJOLr)5_??Bn%,g_U,;ְ.]rEuow<μk9x}ARƧ7pմE]Kw_.?3}=2}6[PB%+Z03f̈,aҶVXப*+VpDm:%HN@ ==/Wӊ]LsdE0MӲچ5oqYf_m?2ڎ;!a`rpWR0/#88cn~vfdY<Kˇۖ[Gf{㙇O?βV^m>']v 5}GChMG6%,2 K8q"&L@uTU4M>`6l@}}}Ty"T LKPYO`KvZ7w_{j苜pi3t%ȗMJwjgY7䬫)޼V//_·z?q(ʫViko"K4-l|DZ 3OI;Y{0V=2}`?K=mv7ac;?|'|ҫ6{3;]ES۪2~x$I0 -[A$IL0˗G7A܊E,CyЄoJji૏_)bқu _@d< ,7pb{@k=veqލ 1mꤶJgz_r` ˲efy]ʄ_q{mK=w+]Pww@,bKoվۛ{j:+e|M ÈlUX@ r౗^ycْ7y{gϕWp׋'a'?Oȋosi/&rE7Fcd䃸?[p/Oyx<{ Pgw; z"l܆svElj^aou޴SۆaOcYS^^?$IޑXܮ.\h͝;m!@0(6;v"`Y Fbhmmպp:(2;ײL`f +6v"#aa&ZЏ*x}!^}vڕw=ݶp#:!{0ݕu֑vUU%--۶Î׎FlWv$duQg@ 3IJJ2Ht: 5ZZZشi.+E!99;...a @0С`Djj*`0jA_ em@ ~dffRZZfʾ/HR(埪=BmC e¸@ 鑑磪 ˅nIBdn?>@ `HNNlQE @ ~`1V @ KD*0@ O@ 1&@ @0[@  @ qs[CIU~XxS4~Ɉs&!ӒUwVΉkXs\{XeX.v=s̹ԗhCe뜂w<zWuyn{U]x﹈N۶.z˲h|'Y9ܾSK8z8#9 xOvzvYbX2Tq[^m\K߾[U,y։5ʼ?~Ə[y+Yv)qEz a][/:Wk՘7"fq4Xc7+lT^*n%NWn{x;ջ_~9KHYZ ?^s(A מ[K ,ˢY@æ/MnjzԺ\y^3Ook#sryK<g׷wQΝkMc;/%~ -T6,cvN.9c, Ǧ(z,Ox> yqv>23ۭ|G@A~!3j|:^ʰ;?=eLuZ5qaYqތOO~n^U??ھmZVٖ/]MǟںwɷZn''o*hDh 򫳎 'فls1_jN;g] >:[KJ͇I]s&Quc=ٹ]1rW>*vTǵ;M=05u";_=υC^/o&0&ɘtn*R/[Kno!]uwc[ץ\Q ~H .̞cܼ|*+y),7k?95o]޴AV=*\h'gz 0*hLR(9ֲ$(Xy: z認z+w,fRa!g1J RX`B0nk2: GƎ","SѨر}Mrt\ܗ Fݩ^w?7ݫe7s*׀Q.S[t6}il*-#~ѿdMjiI< W2&"Uu?lݲ,3ݿ7אrwz:DZ-t] !?u,}ࢧnW7=e&cE%O|F'\ `Kq;dò/$.e;'ぴl7nj䶽#xwY1)Zs}ϻ,/ާg.㑻G%0?r dd4F:u${s.&Uذ%/KLUsp=TL)ڍhr\ֱ/_IEeV.^tfJKq6q1ޞ%붗o#g(,/m/o`Mz>yXݍhX{/,ZNP]1W {BUsw*Sݳ6\v sR^\vQ1g^q OhI]O4j+yzwZ(/g^{̾N.9o8znu/t$~IsqVn]%΢< Ey70r'E9ab5W~('pW޷(:U;d8NV؎Wv>^4.Yw-gL{x rlIv^ތhXCm}g(?u Zg, 8;) - =e)X'}F#`rN^[8߂=hcyO{J?+<;wŪ)O q{ 2~Aws+@  6}Ζ @  Uq+@ cP~,2r@ F-@ @0 J֖E @ 0"V @ [@ 0n@ { ¸@ 1V @ [@ 0n@ { j< jAR[_eYXn݉$%',ˀւeQ\Ʀ.TYweɘQc =L YHt֯eAԸ)Kq98˶xqjx<Zi<(FZss3iiiȊ닙q:YY._QQAM@OScG l]3u ͆$IXE G4JjZJjZz\8Uō5M\V|1-M͕(<ϘůwWL>h2 AoŸ쓥=^OHkeu@&aG>"Ѷ3 Wֵ. LK"]?~;|aȲn(~wLs|PPQch I***"(*(!B)L nژ>CIi)O߭zy7ƅ_HjjjdzviDģ3n`*aDBk4WniDrrڠ[`4)^֭qk> t]93¯u]'=X?":evC%45pH1 V #iZx^,ˊt$,+7S͞ 7.f}yI6k"YS$(ieOYwGǭO/ㅛ!->(r AoŸ{ ] #gkf8RPoc.lE!%J\~o|dԬ%K?eYl޲n}II8o߃$G4_7aVloO>s{_{<;L;[ԳOqULp:u=](v(4C"l&L6bfcٰ, EscD`0H(BrJ2M@fV$a&X? >LӤ ̌P.Buv瓕,:HGIkk+n;㿯mj  Uw9n:du~ؼ>;#s5s'ee3Mv@ }؆1 Gl m@`"RG=qv"kZտJ]G}/YASS#@p}ujR=z aOXma!ﮦi,䝘$x:ujfo4 Mд š" sfS_Wǘ1cp̿o>]}mx| =z45Ռ9?0XXȲ*zA rE7=jCudYpt=jPDT=4 bYt: eԆ/msA]UJJJzruj]W9E|]͞Ȝ"=#7除gc#--'bK1_w.@ q K0P\m8qfZX4:} {a );oݫ)bLux>jk4x#ed23д] b&,ye0X~֯c>7v,n:6nQc((ix^LÌM|X#Ə|#FDdܿ뮾-p0FCYYylڴIblyt8Dz[* ́11a6!I`;HϠrr1 4z}xEMm-9dfynwqalb|̎kkźrr.6m0Ɯ'KJr YтZ1ɜ&|gd¸C%tuo HN)oxW\`PUqޮt]`0@MM3/<0/ι睃%Jx<^TEEe֬Y>>_|){gx?e˸SLj3e] yy6܈˙0hԴ j+Qm*P;F2_Dò,4-,IQYM b LkDuNRS[KjZZ$^윘?2%Ik@Yi4IMM2{G҄E{p`ȥ. cAt]Ǯm6TEAAH Hjզ@>=t'#~RNÝ+1QC-@% ]O0؆%Ȓ!kE4652y膎n衛bw8x=5n \{,ovv>&׋n Hdi&̬\j+HNInr&GkK3z,ˤ ߇&۝1_N'ՑEss3455r#== Uwa:Ξۮ3hXI}}=>/ni¢ ea/*tdæ cdb6liˤ)d2@0KXfHmrV4 4zž0U`e)5Pݰز,Z[[>ldT#?ڈ abtS&O%1)W|^/+ 5mkk+ _@" "r#eذB ϵ6.J))XEbb"5U!oi*m5< =5Up8%yP&QUYyTWWnr'SWSeIIHH$> l. IINuu5ZZx$&&r'CxRū\4L%9)9ni¢~x. 3sRǡy'8嘉8N̶vos~w?*go#w@ $Y vi:{QLc1c^@ 7gmۖW]STvwH佧q[41 f[tewCNnjo1?a"fɈ5o>#3|pBYʰ,(**dGy#Fj˃{}LS?AqzhoKk$$&exЂ:nVNp=ynʣĤ$\.7^OKyn-󒐐iHD Dƾb;JK! NTWW#+2Mض-N̠pyq'7n$ltAIbqoYijl ŨC\҄E{+0S9p(L O_rsc.z{)9Ήl_am AOXnrv0jwY"Ӝ,\v/Jm{j?7Ţ?{qʼ3^]o- lYOm]À2M4hnj||G??|^X<%%gq}YVhjjBc {M}{)Eod;$ 5reee /*²,Yp_{C=a]ը4`]a)*RR;L5q =(J0a;i=hWr9̈imi|t윜~5~^S}Pq6rrrڲ)t:#o?:4ab겎9᷏H9O? ;5 ӑ-%Un+oF:P+evpv@-mH!+Kr( $!Ir'7<,|c 4brrN8~ii@8-CP]]mA[RS3Xn=W4M à k=3- CsS3c5ny6lK(,,d[ %%\qٕXC̳O?n[ zK>u y{"k`mm -VFK0\X /kL;ayy۷oNfĈ4MLӠM ;(2;C0 czS,^ɜ9GP]]MA0JJJV.(4Ea#GQG;oЀ1i`>CBB.w(m'e>okYYAJ[,kOTrd)V._4ˣχaL>˲XgdP[]3;,3E%{&,6@Rn{o<|)uVmZAJ=5')x4A%hrFe/N?qđ"߭@ r-,&&rȸN"d$,!6 /5!O ގߌ ֭_<`B^FfvFBt:`6`04ny 455cF8>cƲeƌÏl+)0 ^/v˲8¢4Ǝ ʦ9rlඝ']kPx<cZZ#򩨬cݾbasG+uuI)0_"41T4~?F[S.]Qd&\.W{*pџ4apy L}%=̓7ŧnojv/EQ^:S&e1eu[x6ͭ.㜳OOBZZ:SLAQa !C\^̶/^Յ+uIbBeYL?/zʈێ!4B }n2lĆ?jYWݦiILHaĈ68LDVB_tQh `sxwy)VĉǝBQUN'NIHNNggK#==#f=4:NKLLnSTT uЌm ]q\p0iij"11M6`Sm$'',ҳh'19uD`n0bH҄ 0Pχ˹W}0dA`W~?>- 7(3O?]qO?/F4p@biyY}&OMyBo˲PY7˒%_-d8㼈_`&roFeYAVB O<3 3( eF^Izz'0/ddpLRR$t:im }OAB_}l8N<( $)>Z[[#z!uzuiZ# ^pp8oKNŃAJj*U$$#- #+r&IQufR4ˉ]?iiAhjhΨ ӕ[eXu]4`PCׂ>?χyS[e&9pFλ˯/dכ@  1 K,: _v`Id}%I"++#_<зgpY9,Ajj cǎp zm6/={xNvaқxޘ6tѴPSUUl*$&&:sUŦ1:U0D;yn].ܹ>u]Y~mA;Tp 1;h Q$A"TUEUU$Ib)S().0PLmx-MXmݮӰYq8ge\w]}niHRۄMM0mtnkO ';~>t>K~{U$@  b-IL?1 QQU)OIjhhl,˝nv3s5GxUxֆb? 뫿|~n:p 6y' K mj 폏bW=\ȗ?m&ŒVuN:HEfnE p1MbN)jU _̈ON8La/,@c ' |4?#7p>.I qNmR[V2}L=Wy47sӴo/߫^< N p~?>8RX@/g1'NZ%L>4lw_A|zl˱ޮci7ւl0h"\.sE`# wa1_; wsΆןp&ʸHlT}(K ur%EcG!ʙ$yV.F{q)V@B4ӏDΰg?؄ev#|l{gzcQo *vӇE"&Ntus̩雧1cQͅO~4hHlEa}\o)鮪MT%.6[8{(\Dqwat/9S%MBSe8&.$WY*83FXilAnI Is:7ӂ+V ql /u^| DX=dH^@:B'.ކ4i&_Fڑ%Jz~~,ɉ|ʵa(V!|19}ioP!%Wc\ȧ^o2gs* t12?r7#Cf4,Z=KQ''a.~ /Pyנ^^3vhE98Z P Q\<.%-$ ˌ҇hz*imQzrb!pC{^`ڨ5@=@r ` Y#WC=`ΑM*{1CΪ:[LOm3L֬rG2q׸( X[28a!%͒%!fS ߵ41錣Pϛ  mG )PG}Ԓ?YVkMHM?.p-VBML;L 4UA"ǜ5Wq àz Mpp, EhPh5+4۔Mr0XĜQ^%=ր>lꠗ??ɨB7jN~E@ /Q_h7.WVbvih/@'ce;yb,/em`x1{r6g$0}<>Ë\mFTokcv3+]rlxG~og9ݙgD2b|]%u/Ap`;D?'{9-/0eo"w!2J@^H.bOޜs:+i#5 jMuM6v-,w熖f8j&J?z=;~.$ d?D]p$ʠuR 2>@K7]Tual ʰځw\>pRal8vfnZfT2:3S@?BQra`3A{myՐs혫!}:4H|ŨFbzJ1cVv0RЇaiY|Iș`Yֳu+4i -vȇsP l>Ñ3aVD`O IDAT  ?;= ڊ>DyfYcGnL=Th +%S̚i).c硫Yvp=3*XNoFgH|*k,mvTYSnK\TOq!Sl)>-0IJ8FncZv$y[(!Ǿe1mz W̙)@oi@ vI%ƛsB9,7Ɯx>2X׷].GZ,ӏʽ/Q]M bmZھy3݇b0ߏ%W v!+lԏٝβR*f^X?ߢN=eԗhyeY/Ae c;:gJr2 Ae22`**ZZ"VZ? XlAP~B 2!B@3AAI !%k]km^/s` m.=Ԣo+. }vA`*M%:ﯹF>ù )_Tl+ަpWbZt]5\? 6>_[/r뫱:Msxf _} FȢaph87en;˓ n$dc6^1L,|w,ׄsXɭ\aŋͩN(_PӖ%\AJnEDDDPr+""""[4܊HVDDDD* %""""Ri(JCɭTJnEDDDPr+""""[4܊HVDDDD* %""""Ri(JCɭTJnEDDDҰUXKamOyٲz|8(oXF^&PTDDDDR5x&wbvYTF>xoBJ[W:fraaЇ 1-g F˻ KNX0Flj8^s WnCb8kF񾟀6Y#_+PskŎQ`;i1~(VoS!FZWGCax7bW;sBfKa?!X~7[X̏^^0הҦTVr[wYÊ>glsce@v#> v`Q1$2+8N 9 Kya?XFCj| o i0b)""""R''Wim37܂nƹ_5 w`??Y1l̤(#_OnGamlcDrow k|o~[ow?ex.X:܅:Oɒb`ҡFmɗצU|rѨ Ft{@QL ]`TǸFX6cl_*L,°Y hKQTvC] ?| C.ަTZ}ls5faNFaks'}{!o?@|۲! ';.pnׄ^ Gbsb>>j|6EDDD2/^lNL?c8"""""ec`""""Ry(JCɭTJnEDDDPr+""""[4܊HVDDDD* %""""Ri(JCɭTJnEDDDPr+""""[4܊HVDDDD* %""""Ri(J&?Z]=ζLYghQvx^z?+yޏm{,?UK&KK֜S߄ADZlٖ) ;kSDDD[R8h =TMVNSۋ0il)5O&[H;y?늓7zW0.-&5lye*?gf>[g?ð}vkSDDD*zIۺ#ZW[k3I{ ϬΙGKҹX{3 ӊQg7?C69]6չZr:q ؚk׆0R`\}[f2[ \O^cowj=yؙҨnyjPkb-+5{ kߺ3+>r0}cŒ7p" All&cb;s^OCx'1Q)` Ah><36f~RJƃP6omdRoQDDe &BbO1Fp:<),/%o8\qabmB-:" W1in=i՘2~8Fopo6]=q 0':b8iǑܖ1#2h$̓[ii31Æ4d <6 5"RJ_8azg-e ARlκcCqY<0bB4㽿L` ]t Mnź9F 9s6YȽ/dq yh؊[Ln0|^V2wm:`[Y>NfwWH oƝJ$["#PZ%b I7k°/8J3 Z]hV>*ǔaqe>\yUp42UzHU$QֵtAd XB# .kbu1"2ݡ @~qC\ٜ^t G7pfZ#hc(3=cr '$5h*[v!:2/^";k=צ%k'2x|Vgt?~<3*Y/$C!`8ߥ+ e= ɽ/"飈(pa%4"+ {"zYK$ד݉ Үf&LI=t<_@9}Iһ1u0zŕbcsMݨ^n ΈKjX u:u^!"""WVnN#,q{$i×i[^='8i_YU@!>Zpsr?yjf""vo|0U ~N^Ô) ?s7wfY8Lw$AڤMɍ|p)o0 7G3T%R}3mw夳}r4ѓY~ʔ8i=>>4_d Zfk5hƨwNI/$h%pr9~ ov<F3| ֣#7>4I\|h?g}B.W{9Ni\Ĭ==dO|%"""T%kn U8a%sצ( :t5xoaEr 6 1Vz$?y7i!5,R4x-.YWmd쇩x0KKzh-Z F@ʶ7m gcwԡǵp 1zv_w\BkUsm|ەr|K"e>@.s}:`W||H#W\sۋN޺l7YCGI7k°<$0 X(5gf>O/Bt҅f`8 KKhGvB;LT*YpY~ VB#BW{fI,!D; 9v</Nԍz;Ꮂ^\5/fz\W͊|}ZWjPKgm_H'CpPKW:WjG8B9#҃&"""e۲2 j|>fa9RʽC|ų14D2Aڼ\d)bҴYܕW(FOgeusRyڒ2"라,PJA\@Qyp"sY듓=ظdWk#,q{$i×iEqQmyq5yoQG."""lsKè֞vcӯIT:~WpҢ ;J,XDDDLe rfu.w|lV^ j݋'#Va⩙i&io[ URYTjU,!_gdgQqeGsMnΏ?74ЊG:զʵ;!G⃿DDDD~`radS=$7 [Y:2Ut095/\#q{ۓ;?ν/""""WFLnm‘[qgqFo୵4qcǚ8IMJT;xKn5ݭr&0o*v[?C69]6չZr:q ؚk׆0R`\}[\ ԠZW*}k>,Q͹`4 %o27DI=FF1 3Ma^bV'ؾ77vf1}]:ޒbhpOp {j70ufWVAb~8kgJ+mA,ص`blrC@&{yk t\s*yB+k||S̆v?i46'- ٓUҹO|S6Hn]Ƙc4bNv-MeßYGĀ4 #&ѭ'ӡCƏǨ ԴfXc i&0'xlAjkD0¹~ZG<~񥝶v,1HiZ&X<~ӗ&!AbSva؈I1`TAm YCYkCzcYL2IHͺg%]Jψmn`ׂ͋#652,fI9e?*Y$;uo4#X*ck #bk (ri^ <&`b|yMJ"ۀ=QJdNy_*g4KqWb,rUڭZnsG2Jb ΋d΂] 6/Ӯ$_sm; uy>l1|՜ڼv^<)fkx alP F)`mYhQu@Y9Yd^B ҈!#ّ_Y/&H,)G7KGHChN2/n: ߓƿ}%͙;|d^L/~; L ?}ADDD*ZPLQW1O3ZVvUj {":MK$ד݉):v,Ķ'rcպ0aݴ 7O~xsު ?vaҦ>!6>QN@mXw3f`#8(䫅oam~Hbf*e ٕ̲5EL6rپ|9JIR WRr,5cf,uv-"o[W`a٩rwa¥̟Y5`LǧA|n]WEDDt:~R٧vŨ4UDDDDWIe """"R)JCɭTJnEDDDPr+""""[4܊HVDDDD* %""""Ri(JCɭTJnEDDDPr+""""[4܊Habe/-#p:P6=oW ODDDD*Tůf;e1ޅTYt$ 5R33!ڮE>Ʒ k/!'yhM;C^_ s?>s<˲ ~[8s2}y0O+v ֦@ /s۝nl뀢ֿo\ujn 1}G0 2`vQ~ۥ IDATƻt'fZ9VC s,|{rl \i]:#bVoS!FZW)Q0o4sczޟ ֡}w ֫?""""rY*~0}dϾ{c,"J|4O⿩fD|ͱ=5F`‰[U|!b0 ;&v'`}fl}*X/ˍ=Po N׼_q?}{!o?@|۲/DDDDB/6^t&ޟ11_c]<܊HVDDDD* %""""Ri(JCɭTJnEDDDPr+""""[4܊HVDDDD* %""""Ri(JCɭTJnEDDDPr+""""[4܊HQuf.g[,Y3X4 m+yH&8K4 d' ?OgKXJO % 7?ll7fE4WIx}[0EO?&X<~ӗ&!ŗYȽ/dq yh>Hks靵77x⭀Cx`8~#Coi`b]CGMf~z#ꅔ-Nkljmg1`0M7ˆIpt#K%1Æ4d <6 5"Bk`,{!v ϡJ7l>z +6QA g$ޮtsM# /9OS`'&-k%8Pp^JCkHXlV$נkH 6I~佤֢p6vfGڗ9.c46=;+Iǝymb11tֆ& b]`ׂ!652,fI9l^'"""?*X$;uo5*"?~W-Ԏ_;Hߖak3ߛ&`[OPal zT]'M N J.-2jHU &,,>]]KL̀" 1-ΐ8X,dNy_LǧA|n]WEDDD~ŋͩY1bUNg]1IږKbT3n1^sG#"""%1_c,Al?iIa!eGjg&VS=DDDDe *_4i?~Rj}!&,\ i>a|4{i_%ȯDDDDRe RO?&;Ґ-S>krs.,Kqi/>AnҤML38} Rx^(slK5,F1a2dmS)^_*:h =TMVNL`㗘YBiU?{/uy8h2,R`r%m:;k]3zoͨJhߊ;8vW̪.@X4@&/Wh?T#{* `bgIh><36f+LȨ1Ƃaf9K̡f߉긎Q7eب_yx=%Eb#`ԚX Jog兩9~$msbջls;w<#ӧQ8@_,y '/x,{~ Mnc}hdO8omΖD5Q\)Uĕ]H5>V)fNg)1?quwѸ#[ad%I?yj^m 'm8r1fXļyrxK"tV˞cOoD7:i=>>F7Rh?v8zI3\5Il Ґ/L`#O܃j׈ՙ`mB-:" W1i1pZG<~z<˲2j*m7H /#bwXk 4e$ޮtsM# 5K'q 5_!E8` ~rXk1[ߤ_E%N(|wqp:lYnїQCЬ 1a)|`</^;!׬h34bȮ=;Evzkz:ѽ!"{tډ2)YhQu@Y˭7'$hJ/H,o'a@5:g_Dfqt\EDDTU,኉m. IRR..KqWqcX5-~J#aO][\i7z;#y':#.-a5ԙ3YBvrx.^4vg2rQ>NtȆOf„w2> o}Iһ1u0zY4`cd7]ZDa1¸ ;;gˊEDDDV ߱|wupf7Oׅ42/+RYĮƜaϩk2 ~xw`/irQ21i,Ig+i'K3K#Dw `[dhZn̸ٳpHx-g,䫅K9=|a9͒ lXt| mu6XDDDJT+8֞vcӯz#ͨ։gcfwNZ{>aGnUJlm?iIa!eGjg&vrrQY¥ kWg\xn9\pnX +*( ފf]z^|oJGڦO䃼F0@Z2DDD䧧URYTjU,!_gdgY_ reGsMn΅e !4=R'(0.(KJBٕzKgsخڷݱE<3z) ^_*:h =TMVNL`u璻-&5l* ~;{{Y&|~$ """?*zIۺ#ZW[k3*8$2˦׃]WBepUY5:X0,63yYm0O j]|+5{ 6c1FWg2}1qx)=҇eڰLƁ,ٺd.>jȬ5y3Y&zG^M"k 㻌y_*Xsk.$+aTHWIx}[05B 1(+퀓Cx`8~#CoiژaC { {Zd: ]p?G&%$a) %܊us 5鍨Rb=GC̙xztvo( 2>wXk pF2J'!0G1h$ aD7:kԨ"VBz$/y/页fL/f Ih9in=`we[w6vfGڗ9r֢OѺDDDס %9Ɏ_'o[q UD>` r"̆hެ&"?uA2|t9,D˨!]hV>p'p{wo?21BOÿ/br~lo!m~1s8((|wqWsAΏ[)aa&JODDD~-dr{8É@ahCff>Xz4Cuo@w(҈!#ّ_yI,_ dϘqkfkeMM/^;!`uF㴖e Pp`=[^=|:ęB_*Yp6`Wqk$))dڣh~Ͼ=(~ 59BcsMݨ0„w2> |i("5`X 5iǞc.OuF ]ZVj8ө3'd D @lH/(u|"""RikN$f^ %ǧt}mk Pd ,q5yoϟ;DDDJ4C .Q=M_e+R,"""p:~W*IwPw'Ub+"""SY¥ kWg\xn9\pnX +*(J+OM<5"""r%ȯDDDDRmh3xap-S,`,w>SKOgx/1 [^8gaՒ tr1 XT3Bi55V--]XsOyK9rer]IO :h =TMVNSۋ0gG;[Bt7Q;iHk" gX԰qODDD~rU0u GnĝfTLqH4oŝ d~+sKʠQ;/y ' u o SsH ]TĪwTvj鵳SDDe &BbO1Fp:]C]E-"5$VT|ٵQ_Ly?gU3Q*k CkHXlV$נkH KCnmh*f.ENvBz$/y/页fq_g78HeˍDN#~4Ϋ0mC$!5u򬄻SXa Lݤ"LgiiYN`>cdKOʧ %9Ɏ_'o[q UD>` 0pk0;\6<ΆRR۾[JH-R&}*m$/e011|^pss{~s{VعJBu:M=6W{62uԛ\wM7e4f, we@4Ew+.AST$g/CfIrwS1Rd[F̙ ^ͻf 8l  Y@ w+>2ܖXl}!ցqXy<!h?`+立<-Yެ8R<ST{ ^ʁSX;^ ѿ#_ŭ2 Ƿg;Іy'q.r-U=8Yw o%IthkRv%+4ҳ?>WHMR}zQ ;4kjmYsmXGY)XaCT[]gC~\ݎj>|-c v&&XMeW۶'ȡ? IDATD MD]XݬHClFϢtm)H(c [s~~޾ř9q;0o #z.0xaaćUP;U2ӔArAu/'oEHYJz%l5* fI}+q,ݭzm]s$7nN?::5mUo

J썫FrϬ_m"ײٸ4w2vUӒw/D,/dky^ k%Ҧi"oG†_KZODDDv*,[W݊>rL}9~Py;:بܼ+zti'.[yt{V03HNpe6 E3>~J%t.X`#SGutI\ƻMc삧Tp[nbbIAKZ㗕v{_FeߑevK%l_8*Ė2Xmc~`=e) G=MR}zQ ;4k_R&Tvm~BNu9u܋bfUF2'`34xu]9{ }*Z!d?F_0>Î'΃(DDDC#e(m ;_};׮`Z?h#oc\njkNWڸ 7^M?1״O\^ 1ʾh|'O\uz0mgm?pQ̸q|- 5]l^Mgx\˿ºyK?LO3@<0n!RODDD1t8p9:'dFv<=,8ŹZ?ߞT`jwsw$ްز-5%ԓED,bc1#JwD)*6eȄn[ n;VN)ڞpa|Ɗ}xu&`W%wR,6g GP) ""RNHKg{_ R64-ADD/psV9~"0)? """lÆ \qt׹GŝQ)# """'`{gy=9g4pEJKVDD98z{(W_}s=ǸMSpEJ"1M`0H$aΜ9ԫW0p:xnNV߈a.f"%p+""rpE1w\f̘qU7`w8000l60e)܊%jbGG⍍tb쌙"?0o6-Z-eGl5XZcP<~@oYDDo0l<:r,ޘxlv``?oPڷ=Og]=1usEBXؚL a\=xZBC0Z^ؒ\ۉ넿Xe:د{zF.֒5+`mEDDʘafc/Xl9`4SP,;܇a)gcoZF~VM%4~,ԣN޳MKhArnl 6 g Fڴӱ|`l8NH*ᴳ o>.(w; lGF>F&0'_jue?cEڵ#/oEDDD0e?v2L l`e ~`7>Ÿ g`!W4c1b}\?+ӊ1}]GdBBkۄV aksh=yX> &ڴ0'F܊>F20羊 ppj-~ #b1Ꞔ͗ C=0N#N+P*lqD㸦,|"""""G)p{,&8xدWMH8y "Y ), }p\>3 }4[DDDDY3gZOMvhE/lH8x^D[ """"5nEDDD$j(܊HPp+""""QCVDDDD­D [ """"5nEDDD$j(܊HPp+""""QCVDDDD­D [7z2dNLkF͚2mҽ#;vguI2Q3#&e˓hPŔ15uIt~wɶsy1dmG#,a9uL,_cMYnAk9̤;f_I;3TE[ǸYW-]C]Js$qR=忥aaćU4d{|e#ZDD6DElF.V Mm ;6 ;c^|Xx\CC/+=lYl 6 +Ob " 5 {i?a,~+Bg2nKhn↱8,~/Yh?n<9lߜHӪ7XPRhľ/&2dntaCbVF) q6V.ix&ٷpʥ[pթ!la3/F?VtnlE +/3sefAǨ UDD䟢NKg(ܗjǾ_li.CÀ }YTʜsNuCq|=}~+g&nOaE: ^w a #3nK s7N{,h<>4+oɷpyƕmPz ŠUp[un cog>aKag.ԡuλ߱qmkA<գ=1^wKS$ 9F"""H `a6}:3/vawNfY -˗j3jW SD6V1TXdo\.O5*c\t&3bܾ9,zF(H%˟9n#d [vvm7}T*hˣ v"e ՛SUH=+DHNqRu1nrLJ~lEDDLV0eKqX#` -ሟ<3K  09elJ6*7ʀhZ DV/l[vUqh:G,ڋ,}3Dn,,3/d,lTjޕ,f;KQKzf̾x07KdtN|Ja+.^C=3zL&]ӍG'ós6+1KzlEDDL e \.6=?ҭE&6O<`.0B䄜ĻmcbLQ?wCzWGqLmy ogެ8Rw rzď%J*|%o|ƅΝ񬛱Ar$|IDDD?%ڸhZ53⎧WҎL8`Q1nhKnj"f r,ixya.e?Hd{KGn6\]K 8[Wmŋ|\^1/v*ZѶl^kpn-' hx8AԐd0xw 0brY\uJe<#~ b]3@> ")F ^Oߊٳ {ۚ':xZv~ j_طy1w0ۓrk'vLgZ-4qK0gN$DDDDNKg(ܗjǾ@!@jPdSF(ҌOZ=0ܖ$`p-d }7>֖ϿOaE: ^w ‚-h<>4+oɷpy9F*ۨF}i+sK#3nK s7N{QzFDDD*`י9./| Xh<Y+nMP7? `Eҫ7 p|kȊf+?OF6+zK4G #,:ػLW- _3VmVM}t5E٘LfEDDDNK+β߸xUgxpN 2sؓOjg{!m8 |Ĕ"7hHڶJy1DZ$ۂHfP%:s 髽"\N!"""W e͛Rfvj3kvhyb$xlkbs 0ހ6DLr"&;Wg{t9`8q9 Z>ެ8ReEDDUi eNfIx ×x6XPIU aw-;#'6OkKU6毌[IxNv+ٗ`"kpQCG_H^ ;85Mr_ym\~S?я+o9i>Nwk㸠Kc 0X[YW'MKs#o^B c^䓾J%DسSF<>GCV:k]¯Yph"w#c& t:[BfH3~Xy#,^ NKpѠkfROq*Tc]дWK(+WqꈈTp0܆H[jS[^b_v4[B3p7if㬗xa΂vlkFNU_رVo+V""""QNKg(ܗjǾ@B-6#3nK s7NBe-1mreH{o9񗈈Tpp #̦^gϙnY$Z5Z|Wm:cɌX}s6^♳3L[&K}Csj&W} li7.^Uv+޻?~GohFžoP%:s 髽ɢ+Fd\X\eEDDD*d-7<~ߑECnQ7k;G>άoqx rMlx\fI-"""=*ۿ`*߭.3ԡ[^t^ɳڜwF2Nl$rUWH{^+Yy7ύlkxq(p m ݨa/Ns2'Lbo1[^~ :#ĩEl1T$~+?ȇvW?nn"q)d=;nRr`*o ZK 'R\@t.gnFˈ\)طc]ݸbR{pRYb;b݉IM%r>݀e~8 GoP[ɘ㛜Ⱥ;p5(0q.zkGՀ0:+gڹWKb XUQY uIMhRՌ` G]2Tr;ny`xpcwPcq쑣jd^x& 7yggBkہ}40Ov0MpDtANO9~ Ԩ?vvS:S9G:laAm*%0SXh|}:' xw9˨rpT&ێMx޴ qcJ1XpZAVr]9oRY:r{Iօnz/g4MH5;^!m1jC0XMN=rlmrrܸAaZGe$p[.,2m"3.&鿯aGfc66:Xv¹{ɩ҂˜+x|{[!Et夈CUtMJZg;R13) UO ݓ9;x*nq3ܓXUq=kvb0ZUcd.Đa/0=]YXdిf m[=b MV ֽ{p"rrfKwp2?!e8[SSຄ]/0XM5[ =n[F\s;/>>Ki+U,zKA܌$cŵ$xk?n `$i81 ω4i5lI35`KxZh&]Dpn*=S9:1į-' G&;u,f~P!>[~BL8/{Tpn78nN:~yZzh@gGկALa{ LW) й_  rPi@ G-?˗ѻm=jf&ɶ1GW3HFHIg_:9_Q0Z?a7+m\ ܹy`$T0-y{,EdxRgr-2Bgp?S].x#iHg7p"Kr\4yem^27΍^.m‡y2Gϑq /o}.ߦjetj|Z{?_J ʬ'O2tV/M#+3BW+Pj8 ]zKvFT_b &;9^-H.\nE^"9gUSX>wD" 0y;OZ7|2z73qG W{v1=؛?͉nGn8K rX^V́׃Y~ g9;w2[ē'Hcx\X %]TꕈdB~ǩ]陟3띅n!Ƈ81Ϻ[96R62Km'l&鳥C`>7"/v `9X ft:0㗅ݴY{%.,ZRa$NkŎ^- AZu ڳ7hD}TّW&?,E=*8Oܼg8"L:7[, T-'vvv%xT7~t(/"n8 4 'S b/]\6R*~&o4'9?'H_ }qAZUæF=]'ᵘ0f یO8w p3N ,2 ycq{lk;2CdH'P8;jIe]cVdm 5G;ˠu0a/ n~Le'zc[oT_J?v28l;Tf}l0#G֗W^ ;%b7xX!{o1I?3 =_?6?jG 1_;#o ןmKʗ\*0"fg FvX7Tfgs8ͻ9.}wUj)c&8r#IXI%i)rƱoߡ!=;e]J!FX,~O+#3ؚac}*A+h`Zm?mp]٠vl1mLOs`vg0[~"T1,;-R%l㥟X#G,n1\xcZL"塽|1}L^]vb ~Y9W >ٳɉ YfAnX|7ժ09E"n37  Ќ;%Y/?bo;$G*ԟЙf04n0 -$י uV#8LǧG *p5{,m/kBdWyT=2 b,}$ L w:[>uW?_0K+Ⱦ`#;fi+FҢ KAvHmQqpJ.P.Jׂ[@PCK8YFbM>g $9K^svMfξ>ɟA`B5&a\>y ɏnkve;~ekKꜦ֘÷bEe#&(>eTSPظ8Leky%'8(kY1q$sϲ)dU;F]Ժjj93vb:7d.dڹm?ex~wjL O>л3>ktn(p @1[!P bC-n(p @1[!P b4:ܬ|[P3IENDB`mu-0.9.12/www/mu4e-2.png000066400000000000000000004034171250406103100145700ustar00rootroot00000000000000PNG  IHDR  ]Y"sBIT|dtEXtSoftwaregnome-screenshot> IDATxwuה{rBhR)TD)"E)/ATD"6@*B!HrmcK+{w{wIx?g>3ٹyϧBڵkׯ\2< B!c8FYfC1g^tRnfU!B}֭[Yr%˗/kO>${,+;D"Acc#۶m{Ooҷl}饗?xqlCօB!`F^TUqnVUzjkkZjտ~W^y… z%B!"HIO&_`uB!B }o5MdҮc:vcf^7 B!]mK B9&=)oc:YIdB@!ĻšGKoRQ; mmS}Z^_Z@2HM|Iٞ{h5iFlޠ\>mk|N6_6}h+LNxRc5Y#ĞJQliڈzCNI[Q/^̒%KضM `ɒ%,^`u5 Bb?W{!e8SǦXkh.53n3x:r_8:L?/ᅥJ*Xz\ugve^]/583?w%~]oD Ι;3s@~^|!N?9 }8Ψem 6o_A{gcÞp=hEyB>G(U#cGoq_#$k4tJL2|{B;d|R5)m4qUU;w.;v젦&H}̂$L2Wq&vgƳ*xk7e8|ˈѸM?H6ٽ㥕Or׏>~pMwTq<R.+8{x/ް[~/rP-85}ɮ/]o]\{ͷ1Ccs9ƃM>r5O?ѹ7߸qiTk~v ?XƴϺ]VTCPdyR{V[I}mR%{C)43np G_}}}655;'i%G}+z>Esջ۲W2o7^~<|U'* Fd̀eϻQe{?e9j-㣗VBUT&fˮU6C[>~1 pl;*.]V`W|9&U> Ҩ<~-\G|qǍ_9 $#H )=LxReš`932^e'm]I$Z 5HuVZ6}KQI+!ĞHⓖoD^aE Z lv`瓎5;䍍b`%gY2m7+.|?\y)Eu 9o٣b#5܊L}߳dbg\%EMfOaك~1>|3Oe=S#/==s)T:tcڏioHe:Ryj2ݳʴ?y)M 4&X-3&X-b…ضQG6?:쬂Be:+X|F|wn9s].j4c'ZV|_ztHg9W9M\}{3q/"KEU i;0v[&?p1DZ'GuwMF9s)E~l,阱wƴ]He:Ry w|ڦԖP.ĻA~(駟)\~O{߭)m]Y``Y?4$a$xi[92vr9&R{NL2S.M3RI7GJ;c&i׿7˲e,!DΦqwdn !]4KGSUp,L&p8ϊq5,vH&Hvi* ec$Ǿno4&j_Ʈ:15>(^\ƮKf[CHe?a|\t}2xB]:%%%5\T`4Tr'f{ !{('b8RNݬz ϗA4M# ݝ4'#͛7K,!{/;H9wBI&6PLs2K!B U^^ζmp\6O EI  AiCN!Beeeb1Z[[|4ǚZ |!B1$3f  iӦw}ĉWB!Bdb1 GuTgeJ;!B!&N" HLu6 B!BIj0B!B'5 B!BIB!"R"B!4R"B!4AEeTg!kO˻]́fdb}eC2B= &X#MU\TӀyMO}ʪ ]w*ʪA|ner<3'~5j_LuU >t~ضֽqcikmTOCc-8s7ic4=<`ަq~F**aCe۟䒏ʢ93{?i~uc>]}>Ie,L2$Sj z+cf8&}oThkmmN5?u9.olٶ }yLMq̱nڼ;}{-lp̢!?O]ϣ:>ӿ3y`s8c<ԟZC=;`O,+h&w^sRN۔B1UrIxqhzJ@[?d~%i\6C4wuŇ]7|M.rfL~߾ڑGn%sꙹp_|?~kd|n?3E[G/n>y䱲ʪq~>_q4_<ڝ1t}6DGy'zg_{,27q2-;coYeU5P]?rn?y9jjk&lpOcb,ʪjksѬwcN.#U/O;7;S]WZg{-g5&zz| zP]>TnS9CGnkx·]CL2$L7e\]wx1m/_Z_ʗWry⍏^J!ϰfOPlؼL+|{߇xf;.MǤf4GVnËW|붶4gX-%J+^x1Ѷ?w_ɧxiÛzW>~WN9>;&9/{׀*s. w65lAǵc]=}6'~G\g}lG:Gnj=??MT6]F:-ʹ4m{.G_?ogO(ɇZ!1j([5M~5/v]v9qkvK7k`ᑾ;rG!G7547qKyy#X󯋲{߹eY۫|񀅜j>\q]i5S4gqAMeX Fy;X<}:@K2Z?h|8,>7oԵ3VN>fڶlgȎkG' Xo277T^?=L>r~|-ֈ198ҹz[#UD`yT64&,7; bMC(:M٧=lkj­fŬ*ZW]q__]%ѹ_|M߿C?{.Α~C}7q5hsy쯱qO˧Oo̮;|]&unJ/WoW=`v|oO6>wOySW&j/?S_?v o7vQ6}1v^Vg9gCFnOe3Qy7s9s^~7ogO-M}i|ټy8;>+\;B/{/?9n㖯k`hn}B1y9ccǸ| /i=_Mmݸo0SgWC!vO>j@S[7 Epޭr.b'-mWC!v>JJeػ W~~!ľ o5 B!B1Q%B!c%B!b[6o|!B!tC;b!B!mk|0}S!B!ĻB!BL @B!F!B!ĤD!B1i$B!BL @B!F!B!Ĥ'"Ѥd{6:1M ppquO0XHEY%N:e"q !xǡ(㴫D"A[GPVR6֯{Ï<ޞfozx#.DTUU{{H$B_$c;yP(Yn ;!n9̩>!s 9d^3,B3!HUpA,0M;X̩W(4b_EӰi=8l޺@a!/^i(88888؎m4[l`|FIƀy]TW LQQ n)4Mcqjjj`lۦ7Bub8>K."ee:; SXXmFWO7UhgYy@/jMfqY9Upi tcۨ,չ򬹔:B!p!GOım N8n-p@UUN9L.?/ŖMS[Seض6'},1tlx{=7iylhͥS f)B[{ >JD)**r |?e׋)(,B$0Ec1JKKD"HB!JJJP5x4İL*+r^?8=ݘ L4 \.8D0hlܖ^AtKJ'.W70/XƉլz)UzC-h98}nz>CNYB;v @F{4 DQL>w~Om4cpNr;P,>#u1f>qyϔSYY=m墳Y( :::i:hWH=vmh,IiI)k׾<랻iض9N=mțh4?^|%Og yK4vxm5CL [B= dP4`Q^wMyE`Y6ae~mmnea$ ۨU$9spMTv :[xnݣ|!G8`aҒ*Ly5oŏ_ ߾ʭ_>|.b߰[ҿc5ey2iː J7ɞ)u/WT'D!:7_ cKiYL$w3Faa1sűtS1'*UKbO/h4.D_.LDUb`(i$6EEEl۾@ L&qzcSV^ٯh3ח̼ɨ0E5F\$m͗7f?k(-+'P৴ҲR\.7U5j/sױjf{b! ie~WdtB ǀ Xcw橫h4@GGЈ7+RQ=x+۶QTh,jؖukYn-akׯe[7{.uӧah۲~dl hjYP[x+W? IDAT\vv+ӧMcܹ466R[S[om`gu/Wc&H4%G[x<̚5ffϙ( Eaz}=eǎFMMhmm˯k^27hc22fiَc;aa*4l]1 4]GUu|uٶetwwH0R))-4M==q^/hϗ*GT"0UA\O*IW ? D@jv}2SV3z[z8QTT.bx\nJK8%<[Q7l !7LP, 0O%TRlv :M'N<1c@LT?顔aZF.mUQFc蚆j,\ p$pmH$0T y@~?W| V` Υv{3gDmm۷o犯]1Hc(d2AK[sBÑ>"p@Quww4d2:דi}UP5z{颸xXH$].4Mjjjd2a^h+P( ttt!;#lRQzmhjoUтxXt=;p0BQQ --;0$.ԍa/_zB GuU5=;k).*&c6&Xjɤ:eUtPXT0m;r]EQxܔc&iPR\J8|b EYY)TTTiBzzz$CUU/yёM7˫J;BM$HL { UM.dq29rں K!7bg}5]QS:IXT3Yvȅ(C~5O=˵~mۘE4>qH8d|7Pm KooL1yNx<"hH$ەiHQ)넮.+iok&XT& PQYC/DiOǦX<'i$py`^/@N+*Bۃ秳t=6|d4DZ"M5 8p~%>Bj|} j3KMezvrB-,],ЅBτ427ꙷ=g)D,އm]`&8Xf$jq̘6kfOec[G.9@aaNƢQ^zE @0VgS7m--ٛMN"dm+O27xEE8C 5U+d[Zp˴(-.C]x<TENf An7x?Hg{(JAA6B1\A B,ˤ #ў^"(@obF d RO&lb w|O68fP_<#:?3NYN?pYkzkoaK+ ʏզW !7M܋PT-c''\Q ALZ{H$y<utsקn}(’3s~lƶlz{CBtvuҰm+wP]]EUuֶ6o΂Yp=[8V0~3f̠15Ucc#Y3qp 2î$M<K=U LӠ !`$M,!m_ৼj &|~!8Xm( d<eYضv2/zt͘A[[a֭uiyg[meÆ dn_~eL ;\ڞ8lpfZl^njy0>fN=a..~j>-ÓTym Fc;iBo&XzgCc);|M: q7t?3/lMfJ#UUa8k^}0bE.jkj &lJUUUzԸ5ES5^o| ;\p'w.:|#(v%X9ƆVVoHMhmc |LjB[&htt*jErLK{;J>p锔6ؙwL8bښǣ;;^2evǕ/۶,P(D<4x4B7eV~GL6m> 4lk/_8䧷cgd{vדL${ -/sHi:w2,׿eYNvzG߻\n\.7:/ #NoO;ߌN|>BZ[[ӵUUUwt`$^_OsSa*tS;mZ^2Ԑ"q((1n!z!N6x2UgrM,ʹh^KEQC?;-gYU>D~{~nR-b1A/"Y{zj7|dp7隙۷9sθe6m0xڛWuϛO4!Lۋayqq_~Oi444a+TT ;slbYw1{ya|xb  -,"D(((O7la!HƱm'd Jgg'~_IG{>,]ᥕ+Symjjjlo'aYrƫRZVFG[[8vj=EUilxZ <&BY*bz^ 71"M.%]|t~S7}:'>yBQ&X7eQSc +{md%sw 0 JRS&Snڵ8_wROmB~ٳfc&GqnM2|vƢ$ z{CX !1o<6n9sٲy3[8#gcYhۍ8{Ǹߓ4 M-[YQIo(-q!r2l$;vί9c,=mgjq/^4tvu^WX4/}SH &axM1-+^>4TPo/>o0e|p]7v=ͦ*OUs5̫yMް7P_S,7DF~rO;aks;_KII)K.E4 DBlB`ᐽ9NpHT%dV<a)̜9#~LsFj^I5mW_}@A3gMmۨZ?GcX5,c^4g|/so̙&df!72 :ּ,Y:w6MEyfX9iw?O<7 glp2֛H$m߼UUT UK߲lLgu۲Ͷ4MvlzN{C0EQza4M<H]rz)((wd25쬢(!uN8e@3%05[d{" NjMo*VUu@_PT\LkS3EAJKmD"A4:ݴ&_P0MPR 3xemd2IAn:jڼIxdNa$$HI$Dc1|~< h^{a̞5Gy/\pLB! 8tvvcr2tKtԂ:gK_Q***9}&mՍʊx8=GRQ^EWF*..b޼y=qFQ.+W>d :kN4Kr(--% bw<躎S\\L 4MLLPKd2 |I0T6MTE+LбL9;I&C݌zt]GuEaKiؼP(iYFiy9tDn29Wx(--d~F]]qS .ep&۵shrURR.X9+^u*++uC( .]XnsaG(y_ ۝ z{zxGK).*ʦ%FUU**+)-+4l͌ij&].s,Jv2 7D`UU=Սx韏)ߓQΥ(hkiz&~?avyO>5o zy{T,bϧw}'Dթ2_=tBK1 ufP!hLL=iB&B}B!BL @B!F!B!ĤD!B1i$B!BL @B!FXͩ·B!]@eMu>B!o\~4B!BLoB|!B!Qi,7 /eI Kza⌸J"=a=.aNہʖrWstI5ٙd|NqOz(G)ڒv:feJx"yqg ^ܭ}qᖩyy<^~]B!& 0L\,{*[F}0)J n4&9c#c*L$֮cPw܏STCb0SzRJ.~=̧QDcXvhhP)lp`N=a҉MW7hXs{[utKm'%^#qX`c'];?'35Cl/91{ ݅M|zDC2`6+X^5.Z86&D ")~2̮5 ]C /SՅ394%!BxbcyuM^M Q-;Tu3j@y;87}~M:nm,t[pM#.}9|ė1ߙn Wp{t/{zZܝ=%\'Ft߇(x^)A#qH]QEۅe&"|%td5Nw Mr{+Ax%/ Hr3BQDV 4:O_G~-e]t@ZDj7ʰ%!BdOU"f~3}k֠88`(5V!-(f 8dErZP(}l-jA:Cqc:f rT0Lh~ 3LdJ$4RNwWi0]v#<'6(l`70& ifƅfF1t@3aq ܋ٵ=tWJ:7ͅ7RT^⹜f15 6jt̍c&Pw25d~6 ͋cI:bh=uI!Y- )7[`-2GA5Q_J\/%D!*9_uyH&Rv}_2mᨀy'F}•> H`Tҕ!űݛ9eʡVSX. 6`c$ u(wu!>zG89lOb8t<> (~#F]]s d*Pb4:>H-B!v]b/@Z4n2GvTvYe`@RCqeִu! ū nѾ<3}帜:SI MVY'@pC}Q\O =,GBk1YAEB߽]"uNI-45ĥKi*->\JLc𑶄B1@ǫ&eQl\GQBmJjH࡮KB!뭙]Gh=OK9P $[uЫR@[$}K_S<1:?R˺豋 ^q#.N=OM7E߅dFԉqU IDATTkAEQ qN JAnQP-s{虙K'|sHIYB᫥x1fsPky *I1kF؞ڛ9$uҭ\Lۀ$޹(eQuiBQ> MM#5lr-Ĩ$|Wm'-G!(7&E|L0 Umء=$zqHL oP].#BLiqH,&+R|EbQ7=3{#-@nTƘCq BI7&-o%4b`.eTC!xwdf !B!DYwxUUִ{@BRDFQtFё8Oeaym,`#" BO#!ޛ# $7}G9{^{ﳻeCTkpmCklCDZI}4iI-F %&p+ӯ;}A ¸c;QXXi.^22222P`/y9r}`X*+'IEi+?[-(Y˭ώ};[Z^tWAD,څKl&-h1 =I>Pp$N5" pVk`Ir-&nO^iU3FP3Z4+ 1lЦsG:\[h ̕ίo/ 8V* ƌDB:D=UdVĥ%536`+DY"@B[GdY@_Sf^_>b5ۇLbQHi|}FFH7(q8Owy+DsC?2`>ʋGa)ʧ\[ŅTѭ/ XF%ᑟoX)esauG"1PE*K|}3!@jΡ^,JgA g o/t^6@IQ'Ѡ"Ak PpxG(՞5ggTTF"UPSN: R} ;=sJWl+gIÏrjDM<̮P4[/n hv Wlţ6f^~7 ]/J!f%a)v]&tFC:VCŐn"nɣY=(`$/;M̀w4ht`RYCtRp u֗ 9ѓ zСN'%3~[=,P 'm5E.UWk!A 7xdz| V aY r 4XdF0tTMl$3ldXtA@J\P-@`dhXcB(z F |fG0k\B/zJuvJq%"z.pj^9?)sMfӍ}݅+aoQ#x:WxT(Q+vĈ4gb]ɣeդ$\~ķ[5)6s`m-il%0:Z@_R) H$>0PtM>yqQJY'SωjCd1lŕ#ivw||KăpMDf1snN˭ϊg@4!q(h,upf*xw@)v`*n%-/ լ Rߒjʽt/lfnYP"s1ŦTl4З)*xj@Qz$'+Td"[1)Y8Yϑ;{kQw+C(Cw|V@C5I#+af|t;[H`b kO@oVcĦLiϲ}(F-x}4inpS 9ej9Wch}>1#Q'sU?sDccd4+季|8TՀZɾbn| 0米ȸhWm0tz/}ѱ8?$[7\3vRĢA8YyNbk]?˭aڵ_䏭L"_;W={e܀>6 :7;B}f>~ YH!&u5yNt!ՍQs!9Iʋe "5P/~KS] |]w&'Ey9#yGY={r/^M첻o:tA]+4L;W%퓑 +}|@r43?i!^9H6"afiPLQ<݃V\\Ă(k'i/<^$/G|Rq+!~`"xz;xJW~/oz8f\I%5s2:&?q4eY #+PZOvc6z7UYms*F}{<`qM8@+ԕf?N׹bၒ_|0HܔV,pi"pbS,OQ"Jsgg15 \9FBgm4tEJb,:)(Φi+EI @+@{=˭J|—,uCtZDs2❓ٴ%^Kܨh`e\ROAvp5qA `뢃;^G5S#\EAu6 5u J-BtK]U&k^a{APL~u޹ RczώȤMG@z*ksA 魐N+e6WGX~+64\1q9WGGʒxwW2 ˦(Qn^dcY8w)Bh,7/RTF]ō2Cud_n)/\5ïz3 }U5g8O9'7a5ϳ|󑞏>ɺy2222@z5h*3V{` gMmE)+V0XΝ!|nsF<#"+N4o+ x36y9r}`X_*_y2222@zW ZZ^tWAD=,څKl&-h1 =I>PpH5" pVk`Ir-&nO^iU3FP3Z4+ 1lЦ-t%:-Az+ _*^PδSTe-tƒ1#K;y;0|YeXQ8ֳD@!5f=ɲts燜23չ?H&Gs>:D=UdVĥt㝦o$Z5G 5% d~6绢~n !;RgQ^APlN_bJ7=мϳQ̸[D[dbb_>b5ۇLbQH˦ZJ.7P[Ĝۈ@[x_Lw僃p w\h9ț?CCe/So&iݝ})OFFFH: jÆg#n!#G&҆6px5TI i#.w+n@AVyBs#*3CAvӫpJ7.tZCymVODi^'g߲4bo\&Sr964=RiueF=o݅‘\>ݵ,"KwdL5Z:sbUv|Z ڌXAL3D2?]# ϸojGTK"1Xk'5*چluj_ @Hyj*,:Z`7cTw4"UA>El( ȿ$hTMkV`߅֝WW/^t@!Qs0l7zh:wt>^,JgA g o/t^6@IQ'Ѡ"Ak PpxG(՞5*#*M]'HPX>9] `v DHDGXz]I)? n`_^9 L"~PT{:={G,ঠTPO %XU{Cb19v'fUCF];`+說+Jkh`/ K %|Y6߇-f>nKVH_OSΎw5Vv# f@뇻pO f.+p S&?"}Zd gx̧N&%l>㺼dddd~J@0ɞ,whQSTB^ yZfY`0}#GѠ Ohe%!GN;AftZ CGUqV\L:?#IFEZ"NxXptF1&ĊpNakgvt %C%j`7. BkWJuvJq< &$DaZHݱײj u.g?ڭ!h2/!  H$>0PtEJ _\]㙞CP)fA/uь[>9i>(^ˑ|DΔQ72J)*`ea(& G8v KhtYk}.*\8,Jgekk&1u<°qvg˴Ju\d45k'2cԼҝ}RuyQ^{Ol_TRG9t0^cG[ ?ѓG9TA>^ŠGj@EǗݩnrHff'l],,{G535܊9 +ܺzZ "(0?cyD>V}'_)^qOʔοI/nWi]&į=v@t mfpý-yd CgY7bӸk V4Df5j8&|g*3>fkvGEAsX>¯CS8SB^feGiJ6~(hO$2.vKȀEj"xZm_ki$9 s?Vޯmo=cU5nM1 '}ge?C2(HCw&O%\u Zع-(&7Һ>!Z6~N 8[n כs Wg: -l9̆әzraD΄/rS.]r IG`Q w/HeR N͹߲ u+@mHO y_QSshy2;^EJb,)7.=I $3{k bq=&+ˆ`\hZ^ۻZx4~ЛYpvAzAr\;F*K=_q(1N NJrHv07jw;г\1KX:!:-colZD HkwϾqrNn:x Ckg#G\Bك_eJLbjn F_Oc¤V؆J f<1RǞmvI3#Ic$tQWWsidt>aK:-&it@T¾9kG]"DϲYx38 +m]e>x%[c)_L8!ITF]XO |G=v>zag{x(͜ϳ ¹KEcya7Z 8 GwcU?[2(Un:??YI_ͮ o)SJ yb)Gjx#DC\ht'IBO)yʾ&ԓUقG,4p 7_Մ_d& RS^Vy8“S_\wr{76C@`'on}~L΍319.č}whd{'G)Y8Xs4k7 G+ )+YBىլ]ҽ?, %6<;6Q p҉zvvdZK.x36u)<[طw {5<T( DZ͛ y7W Z5YgU\"a~QhRN) I4mϰ&Qw1WKش;iȡ9a:EXorB1U:XoUV%eCJ<#a}KKI %67>!BCҬ,9||\'|^xz;Z(e_/[(R9zBQ7*1C(q^B9%HGZJ>\ T'L"͜N&]5~wMn0l~K! \8-w>~+7b%<Z6~NV!W%mԃI4JWb } EWJDw4j/1ayIG歼c;uv8zJחtƒ1#d",T=r")׶0;QO;NmS 6a,N?oOR}2A .ap8db_>b5ۇLbQH᪣궑i+{W3POeigMIjjfXA/j }u=9ub{M{7#9 VѨ0wt2ިT.L|ur{6ɬK#(3jsA3 ί)mDPPh 4USb4H*#T )M?C${R-i!k  oԑ][<{5̌HE& ܁Bk!n9zeǭJ|+E*K|}s/AVs(rm9FRVFcDplCs&>ydQ`*ұdK.)+ J9S{n6dl>H)Fym&;Fym.FtUI,Ԟ.i_t˻O=؂Oa|>>;#kO/I5D{$Y;Uz_S&HRuAAqwAIu{ 0U]RgR6h1q0O%b#e;lj3(lHH{V=w gTϛ%CJ f ش#2,{&/vѠrRV,2IHϢ^rL H4dql<4u:Qs ٙߗpW4f0lt޸Jٛ88?BrD k>W g;6#@Px FiaJ4MԀQnz6Z:s:bU@Mɠ{mYyk^i-G j@߀6IόX 5w۳ؒWت6|0U6@CP8O[.ڀw|MIeR%SM=n}PpSf^nBoƆUю,<⼵HUy& '?EiWHRHuO'}@}5ZG]>G>B)thOκX;'ed*aٿt—z.:Z`7wJ.LFDT|_'w?.d; 7qT͛Y}2(h((it'h=M䨯D[W@4P %Ob^;oasa&MTcBL$BGqJ}"v4%EqW/c&e.H`ofsTvRn:Ei3.R~f1mn hpW@iΊbf3ƣ'u3k俈DNQO:K $M"ʞRpmԵͼnۨk.^o[43SoX.92mZK.i_GoܰPT3mh&4r}1*k9`X[1ۍidJסvbHb~7f7ѬjzCƗvZtz-=t+enM̓'+JDZIK,}͇{7tھZ˻} &Dutcjh(lu>:RC]I=_.ygMڿgRif/*]\TI;`1|RbWl!'ƌE5Hx;#1Ѓ6?{2W<^4p8 f0Ç(D+&57r l.QY.v yV-gZ`4pf-Kũ!)%I[#e?&ZzKV S:Vq; 9r5HGbD@0(q p!T+$bn%-L͢v}.JU U$y2ŕ/jq Fv1䜞b뙒0뇞;8nO2 v:ELKJ%uP;H93Jo*HFE]Yhj .vұ0!lI,t|P"k9?EX ;;DY#L|+ Zok8\ƕs*`+.jpsxtR| PVKb Ad|I"atwm젡\?mJZK?Ry?~ϗ3(usX(=DMfPbQ)RVMT T:|wT=|н_ⰼKR7:V@j ܎r!"^o<lc8g)_D'Z/ю9Sq"TqwH4[1v)_Uõd# EŴK~k4ij2DS6J)7S5t2UkضǤN ȩLM'ͧh?Ipfnek'0<Ļ舋OJ rs8QEɳPXYo ӇaJT b>SRPK虹D=_E&%2cT&ĸ-5q̉q'VZQge[Es#jNtÜxo2SYSh"3U?Ҍ`ڄ qfHt&lL:a>엘QPyju1+q c}Lx7 0ښEIײ+*C<|"~=l. W]A qkQ$smD5_'%qMgl+agTy.?K^52.v0ʒ- %s)uv;~*N#LHjsiC}%9^K9seW'аXDYȈD%(c`2]:0]Ke^'Q()ӎen;O=(墼Օ':ugp^g(jbrDYZۍ5ҝI墹qz*k("'19a*\9@ Q'a}aDMPoYρ~V{aӓ.Cw~mu\OHDɱ7۴ rGu0vl 'sCV%%ɕRC6:fQh.(h)s/ԛӷ+(/j 'iQIm^#.rYTZ'հɿY!]V_u陾^DO9?  . z,ny|rB}3~L~22PT^̵Kd%8KFFFFfq%y<ge^H 2iˊ222=&>|4^C0-;{@ddddd.Fr_f}?D-9yq`9dddz@5f =^ 9KFFFFFFFFFF쳏3 22222222222p5Sb^h 3yΓ&!LJbBA~`7]S\?$BBZ7%odjPW=Oߋ-Ѿ>(kf E0|Gc`ɇxp]Sصq+78f<o;n/݅2| "5P/~Kt`ܸ59/y5PɃ7?ˆ!x`On{ zU6)Gfgg1c ϗϟJŗLOlr=~(ѸUD/^qOʔ8.H'K;&YiwsMlG?hܪ/ 7osr{wH*Te \ꃧ(Q+/qJ!!OœY1[Ň[_* L )='no,~/mz e)'>!~`"xz;ʻd~/oz8f\I%5s2: &?q4eY #+PZOvc&J9o[cK^ܵ7}>1jQ4`Ka6ΤՓ+ #28u&|srSM8Z{D']@g/KX:!:-colZz䍥pnFz$GPG0u2W }^;8V ao^Xuoy#c::%NdTP!:ti+zR]PL~u޹Y RczώȤMG@z*ksA q¾˦6r MW>]ϑ(]T̜[ 杼MP{Ki$7k(Mwϱ*şJ\*{Li:W&+&.1RYRb,}< ܄u8E!]_KSgr]wjj6L+/Lf*#Ԁ]tgw&_z>,NgX]ʼ/K ;T17Mt/ _]ļy&1 m~0PҗO8|=>D;Iï%Ƣ'DCMvLגk_,wfׅH)<#V<!.4[M:eߨvR5ROVe h`(j&q#<9u5{'Gz#~BMyu350d]Հ%6wk;o4Gj$Ǖ,MjV?.^ ;"u uΉ|>319T>oNzerZ/ua F˵xmEY[mGM IDAT~*l:.GVC@`'on}~Lf)5GsqpԂ7cS2γ}{װW3ȳ!: W .ICwK!NJG9ڋ#F,i#%Efl4r]^U,.č}whd{'G+T}&Gu.RqJH"a/9)O2lWyo$[sKl;"}[{BK !B*(bCDWYuuu-]aJ$M F9<Cw-sfΜ\rf볼t<吒)/uG*O'zJ۳ x>8,@`11GyfaP;b+X@<}_&h<>t[XRA+z40RjAGI Ud~RB5QЈG{v'xWl];#W& =ո@Qz>Lo .]R&c@Ii% 2K,c#i4*Hc~ˆ %8L 2o+ߦ(יqnD/n?;F)j4͏;gBZvEB*K!KSIR˻ _SB#6z RIkޖX(m%#Ɗ$=l6~gZB|Ȓx\pUDТ[OtTjNy)-hZh0JXmt!$FhDdhąGE DtR&eHT e~,O{TȔBnޅr@7a^u͏,ۗ@m,''VĶ(ݦ8v_PЧޟpɜtίIn-g f^^xpVf c#z@<=;Ǖ̟xnu9U݌%%OD{BP[akRn06wJ,0wI΍cS%& cLav/|*B%Sb?8g'Nn:ĞF\9D *74R9bh%jzJkXh^z ХUlD[K"E ]~=Wz]B,xMlŊ$lϖrL`jiZkv:wGOev>gU\[k>(wA]7$lc9um㏣0U6ZuiIv2)|_X+%R)k[ْuj1ŞURL=I-bKdr|RҨ,빭H-p7cu=nS5?9gJk7ұfw죲MPO> })Y |?ygaVKeXFzDઆj(`TI @*;9bLhFhVBUl۾ӄ0G4On1@nAmN L T.(\, @]f ) ߋsV {Bo:ρu<]d#%3z1=%'KRH'e+=EB;͞g=-c&+6IG *{]H?9?O߽`bt\ J JS)R3&rcp1DڹDhQAqRKȄ)O',BTyr2t4 aP &:ZLRBO$ʓBc L fx]#%Y;5g@0"L# ˢ¤BL4+\E ػE~ܵL$Bk6S@u h\b`@/Peycc@vU0▲0<Bԩ]Th|mޱ]:9]LeOMGP g~FJJ \B_\(68l/R 8QŤixR#4%#XUM3&CfȤUZ;tq>͐Á\MaNX4Qso{ɓ Ia7fb f"}!9`3yb [b$ZS .s;!mw)J/|GHN]_zX"I/as7lIΡYx7sMliǨΝ{z/I#G%~2S:[8C& q^d'/g4ُ2Vv$z;Ō!&@EtzL:ʰs΄i@(ŗ{&Qb .KEʓ ,gE@cFݢl< p6t(g2J>Sy?d4nO`07Q~%~v!!b]Cg1#|DR9&|2ql7h7QJEWw0F}31k(ml:wu{Ic'v$K9q~$KW2j~_Kye_3=J竘;GC^_L*;{Gil5RjOm(킘f;5Ցqh?պdzƕs- ;<ןmʕ x9RHq̭Oa|#ZT`W8Xօ"zvZnb=x0Ͼ30mn[?7wqtaZv T?ʕ9Qӛ.4{';Β߶̟/!/]=vf/ϣʬjʑsnfa/q~&D&^akt>_uq׭k{ya9Yg6\ umk-neRg==nO9s'OGɶ/xyc>MX1hbg}ɲ6L?#z.uAK'_&ݱo-)S TLoS]Xb-ߦKH/OL =%K1G[)}C-w~.d4eojo>#ZHc^|c#Xwb2Ws\)`;x=MIyOx2󄁊8owGj ?:j㫷>w{yպztW+@]<*wS]iwŶ"صdh{Nn>}l -Nձο8:Xu1<8 wO@'ؒu G2p Fe;Y* Ƹ;__E eDuQr}UN.!H`R>Z8(yQɿ^S({׳h- /!Lww]ŊMD̜u^hw[g{MY[yOI(,i!ĹQk^w17$75c [QzනnY4ˎBJ+@Y.ez)_jDs#46z\KH3P]Dy !py@"'2*ݾss(+>AaK~O"%LFаXIw1LMukJOwp"=;_Bu+֞tXwD{KgzJ]F ʎ~3>r*ܪ@.GeI9RHB]~>EέaRm໯`QbT|^6 US4%^OFǁKɔѷwQl!A Pi4 r#Mt2FL&UO]xٳMDy}\&Q1|"2*[+.bHf!j"*[;G@k{#~U<w l>IpO`|)E'`@eeV-]}93!x妩<*s?m4EmWE(T`?x䶶T&nճ8z0(z"0;cNA^)oߕis"MUd&t$KRkC扛 pb#PGyo;@mah=}rF[aChH2W4X9`Fk{}}#z~{yԨ_HȬԦ8u N۳zJݤZ 6NvETnuHC'}NtAAk>YqlF{?Ơi .3&;)HݻN׊̟{DPmhr&$V($(@p΍CiRč Q6l0q8i%dB!7|2rih Tɨc1̒<f #!D91dKi+XOfj5vaL0qԸ2v`l|"~7Y59x QˏlB]fY3dfbT~c=E- :rfO z.n {["b7ʝ8INcKNw20v`\+xYo3lL5MlSۜlKu>5l>XH]M X"<П>¾R9p5nL9] ?*ڹ2O%yʛG2jv%#mH}pf&q Øg=9VXUR4y)OϦuǏdlǷI7Q$9hZp'2eܕ IDAT-g6|sF˧b"7a#i";1]2JZ.϶xH{p~ULq^ ~C1E)qHdJdJN~zq_a3+ntdX#FgmXJ-֏>,&zmӥ۝7qj7@ol!7 DћfW[v~fG…edd~t «YG&؛kEPŎHOH3?|Ï[3֐|tB'o!##sEd ֜wsK<&B`}ي/ˣ̟ۆPZ au#=Tx:V-GӯvJOq?G2(Oc7;g*sx{bFkug&Vϰr:V-XD ЎxkW0GEe3cn8+@i;'ZǪ$ʲO]VZň Q1΢uZeu#-`P:ޚk/!Wַ;.O5FT/cua]Hԡ} 1l?ʻ'r }#./뗱ⓂdJ"Q2iDW K& =# tM&HWkzBRBP6>|_{yS 16dSXDvѶxmZ2 ‘qЎ,#uT uYqmo}+F>8{5:t+=n9Ql4dSa6vĦ!O8ēXvva`o捝Eh&26y/3OaՖ5ڦg@ '6[._˧Q:MpNfcB1g!wEɺw0oOLv*k;YXH+4BMϟr᛭]l$a##&ƾGWrn*/Gܗʑf-^͗ո/N$azm?( 4tVbPJ&:3@eFQRH8Hf(mc(Ԛ\Qhˣ=;wx+kή$d$*WE xgb37ϲE_7x6R:G{y[ulnwSbjw1L fE(uI~oِz<c)A> \T{0 vPtY ĉPMS~yo;{5\N M%Ե] UeV9Z˩4vhiU/-}8Q`6f oZIb r2 GarlҐ>KB&>/6 k 4PX< K}=M T.(\, ͒Hy2NL40 iҟ!jD)o [xw>*ҎPOHC^[Ɵen3I%qSgܯQqc+I objХ 9uoS=3''M-p rFv6g'Y}Pv3КPkr5:x,+6Jh:c h1oo\י/K߽`bt\ J JS)R3&rcp1DڹDhQAqRKȄ^NFY:4T diL LttKd*%D<*יLGJv뫅BF iVwJb|k(D'3;t]ZwCs N#(#9kcru`79aDe%Ou\S֘s'CUP@^" ȮXbݝ fBRzba7gZc&jgy&`v@и`8) ^n Bvt9 0q6oelХ%V|_.7.vPBS2;q MU5 i[=EFRB+pMᣙ9 Iԡ+cJ dԐ`"3'W=~2222ݡ= gyGYwhIo<z7M|ȼ cY&I.i(^IϿq|3SUo@i:P\/_XyRmfa] ;Yc dW- ZBYVV>MUUGCKbS+$5t(wh>k_'Sg !~̛"m9d_ubXbYeKig̪E$壦uMaa:,jZR7` Wѫh$l&*wsVfy/2s*7gMyJȬuOFFF(V^-< W[1o?i?AWwUjnsJï_@M烣mlMx38ĻcͿʎf!"tSL^BW58WU39_Md0Cn-h /js gwsJ1٣1 |sKtÈKZbOyd|J )=v.( ˥a*| k$YhI˖wEtQ8p1 G39#> %בu%j.ҧG=θc @aKp=,^w%r /Gjc)ٛn}8cب0۫_%j&xL)np_#ǎ~sȥ,I%hl5!nqBx`䇘gN8d)sOcɤJ~"X)Q"z2z\֐Y!}D,-uſT 9oAg4TlC4M^.u&bfKG;\2E{أ<<8Sqwpy?ªZ1`}ls-3M҅Qbώ/8D'_?} /vv-LO$rr;ѡ`87vsFbXӌ-'gM7Ub|q3UZ:9- zٱ|[՜3t':,(kN!*ǪZ)(K -mp[Bo ʓ_aMAُ|M䩈xh&q7 v)4y7㬪%3u%ˏ3h5?p3U_ƞm;wb21! u||p;zA"n:>͇WUh:z 7ņFr?r4B"HLf҂br_$÷#]y7uuX?«);8/l'-53R~^Wϴmb!RD) :NfLQ#5{[Sh>E—g}P1wgq=L, ĭD[KQrz<}ǃ,ڵ "}c|^äɿ?-q^"ޯ{7F }Ş&N FTƙHdN!y/%/՝+Bs2[3~1٪T{߽b*/*,6d.@Iq z+l@hȧڤ@)PO`Kex)RH*q̀}xZ8za,Mh7kA$*W[}VTo2AΎ((*J?АM?b)G^mk4h}9wM>Nm'sD |z:$n BaY6m-/Aj2Y+ʍ[>r`,' E6NïJa{ &+̭$)SOQh 4dSaT3ڵ[:*뺍8ҺTTk>W@S~r]L>v:+Bfq' |I1|^"I2dZ4)r,;c=cD0RqOaՖ5ڦgyOIՃtX6)nï֡X.qðRu>U?7[ "+9~G2oT Vk *DNնI64=Eu1R]Kv&OBC1SkFvL}wg}\VKߔ[ ]$s Q+K\ZvϫMƷ+>80[皏1@N@ j[*T{V A]$tQH|$}fsRhė~vO@aMЧy|;趰2?V*0hՙa*+4l-SSЈG{v'xWl]; 4I)U^ ~gz#oper~m,\(M}g3).:SqqGgIf%BÔKwvFhA')HzZ:lf0dTh3NRtz]Gu0&Da]d_8t >рz^s^@:F 8a}]v8Ogtym~j/v'ӏQXΔPYtS2jx\V5X|R[g&I\|NP#ǔBY~\M5Rq+RPO=`l#$7kEZh_; J19q͏w;\'B=eqm2Wm"H<}>O1ڟ+xJMPy3?h3@y7׫g)eOy/ў#,xasr3ݺSz _JF|>(n̤aIĭEzSFBd#Hi gW]FT;vSnA,Ƥ@&Bp8:I?B)XZh4tR\ =wKD=5GWZti3 7WW4]>>X;) Bɻ?"EM FP~്.ĽHC-ȄB('2.wEb(g@|OKZDRuъj/tәL^[^o;n^e<@a;Xd6űn Ņ ML  |'cg2gl,sƸ IDAT]wk$Th VjPM;tІ}NPaW:FU6d$wywU@=6r<$O /1oq< Da__TZlX oc*LS&06Sh,(R25Z  PO^1Z8vFiURohs<>FhNb2⇸a,¡.DA ~ܽ3;,aKJ' }>rIL"17vuE#{D1R]U > Z߆O`$5 XA;|Lh4:l tY/R%%R)Εםbv~(pFK?}Bcl" mp֖k)U cJ;-lMRv rqvS#}O"Jk0X KؗQ̼B*h] UfOcWz &S ׍[oQWmK^*mrO#:3WAwlL.e8#Y2h3OI Ix'J-P5.bǙ8t)ϲm cpm/9)6'KXZ7ZlPo]3K3Umi3oCM ͔кaV 6 {zf+ų1QBl(K+m W y|:^?p{Nf,IBPf)Ly##UYiT : } >>Ahn+S%?b6JP\\2R䣴(z#g0ۖw~T6BPfH6z_A#1@} R]S1G+06* Hȫb<\eJ*{y&BF7lX/@C۟Os·>>rv?*n6ȧa3jsVr۹/D}>KXGRLeOŒGYGXɏ@UfEo@Ht1`wR̋j%GO@e֝8A#-.`CU'|SE(QDC/NkFPZ(7j%6tSX1s ůSU cgz%׌}4S J=m%u-,`!ik&9a#I8)RIv3]#L8!`+i}OcWƩCOeCA r |QmI=ڽ/6(*fg}[%oh XLG2##:vh$!c=^c)Z}:r ˧OԔְ6>">k2J$vAkPTUݧ;S֭y>$"vk/,#g{M?75uP\.dC#$uoes/},?4&J6v.`ge?sobiWi8/acd/###seaŌV5q?5}S}sDKwWn žFr?a= ky{-7jC<|8O'Ώ.xqJ +z;Ć3?^+'? {9V˜4 W&Yr4]${[/*p y7'vSR/ZbV;ܻy-Bo?Bv ԔOSvo肞~** nWк}$ .6wrY6:Wr5RlKs!VyI'6.z\QTjaN>{̊XUœɥȧ+%9uEbLMO`rd7[> Lѓn3Гqڻ4,} mі`5~.}˻Rb^Th;fcM6g;=;J3)O I,vJm􅂝>Y&~KOvbv]a4 H[*gRs4*6xfeBZnגSDTr84/mq$]O\'d⻔[]?S8{+cZ9r_ОV`k 1Xǡs ؐ֨=\ǬI̚ En<+yk,׆* $ Abt%,)ƚZXx g+$4dfD(WnVPt3R٥ןQ.ιY] E 5HM4y. GϮrS13{NPr23E 4lh;aݓD}) 0U hт @{X=kR:cPWvTMTq’P%^HڼD NBnЎztc `bV(@/ڠ j_[w,)n|hS͜0=vHKuNTu֡O Ts7 3MƳf>#Jsc^ :,]ZU\'VxD# G;\ENDd9@A-1@S& XPi_w 27z<^\ɝG/ ffܵ΁LU; 4l!CP{hNnx 4!Ssv{Pct*[\u@[uG Z7lЪu05Iܤ c~T#u#s.~(59GXAnA"sĻ:)c9Rvmth]QB'v=IXy [oĀJ<m4:S@{ܙ) Dc>z>7̎teזq 6莝'ABNxyvHl^*HٵR!ИZ(ɡR:zӽ=2222 :ZAMX%5[QnEF[O%{ 6W`۬{V lfww#*#@5"|} j%눲k cǗS :=Po3e,]ٵ$74 aT-5a4`׃z˥y t fJȮ"'{KUFeddd.ހ4Ѵ+۹k-=nw KRdeh(vfw]GHy,Q#Xd :*NʧP%xYV3ݕS\տEBA]=l`_@Lvd5)3>&0ړ3婯&|ZQ0PYu'N(C4N®T8 wX[I;<+fOxP*}S-.`CU'|SE%Q/ϳ1sdv[Ç{#`s$Z^6"8z +fNu (LBFڪ~xnxӝ )JtZ֨z <-&Ds v'~!żul+eo;*r?Z,j9sZ̘1NIk̍P\JԒk(7vi ` Ӭ2r>Ci4m6?y蠗Rۭ:'F2222Wu <-Lvg[6aJ̻6v"_wiE(Y[ahih3.Zw޿{{RؽE+dddp"B˂҉PQr-^ez3"fЄx44 $[qddddd~W u0N3C${[di%= 6Tsk{{dL"o,E/eO][nERc_Ipe5,wKOEl9CN?!wXyN\@J@hK߾NJʨw8rn6=?s#$Mpf*-g'sc)pt ;Ba1[)14dˁ|_Xsα V^_)G+p y7'vK {u׶=^%(f5ٽýNtq^/u"6_qҝ1cfɠ0###>tyuty93 K~ /nV=u&~c}l?я{F>Iֳ}1'NV>^sdV]Ū,L.GcB\pu@ IDATқYh+Byxp4[`oGdiq~4 \Xo<)5h*zr%"1DڶS oW4 @w'] T t"O^7d{Xx/Tv$QNVdDq ]@L~EloԡlLځ?'d .0Ŋ ~acȟ$6<& J3 p_WV|pDi3GoS r:faEsc5/'F˃j~E0#Lɭ1n(;;:>Vh;fcM6cxl yy\viHF q+&[Z)-G{7fţ$t3υڼyyVJ̠1i\OXsp/V=s%RwPFNy 5UxOb P"Dyvdśq/c4FNe@]œ$8473Yzɒr7gx?_ O*cn !uڽ.Qzx/) *gPۍdvP v$5ye(!p'L,DzY}U jp4g*ӣmv~ J Lf,ϔؿp]p &޾_)0 EdkoQbk ^+v&Ѷ?l?qA81&bHyuqO7ūfWϳp e(&,1yStb5>;t jc*nzg~zvL)@>oQ(Q*59G1hL,aZh3@ܣZm5 $7'OHl)PQ+qu91KxmYJ)0LG',o0x fh0JfKo<V1^9v9}S@ &}K,;ռsp H|SlQmV)?;`T8yŋJ)0 >gR׉T#ɀxPT:)/TynPNZCAƆx{:vn"xM nyY(q%VH)6 E'xl5HImQ"}1O7I`OՍ~EuNAF .K5e.] o:qʝ ;9*`" iX*oqJ=+duɢcl9?WaCx07Ij &'*h#%@n 3`8ʫs+m#w VIB5[JڇjhΏY))`F2|⶛$;P*@m퍷M*GO=Lh27h2Ѿֻ}[ n_ϱlreQ+%L %vps f9<;1>uP8ƃ$WgQY#b'} 4-N:^@ er/b0jUS9BmYR1!6Ca-TRM[VEax; agRm jR:HBUE4f}!^+=j?G,$>l)~[+fIEww[NQ&f@eF)+̤ 6V62#Ll@ KFuKwؚ Eb(}2~CQ!ϟj@c *s+aO1Ԃ@;ON>զ QDǮb}?$pNǝ2ydnj"yJ!U^,يv7 3JAW%J8n:+vMb}U=1S8kJ8#P\ĬLihJwºs/*=Z`u=.L~ \PdV~'8&[h{OG,:kCtc}TL3ԦV8hZ,`i>!RBNu?z&bf3b Ʋ_kW19@F)T4zК0b@hO4"m4 m1Ql  AJrhjaD9jI? { `l3o`CԷ>\AO U+ +:zaMIә7RMp)AS7}@>_龯PE,vSkm*9Fr\ h1jk4hVӦIP1SX1ry).II TlUBX:㥈{D7* uf^a" mp֖k)JɎY"Oc*o;6 @hs(U"Y~P͹QʎX=?njw*Z`5 .a_F1F G9j1CTlGTȁJpun$z}^~tHK 5@犭jUUwA@놭Z.& fܵARgrl$x'oogd.-}i+-Hxx|U,i}C| Fv]Z7ZlPoIG 0SS%/~6 }|nʹ BMAzxAXpUߓXNQԎX+@u0֡pB p't:~X|nJ؃Py̶KcyYWZ1D:Y1j"7㧝T];T) O%}Wh]å@Jm'qJ+O׺a ,z YzЇVw)?.J !75N-АI K[ vQIiyveR\6:꬯'C7}FD.]l_?BbjL\#d"zPBORȩpz6 NLSbg!s!p&" g[LD8tZҊͳLiUօX$3na !aLuڷ`t&OD΂t,\KQ v͌@=3;^^/D':К ?؟X]j߶`lMJZ@>뚋+nW 幛9ؐOBA#Cg0?"=x&CCn M6g2/4Yb!#6S yL!;]ogr@KQkF N8X)* \^|9~|u>*Ϳ}!iGq‡ \[ؓb쫄A` [|bGlqLIu1G+ Ed? LrCu<_s|UrzfDCVogt+,M>ʢɣX= cmLE)o#wӂrq?m*#p0V4r$4@܉?ה^{'p-)8p0BX3O:+8t%6v1pJS \.?Vrl'!aX0$ cX;UƓα]&[L*Iڧ}@1 q!HJ8ZuIq@̤fҋҦݥR6.Jg(f4 GNYT d%Rew:#p 8.mV1{>uZ**Km<]Euu=mۮIc~6s3>I0$Rְd-!~} "Dz^Ƹ(<%>Dh+Hni%28A;|לji$LAVHyRW"?֬)sw~TdKFVN5V86R+J/ [)2.~d"BgkKzvN?8o*Wa$c|Xo4 UḮapmOe+wsW{5H!DzñGԵP]B& ńT~O%jvᴀT|cI[LԔ%Qg3CHlmo \3w |}:Ճ;gd\:m(3rhj-Eٛ{k0txʇ#O䗌s?@jUVi{gՂp]818ΒpoG*B[cf>bk0Cyk17c֠zQ1'm|5q8yHITZ u %7 u 9y;E(f. NnfD\ ]~JZJKôiD2&Me/@rm uE;2U1!c V|[y:F‰ r&ړz$:0pSc !ͻ_c0Gٱmk=rMTjI\d<|ݨJ5L&NnX7եvS(5}n#XDcef9+sRC3!lZz UwIy+zq9;ӹlpC#D ]W21Z(JwIC(f jiNX˜șĹCf8Do3P'Omv~dK-JK_@Ό}l??lTXlRZrU`(֭['6ډE % [@yڃ_o))l^;Lڶbn'J'f?<՟v[.uh: _Mo2˭r"vBuCC?WTj$_nRgRw1>e Ʃ6V,QK+ʡ V:_jV|4N3of=wt: 5q7HVuLqA8>Ff2~"XKΏ3ՙc-9t9QzQ&2 Pf,fFM6l ?gz䑟h 3X_"`d7Mp\ A#4pۯx{Os'ofʏb(8gOj7bFY>&7 `0G6`K=YG2=+(%qsy`AnJ竔{[?{acjLx%2Lb#xhJ Hٖ=8Q[uS97ɑß%Fy@1@94LrbVo!?]ІrƼ!&qդڅql8QRS>zsSõק{Prmsgx?wcRi[SJQ9K#G ~n,à< [klO:gK|qo__yoIEe1RBvcB[A Bz霈ώQld\d0WJ}H^{I![%eQ!ʜυ-S5-GeFæ:G7zS5>)6Ttٜ?Zkםk +mG h|Gp}Du=^\K?)p .w{@-fV0‘^T#WߘG@3↥v$ؾg1S8w 2vgs7'ȬJXh6>r-6c!MP:0x" =aN(&f7~VeR n --f՚L%jB$Mߍ]0֖5vϑ~Hi/Iezʖ)x`ͨ70;[Ng,|ffƎaBM}{sU k'15Ԛ[P"ή3Z-tdMQ nĀyK9zfR9+47 $O}ZDw׎Tb, ]p5>מGrC,Ky$uFLc#+ϊ+kz'ΦҲq.C06VGW)7J`3T;3q8HG,JPn+zd(0{JԞ*xl IDATKݵm}Jꀸ|k<*(UDU>W[xaw"U,I.E76Բ{3_:]wQU[~ !^HDEA޹^ "6PPQT@Dz B*L&# 5s&$>9sZk>KrpퟝtEI晝'bk.vVѽSu30Qe|}jib>/tXk8[JټŇ}ugP fDgRPi#΍D.Qqhg0Bj/[DA$Р{GgͣFL*-j#K5/+Fs70vN&kX` z G1a )q`(ba<<xBΞ,ygŪnldkTZ:9#Y]\Sbο!TIҮt\#s#lܧa_%ێ`Bѿ?#Ye>wS/z;7G7%vyXu()4җYGJuN⤠YmFgAOH 'e+}q]Oz]-N71 M)mזFF#Ze=^tALz ¸*-Hi^ה/u}qSx4Kva)='KvVP̖=%4ʎdz8dDi]:>o49qÉuOjfDʡ9Zu,tIIagIbks*S5.(T(Uf,-A֤GlclxbzkͿU-jU0RȯVG@ 7Ҹqvc/ rszz8(fƗV7'k?Pj 8jtfSO0,JT>}8HbbUmiXfE!T}0뫙1m4~&׿X]ǖ XR(HA,CS~I#aƠUl>x>h1WT+8ڗġnɦF4;J Hf]+#JGWZX_`֨Y4u!*OLu|Zx(xjMT7usc[bI%{vɳKVzZ7?F#Zlٴ-U99ghΨ:?#jc2h_I&ig`?ƅфŬB@/"hPq^׿vLXx%3b2f_=i{JFB՝I" W.!E<=T೟BZ#:%1{ԃu-EcʈJ ∦^j57R84RTkËUdԸУw~)(k{QKD۠texDFLyL |\B0LGk(̦ӕӂG#t{c};Tɥ?Y%}([78lS^]w1o4^N=)7:,(t-zj$ꃈ. _u/Nq-%GXac-Rꦽkh58)7pj~hL:E `nwPMM - 1@Db,=4j; El>+pS g]1c9<Mg/ =e.A,op$4b91v\.\yu=3=߾82Tή= vA8fkCe$ĸtHpK=S> 'YSԚl8tN`a7K1ӻM5s>?)'bDu9'vpuTӲ}+ld 󹶺׳<xJr%uD=G˺;㭷\Qb6vä|7Z_ک$pҽ,!BBJ./ةLb*agՌ)^߷쭫y UUx9Ю#[=nhWF-2oeh-^NZ^T͟o}קغ,=ӫH}Цz$6pКj}ktA<4f bc Ԯ;j2y%zOしPޟ6WtNO{)SrP'R~sAռ_:o$|`w]$p)*KEōSe˖-i羼x(??)dddddddd7YV`՜~YMg~TOpQNBinxk(t9N/edddddddd:0'I(########s!/ht ±?FÕA~hj2Ӿä-e #0\%+ KvfZE WM}Y^YBڐԮ#1&F@y&Vl5m;Ί6k'y~d)c~fKhdǜHyZܲlץ!,~ O|#q;N )24ڇyx:~}Unt"ռ B);GUZDC|'S>kdcgFGu0uax)kJwwoD9M/1zL9_8m_cz*ԑ22ID;&g䌋ƾv^{%+[˨0JO|u@MR^ULV Lz!*/{IZi?;yX{s9r=a@qQ1F=AQeP4.2&s%l1=M)]$+5׏@'AMK%{Rl{*E9 kq @p*@P? ?/zQMȀƉݯQU$3FM%J+}SutS4M#kXJbOH5™Ѩ|%[]Z’]q~GuGp FDٷ|r(xn w[_w]\"K @".I %y%IGu7Fn nk (P"c^N'0>FT Zt}+OS"McK? NXAZH)$و=2%j@Z}i6]@hCKs.Rl|)O76Y6RfH"& 30q~;P}7o~7xX&ƳV`̨+pC#@HS -dC󃜁<>s֏ZR 5?ݱ=[ Mdnx'OW%QU3q\BP#dOoY+l糍k(zr5_:i@°79:4 ;hBoNt@71`.J&FP0hyU00 `kiipRh0Yϒ;|;biZi͇9b=.68!$b69cT~N\~z_lI=kZ"͔cvƞ˗Nh 7Qn߾$ ڀ Js,^MdBtl殕44CxYS<yRR ee׿KN8HTky/8Ҳ\FV9aI"ҙxG.RF^"Mc 7N Μ-%O`g(_ړGM}rW[v'bAqAp v3uHeX©t*j1jfC Ud;^ͿFƵ(OEr^]9>frk3+LI`0-ۨhID[a,dN2+&3ķUR:Kɨ80|T/@M:e]R/,ٖDšjc"l9O]Y`S1)UOoP_͓GPA1s4*.4B}ՑLZh-N5f#.uL&m[4ηOGz㬀 ג/. nD$ԒƎhs]|I}׎'*53m'j;ӝKد~yR#]E`\dbdDERLDq\53b!$:t a 8+shoS3 C&tLH.6֔^4OI$zu')(Q+K7Qh7>vܾWG#t%o45WuQG_x* ("" @g8sq\慣ccr[T *!#X— {4y`R…Rʾ Df˨A71H)q"Y:Ntc Ji|}pXGD T nt8p c3uL4U\BǥPGnJIcT\lȳLmbgª}ȩ׉Lcpq,lvd׀xfgp3v1Kenl$^$}|n?+Pκ}_K'/v'Fij;R.S:oDH/F$>È^go{(Nyw'H;&練`Q2mCgYiZ]3[g1TlȗT oMT}&Q{*M?ͦ[G*5k5j˷"Oo7;DŽu|jyBg0y_ AwX,dǙMbd==Bc?Nmל3&-g9q҂u,;tE#sXObvYLÏ|t@G^6Srb  IDAT=mpU{R;Uw{Q ?=u&@HCq L 7ߊdv6p砱5a]VW$b!1j\k#oڭ|'{[Y!qiyJ4fۋ}iJnKXniE,Sa3zN~{R:0=L=mڝ3֑ ב8{)ۺ}~} g?EiV֩)²eyzئ4ؽ= vގȻ\Nt ='N*###B&Ʃ3>qt{e]l:>rDFFF p~7Ouʶ֨"}|4 jyNOFFFFKFFFFFFFFFF械hs :u ±?FÕA~hj2Ӿä-e #0\%+ KFq/@&=O2M)$]GpcMEM5;kڰh6D }tR6{IڛM[.i/ٱHyZܲ!,~ O|#k&#)m;RO{_Bw Toe/VEI'%Q _}[c"qxOeʁ;~|>BԠ`eǙ>#cR+,I/oǑo ?>Ֆjz ZK4q7K+|,d.7:qĕ عޟCP<'*<}>~%O-j.scP9q ՞4d҄z׿"ĜS )=B3[ەΥf5K[[2p pHz obOn =iIOOH^5y뷅kf 3*xW okP(s{Su=>>VIxҔβ_ui eCNCW$p6e%ena^_gUdwdl璲T={qD 2}+3Oo;pōўT~Ŧ`$6tx<;]e&wegE&֕euQjݸjH?BQ؈`S/gLFT-ϼڃ$4X?Jͪ<\ݡ:veQg1?cSL Nc?˚BM1<ߠQ058\ƻ;6P|wz|w{ 8Y|ʪC/ZխoRFDW-G>ǩU5J챀g_Q͂ϝmvׅE६!+ ݽPݳ>`0z@埼uhFPDVq*J5[ꄧx* Cr8iYm^WF S:C]鹃+FSw)ݟc#2.ҽO ?/YYɰ겝u6g8$G4b3 g^Dkeevdÿa}#}K3g>33] _+.\t>#GcTD]UXMA /c28{XsӔEB˾Rn~z$)"1j*Q_ygKϠ{c _m|_=kC.}^e/?_ G"pWd yd~\~QaA\BQ-@9bi]X'(5KR  `4w|җ0e&\Fv=qBG0(ȩdr*"z55ڂw1ԭ;gf8!-I]B6k7ox fZJ@x|!mϫ,I:Gy,6Dl-*|B(`CNQ ep,^-Y&K6s|[&٬X,/cJSK>ku'c%n#k~-?kѡ“`YK+V !+>L&p\m3űl9=<ѪH^v ڱI%ȑa󹹧;GƮ}8 mlam>mD &PWЯ-jJ\7.\tsD<ѻԮ勔L?J+SIZۗf "Z@Qʖ<)^Ʀ;׳tF,iZ$gwPf6RZU٧3 e)Œdzaa{y0[0gBc N)? r\2Si1Wxc*PO!rUh"s?q7Uݱ$Jj&KjrϘ9wŚ|q BOKGV4 H׳:G8M̀ 7BVI%*ȚtcbPI\B{j<8TmK^*ĞPO%ےBiv|,ՈR12p|'%.^m cM(q7ng&$eXO~i>z PO~i*-/6%\iv1ұ=6m[K:捙DA:F(yy+|ԤQzvD"g+8]̫1o|3^+ݪTPNAQ}6GFB͏JGX>#@~NXk$ $[/[uK,XǏ))btϛ 3]o!V5"%|IC]t&@ؼ[ 0շ3NAF#@M:jO@M: Xϼ)H\ f} "=&Tk >Z@WN쥩FD_H̍ejW4Ў~ώ^Id?0b}RH)`e{k$B8Dr}̃^糘@R΃*p׀sԑjW,6l<ƺRb.ZˮlPu[*[68J OlT@c0닩;,[6X+Uĵ.K2p#_pe:sÒDN3@]#I: [bS3 C&t4(xpCcKDɍ`cmh'_4}zN";Iź]3/Ցru8M!PQewpZ/5Q92jM RdHVܳԲwKH~!䳿n6j8O:_LlaJajC^z_6;ky<338x;SԘ%bdhKA %=Ӵ)5$crgw6lS2~BT,bg=Pj9k$튔 ?$J)~nL~g'n_ZTnt txс22e#\'2=:Q3jr+ٳT>i1#7Jg")AZe~Æ-OI F!9gk*?LCAN:.NC˽>ʕD85e[9I>J"͆J붗n$KƾPf*Bb5NsPY߰&#6.Mz\4mъ|Gc<9Vq\K|I uHekaGk_L`sUڌ5(/:Nk_^IbwOJO.IlPOQ8FB_ -v`Zc%%ˊ  ,[L7o_m{_Au`'+s*)Y󣬨6<SòYۑmx/5 .t5܍s<<fȴʢEu{T2222˨B$Ek-'eC9Kkɢb" mo&\{FI|wo|c9`*aIΓ@d"###8tzGv}ڱ*W3n"b('#s`·'*qiNj0M)wQz߰XU`".qҒrd sR%X22222222222y E4 =M_E#1UgX}R4a-3= zJnoR^CI,]ЃO mLS ^#7 ĺSJ~Johtcz>+S>~ j}|:.ɂm1޽A_]vRx0婩/ܼlE+8, 'cBXʏfmNc,18[}5Jn]|PcpoQJMγFk>/?q|Yo`գͿ=-:&M{Sp"nqmiM̛2ۄǐ~"S&%=&ۘAJEwq?Mc=rۑ6vSܱkɒ8j%4.[SJSYN sFbZŬtc؝ЭYT.,<1g|<ڃoVef,gѤ|7O+drLE;y&/ r(΋222=7ώ#ǀTu>j87 4$}rR@5K;gT i{R*kL'uXPVHõixuMiEO y~6om =]ؕSߴ߿o>dc}KPs2`ܕMߨ s'@7| 3*_3QE;=ƃ8_-C/Z(K[UA'oec#j ^̺uB-ְoy[޼R,73*.ttW4Q|㣆G+"|7_=K;8+ !э_ PƟSvkJAo#Q꘍zk,Ec[4.5>anLsEj2 WzRlç=z8 sϝu?+pz<;Z!t5^j2|fVc+FHSJ;RP2A^=bp)ʺ<°>lVOh e1z<,m8D *Ȼ5A=Fv#oBԁO"4}D`n՝S1Tx{1U |ڈf1G]J-YU#pg<{Gs%)23c(8DwYJ+uTTiwƊH'-=XC{t~AI1Q<up?[>Xc)Tp`-RM'ޖwS,6J9|+oԖ'BM[˛/M%⭥%%OaZ7?FV{E$򧷸ck~msKŞb³3FJDkzJKۻkyP8TNY|4%Xyb\[v#/]Ϥh͗ddd.)%tטI{3t2Qe|C\@46bRiQ J?.O?g݅Jb1~LQG{T.Wן1>Pm܀C2ywnhRaz RS8x7͎XLԋM]&,ѰBF6oȤ:Hg@0vK:QTO\2J'ŇK+3䋟-}m݅GKCJڗ?1_Gy'@j3:"ѨǤrhcl'VƇޕl=\Yԓm?9mV3:F벚O"qln*vh<@03ƪb$gX^Hs8W*v3&Tc. =%YN&Ӌ+4<ړm1)hJ|zu  IDATHk>(]V}IFF撢7V9j^Ӌc|ӳzs-E Pl+iB `#Аɛ7H)!W䅇IYݫdQwǧYyh4Oo ֧Yk;c7!ChrgC._Fn;//`,%?B"-HDPI"Ko7[%DLيPz-}lXf73rs3Q?9u}_׽߷.=u˾gu|ZCypx:D@@UC ɷt dn)[3| ׷`rB{Pۘ{8|cf7R[":w0(bFQNI*y'ɏ5FH+DdSX9x LjjcP9ʸitMTE(:) +%٢Rv_m+%{ꋂRD Gc }/6q_yEk,Uz4^- =y+OuIFF\Qg*잳- gs1VAdJ Ptr[&R&7tp<g:q%OB>OX1_M'[N jEiJ}@Z|/*<[1RSRX倡.;O3qX*ꒌL̶`)Z0nd]}ց+{ hXU*v=j32ЗJ; X+}n i6w?P>M:Ƣ{AMdT<_rfD/.JjtĴoIFQ# kθq(BrR:yN PZ%:J4pj ">}V2p{55ahJQtu_KhJ;L4Ղ*4tY_OzG/=ف6 ^2Βl,MBݶ Q^4N50Ҩ}U4~=Hz(5UZ55}ŝH%W6W驮.8.+7ࡦQڠ!gSl [PF+ J*@cGHg_1EʖU, Ô]Pv~n#q1%JJh]\,x3D/2=I_6vg.Uejrld USW=عmٞIYLʹEF C-Rp 7!""O;fc;iͬLȨ ݹ]l虓Ѹ܈ b%`zy<붓LeALFb=s"VmdYR!GfNjY0O عc\K̽o<d/x;S]*FP 0+iۿxhpo#'$qg4{U 5D$Ke!~KU,眵w;E+Wb7%.Nd֒ 7O+W@flqXGa&?E[D#1ng;lɥJߖ~kl|Q%B9J[I.x%ե̺PJ7k9Ȕ++V'N퐑{pW/B@SOkk}WFgj ~.s):)##S1q %!1 B7""wm@Q['N\(2KFFFF- ~ˆRhu J%DEھ]݇ -W߼_~cig~CFFF."o'[ddddddddddd)eKPSo^U0|_ckѕ0: ˀA`yb;/?BJ2wrGp͛L)YpxkƴAW1ˌRDt^.VQ@TL%ή-Λn)hujU't޴uuf]]q+c?2hfbP67ˇ/[[Hr*֟g;8b G]0im:-˷!S }{bsrlTs^~7pF`;Mx hs姼p$2CF1zyFO :\Q֢2:*OR9H2],qJaH*jhӧ?=)8=aUg壟w\\9w}]s$%1i'H2&~cѭY,}:'g֝ ]D|)svf1n V &,_&\¿&kuj1kVΫt13K9 ZjQPZT0gpQ2eJΙ[Iow:n]W}\Y;N愩^ %v|(__,FD$2e}5w m{0o]-KaI nu$^Kǹء~v\츫8C͇J98S-~ikCO M(EܳXeh{0wO71z(En]i}gϔHHFmZJ0X%ۢށvCoxkW%ȋL);K֗Kx|r@E'_1Dχ ywtЮMB[ΚW=]yR UJRmQfg@ٲ7 E+*g+z)UHMe]581V kLZ_/ߺJRU&($!nZRQ8+<)/\-!֖Ǝs ׏S'L\ɶymD=VNrP佗){2[N<,i>[ͫf0xOw|udM煏/Ph̿R2♇|s~bi{!*Gjcf1u/ӡJݍKml4<קU( N O{#ۃE CnϿ/2zaT]"JRvz&㷽\ܚV Ta@o[oE~hiĸVE&_.UϤҼǺT =ʛl@uumhz+6*-RuJ)X`/DZ2}:dO2n3q^;aևSzJo:WRk?!pӑӿF:[KYWAlۓU9bn|qŖ;`EIӆrhL~3dB TlWFsr.Ѯo"cUkCD .k}w#28gu_[|SY FF%2gUϚ.fKV@F zOOtXqtID˶ Qc99+0"+=ө`&&98f%Z90JӳDP= ?m:_m!~~rj5D[!kp㲱SyL阬Y<-+c-`M+D..>Bҵ2]"ZF ;%M\]4VJfkOgtI%Iz&<%B ;& dٙeVICΕGJ|j*W? BNK=Px00֘O݅}wF88%dJqmWٿ;oU`:Ljq\[}r- JqʢWh :1ۮ. N  L j !d_ʃ%JJr=HaU$+q lM %3ExyXpmL{>[#4<7Ç8fv ߡ¨6/ht,(/~7?.=u˾gu|YO>-1WL(th]& 51( Ne4O*΋ "F\ FlQW;/\_%⠯e$H)Opt9=ݐkS7 ^$p[.ς]Gu,a HiS]+DA@Ǚ6 ,di\?r~b̄"֊"|LI_ E!'θgL!uh`x3N ǓqvC }W$dJciSCE+N}G7#\MV~1!8z{l` ܀.$[R kIng ?ӯ1g=P,J952IlJxȎiXEM\n2w7 7~bOd,:W6n Xݚytuss`~E5[MEvk+T#t _ N * ŃAGhD,;J5/v|WTWOiXw,pJcz 77}yYz@=z*@BOD<)T4n@@EZsSHa j<5i1D6mL3ϹEN;Vg)KžD?^eP^2ռ{ZՅ\I--Ҩ1j+Ý'^t+7,CMZ.uiAvs9yW2ޱ%LkFۊ|nw|Ϸ& 4M)OWI>AuٿD^-AN$>ogq<;k@XY kθq(BrR:yNם]="Lۺ~(-mu`O#n] P #>_w_IaKSlɻѫRPSUS^竿KA.aTiNOuu!vI5n7ࡦQڠ!gol9Ȕ+hDIvhb zh~+6Hٲa}2 jNy<7w;DR voƒEg;-۳>)+!c5qE cт}wfϳKAKAK̉XfeIY}dW)$.vL962q &tY/f<-` ΂J]͔+.:g.z%'i'x.Am\49Ato Ѓc͏c=^pf㒽t~q:\V/'Еy~AG48I޷͑/~*KXĚOp bv".6ch\nD[qߒ`SiS{zt,}uѸ㜳nhUlzEɝZrA@_IbW-n+1,}ę'8_h裿Kwvx(#/Yp/L)`-X22222222222[Չ| _~O\U{'(A*4 wXk+4atؗMn%v>^~ɋ 50bp;+PUX/⋅8h\FW@T O52ө[M#*w.zD.=69(#Jkgy8Pz[68? AKH9(o2ϏՉe~2\l(p\7p 1YIxֿ U ';S­h sTv>[]G;9ţovmnyMNyWNy~ tyY-R2eddd :(ST{&iYpZ1(QOs߸nyָc)~䋼 6^ҭ:>#\)/å Qtȗ[GDL2Wz2^8G`]1g|W沶s̙r-;r$\Е\pOW*߽NPA@4z#w6q**65[O8!7ő*TPt+?g@{}B؏pu0mz ~xc157]PXv^-`"d]$>S"R mi={+ug cy{{0_F`hb,o…=׷.a ߰.W S7s"VMڶx4߳la y!J[3rg:x=,j,mo4Ќ/wu$DzB633DsqCZ8qRޥ?Ao˗2gg6N/vw`6sP";)=Dp{~ a =q,]O M(EܳXe\uywtЮMB[Κ6DsLh$Rͮ ܸIKLg$yL'd5ӟyzT(? E,9KvDx f! O2>:xEJ<)':?6 IDATn[.jڃ*; ;M[@Xe3HvjveI^bŊ=]+WADͼk#f)<KHכ>067 Lcx?o}]t&m2hE <ҎN}UYg$ T/`81#\-5౳͕t_3,-õnZ\B*BCOac$Pц'[g "iR0fy1N?UQDR>bږ|̛ˈ0g\GzGU. M01㴢*UaYKucȰQNl=S%nAO<ؙ‘J^dw^jٓ`-^ 0:6:^˷"SY|S<'Atx-k!1O gIK60y*{1ڿRv*i?ux~G>778G$}oP'n dפU_yky31xH>KD~hiY‡},Cn֝ToTO m6u+o\(^zʴgfKu-ZQ9+pgY`d[$eȔn= FG~R>".go&źhF wZP{jnoݾ BMܯaʿqjVox R]v(8@/ȁF0':"nDI(*8 DrA|; D JZjm&n>kv>p _OMJjD̤GgGˆ64)ն9n Am,ʃ=_d`]\=~ ),82s*)5eVqә>;"IgЅN ZOL˙Y[X#mh+ġs`͞?U u"jj8B3\ΦdcRc|œ>! Ih;qV<̥Ɩ;`EIӆP M+D..>B߭x//mYphuLdZǝ,ʙ\*ʃ%JJ¢f֣cA]܈B; u;0RBtɬ_& o~]z<}|AB+ag.N NeCSkЧ>Ӊ2fhх-%+2L yBPcT;0ۊ4M\|[qetAr5j'kWox/R"|ZҖN&J?Z{X9xJ q®ңrC ^iy]HLߗxgt{NM@EG(qSKR27}wG$~,;SN2eddd @D'n Z1(eb.5A"V}[CQ )9F577"YAd)[,YW9 ^fN=۽DMۣrL:*u+s\cZͷź:Ujyi&c&u2_&SbHX1ߤ4ԇBCVnZ=j[Q?3(}!}83}ʼn"GwXsbjnis[=ypTDAFQWAłTa2vh>4UxvXKrcKʻB ?u*N/8Rrqw˭xiw-j#40V+Y6-Ƣ4(p L3'DA*&B47fn7'V8X:Y7\/x L^s }lW$eȔnm& ~ P׮2T۪3|ukϗn6r){ᏎHDTM[TQ"59 !oY#[VGs n[t'h-Y,k-XxswOk@ PЮGmrv#MY0T㩡Oӷh@Pٴ12ϔdrQ0Uc 3ltv%xS5ttڋ{94<45 B5&< P)JjtĴoIFQ# kθq(BrR:yN BяjjڢoΒpY(DS]MFNcw@(F]/x[ߍqAC3}/A*&dq6bH)4<օ*~Xls +ݑ&|ka)L!dHD2'[nMaN;O3,oāН`f9ˍh .[8X/W;o7gv2~.l'fN$)ƃzk lmȻk٘|Kñ3"ZB ZgN4mޗ,Kai|7q9gUh+1,}ę'8_OWJOP] _R}_'N͘ *l[ޟ8rży(qur'\B]epRO>B k-^bݙ˼EU;5F\a'8kex3bdϽʻQD eȂ7-۳>[{R41=|4\)8YWlL$3y%D/2edddŠ+ĉvȔKTq1] h*֦s#;;] O- M> r<ݪvSmTFFFŌoKFF{(zCFLMdCIn/0gH >Gj9's넌L䞼.#s|r;36 .>,\m&_F22222y =Aނ%##########sO)-Xzj: .r.݀&N2iحdˏR|ƣ̝7D&S kPE b~/Y˺3D;K? C6܎F j֋b&ыoz r^H\֫{}6VE- viLq Zz'3mT u539X1kR?}`Y:߬mtawi8n b5 %$PŜUٷI)Td'c⛂ڏG+u9qik8\ RԳ+.)2hfbP67ˇ/[[Hr*֟g;8b G]0im:-˷!S }{bsrlTs^~7pFp;Mxkhs姼p$2CF1zyFO(#XyL;rc֒FWs=fHqD3ܽƋyfwJ qzF?o*h^nⵏ#5Vo~zgK{=kiꙌnz5K0%&ÿajH"65[O8CH%~^k-J/Sj!D*u7i:}38؃*n#Sۮ >{/ϙ/(CMt60<]խ> *4ch@MYM y罖sѱC5"^qWq_ #U uN=; փ \Yg;bGED[I[~ê^d (қ Q"g8ȶtv_@kIL a=]L1CPߨIh:~o [E/O܁SgT Єi[+ r9GZHPRݤ׈#X*=oz$ ~Z9"830m/\pjGj͜=7c[&6KEp-BW~姐%6 v.YAѡvqxN.9ö"woLHP;ȿ-&z~Ctў}Zܷ܅C~F~}GA^傺6v4=NU`2%ܳ>K8V}_fOgƒɶ_67Skf}8s%5,VCi=7 >?ߧbG$? PE=EOJ$UDоv>ceqoMjۈz7SÅ 6"Al[53ۙEC,|ߺ-oONTx=y 80k5ZON:E(kӛ1{fKH#;cu] Dl3mhFS2W%-####sR(n8I/YvVt[tRPs呒 ZauPtvJ_s|4p>!aƼ}.$K3=q)!S;ȟh ߱wбo?F~om=D͘8.wڻéO+ہQϴv KfdNX P)Th :1ۮ2\b -<8a<]" *LС_& o~]z<}|ܒ2uh̿> u(P~[4WEAPUlk} 7͜vvC$u`nR~K.NO1Dʧ}̞_O@4zië{B\(8L#*'L(j`2_&S( Ptv[&R&7tp<g:q%OB>OX16mj (pPl9d8dY|Iuy4PbwN_M76-ki4S/f1Vuݢzɻ&'Gvpgt`Oi;>&ҬTmKSupScF2![3ߍp ҧy/𵞉N jESX~[GD,|27U`ȺdW{Uz&ggķ>LN`}:q45jtb1rfTU ^=zfѫRPSUS^۲%RH}K(ԮMDZG:.RbiXZp{1ύݎ(QTB]d$zO;s*cWC܈+dg%;pj>;-۳>)+!c5qE cт}x$]b6ߙDOs7lfe@FMYY}dW)pdjv؛;60;˅D]l虓Ѹ܈ b% -UlzEɝZrXRy?πG+Zؓ>z&5<]zA9W@-h 1h9̶yݟ=#ܒ%˒J5gByK%yp *'&값{8Ȗ dƒ-wm D_ٗ,K2c>=O8StȣУJʖiaŊĉSj;dddd#?n'#####s_3cw, 222222222222WFFF.Rx7G{F٭jtͫ&pϳul`-2QF}4V2OlGH)GWQNbz)G}J[vM1?eݙR]XȟFF nG`J C|pMa}2\BwK6_{[h<* V$eF>\~&c&67<*h5b̴Q%oIjgyKŷV O5竒_VFFFe6D=~l~ok/: jї_lϱTխ?w2q2@ڏ|׻\aV+0tP["ToC5F1.٨%oUƏ7aU/#3Ε‘<\ZA9?|{;Ƈm1cCzk8 J*u aa 6eI^}GX1gʹ?h৳RQ/5RyP"Szyf222hnz5K0%&ÿajH"65[O8CH%~^k-J/S!D*u7i:}38؃*n#SC@ |^*(꘸r9s& |/@]#_F`hb,oe;pgʥ̎ Q<17Ercqī3)+.:P5aԎ\)+=- NgE'ǎ:9ajIa=Z݀ZU|*jĎqِYUIU>h`E+΋^ '|x\u2i(M[=aw]* )5-ߦ IDATmH1ngKBw^Pa&ҁhw;bT'.bEZO?-1fP(BIfM%ޝ ;KRi~k@A=xwU]~nM @)zh&(RD^ 6k>QAņ" R$BsvI!pI}ϳusgf̙kĽo Eno? +z?JH֞xksZsD|t+=cBpUY~/h%$$A4mE:Q,ǣk(>Iշ +p('麋t95!*}7/)I}>8_ ؕmQɬo .%Qrqi;yDVeq0ɃfcҠ6PnM >=Ӟ_eu@Հ wjE2{L4J Ȭ81neng^~"³X}Sg@ωxĦZ IHEaG^xe\$UkC'q_v^~]Mq܂NCAQ-J'Z9w.HɌ*G@GXp# < 39U8M/ˬ_eԩCPɆw0eF2pbVLQ)_ߔi ڋ;yxw9#'vlfβ7b,̆.{&^S6`,XYyl,f K9,OM滵/5Ȓ%?ƢyY2 Ⱥ"Dg,fߟ1)ŷ_ ]Oop*^>ѣ XNbǁ44d{doaK4슠p^qDn曮3y@X*ˏ|&Dɼ} ܂?e_9XR%R"ly$J?&U$ @i6_#Avl.A*~c `L@ҫt9-JMQe>QIeT3)T&}dzhϟ#Vx#m&˭ܝn<[A0p$)09̡J0w9[᭎"O4],u3KAIXP{6fS)1|<;z󠨝ASnS.8ӭw#1an y"ǥӃ,@̗_&fjZSS`m(%#KNd*kJfMȄMvo Polѧ {P;RAgY8+IӋ6'T"ҩƌc$w7Wsj!)όJPj﵏-eU5`4L @nk=?Cy l倱.0힤'8ĞI 1׷kK= QV('9[l+\3U3 8kwjL!()Sɸix_a˵`DTh^]VRPQ= cҠUڠ*2v*Uնʊ(a$tѩl2.f,HZ=.V`=Č3&]ꏶ0fT^p16Ŕf%$$%"DZ;G}O5ך_]%4/΋s+WVq.N%W(Pn⛃+r cb\>~h0-lV*"S pw`Qգ_4l:Q7cS(Y3Δ{Kg$ ;9W7a ;gl**=>-i;H8]BtiM=0<6o5T_,gCFm[@H{joχD|쬮prk;: Uw=z%5,vYci8s8֦ :Kn"e",XEA ZacU!9:VQ}&4?=j--<[O%$$,Vp*ܻ2?!]C!ǎ@[<ҕ#(<CH'*S }d]o %M[yaSATOf#2&LW4M0^AnrtX֍jvY`[(v;1I*i~(O6db$$0}6udF'jm6m8{Ȭ6a:d4_}6!7Ԩ(<:쌷 d>:NVZUb>oN';B[vx&ޝ:BOL Cˬhٻ#Սҥ}<H7.,HZ=m捍 myxOY@` d 0:K5Lޅٳig'lJ2a {Qݷi9ckk%]6>m2Z!-YѾR͢N(PP@a篯&ތLӘWV_§aA{HmDT< ;eOi}bV37<3EViæ%xoYxi=gRuO|NnźI<3gK.'Trnzv>0~}bxk!`_9`3YVW>:Q*-s7scڃ 8-lӣS+d2-Wnai'ShujĊ&c[ȉ;r y C썛,!ߨs,x{c;>v}1{>-'5kE0o)ž}sf9*[OL祐92Mqco\*suVU]i]|8^cX *E%wcel bHLZ^gZrNmf3(YlACA<{tfQU9_8iCBBBdk׮]p퐐{½9vw;D߂mkt pK$$$$$(:eSqqmς$2.C4ĿF8WBBBB߉9?Q\Q+O?pF%Xwi A`m:0t4E-_?P=ͮoJ];V#?fYIm=d!OЇa-Ytx GK لfw!lMsIэy^k9qSu79k9mE|/7maKwlʼuX` dbNhdW gY6܇_|:̋ 糧Lde"$McBd#?pqvOt艟mYv_ɯu)Xhgmf}mhq7ZM˞xK^} y+!!og@򁣇糓)?mQīKr'.E\Is< r6q"S }robC kv-,p g}M 1;wJig7YMm}Ob#P VXIh7݉".ػw?Wo-+4R[VpBcѸ7fHԗِưXrWL>ݶǎah:QbNV+اkLj!rScnիwp٬b܅8h zIHHhMn`;sN%<6<е=~^C@_(/'H尧u(uir)O}DxXOg30yQI} 8Aq_MKBhpjMSd8C^n;yL)d:f$.(m1:Eu#s/~Eh\ʉ_Ce8q%L}fp _ 3;ш!X9s */L p3S=ԝzimOr'>?s4^X~ر#KyL>uX+o$[YжtEc.!!e8W4@/FFU?}gsҏ>P I۶/{c@W{7p?ӣFr>ͤ*rr0gl9;Hyh;bkbHwuEE3td&PׇPysKl cu٩%J~fCL$ (xU/]M3 SsgGQCLqJ?ɐ`<H҄L1RM{soEWx#CH\w_Yz(f܈D#ސmqU*(k:vI L9k']M=2R# tLV^`eAkbf Evmq'%t?FzalHj}wu'rGNU!љTvJ8ʑ DgZ٬̤<jk5J*r#*)0KmeF0jJt1_ԝ",=V&rI8Au?H9;oYX]^Jc-8~= f{7rR<ر_7*!qwh R ;`jU|1Tv8RhGO,\ZD&.7 4d֮'gdɾ#_A L PQZ8$;/^hIMiI vr(7+* kB4kyo /Yְ.2+zU<:4WwفCԺPN $Jή| &͇ԫ?rJlg}XYh>"Igkh*P:=kCPpa%ʑP*ɡB-*q Bez@튭 J=gg̊0\}Qjw]exԧ3",jꁽ dꋿ1#)zԑpK)w ui0fb~ވɮ_h0AW*|: m#!a1 ݋AFGFQHUi\*D\c{ 1H±Hp!Ŀ .r+ !}r1&ecK>I-- 8OKLɔkΔ8jXC!ih>!-*)M/ɾԮӜ >Ͱ[c%T[NF/pC.qH(?H2 Tr@WA̍`__Ԁң-6c>'V X5ۍyW^gb;)I.d@HOz6a_=;cDhuE+gGU(}5 gpPvutͬc;QjVG>"7zM"6*vc q巬\L)w]HWfc@,?pia4+f}գ+ip#WJtc%G۝ɽ)bvP[OBu*/P(4|]pSq53T!PSڷoW_}O?\.uB$ Qz>aoЧ5e~ yk E|̊n4wpe4}~>m{Vg,CptfͤV(L۹f'MK3G]7DLۍE/qpvPߗǘԦ˩ŬUJ9iWud,tPb8yоL+30隆Y< ܸpg >=Sn*ӒgPB:.@Dט1Y_*oy8D\՗ k]t@2,TU/ igUE*uj;qr[NF:CBql@rCA~ Sy%PBVuR=g %C{n.]/ϕ:!wڵksm;$$$$${=@@?csՇ+4t;XZk ?h$55'''t͛y㍅R'D⎲x[p ߰ursלwIKtffٓ$$$L鈉a„ lذr/^*mLH&!!!!T)3SVĞ[ח 4)t::g2uTN /QZ'q琖`IHHHHHHH˩]%ɰXb_Z ht/~K_B^ ^cL4"qGh= r7a>MQ $Wi<Z@)V!k{ȏDe!OЇa-Ytx GKq* ;}/Cۛ璢z#טs ~C9l+S,}p0|~1X4f2Iup %j6m-],8z؆3?ub7[swCJf" -Yhdմ칌gnHS;/joͺ[X_cz<= 7廖zU52t^o܃gx (">;Zm:P'v2Y{td *1ӌ -;zN[Aj;#G[oh4 6oފL&QK4 ]t |(Q%f".$9k9vA 8[)>7g1!5ky3xbMl{߭mtq޽wC?ƇP#kΛÖ7Y#\a7YFoߎ܍n|#"M[O\ryӔlm Xz>mLj 4m2ϵiEQJ)Uy' }kɢHi\!GFk;O᝭oxf~Êҝ2nQ[o_khO%vm̍0Y%ϗSq gy܈wQ9Yn|!>>h17A{t"B+ؙs- )q䁮DuBF~=A,=Cy(;MKyz&ăPz=i`ԹU|sµK۔͹oГ|+"̎,) FKrpN8L P;A =Eg&Ŏ 2S:eCpZz{qUX%>aoƁ.CVb4Vo/{ f؅ nbޯ?beLSE|/Ɔh_4vcWhvK6Fh /(ʝ_2\7oŀPM7!K>yMxS*<=d 5Ğ݆x,ah3V/uDz ]G*!SXA\>O~&u96zENӢmƟxIihSM~H*A膏{{&YgC݋5+W~ʔ)38r4"(4W8q{~:w-lt wZ؁I(C.Qઋb/3ye,1_-"܅Ts86m |?> 9QTzJ;pT>1 N [Ĭ"|DEzZFGňXy4&9#S1H+|;|m wU_h:d,Ƶin~dUBk?/<[֬[ !=1/ahl|bN! WR5F2>Yq ז#E.L\@._wɝ AMڙe,;vy}3$Sw{A]Xk=|y !T.vTs~dֺfHK6ue}L\ޥ]ҋLȌ*-T \KAK{<<=`J9` FV.y$.b[|2΢j>td-A2&P +FyL"rHL},?lyyWgen"T%.2gBw #Ws~gRF1's[ϩtw,/8})\djk7/~Î3k 9~f%W8b2L[͇S$ҩ7܁tޔj1 >|۷3k,oر= 9'4= 2[Zuϫm@VfybAtBl1A%] `sk2\wZ`n L#{?wN_x[3baBQN1}e'31^j*3>bu=ё@_B9l-eH=` 'CL"({{ߏ#r(mdfDiӺ;<3/DRhfhִpvF O@I<:^>5}|6E*dm_F{y@6ɉ[S+ɴM.}ě54˞|S%?!&ta<תMT"jNUաp%DIxbPf9yiTYՖ&Go/c4+ p,(Lwe4tv6'JŐꊊ3b2'{ʚCė򡓇JvKa2S\bufM麮Qwjdd4aHdפI?-iD.#l=|h7+sjfJײda kGaߗL[HIxx87o槟~b̙ K e2=sݧĿX~BY϶@V 5YACAk˄L[)kAD.z.mOudlH K`(MCL- +<ȑҡ $U,"U֝`M 5 @G8S{Zd2d86܂sד@i֯|w#ji,BX,rk`*l1'U PnX 88c#+*)Am@@F/`D@P W_Yz(Re:zDQf`(ɅMU|||8vرٳgҚÇtDedHK$Ȱi/u BY\ed gqR i% 5ACfи>?| ';.LT>*R`JO(BmZ7&Ogd׾x={; r?T3 h+6BК RA:Tv S]G31`?o$JӾu3j(Vnɡ;ܬ|Tbc7^ls)R9_k̅r>;pZc*֪0=Jk퉣*RƊۓ թ+růiɏ͛s1N>Cpp3f`ŊZ dРaWgA$$ʽk(nqlN6 rc'ppWaBzEk;oRD&TWTy$&]ӧ#p ghk9A@ibugJ\ND50B4pepס{I Ž1+ dn=ޘ"jcY/?c%ZPX9a߾+I)! K# p֧qSqקp${ҳ+66 O{S8ENh+TݎLACTn= nKmp]4;3b(s>`pP " ˓6exnc.q<~'Y!x17wq#; P,6Jy$V[|M^li$/%',A2;|< l ;Hyx!ʴԗ+)Q00]T'J>g5uǯ29:pCK6N,oӧ2PPqsrk4'V{1}gPNt M?GBfyc'O}KDFFŔ)3X]fϞRdժU6m,Ff@tppO{yٱEB1".ͺLo/E b2M!n0(c۞8ː39Yux3bCvYISpho #M""z3>vke{'ń݃)B2e[Kpْ靧J$ƭ4T}PG+ʙt Tg ^AC @Z^צ*oy8D]cr \\ryLSD|RVeuWwzg\^"MMq<2Ke*~ )S[|m<*!{7g4,kCAvĈcLBjS;]MGP/)&ql O{I؜O2n .F|BLn p*;ѣ؟xykH%SG5գ#> }uь09Zvꓳԭ){l=V٘5k6j &?ӧ_r9Dll$%tLi퀫C:>sCX.]NͻMkYڇ/בG_8-KKdrtqvT5˿䛤{@ANp zeIxgNmnf}Ҙt` >O]3@-Gū|f4#?DٵL_zbvO;}xwf#%w#0J9ڌ6fKKj%$ 4XD| sǹzEs1|tgz͇N`!|\<+CҘxo< S}ǚ](r)OgY`νE2=&7g[T<:/u+~‘Sa8E;͢y(+m Q+2N*XvVIJ95I2%DZwzX2f#.Auit5/8ԅ#(4uV. 4F{kq_06r31,|;V ̂YH~k/)RDBÒT^q$E_$ǡ3*Hƍ^=&\=@1b43an vLa翣|keU@JSo×51IC^4B& Lv1a;_DH 2WJ+ٔ;7H_>y#dԴ l~axsC u#8O|Qǐa-b#=&w9PŮ7FN%~#^4H]:6gQjB(I<Η_lu|2ܙ25 2,\&tͲyy{/9ɹw>7#`zζ@SB;*@N.nHLl5.F`y.@BN1**va ,u~Ѝ 燗gت,&yqLԆ`w5TVPuoUi$d$M˽?f "w'S.XapO}ϴeWt?@ǸӃ+uWsbw;68Π=\Ɇw0eFKK( jm8+/i:nC^ Aʆ(#FxfsD]eңtO 3yvezkeҸfY'_X̳iұ95+99IJ9bz̚cxv^x~_v'0R<;/3d"imifټޗNn\k:! h Pz} kjڼJdֆ~ޤKE|嶠r!ȩʨ!= wjE2{L4Jm&i,*+E#+áu8cH2 Z!'MzzVLMSV^Ϗhl?.e¨Kla_92f5e?ٛwEP$XE_P6Utѝcd9-MN ǺN 0]Ϥ<ʼn= Qxw"/k1c0f=ᔺRly*,}=uK^|.Ur %r?˖GcBP?L;4/ ;d q11aIdƖRϦ22]>47dgcv>Lrnp~_ Xpk _!fW:ৎ#BKGTړrNK0WD_c%<:9i/ʠB%ҸH.y +XÍeLʀF'Ow<=*RX d,vHC#O'eEBBb"ǥӃ,@̗_&fc\PJF2T(uhͶ_&dBƽHk6=( p̳,FEdq [~AԌc1sV+:Wv.S{R>=^;90cߐҏgw%Y qNwAÙxjl;;u6#U0*º;JqB(vH=55:0 IDATQOFs6V8gΫg +q((1wnZJc:ʵ`DTh 5u.VzfdXꏶ0fTgãIHHtav׶ka׺`AE(({ޓ3GLB<.={y_c*÷~W{j?1f "uX Momv=ؗ@uzMᖝTMD"bq`aj,e~ iMHˢ:v4j.Sk}}En0ys'ּ6 dTй'~e֙Jü 7=?_eX**rO.CpMKy5^'2*ʦ-#\~+H?L⽜yxP6Lc+F3#gs4F'ZүKj:qMC7Џ^v$Oˁul;1d%Gx_ 9#BKԵG7O Пr7~5fNώoQVU׳×M؆\ycjf) 5k( WP\ƒ9mg֏g_W[{lٍD7Z̶j= cAzݱ\Ǝw_:=N_Y._Onm)WvV?j#s߭=ߏN [[г{taqnw*VW鍗 (/b1WSؑP;%p rI('v@?J~y-J#1Pt1&kqպ,cbYcH) [-É1Clxd-G_tďK|#̸f\/!il7Be 🄇3?7O(b~?&LL @gRѵ72%4w`C U2&ڐɛ"A6b7> -.MG!+1$4Ɵg#`6fm,oA۶%o_=uhIָXʀcžfv9UO51 ~ѭ/[r~8-N 3?dX,c_ࣈy)U5̺gEV }x=7VlYKpU]{:% 'V==fB9?VJW]w7s<<*u;yMy-Eۅ]Uƾ;Ypgrlj瑧fFU[p^ gR޼&k%f#3̏ovS|dIM}8/3"+F$6WK.1N=>={hc0~,+Vx͈8Zʎ|LHF=p{i*uU;Y#- 1f#ܿ`|vwP^k>fU?u}~¾ipO\+G#M8US5W-`om YK^O|[v[O:^ˆ 4j]_eJ)o`V$48jvd)R~vvl4G֜I )Oɮ~}OP) d1#֮~׷fzo+zwEw&yN&Mo䧉c;O~3[&E̠3[׾LJ\tywZ6Ÿpoϩ҄3.ƙ 3K>';^Y>^/6̱q30e}ªXF K#ly};2m3ܔ[yoU/upGOƉgw:N7?3 Nw9Dƞ&ۆ_58vۤz oN'%;nJG32*F;XY]$ϯ,?\CX<8l]KyYùS`cCSCuo&OAA`͡;H|կOg>7P? 8N ƈ|9K7njA=/ 1wÐ.P=o6kϡ>5ҿXMՑ jc;iq?D_zCU/=AɅ1= '(h^dd >c~zʲ͜| I)#=<~Ɉ̽$|][0DȿϝB+JΉ Ei2R9s-ߵ X)WϾz9oL "'r\* I)iL72K~49 yF> #o*dwBt0R( $w0ot|k,TʛKP?5l;VVY)GɫGYR2gJB2R^\JT8 I}uXQϾD˜ykt\ӌw|&_>A@V/} Mijey-7eu G\fҧEnYGMC{tCqn+څ[IHE\[<޲d\=xC -1FƏy BPQeXKP'&)3,J嘚$ۮcyK r,0>0?S*RX(_{Csvn G0W#2n1ށFTG>v;Rm[4n?̤\]S B9ucR6ovz$6Zr(h*@@2aM[WXYn{%5-%.g [| MzVQ x8YAH"7zUƞ&455.a*!w03d0Wv fyi]fr7&>Cr:p4>GWxeV{{vo9xq^\&#`& ykEl^YېFD=@@~;2ЄNԕec#3G\ƘB֧fO͉wT[{j^DwhSNѠ `f%ёvX-`2w+b 0Ayڰ% 3>Ϝ*<Ӌɼ%1,ʖsx&}2T$9̭{N ]k~^8x?TjIH]cFLNվY\.kğǩ Gp(Y̜|\J f-xNQ7Y=Hfj EUK4JE|P-G -7]zY˪yuv)do lLw-w(npQα# uY w}6{K1]B7e¸t5LTȷPHL1P5_Hg/1m$QY-Gyn[ҏ0k(w6Ir5]ĭ}{#Bz\?ڒ~|:s:nwqʅKZ-$_ -]_H8ҧڲ4v.tA@&’Bҥi&qAɓ;rg- [9fʢ2 RK&m5az%qիۂ;*+_[LѠ Ppn"%?o#jy%y5ĵXw-33aEæCRGW63¥8Ft'O,caf󣘒r ڥ}8(IIJ@Иסm;cn Ơ‡pԳ([}7sCCQkWPQ(3=Oq&G+` :Xu?LoB8.}A,?&͡ށ Zww0#bB&&a0JorPVG(CGL~\\:h-NcI1瓜 \]A$&2O:`eQL{)upۊmSbvj]mRAz zk: 1*] OjvZF7*N,.ZPmԸ1'6gǝX.MoQ{.*Q ғw8tѹɩjyuGR^щ$lߵ4X+ss3oBFŅcGp!PS87coNň#}]?S(!U-f95\1Kɬx֓]3P{4i|ã)jq]Dw%,>J X<MJۓ[R4[NIVe9/JRţp <+|g i=Pk@bҀ?X)ż*Qo9cw'}SyJCY))~mhf*'}BU?xۏz,?.*zg KXXgi֡m[Bq7سu2­P]7ϙ9zDz'_M/<c 1Hތ}m r2q4©dM؂SHN[?O1N q <طRB8I#鯅'"T%M?fp$X9srʴ~I0&dߞ4FN;(Ry!vvB^w ,t؛rQth9vk,d@.1A1>Wx E=G2nt:,l lNa;@AP}o^jʒU \yzJJdv4Ē oKT7ȩ3>s*o_N%L4> Kޞ: f(gRN3L',.i>涗Pߖ$ ~eN!ި@}[y\*W!x*āiӠbgI H”8:hqYL^޶C{¤e,3rU2F_J E%Rtiyl-vBdfbҠGבcyKYpdhX\ͬʤB@_,dod=^~N+Q5s}DRMG--ᖑN(zzXnDG0FyэoxvQ7nfxj%Ӌp;<;igIaoinwy~MBF_ʔqrN*:%M6{q[ # ,~ڸ>c'pثذVW3+[b+6<\-?>SG91 +pLa6>C0P}jo!c$ sw젚޳td֍$&}=/Kʾ/0W Xw7æ̬s{ÓZ[Ƨys#̽=Am |.HZwM%j9AKDz\2&n䛖 h^۶G+{5pgEa]ͨEKaiZv^Lz5K-8(rB׊S #)v'cmҶS5:sZ2TW2#CN5+xcwnx=Kр7y`y+zJJ(sUz߻BѺ}h@لRoO̼e !O` !L1n.֭{!瑄]YG%BtxBoz] y7I B!N Bsrx;z|{D!$B!BL3 Ƙ"޼ m賻?T+<6m_&c\]j!}ugu@cI}WM+ȯV#D{C7jjA1?vbU!BC_Sa4ܡ胬ĻM̷v[gUT.O6uv51Z _Sǣ病TB!B4qbx"M3=DF; Smgn_d9_vi6"px/"S5\U%HYs36>Dy憞Aܷ4 IDAT_337pJ8#\͑;mZƆѡ#gs7nUKC_krEbpY?K窯ÊɬrkpS6ڔV:h[$1R8a("T6l#X֒GCBB!BDc7' R{܇z=u(jC$OJ :RT3i]t2H> n%꣣j ok 2 bqFjHq]=.͑6emVZ[ݦs_Kz69%xx<^kB!d%n$}N D5 86?5ECc`if':67,OҺH ҐYvryT%;d"1091Ga7c>6BzJO3.(jYe5x rSWf36k-RZnN+ʢ UgUӲ Ԓ^~l{k$h =-Y]!Bap;~ufJql%bo a3.P] DA18{T5!f[]gB73q1`D1YR^uW%ZMP{E.pZ YHWȮ%3B!MA*4ê|ga$_%&EyK\u$C\^RC"Zh3Zί:xL/ܤld}Hz;!n-DQZok!B!w HtCT1= N3~KVME0nhTS[QxڃDODE+7:Ky7=[>N-Ya]qYNڲ >[nVU( <ޞe$` ӍLVȥ{E`ZW[ize:4TP*1QvtAͶWږsXP] `]M.]R;+tTMTx\=;kUPʝWn獯cfG?\#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!H"B! @B!>#B!g$B!B B!BD!B3!B!|F!B!b!B! dD!B3H{Cqh4ٿwx<]~"to+?>i;딧)ἕGď0gߧpY}X2 `w~> c?X90ӑGZ• ŸG>ZS봔#?K:w6ƕ~n9a>}m Y~!z@<]}vL 4{(p'_ND3eЄ @5Zڲy!ĭX 8-kȣ5\m%zq6:3;x}ƯC8 txu]|+nu$tSc(>Г\P}i6SACLC|=#]io>^w.ğq3t9Iim)L ͠#\>?et $@[&nn[} EoU3 ,řϚCv2!9`l9`U/a} g@@M^ /o4x -B1iy s,qpJ dµxjYh3jbqI?t1Xkصr+֞t>}sn(Bu.O۟QZ{ay/O62 gܐWgk 1pH{ s` U! V8\w,dÃFe8pfX̞v/Crhl_O8NjƑt7%Q0H ӡ֕ej~W؉G1/-eKQ.Eߌ%l::5kfU=k;WŢEUe9V:i<_P<&.3gZ fOSf)>ܘdžsMCm,`'hq9d$=HN~ ?aÖ T@ucҕp-(2$o#fJ9Mg$ 9F-bC)jR+*9*~tID,ع6DRC7;\'\s_QȁnKww!|uRĕu1|઴R[bt},PGșT[YLa$Y"Huav4 osR0MA21\,sp2ˡzX]feASxlL𳓞IS!x&Lp$Œǹ:-h&d=̝;HNі︡A/cx>a4c4Cۗ}H>I}fvB9V(7~g6,V'nֹϝ^a@ p| BPeW{3_ ^w.}gˣz˚8~y]dq % 6]0|tӖM zAi߹pܒd`ݙ' i##Qݫ?MCX~nړIT'h™2^ԯ %NT7mp(Mn3y*. QNuY5~v-f]+ge% Gr6G?$=<- &3d{J 8\ ]B/콦YڅiY q Zh!XpJ\eTT?b-|w[s8uSY=l26h鼵s~ة}eZ f w=oo&T6O1i_[2Bk0bVg?P{`z2#jfKHןÛiHWa3QLe䴶,F#O87ܝ̊99؁Pݑ]!\fߤ#m;mts_f'ήom50t0n 7s3} j@3$* :``w?jZ]5T/ŵ$f١ 4k=I 334liY.af9iNwVSݴ.+{ UΘ0Ɩu>o*tm&􀽱y0:BQ!B}e;T)C9ɛ%7O9Փ`/*?'d5ޮ xqDEqU ̠AP_"*ʱ"-I{A'2_{JAHg@>U-Dž<)݉DRkͯTjq#PkkE3$ bc;0MtT]T*SzXX~ 7b|@ lG&Vlb}ip7l1MC(7\ҕ|SFck4&3r9( ]˗o,gOFD0b`f8 /<%QF]C{hX?ۦ: ωRf@XQs,)~YẸ&^p|WNnV&I#өBL X0j0Cc-2D2jpp}}yM)yy쇊9Lb!+bmncA}謔p{urjt)Mr lh89؋X&pV|Vtz-7jAGn*l?1o%6(I͸WTaCۨhA4 R_M}9ړתu Czew 1:nFǗ gٟ+2Kn:ǂɽ S6ytݮa!~jNJx>J#1 '3 4v@G[.ҜyfԺ2N$&&LzBJ *y,ѱEXO&,mW=h#uXԵ_`3%8Ǡ- hu :8-}4krO;Jk;w(Kͬ+`0hqs/ޠEqq4Fr1kjjt!~ʴ!|z0sdWTIf'6A(#{~f/~ܰukރ8wЄDHZL 8& DE$rDVq\DֱXDck-S 8 VJpkb&@I6wIH! ٓw{y9GG>r2(ɦ"Yj4p2h!:Bᮧɬ8;`o,N7K`X&tszD<:m;kd?$l6m?1qlOz_#A]2 ucXbSN0|PP 1X@)̺s?]CÐUP&+Ά^) '`qx~Bǻ4oJ߳].f0+ Eww1,6"{c w:<%|v @/̟`kM-}҃}])iJkXOF#]TRF|9Xw$S1֚歹Ty0s]?h-u[SaĔ@bzBB Pm=GcgeF k`ld&Fpz} *qM$zHCKoIDATplfʰiw_3UI~܌exL;eMp{pOg,+ɛEg:i&B\`]Tv˯~ ӾϏgrW`,[+f#[_jnărzZ"O3+N*mVMx\ kq>[sy߮emD* GRKi 0'gjt G.{&TDZ_RӦE0) G(mǯ"9"yEB)2p>ΌdɊdZ'drz=i2~] Kho& 3L{S {gɊdL;*u?=Qci᪺G| iTaOI'RֽCD~ g$?e1d^H/ʟNbq<<5.vl!e ̯ $[`@qj~X"L#UaHUu7+mRQ\C*ҏsY4|4{G_gmH* ƼƣQpzˏfWһxpiU&8eU*/ggj$ZW]룱NBè;XsY JWFAcngin9IQRp⪯`1LƣO G!>̪--քT'ڣɼ}?c ;xvZU_x^vPUv~ɣܤDO~LX5:|I"bbq㼙ך IDATxw|ـw]%cM/'!Z @BJL $mp-Kze?$d<ں)ξ;8K.u._gh\`BP( b[YJtqܸqs=u;gΘ19* BPugq衇5{}{tӝv$zpqU׮]/^~AJeYi]P( bBzZ4 )eMwښQQQAYY ,k6©SvҧP( bcGVR),,ܭ{4=mʔ)H)ӚBP( H@Ӵ+R6mȔJM6Ls:  BP(Fׁϲ,eS(;{?_~0KY#At1=ם"(v)|G[%/iy6U}^_p FC*Tω:>Cb`(}[lVwu+Bλ@ӊw}7^[`ӶlL>MXf ---dee1qD,bѢE=ʕ)bywYSӆ74.מqZ<!t T7)9s%0^Iw~v2r 1HlJZ2ǛE ?åJӢrϳhu5!\Le'Lgly>nKuqgp _R~:׾%yQ~Iǔq*fx-q6\KAN!yoƩIOxo)XFv<2xgxW"ouFj=9R깻:T(:3_:7t{[چa D4&Nƍ)--M:-PJuwe#/~m8+[ SQj ?;Li=O7xd{=`>9׼xՅGܿ`ԆR2gtY%4}_틷m!OOLb|!4i,)iwԛ8cc}Csy?CH)$o'L:7NĔ|~ˆfy0Ncv>#S㌔zkVǎ}!T:O}i:oF|IW^IȤ%P(w9WG8f[ 7{IxG=C3o=b/bC7~&%^tsI747EzVW;no^2/$㏿{[ˀ˂gfU/ԟ:!NLg_XEXFoY |r­fX>+AouFZ=wl;# H*F<Xz `J n'`ng\Ӈa_!V @̬~ lAUđ]f265/7w<"VUƯِ 0Lb + G^9=j*^+ٯP(knz^$cyCF6O:kYb.?.n}/}iuy/u<;\[Lw=<w: Ktށ|^ōwʯxӸ ϛ [vWzv2];j,edagGz=KɰsW(]P㠃|>>>ٗwnqxmۙ2e BLO?e}EԩSzV.AS:r\u~lwZjWE''ä>}măh"U_ a"MH)9`Q 哙xL[2aA|@ev)% #߱EPb_,swNCJ3O !1[9v.K㢯lSSt:667b}GgYFr=wvFB=wUPGїa߮m7:ןt~oia 0 ^z%VZūi}U(xyN:#ɻ {nxh\{_/Nײ/?'^?=νp>~\n/g]3nr)9g)K |_긫4ٜ]j?3kO*֝}zpJ#/x=/عyn[U]#ճb%%|uEB{G&ܱ7ݾ[ڦiSO!dԨQQ]]O<w99+ļyG6BÉaǦi %ebDɹU>6nDJiG#Dut`i$iG7rP<::ׇSH+J '@67>Cz__y[1-Uuj83I75qyW꣗ms5JokcHuey')~fw{܎Mk:[= 2~n|/x~Mmk[*ySqL/qxuAVPf Ot˞Oyw G-?r5 9:0fsZ[FGLM&Gۗpى2fhv}UCZQq }!L`Y:\KjŝϥWә־ˉ`Ԙ);MXT\@Qqɠsߞ䘙cpTv4|L]D|_L&)gN`r3؇Qeu|T=_]1{7FŔ:glSJe/mGcS(+)uUΞ꿧ki[}oEwފK,[X-7{])%zpFO`yPGJɓZSxiR~x,i\?ACÌ6pǭ'i&6Ed-Q;0\h9~{j{z]]k_ٹP:$r˟/KnŰ n.zfr&_qu5˩Nu7O𬹬Xw<}N>]ye%Mt.eي93g~q7$O=PViL`s=/V,$T)?v^퇷Sp{~ʕqduz#,cPeL~#\Ʈ[kӮB6Ti:Svo[VW8v1-jq,sG繷]fG q#ab%^'|I%gY\8r=SbVMȤ5nlp'qpձ8so~|\˝-kճ۵*}o쩌 byC9׀%eT13Ylb揗DZ _:cYȏ =QQ&,QS!ꪍlN%@*s};'^^k*)uhz-*vRt|f=y Hٸai2W%e87,RRC#:{3vfN4rep3g?ʏk9zk(Nߡ(_WTݛDC!kw2B޾Gۙs픜-^/e"G}7ٙ҇K7?;1< Rl 뫫qةβ;}ٴ2n*߿(%ҸO?_UyO2wqrYZom0}Eof_e b{gO s'ܐO%/)..=p &Q N87"/l֝Z}o)i;G7z8:O|a?scG@LL;뭍*~0K˽ Y']͑Sxyu7ʪיyo|Sn_&8Ϝ|;K){Or\ZG.#{Jߔ*Y ^&*{rip<7v62KS sWpymazIL೫d 0<|L:"-uf R|ğN]ƭ/3oD-&>xAʫgk=UsS ݅KϚx1vY\,*VDN\t=Oo/8ңɱDWit%OorҲk7pW10uqrsAdZF28<]rކfr˧sڍK:{j_cpW῟ݣk?(q-l<1>kqO۪&˧sZIc}ng oCUT}7C%ksٙ{emOHk9)%c~| YCR߷S_޻C{t2K{yF"P\@oJL[[h6p06O7 V"0-T@ 0tBD"at]j},loXOFFvym IDATh)vNoL(s/ȏL%qnkXR?dvxRm~ְ_qo-~ơw"/ p8sna5|v:#$ bha_ӈK P⺟Dц5Pßpa /cD7H0!H}}[/ M(,̢o8˲  i6,dᲥ,]vIu˗b &ȨN02ve֮Y)SqB+|h&LHUUee\<_R6s-+ˊt -eI+dx^ /ÁaDфF8`e\͆9a #8.innpf0MiY-ŃA&ڹ74?XBAA5{߆dj]VTSZZBvV6.MУzýC݅/~_|q|xRmM;Z]ƕ}ҶJ*L c.{vaP_WG4g[SYL8 G  vMXx1?9t Q2'‹/t &+~c\.ƍ#`}sBP^QIٸѣGfj6mcs*KhI -&-i!-SBd8t?0hjݎ`4;o=D[i477e8]|a4qA<Olo(ͦ))NrO‚`yVT-fsRYYCj8p2dgzVP( p~{;dvN,\cXW(a}MqOEV: i[RAV\Y{b? 9:d$i[b#77+nl[iYCm64)(/O$N۱,H$n,BhiW^/\u sD0iDjkk8i"N ;jFw߱aWD؋2P[Ww@lss#nN4 D_lh6E6DN@o't8lP@ii)E4E4~?nP愀XYK1߂e:gH?["lwH |[Zkkk8`.EdddrHXA˅cڤr{sBm24þq_LL9Cwcwz-j|oQxwGh }4ѣ[NB{wGGulyI Hb#z7z+oVl6"!|\s5{h&MDmm ee%McS&JKoP5W^34>$@Z:4-F(emqXOcC-vˍimNlv{rvOH)(dgR[]p ˂)(,0 n6rrslbrAӚ Aa|QII%$iY999]>O>,$84 L#a8^NN͆@-Y._@s;pG VZP5b/9@bB L\`ҺZ['#SmR5ð=g4L#þ"SQ^NKk icFq\6m|\}{8].JKKC4 \ʫ̙r//-ܜrsG˜$6쫥ȶ&vbkt4 ˒lm\!4\.'i[KA~~bʤ׋".D;bJG|*@qqLLM7 H$2dn`zTFkFo!SBUM0^$d=&R r}57QQ[B0þl~"'r0m7y !c/u_Q)|{^gk<-VjK-W:, 2 B{~ǷهNK }"&ܨ$ m2q:ܴa1gYYۄ"   8BönwPPXB}] Y8NBv< J񷷑eHJVBtu@qdddHAa!mmmڂ㥱\Om-]ҢP84dn`zV0]$mrRKwW`m&':͊t0M7vPVˌjB3$þF\IJb :IO aY- 8}cYW=G)%~1DGo<`i2{}nddfK(}}?ϽKŘ FMmmMED"Q?zH)'^9H)ȠaSjm1tar mX|$RWWCfVNp8ǛEc.eXGFF&WLLӠ=@ HFFЬNN,s0ݧPM" ̬!s6w#5MYS3=8|㏚ƊmV>x?[VĔ+{[M^B14 gBŦu.:Zd|a|I2]tE"Qn9~֭[隊tp˯@.;_e"  uQ1MS_WO‰dԘ1ա4Z[ظn]+ȧ|̘Y].7+V @"{[4Z[ZcsdaHdUim<3v2d/x\k8氉8Δr,~~'i[=&n5y 04þɖmq$5@vx_&W_{/œ F,{Oۉ; #aU[JXj ̓˲,Lˤ-g~z<;*+'Þ|)hV #^~?{7cP^^Ά )/`͚լ܀h4q㩪bLERJ.\}m-::R.7/]p8(*E[[3u6鸻aDt]tfD:Ngz/䛅xacSh=moU Ezվ qEOZIuPkA/D!/{ c- rP(@]]5pplAR'|,]?9oPrYiD0 WW/Z0 L^ѣG~C%Tr}%^tRJ~!,K2~X6Tmh$ʳ6YmAdggcдXI:4MZ[[՗Zgv{l_^'!&Ff*oF<Ylڴ +n=/..= juidffX_O_۝[Evv6ᰋCɕ]I}Y4[L/]{W͌Μ*߭DVE}g6k_~te <ԟ8뜟mU E"'ϛvd /q/`Iqc'괶5 S{ y_s[$7l؀כر%eYXIkktQ]_ӥI DQZ[[M#/ )%_~zuuu5JUܳ΍M~~/xc&Ə&܌*놎iI("xc˺:em&eLׄFcc#^oFC}Zv1}ٲ(--p(iH)YWPW6;& 4AQaQ'qM{ p2ٸ&GTFoJ>쿜rq<ǿ2Pl ٰoçZLfI+9o]p!W S=;;Zbҥ8\1+E[[^qu}grz8 H6LL*  1i$V^ Yf *+9'c&`ӉSO +Q]gXآ"Zڒm `>@ Qظq#me$?C7m_,+a;#465ffC]Alᖮalv;i9Mڊe+(qdJѹ L%/C7‡_f |S 4 1i"UG\qE<2v!qP(Ni(/YĮKURXPdͫwdidddqIg$Áea۹4 fC7&N2caC6 KZ!v?.rEff&Bn7~l+xt=kv=?6B$ӇKߟШIn41roDӴs6UאE~^^r`0DI|87j@yɴ0 ֶ6u˅b n`eYDQ|67h6laav{|[U+kzp$B8&  x=yPO?{oƏ?˗BuWJIcc#^o+aGC)<b !(,,blX6mbԨQ1@ޔٗ‚bB7''I&tR8>N_Sv8zʢN0Lp8#++ ]p8e䐑aS^vv{ZvwF`YYY#67e0EbP$e1nHK4Mh8\.FncBӌTYC[[ib+(%=I=Ԛr'''yMM[UA#D|A Îi9pӱ}NANB^{T?% qŗ'}*m~ !ciL08_P6m%a'έ4Kox^fߠ$!zH/]L*--|~wdg'R M(,*"/?0I"p88eJc@ĕ1'70]y4M]5׾&Ua_9[o3 hEK)im)Gy$-⣏>ce}fB;b޼y#Ok%ݫ>fB=4;1 u&8/+eJmLi_sR(IUFZP(ҏz BP(v P( B?BP() BP@(OP( bB) BP(;v˖  BP(+n9 BP(CW_} BP(v$:?=\r( BP(xl!ޭXj,33ò{>DCP9t*{LGY͉=7j3v^~{ -},j3mڅ,Z?·~'U&Mࢌ^Dui+z4&6/)Ue V[$i̷qzܑW0#.1W @[pw`I_0N7r5 > ~ mx/AR[_Jr|z`i LTH!/)횴tQK[B,AyZ j->nZ-00ƫ1'GP.SQO6BGJ[-B |d Jzc8N`ZPjFO %S\H 4+CJ[:D-~Q/ҘGd}$jx.~`[\DF8-y;E>c-? ǣqw LS~-Gcrޕ;cO=-^G巳R i"?G v( CIb[o//%{+skܒ'wZ 寧>DJZ$%m89qK B1RHKT{: $Cf I5 .2hx&@c95n+Ը0fXjHPRqc-` tD`J0VSfekeX|g%Kmk 4 [&ag9 k*_$%1nl_aߴHډ@5~- XԸ;[,W· m=}k=vu < &75[> {/ tIG/4- %kܙG:SⱠdlA[%^%B).y>Uf:>#PӸ+(]<A|])Δ5Rn #0B&w%3cF/jqnl&%_F4`[㭐EU &Tw߲.bW.;%'%crKE$5#$^`_`)@baI y})@OI#+L[cN}~ɒ`CpCƞ}IqWPgbUX JKn9>GMpO0^Mt/) "=3SYW$"[?ӑ6 huHʎu~3GӰ$+Òyˀ3y%% k,@@Q]YDuX(crLGQXF;+6D%ˣ V~;C2? yb}D3S_ `qOߣ/%BA!ASB_?݀;%v\ܱ"4 IDAT+[%6IѸXX.dBMo$6H*-@qk(v ~)NgaKx}?;31͖Kh|hIE0S0n-x `ORo:sƹb~ e03z E9z=GbP,xe*|+J$2YS^>#xw2AW}Rf?0Y3w&/f愭M8΋r? <"ϗk@# n%K~%Z=XfTܒP+qhW>vmKJ:rkCźT0B3u|j*bE'nM|&rNvc|XPQJ\FEcסAx X=kШ<" ->3Vd4 5b_f,"a2s _b sUDpi[`SG~϶gjEsQzпmD`UP^^GH>Bxv C nҹs;GoүSҢ9u>]u'7;tZ m6'|*bO;r9sKc<P,k| glHqGZW+A|$wV* ՞`Τ,]ӻ%C9Sٴ+= o u5\v;O; -]Zu΂7\NWAgRSuT},>@SN|jveKx{WFQqz-Sm7LU~,`uAg}mftBhm z T<–_<gǑ 82c:]YgJ/\Seq?D;0v/tjIg &f-~N{AͪAkBC]vո|0WuUx][a%@^^8l4| vg 'W骝x:Ԋ`G^  `/C 0{c֜Jp'vIQSq•˙ tsJ3EZH&f_tzj_8|FfNI՚4¼3Kx 5n$}&o$QnPamtA`̺֬!Ŀl?L{#50$l`k߰7G;9vЛL8䗃h8y:ܝ SO_4ujnz9߲SO`S'1mf7ԢVm_jŝwcn}$kn^qxÝ䆴`{2!یVW lOM ` >A;}?1mޓ`/^> ۫l($NV4*V1emVTV[}TZ+`: s^`Gf.P6~QvՉ2x ǥeG@8q7Bժ)<4rN%$,wQF < ̓SҠuC8f'smԮܿoKYP?AD'|#H/?#@.׮MAj@ k-rbՅiuK:voGU\]׃G֦q.tOc4|niV 3Pu%?# C8ΕKjhּ.Az@IڵhP&UʵkѠvE15BZб?5FQ3EdۊI%kJ`Iڒ筼B >)lY{ԘsGheR9fqZܗ[ҿFH޾yJjv6MT9w~Ws¥'J;Xϲ:XVh sm1=:Φv }&ƞ&\fgX}'ih'&: gi?[Q̟-qSr cUONs6)}h01sTiJ8ݧTX1S?=OO!Dy9u\}>Ncܞ0_ g?v+A͎%r>\uuT;^dᇫ8e{۷5Fv37E5>ܗ:u24\'YNkϸCv&"E)]V^\]o]2Mo ֳv&a~/ܶsGxE_ONa5dp𗏘&:|[X9k6Jν똳46_MӧȨGj67G[nlj/ӮRnoױr|q:\à>|=i\\=kfs<- Mr9+f/>BKGl>T;Rd؝MrNVHQ@Yt_vgCq]]"Cat:_C̀*Q|j:ɔ7KwxalW!S@eխEs:O"jբA xJSv}]FOzQONk\lI'׀i?zE^Lk26!j*3g},jfXkŇyםf/dI!7).uQeкOB!ͭUTs{JĞ~s?ce=Lt4~-GҞf>akٛ^΂/l:UL'wEM2j|oZPT6~s7j[v`Z'^ >Vv,Żk o}P##;'[3=8 hKořiS2D7NmhGxQ}Q +_痐uK^ѯCb f !Oُl|Yp8=H&#Adl gjDҾ}u3|G,?jLc~Ʃ(Zi[w1iBjRؿ~+w6q«P+bcߡfD3Zc֔wr"Z5}7;|eHwiRS5̃6g:=&V\zmn C_1A: ?@ר(}m {3l#UG"kX͞rt,ӗwXsjq^uw#=60*ToR 6jF[V}>Zh-`c 4w4nrIɿ:'xz;f1p3 -:f@C:FgCݓf@ɠ+s9c񡓙؆g߂gV>XHwRG Yo[qOb}߭+bwsEø#ucGg;^[U٤e_i _G΍*y{}}PmBbKk~/uk1uL-ZO=èoԚ=`W&B]ɥ=>N@5z+~GW\,i<51`DS1czeP8'08S|u աq,Kl~spxy_gY_Җϙo1mC>u{=Ɍ?btdW,l,cJ։$XB9cbOdG9=`s,~N_֓Z%UM.R7/a~|&>3!!2k½u?{ǯ1>fC~AҜ*-<L>_:|%0SxqfXScHO$H6֔h & ucW]FRH}*^u0'TjGo|Ky\ql|$i6PziԵ{aSLK ]=Yg)l>wNHMd"7'f.K3K!.Qv}ؼo=݀McqTtj'Ǯ 6NG'F{nwN(uxyb፭=0|B2?OrdޚНC/BCZ|5-/baFLJs=9JN_ˌ=կu8vȶV/V|"/DΜTLlaCN{s9}lK=!.Ux_/nIw0OF7 (-:z'Z\6L ,.(ͻT@Eu9 HgOjn7˚ZBtoFSJRQ=8x#j!KÇ+4w lNϑ?,b{V᧯nҶ3oi>|>ZG),_ǔE$ؓ8Gpk MS|6E1cA -U:xzJ2U㊣3{^^KqcM]4+-gVKP(~$8nl-&ҵAӛYf齩 ^,d\v)e_}H{&kY/ҜlڐMj !tx"AiSFzgwӸ FV#4֫JY>ūG>", 7Kҙ7Ot@`C-ի^}#:n}8e3RדܗK0;6_zO-.Xޫ&[ {Zx+X*kd]J, pst.PLeo 4~ xT̜ժ;qcDGP$(kā`5ExP}kgi+z nҊиW/ōYfz4l1uؗ;:WC}@sƾ<}ϧ _@cPl<9?LҎ/%YXԩM9uZl1,?;9z cTokҩ3x*SCA~#(<;#Ŝ=w%bs1<7d _SQsOd&~M>g6}zZtƔF IDATތ&K<]].r1:^5!Jɳ./¿s#4b{O|uvXG1zÜyycV 0$yj~rGDOÒt36=N2l;Mߚ!fy6q.k-1ʩK|)OKp).L~ Ipm4)qԏV%H9Ͳi[IqpӼP&L5g ߑwgH޼"[Sk㦳8^#$-LqSvαW Ttj*=s:WӶZ Pܘe- -(É\v-ZHQ7 >C?}G|~]>/;'\Vw,[1tu&qW :Sd3 Ӆ uLR\pQQ.H [n2+~Ό|3f;iwi 9Am!%d ?|Ba⊽YB?+A_,Xh{`R'䲯B!\B!(bV'ì+.l k<6spR7Vç_sjq3mDT<,bETn)2%(JëfWF[LF'70L[YYŃ๻avqdWũotE͒+u-ҿUwa<Ϋ]5ί`8L 7p#MAjѨiL[Q|ItA-6-x:Y,3U+VC9'~)*w _8r j ǾsN*6'n{ne<8OaЎ zn2㞌b̽d(c͒bܾ k aT "b拇܊2Rǖ\@1R0׋ϭ(f)nd]Q[7Pg2[JOTH685r> k껓 t5gSGXጮW)Zg{rfkrfH>eE!ѷ-!8Ц$ʖv6t Y5{1J~N.fD> dzq"r{W&̬p^lf@7H/_qA Tdө(g:D,roJ+Ĝy_î[o7vi8b*<>eB2j|oZPT6~s7sZe !U})b'F^Jek@Vva2w~J,fX>3מj'~6#g<بѕ֯xuK^ѯCqm8^MyĞ }@ 2a`>siH (>uxxD_捷α5_0c];cm',ݟ楑j ?y8iū Cѫ.2gܟؓTq>Ƈd&UV`1w|ui*U% ~/twum316-<8Qڐq3;P;~;'ïA^}ndۧqʪy"ԔNn|XX1ZK04SNWx~wqissjq^uw#=60*ToR 6jF[V}>Zhboc 4w4nrIɿc"i mGu=ӽ9l]}ՓOņFצůoW&BޛއݺR!v7gl^4<;R1vxC~UlR2/>6[f1x?ؗ:Mg䵘:!E@٠vlcFO߄edu[!Yܲsj;<9n!r9nOW ?O z23fX:mxv-(`O&ROFwHo3PNw^GA5 gpQ$TAߐ8u{)U_Vi/aͧ+< Nh-{{Cu| mU95\##,;Cf3Y|'Rϙo1mC>u{=Ɍ?btdڭtAtmӉm:nr>Y~ HW^~<JSִ,H=x kj DƚM9ELtɚbW'2k_vr@ZvhB Q?1s^ү&X5Eйa|t'f/IӒ&P9u6"^KTITr곽-9O=jp$tjw'Y nEdCo*//\ Z.ch-_~?I}bq`|kWnbeΟ}xxo PXvbh 7NB 3^,YV#=gOBW.u[s_vhSUOƳEl3 ~0z'G[|A8iAş'[rI=կu8j6{VbX愭P7-8-N6DQ2U'{H;rˋ"ͭxyftfO <-~49U@Aoёx=-azT@Eu9ȳ~ zEf;秨T MG+>]̲}Yz ДOd_ΜTLTlILXZu]zܶ 񑑤jKX* %cv@3FfyZe%fǢc|ܳ|\x ר.Œ*+~ZX:|al6/d_l)r2[?8@Pp ^ZЭ4K}8\.N>TXEtZJY>&2,#$k䎙m5r< } bt~6M%sқv5S?5_\2נS\0;p:O3'jX8-l>-𲒪{Jib lt~z!i7[څFǑGJz,˦1s x*yZMz (XBO&!I,zw3ycU>ūG>bBV%$j%H fPLTmמ uf}tus3o%:6;ҁ*&#^oӬڤc&{%~a8+v- <% Gs##:] ~  , U - zG-P,5?.\?4=7?w_}sU<4gӷ }H;&Nz[{ǒp]ݏ[4{ӫWt>ګ3>6qՔU/W>Kڒ֟^2v-#siPO*Va%^RŕΖN4 a+nJXԩM9uZkMy❜]=OC1V5<)݂СǠF ?Ñb鞻9ҘqM2/'X22Qn9)ʥ#7f7YrqH39:V_,kZ~Hҹr`<&O X/b92Zc$g YO^t͛vx^m_S:scѩS@/jΞA#dMPޛ7غmx8~nfӟ?@QZ:b_5Vz-̛Qǡ>y8}=نgteD]͏G5̪a`$[O~5]ZF}?wQ}s&k+ѧ/R}-E.ep͸[,q[vp/⿉"LV(M^\mY3cw~$jq3l&nX_ ^}^͆iq\"BQnx{WvO!BPO Q4K];oBOO>>zB܈rw];BqS˾B!H3tg9r,^4DgќMՂht/&p\Β/r=!8Klu&Bܔ AfgLf;^N+'K>@!Z|z?$EtU["74}gpē&ÃIOg~o*x8  !7"?JvE*&iS7yTfÙˡqD`2Sg8kqfsSf:-3>Z0\RtL&{*t\Iil_N:!èѠ@zK") lBxxpƤ{e6haԪiDoRMbO'MGc& QpI`:=AR3&=Jj&O&)[Ehk'WeV3cphi<1Ť e7Zv u6v ݾaQ: ^.g.zP#6ipl8ĬX]B!\)zXًXZGE# 3_Ű'՟JDu=1i.|;T9-eCa8ڊ "/JR&;%aZ*kH;)NX%:f-T7YD$ϥ'S:rT|TIlv~OLԯE>'^-é2հ8NlmF8žK(-l4JV~LRd"XXgUĦ;g1I280-;xF Qɀ<0ge{ D"{rKY6iGp_ut!uyg0ӱ2v~Tޡ 'b8rUTj !Lr@\.X<L' #IU?=:8QT0LQeFf O ٓu]Fz>h'(8͟[UNpb$x1c.^{I 1Kq9]UIGƆ֙3_-ep/b'T!Ha` Stqaog}+z|6]@Bqù,SMҫƚhN%jR]nm< p#jRw seaOsഘ39^DiHO ){ o¼X5؉8/F#./ O3>}sW)Bqùl?S}V~aOz,9{`#8\ibljg_YiPsؽ=tz;Jtraa4 #.g`E*Rs+dž?oPNZLS]|ւ+Q8U 5H5džO@۪"YOu}zˠ]vb@fx]7bd'yt>fNsQk!Bx.dD_!F^xіǡ% d!5 fL(XKtZE?U܄\X=XO 4Vzv'y'c%ft~,XK^(WZ{:ͅ#%2 NL 4\v{RGWaWUr`{"GXIJ~+ۇRco4G dG^>OpA] [|&{M'ǩnBI,4ݧr[PʞCBfELfTv@#[Q-C $i=q1u-BtR9Ͽ" !B(݄B! B! B! B! B! B! B! B! B! B! B! B! ! gr: P!$B.Z6oGۑP!$Bԫì_p B7I'8Gnٌ))B'I'8ѣ<t/=;CB v8@>}^{IBH'8ncٻw/O>$<̰B! $Bp8l6vɨQ>?q' H'v~022'23yMI(7)΀rl8NV\IժUPgw9(9B!B˄ӽ{w~KF}Ao0t*Bd䲯*UDdd$>&/o//׫GzV96ҳڝt !F?!y+Wf֭?x$ Fff&g AA^o@ !F?!yl߹O?;3?g`00|*GDPV5\.ΦB  73csNR2d۷H'79 w#/2/ F#Nϻs>fҤI,_ yS!WI?!)3,@AQ{+#N$I8F_ ! ԋEQ/Cӱc"wEOfBBBE?t$B хB! B! B! B! B! B! B! B! B! B! B! B! B! B! B! B! B! B!/(+l9 IDAT'B!⚓?!Brp?~zC!Dv2?g!CBW~s E)ץuԷ݀MؑyvKBMM)E)U]j֋.qYUP,D9%=T{t'-X53KWP~.'ϺmC뽋#:q_~YulWI3| ޖJYOR˿J{Aw'Z :gt%t̫B8>ᒺe_:}fݏ@GV/xj,ɽ7@BB(&@~I|JBh!c[ȖlȖս}w~\ٖ$ˢ<~l=s3}xj"Q@|tf-$*gN:MS?AyFeaݮeܰ뱽̷03zo k-q0skXA[?Geh6L|*ab'!"zF49ϑI 90uz+X7LrM3an3N]bۼV4)t? C`ys1ڵV1y`UbPt]v˔4S0囗z[5sӮΣyzF˘ {1eT&V]`Tl9! ¥_Y`{p!ʚ sH۵BYm}C9% E~oXhyQP<;4O 7`74Ĺ(75p+K k*E'R4,2ݜmYVhCh.`{Neds-Vd޴!$Z Xψuȍ & ۋdZiTHܺ_lÄcxkLʬ`v1i: Fֱ>66jڛUF`TbMo89J~ȝnjlݭ0gx;>(lQ G~Ek `Y]ɣw]>jǟڠΰ1( 0(4Vbu{>p鄞,؇=nwPrQ_Ji5u^+I1x)*D tKG'Pv a\>g4P>=>EÒP:@֓!&nHJξ|1hGb,'h#ܨED ;ip$4,Sc,ޅu 6(;fEC]}Q5_8iLwQg|^b< }.aYVcW.!3ݾR(9"wDo0jJIu7,VR󷓔IU'_^eI7s*vNĊ|eeȧims]L l1W98G̭4Q~G\UdFRUo%u  Gxt6.HE5wᳱ=Z}ĵBTtKwF먘N QrN(f򷙾6>*ZGLkXx'؛3%Ӳ?5BS EQq,# &a 2wo:+3^UYk2񤦲]9=("LU`C4 .! C3zc!#f$ᣄ^$y6#('a^Bo H4f]9O!qk9@~zk ''1YۣGy"d{{aLa?P@8lzp%*$%QxJ$,ڵfw4<)F|&v'/{>,A/h`uǘ0ǥsGbQ`)^.h8Qg1LVbkJzN]L4&ġmgڏrrj48Zylu'v?fxcТL-P؋pѪwvu1}ld,*&< [V0{kbk[^%Sm*fog~!x4Y^2뽔tmQRZTV^nh)YRO`G Z^*(Cjyx?.**}jǯ&ӍkP?h NPScR a -/VkØSff?ٱɮ6גһQ:k=@qn*&h"&k1G.Mu,}wxB4 *1:l]#qRemH-4*=0PPUH1t&" 0RM]t + 0ܪ][E?/l덇'_.H|F lZ+$Y"Z">Qy[܋/ $Φ6Md); SMOŌSdrs$p`k9zqH= 8@`DS[nR)d:<#AiHo;ScZbU< {l Uƚs"3&Zhc BGfPL|&B6Ad'M1k÷OQ1a3Dֿd|~LލkR@t<^lIc bz]gOd$zvNo{zM/ ^\DF]*9hHw :- 'a*+")'㺴 Z׷0*"3lP\uk8j^Z^sАĨiQfMKaimѿR Phd PM͊܅aGU֍6(lfduA:{ߡUh-xHjD~6靐6l_Kag~Tv0aA=g!}Ybr| L{{KA~#SNӥIc:'(Nl#CP[M8sqmg}M69ɓ0aF: vI d v}>l+ȵdD׭gr |C tb2a,\Ϡd4F֤Z3.pS3rؑ&g_ql$LNA۵IxTЬ]G}J][&a0y#@" .Յ(INZ5.\ k1X:V+ . z^EeUasF߁dY:bR? uQMi8iT(T9͞9jU)8P!"S_6Oa` jVq9fPc)S9 )va%([ԟۧE&!Q͊ L/B @Rm$3dŌ=tĐv4Qw(c/tR3%GX:;LT*M !d¢Ul3CfCg{R"(ds2dI VB !hs2@h6&zzY|=e'&ȡKEQqF=׎mr/G#/\- \cP:~?6Xa0tjʦ.A7޾/:1\{#|-4xffJhz.f-9wLdH&3JVR[Ø[en8Ta&[ Y4jI,ɨ81՜ʰ~?ގ/@zZrqt߽Tݺc؀CD(lkr A#HE%_Q8O+WߪO \-\>?Wda݆3z%ǚ9&Og@ X8i?0,LU8J V}HA9Z߆)1Ǎ&a sP3 EќP`^=ș4GU)ԩ5M6BkdfV j[b;1/ HkΞtaEP0=ifN ,oJgvCaW':Ƨ&pn;X>22>tv=ᗰ q_Tq[hYM ;_^.ڂ;k5F\L' m\9T+ذ >R>.8 &~ul_Q2|xy8U8LdkA{g0il pxrCg^TjWKap ζA}HffC|Pص7k+k3CFRi1I")bԕ[*M7_J̎JnW{(bA|;ک+w?c&+ 6_n?t@0i5b5'oW>OjgbMf€ +=B)b~h`jXP\Bu0=Z"H$u(7Bۀzp7 l/;PDXgwГ0'QDj_P( C @="K8zHdWP(-JdYL8 Zj?~y t*)W;l4]L؜fZ(P˚6bKa֝S&ЖH$DPf {w($D"Hz}wH+H$D]B:D"H$!'H$D3C"H$D%pz?5L̕X?<7_9lqd؋^434l]NIOK7_=m2+H$ [-4j>3E@8؉|OȑcOtbBANJWGsq̹s0i+s_cH$D"Vp'TZ@K\Tkkgskq(E"CSyG_qwÜ֝[~6QI ䷏!v*ne}~ab&g8~0>dx@\~|j6zDʎLC6/7290[\Sgg~~;~-Q_,werZUP6_NtS>H$D}'|C1>؋wF=GF~^[Εz!6u!J*m=;u͆a.#t&L\& ۏPue*Tdŧ+ )&pxd6ȌK+YSM8̍LXS'?D"H$i66j@AL&Pׇ[&7Q4l?vY4 0/tNQ4iL,mQH@(Eΐsb?WIϱ־{@[H$Dm}.Rl*FY*$64@44 ,K4Vr۝ -&XqֈZ̘BAM: &K[ڧBB#3e+vMl~?_3S.ڜٓ_ד؜LR+) F%D!LA7F-ZF`F!yH=DUN4(fӣ-ZEd ӄQDkchfD"H$UbYڣ7gP;;[ !މ͒U7fYw{"/\|8ĥy<4=z- z={2g.Ku>|:174Y+\KxՋyL[־v۷h\zK.HD"H$o.ʛo)x?m$"H$D"`>#yD"H$w I$D"|ΟD"H$wI$D"|8 ]wޣl۹#t;w$"^~nQWtVb {r޺+q A\oLdr^l3[*w=6!6:+F1<7_ lnޝT&j\{:/>{!/?3;&F:ԁđc_߮Lngg]rY.bTbEj]͸fk[lh)H$_"gtT}1_峣a_ '{F~.ߝ}'ltyuzl?} lһbr'wS"H΂kXQ8+ FԪZB\yA=w.1i:o>x0W|m8Cs7lŒs3I]RDME=C0uCxJd7cv2%?<v7.daZj٠g{PVbg; k?vаl||Aс/X[gNg 7V{=eWR˄3bT3yaPdkhzo[~WZ$hH$S9fDHpr'q|T C'@˓R+]ۤ ECm]fv'!@QՉ;XQC]|"fպpgpn`u9/ㆻW1ԍAڱFwM=259jf(Ys\b^pjv_Rn{L3 ïʹ?"֑)5!/ӧsYz[_tȃbfF28{~~0i[ (G|nYOo_3ھZ[2?&O,Ɵ-/֐:<)r4`4hyEy`EqC>~J\zL l瞇p~ PR~"PɌweZ0ZE]`JN&eZn{%C];ۇe|Oz~ х ZV?ϖ1`z&}HiM#cc x|vxxW gxGBTۣӝ҆H$ə9eQM~O\s}uhpV+6ӷxlvzo?>njĘ nkf*@>.N4f]ػmDx`Of?ҢPDk<Y A ?;@]n7;cȚ:_p)ډ% }CZ\I%||0}-OWρr-1Ķ6τm @w{¦ ,ȴD=jb_?"$:^ag r5EyZhyEO+Bˆτzk1=U;}59& UQh#.F%? Ѐ_jwR%l_f{͆u.\FԺ7paPi?Grr߬6wVo ʧWarg'#E ۣ;eD"NszS&ꂀUU Ti4P{ގh`x*1#Νb` *FvU2)OՈ1gF/v,٭2|ݍ sTI'ыϒäk6#ci&boSt0*wG&dxzPh#$Z \h1-Ԣ5Z߯^Mn h$?p}y/yX+k[xoG =[Sl)$#mQ[s$4yFeC=@QQ=fCɌ5G$Zfi @`_hu5rȥIU n>S|"Oþ;yVW_D~-KQ0'yx _P: * |ӂl)?Mci]%X_KES[qCǧ8Fa']_! ^XM-M2LtQ1Fq`{i 45bkÎXzzW2)hWAD1gZCKeѣͩ$qOixe%mvvw׳[ROfA X]Lcw;=NԆH$o=KBc6<̾9>nfTS!1u?_,79?/)mt1lY@2T*W:jdg] *&zˢ[;W$pGp̙]Sʠ=q8!*;)]d X”i8Pl̺y8R偝<^Iֆ:JŚ=VΟ]-1;[)`ۃQS5-!Ja&grY4b Zl~q@()߀;l4/DOnS F F:7Q\ԃTa%+F8*mu:I#b04( 63Z=*ޢ䛒rlVEI沛GsE?kdE#}xU'i~a#;YE4u.S"|9 ڽ𙋇{yr/+|?*UeI\ϭʾ]Ǿ DlDm<0Vm㩚&>*,@?/Ǭj$9Ӆ^?s(bׂ+U9gBd{-Lĝ9|8}.{A~ep.j +OQ3GE,$Y5/7j^XF.?t;l9rf=È"?ĭk>|&8JMq0zj9g]D)?Al?#z>]e'yzǷP?ײ,7߶TƇ_p/ dG3Ŕi-<}Z k"}k0U[gJnQZa K{nVDD"9(ox׎  (qY,:6jmNFKɓ?e~ SF.: !,iLf͒j#Ǟ6'Y%仉=on*]gO/7($Fzmebs_B~_ߍy x ieïJ$vb[cP~;bAʿv'u<ͽ_Dڹ?>V:(H$|?x2^HN[oZUk!H$i|}%D"H$ȟb0x+>3%fΌ?e$dž;=#%1-/t~%/%0}kYәQx),Nfyac{vzLΘ1h4ʥextPz2$L8.e=%Qr8 (&N!xw~6.'϶&VANLx;kjh|gә0ﭧlbLI7լ{m+G=FnS*X2{0vV/2 Y#T^vSJk D"HZ4ϙo}nN} #sZRo7RpLlsݹD\Ubr|i754;r8y),z.>o3/xrqr >RlHN<🭣ZHރԇth8m{`Rf\7~%k)kr ^йpLv8ovk]ޘT&h-mdQȂ@\vq); 6(AZlnBol2h!dL@ ʍ`.(T P]zB ?d#pn*jW2fons[.[Ÿ2YbrD"sϯ>7C^Q܌y'UmVm fpVS7WX̷S[1A>X# 5+Syѹ>.avj#3ke$5\~ 7` b ݼ{Oe"z4L;PE9ރ6v49 As2 #?N.բ)1@"ttBb6;:cAPњPhe{wSEaq5=l勢a5 Ky8vnCI gp]ٛ9f2c g -sh$آRQ/y%iAnǛxg cx@ L40SCwIPzljV mTyl%iy"!WSbONZx"Yڮ[HU)n`۞VV/ j8iBBwQ)""XbA򠨨PQQQEz f!gg&w]L3sٙQ^>^#_A:1iQ/U! $'}jdiu+s߼K¹&Ra#9M$ILѴMW3.4^0DܭE8'rԽL>4lSfA=ka:AKBRco]T X ^#5naҾV\*3q1z@h5T, -JftiYڏhwyG50[Otrm2K,oyX@1QOqdMYes(&[!ZCj>ws@uX[xNv~]TꤵFNvF{r}'Y[+@.6u@On0Uoff[/hUXxn~&?;Ĵб [!Vʂ gg?oefu\쎧ǧPr?? tq$woG>*\/B'gݾ.:Ztv sE ZFE?!os?EB!vV/ !B˾ǣqB!˾ޙ{5F"[g첣;]B!-=9[b!,gM9O6N!B!D l>d"{}N-:a12ه ;F3;`+\bWX{$s -nY6)>.QQjӱn4=;cˌƢtހ=*.W[ mFZTEVPUTkB! =~_P&R0HE1_鍁B0AG̱fԮhQ^.h] 4쭫TxWoQL-[&V>}'>>tud96[dX%B!'9)бd @}D+`~bO&>,@|--M~u]$&i;o;8^Jխ}>.M(_q50aRkZG(phO@Iz|~K1ѳw< (HD6!B3{C7 @/wʢyqL۞Nˢ[ !BXB!W.;ނl3V6e*md0'%)ž@\\=%(M_o`k.H/9Mس_~8Dt-&;\w9,NtDhE}`7Zo69:͠ ]&'KB RO9(B!*_˾9v,}&FJV 'f0>٬I*}>brm,y'N=FǹTMl?TfPda6ҳU:E~ d#Zסpa+o"wj%h~5!ZWLZ?FDiL4ʩ_TxF2tEvaS!U))84:PߠҼK.$8lPh*<&( ͨzTãm}^˸,u|Ǟ>z-U vlS*yJCVy"p,[ןmgwr54?;ւtRl4hdEmbGIK#KS+j%P\{jh{]CZR?J@9BQB5( [7qVC|5݌kV|jdw[ld!{^l(^ncbq`M# -s (:Ыm}| Z*#Mޓ͚tt~}{?YF>9'i[p B> (B!*$tS3ŪJLm.r]Z' %I4Ӣ`kښ~@XZza (QpA1B~"H.M'[O'D~\pmC\M^jFMȘŴT-L4mEw27{[I?,[J;>^_=0c/ƢY%kQE2HJ7F~DD~;@D̻+HSK4u(|dr|W5 VQ9BQ!3hXB fW(Vtj2BU! $'}D5=8_'@TA}x{,neqiB Gϫ8ٚӃ@16ywaM"FoΫDץI5$arUTN!BT`ٙA5?oEyqbTë=$ ,ΟMsx,"q) ^.;fDzSm߁}KphӾǂ@n4=;cˌP{\s w(_Ȇkؖ}:pNpG .ַ;ZQP退Jos֐"4_:E~mУw-t*z6 .$ V, VoVQFrÍ-kT|Yh;BQaʱeZFGaB!?HY,@|--#4c/kklYNwshf~,neqiByg'S3U8CMУѤ6{"X=޺qvpKaa!׹$B!D xML۞NˢϘwƚt֌cUG6nsZ!HjL9Nۯ2keS_e*z`Y;38_?eа".Kf۠eLl^;KZ=XF0)?$0l|e1)c_vZTՌAзa$/~xn!/JDco-\֎^ Pcf-f}:?Gb0[~O|ZK&6ŜϳӻŸ捼z{.}mC:W\"P]B ]tK=~#0ټ,jv7'^R9,\t×h{8w<ΗgL!}ʣ7ڗ6b<#_;7_A33>)_bCKfV)K1]ԋ&zt,wM`':ֹ`E5L` h>?M!Jalr0{z$?8XәypnBNS,(ĜsHg̟H-W#~'0fi&=.?ba''/Phybz$wzY4^.{!cg24'Ga}}7~ .Jj65) <2rW: v1rӽ )x#XAֻcƎǍ#0+ ! :ti7It>xc.k}`7HD b 2& q -7xsRP?cgtxW]ƄA#雳q&p4djЛ˓zǽiL cW[K>!D5q+D6ÃgXzSz&PmK>no*y (?HDlUn5@?ȸuL+ 97?šgͽ҇.&Ӵ&i/K6*c&㌬u֎MN]*T=w/GrSf*;,Xkֱ~Z q咞E}]C"ItHdjVr`@ fl9è_8LuѢ?K(ѳu!< Ed#w~:q6+5|E1Ig[JFBl }'%Xi՟ׄQpĚ *~ض85E/98Mmptt+˸W]HcOb6yU@AoёՊ޽yS?`tW7 ޚrEu湗{/'LE:sFoS 7)L}HeӚ7}Lj-?ڏf=wf ֯?#e2>^#<h63ʍ E~SA1`qT58@9Xar=^F"M 8TDgܘ+y,w6g+#P'IB<8\4oը3C.V*XswɮT(7kgUﬤ>*_O?V_YїIv!>NۿyV1OI7.M8azRY==`U@ۉqϐF&dH hQr.vzi%>1ؚr׽ػๅJxscH/6ǚdz%ؗke 6&<=&bǀ;'IFW&M6 H}B_U2S!X mw̾_n>t?KLy%;>3e<`{yeV}b{? }|>]2ؾ6-GLUӵtYp=L~/~ׁF__i|OӛǶ?᩟[Gwgf~z/-bj|9lڞGL63tCZo9Vq=Sy#_'JQZ|r=ޟk23qVx/Ӧڿ9|B!D '?qCn9rqm:S;g+*_<3k'xg=ͳCSi*O;iҦB!q1^<:y Lz|ꏔq8I͋yelq46ޚk Pgp[߄B$ c?؟bs70gB!*\B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F$B!F*/]H^$d>*{ֶ`OR}6:(D664yTv꿳)T[+6Oi4eR*wTXp={<O7׀IZȞ~ī f ]žŅ;]M;d~r h,ߏ=燳oC)M\4oO:.U,ݹ{UO9YZO}vœ4J:,uHnmpJ2ݼTF4FIDN?~<`X=i(rX\1ľ1EQC.MC'״=#7o^m]iƵ8̩Xq'GҜTn.kb:iz2y]ŧZF$axǣi/#_EF?8k?߉;SϱzFCSܘfӑN͢ݧI[z0t{i6|?Eym)IIQGŒ0Ϻ-ՊM=j`ml\ .k1FW]\5CL}Uú`ԩxĞB;~`:dc%/ 's|171읷 BQqbŠzp~; h _wT[|g :m]7/F#DwPʯx}~}hݾf>eyxb~75!'1f'nA3TÔGt'xz/(۟#er;ZYg9Փ{)8zuc[HoZkآ( β/YG|(\/܍樦r, ['18pmgq86z`R a6U6D9{ v?b#Pfs1oUmd"b{<(D|9z-n}7{}ess{ 4)cͯ~`pƭ?tHSk\;>h͇igZ6Ԙ(kRǻr_z05#¯7wjg7 )S՚Z%h'ǗO@i]샶Yg9tx~e,ΈMSAlsa zuuЩvv@$ճ+3mKVB%W IĴ9^`B5xwchu4>/A]:6IUwQ;N" !ןxu0o!3v3?a*ܟPACHr5vQзN[5?|QZ}9$=xQ (庝4*,!S0\(?Ɨf1痓Ǯfw|j 6Bliېcy!nXB͇-;);?P"Z+9;O΀JKA滷x/y;+]Uyt_+2Q)4VL/{eNMu?ˁ(:*|+߁_|cT9OfF̗ b[z˿ PB}ZE`0;Bud0`FopsuԢ#Ƚ_#iti5g u6 Qu~}~PP,z#JB=_4χwO6Kkbp~E׹%#qFo| I ')t b5bhI<*K> ϱ>-Xz E{1^l/>^~?!~ 57H,!S|vlQ;"/Mx`A fq5oC`cWKк\5W5;%_ ~y|њEBYPx/V-< wފӃ?]ν^ލ{ _Ų.uP,11Bj^$u/kmY0[ .=HDtL>/ IT\ U [Jԥ룱G w9Z޼}d=:k=l V#}rDFG9@{ié%5fF+˂"?Kޕק1&BAǧ9wh| ǑM'9Bqn,캳kCV<'X4ʢ/ m]?sե͋k/LZJn|sp;H$Ǚּ:Nh`u7 ſ}mEwT;Zȧ} .Z}85δM]L3,Qe[:m(-YOc}]UV#baŀ.vš6scBAE>dk3fN#Ɖ5b^=Ӿ$ c!N{}%;] W^a p"M,bңi˪x)NDFcצ9KOlMCڳH35k'خ#864>nS*ΊT;5Hl23omqKV;'S@NIjl@,X-{|*7?xzO|vh NSyԚ+yzMlC `خ;ilj @f61Q[iV,~mD.!?#~>B?yK]"1j\%/hN\Eo rڑL-UsYSI y=}_rb gݻI_SN2^ĕ!C$蓖䕤z#p"6;);%cl#xL?MÁ Q+Ye7u"Gv9(98x7,k o'Ci}Q5k {PgT~gM=~|,O6W9+yM,"`qI)·&z2=6 =a][3?&>7#cyr$)B_F E/zMŠ|L%h6JDsjtNLY7:z%ч9Ӥ'Ma( spI6QixI"b@CKP]I;b4ss`RH o#Y3},Fk|t>8Nhcw4,Ga̕pC ')-BIn$OUB?vFUjFI|t?MhƘИGJRyC&?$qը@ O {xʂ gg?U0{v>({)_7Ư&:Sp|8bz~Mxc=n25mEN1P=:oa"hkѫ v,c֬liYOԊrvKL[z/nЯq@8חʬJ`iԏ_Isضr=Q=p VKѥNݫQKsMIVxw/3T<]}탗3U6 Qc҅0q:5~Y+sGt~he$rعf3G׶6D1~4dy.iWLc#DVmtnݞ'6f2<3qz# #eq #y/L7M @^%sɋL$VR;W?1FQygOg(m]hr>I|P'^K2Coj/O4?/uKֻmƎLI[&3q>:{k[RilDiHcp㏏`gX ꠳sѰ> =`"hgR9y.5O]P¬{J,Zm6<k1la*ʨ+hsѰK(7O~yݞZi1/㦟4rU m =i|hFؿgҀ#egqے2*ĘK6a<)rtBm1DKcXB3‡Rg-DCzqq׽MM,fA.=["6fe1'/ٯ{ '˖l%ׯ9/$ЭeaYvjұfnůz9z%{­{KmF14qApX)g&kfbC.~|cLCB&WXAfAfYsE9}).{ݷBAå #XU1$A!p+ECMȫwbIO7 /ę\):IijMQ$My>~P[tdl'2 :&/ pU'pnު?oJX#(&`3mQ>օo`i*&"KeSXTzc+Ƌg9+ =͸}dvpݷBAUO&[Tbs6!B̃_żiqy|ul;Z+3~'n 5"tPTrJGauRu$; ~S+0fT/E^#і:mv" /]U6L}Nulpfs](xk*ՙ^YJ:|\OHo$ҤCE{B{=m-2Fτ+7rߧʅ۷ZBw}IqJ!J[]B˜kteki݇(Bxls$ Zh7'QVOÿ'_Grqd"P,pP$ ]w:3>bA./Ѫ vUNi`E%Rrn Zm Q e f3c7sP^l4՝&#ktgѵM zD Xd%')Vt;M"pHN߾3aIÞZfUp ᨤ?=.'I1aɌqrX:ҽ]?%L+eoIJwۑCy ڴsvPLY7-s(}bra!wm70_ֲմd{-:ģ@׼ ymA Q=p]<YO6֛ÏIc]q5?XHD)~z """" NU16MLIDATŠZ$l&b  \2DTͪjr yy15xe{Is5ԆT$0 ͠vFkl0lb1x'"""ISIuFk)=1*ǟIC~>ceiA%;ZޯEI\d&ksHAxQ%:Rp^D~l 8vĩ*Kl {ͯku$N?3݋7n\;zE ٌc|Zʫ-C 8aZ;umr@7Q6_S!-=wυcF>~6HO!jyM3zHrsܹ aB́ΑJ6Kx{1JY_mafp/VU'5&nUjYm vSW%Y(?0A pd-><$I3ta-#*^ǪNwz0vR嵬Y軨U5A|"x"^Whj{VM|u `ڹ,&pNÀhS/ug00f7Cl $ " ]Ph?vMR2Ͼ.Xj?LG.SG_s :RZo\E3_zM%Ȅ`)צJlk8pq -ˮm7Ө=Dj3z|l`Z:+ жICI{:\_ $x/k |/7 OWwMi8|pbno{ǟ|: R־uz5.dOISئMG)sM):;."RQUvHjy5sfRTwV{#۟ͫ$3</xK~J/UhnyTG~1syJ+n]v_3_`Q-M_xbp޼iD^k}r|n\:yCHigQe1-o}Ȁ[V3MA њG2CO)3I->#?eo]GQsx ̵_f*cYDg_'6#qSp%zT 8W߇7giD8r)ٶcg:XMS\;:£Gxͳ:fo)(ͯMopA=rݩճ7LYfy1ƈsWapzv15=|Sg/KƑl6"wȕ3{*?hU3g qS~.HMI'|j~~ޅtDکf%UH]dIaCBå3bΜ?. s&\}Q,o|B s CFs7l %O<ԛIg:*%խJ&xdj {?t/~A@N]9a ko))cFgW 4!ִEhwE17su`}CG$$pƈ1c6RWx& (T.$\d]EQUS2b\ίM:0lN#ͯyAcliZ ZWV$==?A'E >.mU`yئ8:"ݰ [;ʶ1}W)}~Je?~xe&x5X&m; 'ǧ_"LiK:8.;"L)+8rɉY3C\wIDCXk;aa /^V +:gG_yӸx泍ڛdzlO/n/jYFXU_TEc{+KU yGM`/9!V c7.`ʹy<8֭`}l'Wڥu|\eT2wY՟Ϣ.8FwwO nouHTY[E4wY-񐃘{L#t zؖit*p`Ca/ZFϚMch:,9p]\{̦U4+I'v0D_,vi3^ֽ ]azzɄn iM=m(.p,NgH*ҵx*]xGO.û{ w(ۦM]=<(q VI˯>ʱGqk@66~uœOՉGS ?sȨײ$}{M2dgR^4:lz4NN wHh8-ߌ-~  BxPY4lZD9c7ŬQvui>:8NӠ1Kw*+д.hGq\l'f{}obLlۿOvz|^ ,R(/?Cǐ(~x-R儺 /f, _ 3k}զࠓƈyjVZjt e!<ӵcpRBҪ>Czc 5pMb}[~kXR1M)+Ikvn:IMˉڞ0g ̩muvS{±Ԛ_81 \Nl X54I}`azۦ 3+AN)n3qqbA0LmNȳ˛`RAY ]oLcʇYd-A3ߪ$#}5ά'[Q+h3_?ϓpɯ~8{51ME=.;]^z%xORiPxLqV5EW.zyj5m^#(W"BKͳINfQ \SQGl#f ZkpӨ0? 6Yw|mbFxpg+lhu0M(He 6O5uҮ8 Niw䏊p~/\|kXؤXiRO?hи;7tݾ?~NݾV04lPϽWe6fc>6WM'xcFd1G; wU ce2 |n,oSk>4?"M4y.$1Q/ڪ3RG] PȤGu3(ۊɤWnlusW583gY^Qmx5ছ~da̘1Þ|u򼻸[si|;~=J+.y|ZɝxK$:i7ٙ lk]y]۴ClK-n{BtKvph= :!?7'.Q^6;*m|j?Y|ojy)7y_l,67cly;}2]ϵy[Ov,L^ 5Cud]92hoQ[߿}UN268Ūj੼-Wt\7R]YZ4)UtȹPA<#CE,2k)ތ L\=79&щZZ&,U]TVg+MSBf/]{>9Qb0ư1-[A56lN9lirʫ,]uX>[wm=ًWo}V|-ˠ) d3,*bWgֱŻ桮l庨Ӫ.6[J=w,LK jlm{\oUdWN<\s{kԼۖXڍ8KNl k@6c/cyWVŬimUu`a'WO5ER`nC/){K-[ݏW@xvj:S"lXdunplh,9Z&gѶt}gU 5>#B$XFm `VϑT_"_pR1)6}gf^vUV&}7EVy1tYV[`x/Ki#OH7/@k]Ͷ-klHV:FA4tƹkalm)<=6疚8ugx"'>){ox4x.q;P g=F' \YX$gv;Rr=sƟ†dG;Bm26yx&}K,wz* BD13ŵ"g;XGbC,rTv6)AhN^ri5 \y\>.쪾Mg #ת.?P4u/ [B2atq2 {hsBwv;l쿝|VչCV>5ߦ5yA?<4j78hY u`S{+m:9)DWqW۞hd8wW ǦT)"WKM_w(fcmy,TV&$+kݝn[hjͤB'A٥M~h=l6 Pϰn[#>X̏ނaefo9 [=܅J`b cJ@BO& L}B J;f4fuJ|ogq9Qg`O5T İ3čSFNCa(\!0le^F#H6?؋# O)GY#CĜ:P_rGᱏr*mhy#!Y]R&ѯV@ #>@c_wfcD;qQF0 0R9V CB3j@7r_6m^ >$3N믿G_ĢMbY }tVXxg# 1}f:Fȁy~W~'FGDyHꔘ5>qL"—$9ikx捝: ܂ %dXxrջ-ќ5aU{Rgݨ*3+ @L`d"Яa ;HyD- 'aұXXHiI*VKbtE!ILdSxp=btu#;e!%.]G{VFfr#bʶ$M MdIRqB0"4dg[ 60f­|c&P@ѸeN:tDXʰf0:`BD,>Qg^xp{=Rk" :;AM0p50bŊԈ kt{BE7<(WC&,wM1c<<yٸ6>j~t۟e3KSvw*9;:=&>sW<0Vλ**KK\•4KN1ڻIp%y4ҔfR;Hq m< !bLh  f$#7s.KEC¥nvâ'G뤌Lb7<Q ;^ n0V#ǀjdȳaW2(Xq}(㦲PSltNU*ßzB<>耬\-@mVF MRԊY3xf[:s$$0`iF !ԁ<( ӥh..jk vļ21:vIFPQjz &q$jH NqxZ.:HHzYB2#nFˉF0!YDo}D ȱ+O3JK%ew)1t_#Yhҙ:[ȑةsOIb}AE͜0Or5@pw"3@=Ԙ }xG 6X|)Cv$뙫7jF;BP|S/f2WfJ`FMK4af.X ",/sBIT|d IDATxwdu{6}VUfy{@Q"@ER!J;349F~fwI wEQ(iHQH6F{_]Vz\^fU _ܸq\u\u@8zYn븎븎7rѧ&)=̱x*%!R"D4MG455T\ FK{㻢(,<ByS]R*:.꒴i7 !4)Bz~ Lβo:iUEaP%ݯ)%/l2E5X!%a.RJTU ItMEA]1t UQPUUU<TUEf?Ri_ϪuTŏ˯VkĢ|7+Fo6 cw Y Q)RD'/AiA4urW\WI?~MזGPAR6/t2@!D n;hJF\ᓁl IP? jY^U4E_FrRVL 6}b.m P$" s*pHM65gԛMBu١?SU0 6EJ0`pZs !u)]4Ha`;㸠k*蚎8'<vlƶm O\yx<QGm 膎+$ a8BQ@ Z 0t?B!<ϣV*BzJ,% j BJ*5byO2 TUq!k*'- GMU EЩױl2 TEb9.Nd*<*) _XQT 4UAT+AfTuq=Ay~i H]z:PWyAN@?{4 #}QCT͟ / IJf5&e#%kIͲl\E o% K2DJp<חZuᅩ>REx XC\®0MDzY6m7'wUQA񗧖p/$4_]m]UX](*aODU0 'A_4&l8iX+! !c211NVmOݲm:n ;Ľt_ L<̎j !)Hs<IJS7f6c#zoc0҉ H吋 At'RRoi &a\S5LD|E 8K0RVgqn6 LOf[$DUp=pȤ!q.,&;TP ȯ*i*?͐aI\FڶK30tkai,UUAkMF )RiY& 4t+Һ?fJ,nK4ھt}ЮFܚ )6td'5MCQ5(a6PȗV],JARDXd#- VRkQ CyJeH&TU!`xh'NέތawyxB044DkkKDbgbet)yHW*x4`9/ )5㓕S,"J Ž%lضE\"UCDnoFJGgSv-tvvYxy^^flcr1z )mTM 6|(0)Y5m_CLM%jF"_x6jyj.XK_[uE!2QT_7nS$YP5]R G^u[lJ,is]GPt][5E;sw8u ?qϵ _䳟Uef쥭- #)}kX\iX@<GubIPVIM\V(W*dyl!iI$囝k \7[q`5 n]i&Sse{9sEmGl 1LZF4_k2),ΩT5b@9hـ$-T#s(_*j5l!  /]>^/2u'rxOz)p V; seV2Y\Vr{{aߡص ACe6@SXhLp۶)J73q];v022B>bx'hXHu Z M²i?Pש6J\HV}qW纁SCU8)qf\( +3]m]"[4t]iBu.f̀h] M]X:_+|gEQ/>t3g.ҞnG:Ur2,F$N,|d$PY?ϕ "<0suiDYUa.EIR&L[0;=xͺmH&o6^l J(MBϦ0 _54O8du$@S dDLUp͠j>:ѐ,_5A%v+/Nr4,Oy%2YpxY/% \QZ˗zi{}I1ټ0 ~C5}|!ZIvNgg6'&Iwtyn8pb-D |G>Ag)::EHƢRJ$d) Hbnu=,",u_BJlhZ[)ae\(~0l&=FA*mOY0 y}p:y;vD:ڕxW|1Ƹ=W[H$1x$e;qڒ ]R]5MR[mT1P\pBhVދ ؼyضM$K :jH$.okmZlA4^5Ox H@S|o=7 v8?4JK|s,zQ0-뙟'qQUNS'D"3;;Úۘ@2ey z';k$ \:OނDb>+`زq'&8vy6k0/ uNY֬DgrR}Qnf:zLΗضygNg`=UOs xE3?5F"en>Ǧ-H.QÇyQ{:ɔ*WFFa󶝴%s |Go ;8.U4$,]W  NtM $a,RJ\qrF+~P#%0|J!Mat6CezErB@v>褒*WFuedmb <9fk.}} c;.*q{8bl!a&{"] (ӌ?qW۶299bvRc-=~jQ(HuVV$K<8:aq5g>EkKMk ]8d&[,**{9?trΝur}M1{ԩ39x.Nq3Ui G5M縂H$W24 U07?yKՔhz yj_җ~}lݲuMu@Jsy馥%JU\Sү/?\c@?-w!)T6՚ b:NPJ3`[vP4ڪR(,_K233V{`*I4-jEg:MOge5˦%zPMjEt!߷sR=.8{+IYA4vl߱DjƮXKKkbrV fbV\R5f:LeHA7tTUqҶ<:Zʢ['L& K% R\>]5H̠5fj-kW?yqKl'b ;bxd0t $ԯOZܵ6⹂BHYPkXN$![R %(JX&S)!;L2% ]Z4òèZC >t$rt{?9gre|ӿda۶Kھ JB 8~1f|w!i4U]D޳,0=LL^f~~)]gd$ǚ5_ 6nՉ+T*3OP֩Vyn IDAT'2uJ!Vyꡗ H* k:dl.Kwwҩ\H?NMs 'w#*.aWIĢ"Q˜u(Wt3]|īש?NH9ʛ78p`j9ſTUKKX)5+1:mϾx üD8jo&8U/wx+av&nJ2\EUzV_,"޵6m݄X\{gؖߑH$Ÿ)!Sru!2~罾q'3pQ9{<96lc*''|XlL"}ߕ@pr8l͖^~~><_i*7o]1i27m3gN׿lo - %,%X :h7\ϕ( fIضUr(Nb84Z}Iꋮ;,.BT /[~5n`:dOFth4F"qUud lY/}z'3 %Y/>WO50qiSa۴C0FgVh $R 164JKKX$-qʰt{7ȅ ^Pۻcϟǿ&T6o+W*~4K6,_Ea_]V:[JI6'̳ 8ӷcnذ;Xv<>yk׬4LdΕ~zBAWW=%\iYV}E b2F~ъCmtߴ(AJIR]qRR0vd ,PxKk낚`4EaӚ>уd22>0<4L^'{^v( Bk+[UH8L"VF<ǩiI& $RT,rBk|$)x)>񩟣:{Pz>y;15_`rw7ƣ7~ޮ4ݧiSFjrcw_ۯe-ٔf5qsL|v^W6 Bsd?"u]g}/:-ZW'Ԫz| 4hAۭ6-V2yx! $ݻE3V,qbk֬vDߊRnK:ml̹ D֕L TkWW76֭1Ux2ņ5}w;ؼF 邭[72=58v!}ǏNn4; ?/%z'^!ߏj:/VB-w/~~R6hH8L4#ՊiZ[iD׬ՃwS,W"az:@J_K2alAgry/cUJh(AfFun>0?7یW1LLt*I\O|/Y.#//tco!DJ//VVkky̋_Bղ^[/5k/7r8}ss9NO۪) Je5LWY⛟$R(T,.-)Zlng{{93()/=\.©:tm[7166x3͛7r¥ΪPLLN!d.8eǿ*UJ 7վVpo|뾅~T\븎3R4yB$pBTTh$8K WB©N'pB^j9@akuXVp7nj?`Y;Ǖfs|ufqNrc#M[K:qTMѣl{lN>#nR z6$,=Q‹I͊;2ds.n䩇R.:se:ԁmy̍ELV8eD*d ~sB)ٶ9JyN[8W#BXS/昜Vo%ڳr+H͠UՐH,!Ql.=cO?Cu| Q X"ǫՐ\!Ғwn. 7E)34|E]x؅;m\Ǎ7NK?{7gN\f=$oR؉tTܐP*ڍIޤz PIeEtouIzBt0w7im x~x׫#Tom{RlێB=a*tѿ&Ad괘 W {[l}?q;鐂cՈB,E]vM nݻbד؎Ck2AJurORKKlX$bCo*x}|+MY_D8SZ$QD+|lq/j`U,fdfLWg~gKsLMֈBdkhDU%UKf&! Ps5܊E?hEUP`f M,a`-tM'NvB-%ho7x6OG{iOghݘ7#41DJ6_&N3UYSs9Z[BSQL:<}f7wnY%i%^|@8RfgMO.榡5U XD%%]((:ژ]k.9{مg&}`.mpoKkb~H5"W_^^>]<W?!.^tZ{yvZ}5ī}(Ҵ[;mubS)owfDD=B2ЕfCrX0?c~fЙ͔(mtMe}_;p˷Oۏk#6w㭸VTs'ɶ$>LWG+[w`mWNi}?{ww$ة|s0(;hM'Zº[ع]obϦZ6pd:7ғ 1>YsiڊωDq=K5yGtB'!qp\ yu{]Os9>s%bz.Oҿrflo.B:m?I8"Bn~绸ƽؖW>1ٸq#?TO9C{:>0gΐ?o(q$MfJ澨w?-NnjCGsexw-~_|ɩ2u !?1=1\L|OBrн77߈y+?An9r `箝ܵm۷E650'ύK\2sÜ8JBrӏY|/3@#H3Dub@aJ\u?};8U72PTqFN_cu}ݻoTaZOaӆuJYdX&{ʥ {v }W E чyϽwC? ;_'n'92mDvvo!GI\>wER<\vԘͲ!2Scۢә߿k#Q_[nF[h~Ϗ113; H&'ǨT,茍btF9SkE2 rO;JOGѱ*x葧ƃ=AZ?x-d/~\>yd=QlΞnLq]A7'us0 ?}}ۗ䭐/ ҶT\jeɉ9 CUM '{gӭ=y]dPPP]|Yr>_๡c<}T=N*Bnf)\#LSg3d7߰'y0Ό3=W LĉŢlܱR$?.a槳 lS'Iwo` aAw|YPf_'Ks:t G^i{:9uk֮V-R{gp$3(;t3"[f9'q]$hOh>T a.W!ڒ1d))U]R$Sy`|rE5x HBѡk}LOyh4JwWbG9ھ^GǨjhxt'P i]=l2whmsy;Ғd~nm{eEy~R$Ė[0M (2CLL15>`24Ml $xeW|oףg^x|1" 6톍fu#eGF(p.U~b5_?'ݿl.˅ Ͽ(4kHhI^튵kV{}CCW}(ʊǼ~_{X]7,h4ʱxL}6:h˗< Vʥ/^60\m@\l6!-^̙/v^*dk{PUe䩩iFmhzW(ڽn3_k}{ANf{DLJhS,O7HL$2TV2G܃t*$64Wwa};ٿwLMJo +ptCs%FHC! hc1':,˲p~qNz(iz3c9/DӜˡ[WC Q IgSw;x^to︕Y<`,DM]i /h諎"S|A M]lgrzXl6O4T(Jger4t2stTςm۷279=Go'Z%!R2RbϏ[WELXbDulX`îݘ/IjMr)N|ny\$ k.b*L[fOLQSSM>opK2íiHMMi#gȢa Xf 'Hҕ9rpWKlffv1JtvPϫ_s7.U" YJ--=uyaiLP@% #óDnJ()]{̺ƊH60 E)M{glL!W˰ɥK&U17,+DҟӇ"Dkuf~zy eff&1œGM,$w19dtEI6xaKF@bj4ݭNn( d)dMGK,$ yQ[`:@,XK/< +v!#e˞E;MZiJvas}hmGo:W_|_.,dj҈ܼgxHcDB$Lh.llD[[8q"[ׯSPl%ne^];uZ%RHSs "'FAPkhJ-ܺu<4}}L>y鑰iF2e&Fش\dc?#wΊzXIԎ$TCyH$?Fvwr|/o|n+DADDQѽw4.2 #/-X1?W"PIKx nLɰxbˌ-\ȓNʐ˼rML.1aرOL3T/.&h߲( a6Bt&Cg)Uk.bR+cTDjˤȤZM59JlSg-$py՞M{!҉'9榧5!U`mG yHU-fpp7X2XmPL㏹HD=,^d||,gΞh@""q _8æę fS'3x\T y '(s K7s.2e!SFnn"W|X?uOv %Xlٵwb&Bl?u$ 4f`d5MYvR/*$T^6lݱڰy%wמ=\.|}Xms3'en4'')*\gM:Y_*Oh~(ܮo Hٳ7J%0|C5]uMwcrY={\>ҋM[ $8ryStuobqf?Ʒ $nϪ3?7b;J(_{7ǟzhv& $zQkz!ZQb~aWw2oëo};HWt;mer!CGTjDw.R$q ץa:p@}3o9|Oɷo鷑]F$ D&<6=];h@Pz{ڷ~!o}3|ߐ5w~wԇu%oo;H&"\@)5#0;W8l$DAdrzSRYxsڵYep|hH矿Ej|Y9}~6qw~ H-L Y8o3Ǿ(KU[[cT8}}i.37KMLR(Ky t^&& 7#wwsjm C9T=Uϑ(Ѫ SI{iN\m' P*;yy+eUhˢ_]'FZ7$B*AKH x7^Ta7lk$(BUmwt p(}o| "Z[^܀vSfudd ]7g-n4q|?:^R$ + N5B%,BUm(Lm,D6&Z(--e$eǩgZ/$p Xhy-ݳ;4Zƿ}|6 L#IipۑC45S.cYKyvCeE6g]|ߥy*׋2j"{=M|~udtmo埋^R!OegU4?rDb?{7/o/uMshN>v#I ѺFb躎e蚆hF 7/Zek0MʆL-ظI0I_U&5x|3-?s ]<|12@kS-}rd/}q>_糟G==g8uʡ}o*ɱGbm-$yN6B-:%b!_Yeq`ii~$Iuqyt\.(eb$ޜahsyTY6,sYAdzG$xzڻ?~~{a~ U*E4]=b=2n ~3tV/$s;7w^3+EX:EMM-۶n-V`$b0"2BQ>$U Hxήp-==i(ȲT执3>1Q.?:B`ll:EQՎ{0F9so<'{P*ql#SVGHhkKv:~" B޿m322m5U/0$-mR   'o޻"I" 5Ci02:(5>|>{=|g9HMU/e-4} oߊdە:HCoo>7T?\*fqyX=稩JGI.%yݜ}~f?!_HpF2 P_DV7mD>ajbXuSq4/;Vsg\[4 VT:lؾ /Q._Ȧ޵a3zٰq.^k1kFVu:ښt Ǻ"ĂCs:7tZc^0)C[[  P4lZ֊zb!/1np\ Hb:I)JD[PBn%P4V-B>gO_viKT7L n&ˤWm/#Q*Tk' Է[]/"E>BX԰VT N͠ 1 hB|L[`B~u_ׯ;BhhH4iضMP 2!F'nNyTXjP,`N.ǫk|lTZu1PM!>/r=NQD/] 3؅ao{I". {կ}:X):pꋲ{[NG$`rEr9&#ie\D&u5޼!_Iϩ3i"B/Rb87<~m N9tzϟ;΁s'ryt˄ΎvN÷011Y ,t`&Ft9qvq?`zz9y)o$cEll"^,12T]³µ.zҴnt.Mצyk2x:G-m>P1Ig-T/G.di$ݛf~6O2U\|nR2,LB4݅Gd ]Acu=Tɹ9d KsvO]$oc?xꚝU\:W*T9~3hL8).RG*>Vg=liihba~db?$SsD"aNrE8{ Ogb&K#<}4xTo^bͭ%C芀$].uoJhٽ] %Conug>B^gΝEصsC6b<[qGE D $Әlno[ک* Z&ZvmC;vHW⬲‘( "ո5ɉq e 7H"cH 0 a68?b2<\d+l jĢU|.G'ܫa4361Kc] i")F&/`i I۴.Q.x2.BmQt`D".(-$JeT@RDtHE|l5_Y.-<wPECvH@J"nty$t$?$J\Tmu[,!3GF^qE$v<հY͔oJ(`s1+xn&ş}sPr}'=$_9޵1fns5GoKK/nhgNzc^/׼yۛqL5Q+Lj:֢) 뙭Dk0I~칪&b7e>?A!k"{ff<"o CO&=@R.7,+/Ol+6[T#k{IedF6Ar$Ҫ!ˠyd 2_!=S Gr8Kf/ٹ㗋tpk x$vxSŃ$ " <$уT"梦EhӸɃ+$b",E.ܿ@NU]nj7, ^(ҼKj,G Sl+$jU^Jpp-1z,E}Q2c9FN:E~dM$gJ41{!C.(a&MkIiRض}˴"Qy&xxo lR3kUwOU=cpz&FH,Ya1ePSg%̤If$8sQ$O{[ #3X\6(,R0MTIbGE2eQVye-+kDlƲ2lDY6h$ bYWh!F"ic +F^.et]9,P΃DpIt,r1ןs<;$ I`Y eeumFB,F$q&*.dc6 se-<.PZk^r/ gpFy9i*\EϙyF(F5 +e`p0}ッn>HL_Xx|njjcqN:r^ܺUUII! "M;n7hP2;;=5 YvƸ%DžwK'بsi Ni3OT&P/ie)fC1a"RժcPY%p>Z@W혨AEX.rF*ٔVEe'l6F,Qqd̒q=)y 6.б sY6ir ؖ#Ipd,æ+,l{UۆQ16fəs)kaPumYe8mi. IDAT!P^PՉ HX TZ_fkh:>EtUSPD&vmi~_innd6`ᄑ{"g?1n} ~G+oz.ՑP>~4LZii*H D<{|_?.WT1f>:1˛ڱYڤ))0~:CI#~ى,5Uj>2Du&Ngk,c$^fi-x|YakbMf.hb ;}7F DluC 4/8yJMkTHʘ&Y' ur#ј<$%=ǿG/W(g /irxud% I>DeSm9BM. EM:#OQr\A$gT5i?h }f>Hp1x,^cbu{x trM#ՕX1l,{zsZ\ ]DTܪ¶ m<*z>{tΞH=c]aq]. /C6Yhn\ṷsqʝa6Kf`t|quWDZ -T!,b(U?RɌU:^W@BR.]2Q<2_ΔQ<.7`_ d*sedD]BR db=$^bB*p+)$ , ܬ(P*kT ip( d vIMoLAq(JG)Xeb"֮?#,TD$QDV),(fL$YQdPP_bq$֤ElQDH( #o1$JLMM19Gg6nۉ:m9TQh0amPS_ϥKԵ!_JOQ w7<ؓ>  axx99z/rÌ344RkZ 5ٵ3(&_؏eEXJ[b\(,<~N|mh#(FIa0LzilFurj !dmFIU1K.alFN\BBY8\p$f9F4aTRbJ2<\P]"IL$LVd J̥,Ŕ̷JGC~rY(d7T8v"(i; F 2&Ed-W-6fPurv֘7փ/ LӤՀiF (HTiR1t6ym[yg x$'9~$ܾ~XXy,ۏމٶE7o} ׳4gpiNJ?sed $.F A5e>e|=bh# viˁ̂"+JTn,e2;t7~&"XsGTDa\rDWQ/R.9Z]X2Y)}ӫrENJh; \J˔Cr%r%!55+&3prѦ3W(-`MV^ܪ)LɊw6*d}y˕ ܶ L(ڔQXWȉ:(m1 o~[JE~|s\"ҷåۄBێ0xCG = nU| lLS}USJ%b+=<475~mFyw3Z {m⭿n>?~y/"mm/<X+>TQ2M^\ۛx.~P}(u ?h75;{uծi޸޷VQō 59t6,kרvm6:kwyQ]Hv5'mj7ޜ %ŌyY_a;кiݬ{">3d\QYMeX]]u=T"n "54Uǭ\^'$}E1,v2"٢ͶD,:>-M+H3*FM8wy#cN%8q"Lo~[OMI~Wbd|mx:c3I޵M0["U t- .Q q#`3S(QUхJ%Xso&.?NE2,: #onte8)ZYr\'K]ˈKh~tU⋘@apE`9QBm` W9 MUx߻^M\W_{3'f'ҿ8>gOP |{h]LLp\9>,.r^}4~oȗ=#Q( |~EdxdxVLǃ.AL6[l8an O&aQX\nǞYPF2p i3ѓ"9kYnDW]5 }9qǫ |%+R0eѿL) ec:ofaqh>bڤ4($5ї}B>ZB ױdllN7v-]N]s|Y$_xO+I3깖P wZZbmȗ$";c!ů=V&t]CƦn5a"]]=w󭄂7/ ]mռ iHC~6W&j4a)etM z6UUqgC5w6QUC!mT_s&7j~xmrp"-W){*qnu`M.DQeA|_ӵ+Ӵˋ .Ҽ? M8*i@^{|\l3P9Nfu[=x2ݯ DP4+Q[I\:Vr?Ț!XXؔADeYEEEJ"tE4Qe ˲Hesx|n|>?X6U.Mmje\Lyuk$"*?8e fQ 5DdHQ J7jTD % oHŜEj970a%AL06j/c,Y`mQ}(6 Cri'[*[2_i!DA"J" *M(" {|R%Q%cXP(QS4,F9&Q6@NuJ%ܬ#*k]:^Z[83N>xIR+ $!EϡUV(hHM|lDrd-(ҺZE9P6!SrHkȕLM|^Ycjk4r~xU)e- DZuj)*v(XX%}6D7)eﭒB@N ee10zc/#ceU!-7r(CY:kSC#ހ㢮)'W؉L"-2ȕT]\J,qn1搟lmdQg1ۢ X_B!^$J(>|]bLԀYsY}QbC{IrO򶫫׃H (RGJ'B+~qn/ծ{ˠy$ ۞i7My_~TO4hz"j+󫯾|5"F$͍٘K)JIgH!:04WS(ׂ?NضP(4fKJ7P5˹VbMޣg]`iŊω롪$oa%**x⹵•B-1tjc 񛱅6c/-њ(xsq}qf|*Kil&>]۞_m׽m.٣/f T*ZXBY2M>Ӊ$+XQoc6^%YwSBl\6!R.IHXTyʕ ~w>̛0xx B4LD@E~7LW^<.,,a8M栏H46w8Ѹl?>eoO6/V(pji$c$WXH@JYE-Q+d e -4۶)jp_ޱnFD|.iQQJ*\sLm̿ߠ&6r^*#.Ӓ0ML*R@ qqСCy,//o7ò,& eDĶZ\ DU C[kXYRP Al0aWRm}=;xhCז.vnoS|Qq;ymia7o+/^g~N, bZpO"m+ Biu5ъ`^'(tOa@;ln>μ]˔$v#I"UlQ)f0M PS:u7~k+UAlareض(d3LF|)w]`{#6J ks|''tjL o'sYVV%֖uOsMי)E| ,ٷ z6Mv-)/ M'6&".(XؚN#^aoUTćvm򱸚OQPUsoo# Ưo,6zR~_~|| q.|x6 |N Bzq +I׽iu@(W_+4[BD'/ϩXiҠ I#JշiU C7(V5t(-٬Ē,Y;)V0:iDer^y{ƔA (BUnPMU5`G'(vSk7̖,A-wQR:l}P հe4h~i E1,7Ne}T~ ̍8vˁm@G{R^0jh aՠA۶ C7>?ircGOVzeQQeӾT_//<,UUgy%- O%)˸E(lDr{$9p08|x|~ZU&Ö~dYryimov!ZB>v$A CS| n.7QA,mik4ьpp8jiiC1c?mvG"~` P膉h\x583Iffc5 CܴU*im+֮oĘ0MIpX6%Ӭ&8t6H+NO*YAm47Ghjm3ĵzgj|{a\D!d9fggXZYTHgK-.p4 P,R3j2^&/i2i;X^1D:ؖ,|}{Q$h[d Cmۈ°-f刺}tnW>r' ɛ;5t:$;vt2̕y,%^atnVJlv+?6lo&M˼ʫoxeûW\eG^`q-sBM{嚟coƫ|jϙx沒9smcJs;unW_yec?319E"Y kOM8A"Nʼn=,7@QddY$m'\Y˿p lʐQ[i k +eKBSMZC -YZ*# 6,i=AUlJEknbN(h%VLCswg^Gq`YhkDIQ+N^5pdtDr)tX^(a[6jm IDEM,d̶-TE_ϳcwׯٶZhz(Nb,r0X6᠛|GFR$zrR}0n\N;rzՄNr:ɧ>;V5xU2:q-DmM$c"!B9UfC]2v?rUǩh"i2w۫^|Lҹ*[w0ĩghPe9^B~[`rD|?feHb.O8`lUq4H&Tm FWac TQ'ɯi^^[`tU%Rj[*i)Yƴ-|Kp8%$IV1-Z"GL2&ĖJd2tJlBg0X?@lfI2SH&]~\TW&X͌__*bV FIe*&2#m:F{PUC>IFV0+2OR.WxC\~ ȢC(:ebW[Mi JeHL&_@7 VtE C;Xеt,Լ#Tԉʴu.7UK:E>'fJl-y@lTApC3]f Q:bnt#K $(tE->ut:Kb Tdy9O̹ȗ*l]#8%ϜUt0(H(H/JfI$+B뼭KwO7Odۖe1r,S.OFv)K\ HgGY Ih2Y/pɔMLAw` eaerY$z1 b.G[]XP̥H2XLOLpm: XY^Vu'"i@RHP]XG[[ ziLޘ^?Fفi *ӧxwK#aU?"XEì_%)ԏAntBYҹL7Ibpi:iqYWW량dD*"G<^*Chsl\TYaXE%ڵzGIx=d*jUŴLVD"✸px}親 pMfE[d*_` _-KQ\.m/ٷg߆L |e,V9|[ˇC??g׮]oOD"PBqk0Nxc?{u K7L $´m2" b*I#.uv:#!{7^wػ [leE_q1/͑w#xƽi˟{fGo&/b|x"Paɲ!+uށ~ ܬ5=w>}=ȳ?}>0 - 0CKqL-ߕ I<=K|GQsq/qca ^z{z+i::ڈaed `nl᏾c YZg2G!>wNlϽ=i,<)I}1\$Slavng YJ sqKpeR$h;O'L~u`[Lghk K =xneLY,G~D-eß<3[ٶ}ۺD| \|~_o̱WgpO_|.:ƥi@O;^Zb,,-sλZrbqjt=#[9{XZc;%(8:Q+q9CE$/qeNpur[su|O l.{5@$Kq~i.\taQY2S7Hr|6p:PdCSpez?c& 7fX\!2E!ԅiUى㥣t|UmHb^7Ќh#CCDG%jfں:E!9q4vFO!;Ȳ(tm04M*S֙-ȲƧ&XZ\apK?^7VJYR,msm-Q-| 4$1v zM[SO_Y2u]8_A!~mQVVl몝wzvށK8뿓UEe0[h3+ܞ~8ydYFq(dbLLLc3$s(n_\:Q9:mae(( <?K/Q(ٱg;=ih榕 }πXt:H ]x|#%>6'E2e^}įnO8Ro/S\~kkaٶs4Pksq&S|>so >%|N^FQJbǸv4;^kбe q҉$M?Ehbun}ٽs+7Vy.~9aDIO>}}ȱY>K _\RYgnɖTˍaYn*:3+SFhkmӲ=Юy 27gH`ĒI:qY^Z@j8<>FFv6q%lIU4|ޮV֟!1w-jD&" ,b|-ضMZ`Q[c@M ˅ceySh'U [vi-(,jG4M~ß`:KO۶0 ,q#vCA (yg1 x Iz|>['VT.]0<nȱ ?T3CDDq(Tz۟: P ]NU*?{%L]( `Y6e6P ~)XMT"/ϔ - *D]`<^b^A(M3E(6p6MU%C7TԵ667uYUڰf ?tM斛n r>_.7c"W %!m0jHaȲi_n 4]WEm[ Gi+hic6i,WA9,+iZȊiZ,uc:H;ۇ1{*\ A?7skʻ"Kn|NRDIU^,Bg-#P,ӦT idk A?m#i2NͰUXvM&j:|R© 9tqq;3rA^htcoit: KYnDxf-nn%Cy pl'ܷ! +=D"{]x}7sӲ,Z5dxfP}U ]>KqnƔ)"_6p .:ZyP)f8r^VV3u'8pNI$g.Jnj3ϥHjF/~qΝ?O`Q*FپJ^I9̱r#¡BwlgGArxWȋO?)&&'ItZ|jͣ>ȖNÍk[,]#bb)+dL]Bv}}9uT-MݻE/uxP((;wfnn[4$E&ʓ4s:vs[JUE~K\xS Tx^VLzyq*k)i˱׎]\|<3\>IȞyQ+01@oo7WFk~ݴ-x`J"6i{HM2|aӾAׅbxxPxIS|˅ELAEbӴ:CE_oIKI:-Z71=/FuZ[ BP5LOmp(5r-mŚP[KS(4jw& )" Ԙ^q=<ضuC>+AwǫôAq?O=r80 cmhEG{PKp98(*sm۰ *j8 L\.e#KJ8mkx=nNݽ}DZ:;DZ۰m Yc6.']v bI j$j\efilK)"jx (A^_CfDAs8ToƵ9 ϕ:Uzz{I,,ȗ@Y)Rb:ժFO͑V"AER Ė:ۚ%X[$ CQ-OI% IDATݬt--H^0%UO[Zn304@ F/Oprtul]6M- ٵs{bYrMvw?LE즐1j~-Hnm3AEVjqxN^' P0;>Եi_u oMP(BwG?KjFMP\.VcqGsy>+x>24.^exXA4(lkAŴhiur8r.j`,lOWFTГa)zaS/3T.]p pXlX"E8kl+cA.=NRu{ו+uqu"NV3s?}(P5L ˱%g-/s ~WuM}WfEEٹN!Ul[f"J<]` c3\XNo7x [{w?הDMߖ^>!=׿B"O.W\.w:˓DݶkA(,J+Wj%rE/Q,-*Zftljrm}k5A35Y^bK)nd29]¶m.6V-3k7J67w 5M2Q7:VKf~a9}We%2q'b =74kb ֦I"Q-.׾ҙv#,sElfav̆Ks5kZY95l,HNaiEafrfr8yGi*E;O&_ڴrHL/V8=!gWHDc?ȷ2_ڴgò&fb S4~r3\y(fe457o=~l j[G1<"۹8zwlg~j޾%3en ~#_x?t?;i[عT+F3'R(ٽg/Y[yn ȲHi9utr#arE|Lss8Οx-'V, nwxYF{p:W ]{{f >""Jt͝5H?O>zw] &oF rߡUҩ4͑H-Aј]8Q9qu-\H)BOg 7ƹ ?xSo{Ǹ61 #w1Jx~2S :?j>p>DKtNb!ٱs'7&b4 y?cXNk pyb6~-!m]]̯ 1*Yfc>|٩INs[9],ܸF ؽS 64n==H:SE$Ia A\Skzc6*flR$^ѱXNZNb "l37z{5[ ,AT/EEIS3{GЌƔh vp..ͬ0?=M0b EQ(@ AU$Y05e(ueLٵYy'ab! kkm4a=sE>ėض رibD)f3 D:A$};9uDs0ș%vD[ yIC)gAANW ^!'H3 ]lmA-g:Gd2W?E4%!XXG:T-Uo\oAJU[vm1tŕ$j}zc`[ TUYhƶlTͻW2TqP­-41ssl҆&EYTՈ6<{zrfmmLOp"2'O#I ghJPT)k:ڍ~n'c#36`O._%J.򚠨Vʨ2`q}bD<#2InlXS4 9GkNݤR.3zmzø/M6J[ZЪ%F/# 277F>XmVEfn{R6LVYgAM.0ߴPUfnm"S񴻝T2ŪڍÙs,S9uڻIj1kXeBny~7$ݚֱ+L;_]wW󁚚3V'/ E^?^37hɮ.ԏ͵c:}O,~*Om')lʼn\^!#a^ܲD_G>宽;yh ۟!MMa "jE@i^h\5dj"Q jZՉ%b,so\شW_HfٹcS?<_!&b { ~~lۦOanzӉ뢜09JE cXfnd߉G;Mv,u:JKSp8GJ$ 7~~aYe&S^}b:'yL|^ZZjmbΝTUev`%ݐ|AO'phkkNN/$gb|}IƯ_GA nfa/?&1;CTFr$0(M>'.K\vG!;؎Et]#3S 2عGX rAO=8*3Г+tj^Y%` X c*J V|JQ X\})z V355r{n;AOjp;o2*W-1/o`jgs29jgV_wC\8{~ir)O2O<$i2=6}8Q]W7WU;gp$SLv5ܱ~); >=}}^w_mrBz Wgsm{ "L^~-Z5Vd %# :΅W^$Y^fp]l:=b{~\x(n(f/+c@'Y,o+y"ST0*-zai$.2^ qyqabt2 Efq#m|gc r$Z$z@oO (TK5zs)&[.::$arb QI%[ņu+|߹a0>$2]OM( %|i_V/`v!OL{mlۦZZHI@_ŎK[z+toݍXJ_oMeKV-8ylwLlY8n\8Ipμ|;21v#cQ'mzѪ"k cZhLeb"pe$l3dPA8w7]D$Qdp$xVZsk !}sdĒEtg/l+gY[˓f|jV$ ̟e;QӳZqJ2nJIdv3:cjǕ p`2Kc,6Zp8nbxC9UUA~Gulc4&ͶJI,u=2]Xf{#Ev& Axv|BJ7YLPN/QĢ+@?K 3vM6GғKLF x,/^"h-U64$Hh$ɫ+( l5L{3&l#lXr7m0}=DA#G^Fqt,뎍=r#&6Hs>|QIhCQDNL_$Jظxg :C,n'ضKHȦcmV 4&ˌM-01}k kz!O.rHӤ3ij6Zup+?ojX7ct5̶l2(v, WٿoIDQ O~ gN2sO>UV ض#47E:@$ghd; ]Y eP!\i?sh*˅ +h)lBm#4&.\fOuVMGp\r\ms#\=KoyByv Ɉ14#Pt" jhCft'?G:{z)ט^ûxŋ?Ah,DAW-PH#NUd%_fnim. {{ v I:Ͼx fh6/yeڬ \ňD#:xd2H]cfnAL7VT!STd4k%uBS4o0FqmC~;fl5[-,fjr\^`z~}1~cEE;dr]\-ҿw+S&_\P1imf k*b,D"fb@&\s;wRk^/^dj*D t^]wrub1+w\BIL\cP`#CƫLVET^^ K< F j5ݹ$F ѮS(6طg Q7۶H}+,-n&l"˥dffX!RI'M< o\bC`}~%F'ʷNQ,qYg(:ߝe6:\?Ãtu嘟cn̕^*7Wq0k)w~]7<=䧈 E)U+XC$@6Tiື[!ª][f-WH&4¡j攐N>בfES }˧#}=<G2vf/Ж!/Wyrmܶw; 6ŗ8m}G77m;n=yA@tDQ}2,c9.F +E6e?Y[-] "\ePa}AyFƯ\$&{rON NMʿjl%Mr<]LuO=4zX/IJ]ږI8dh6pNCou nLwt?7K9{3/~{} }Y7~`tMǴL4U4-t]G% 2Dޮ `1lGH·<dž7}$S33ca{0-!z2\}L 븈xC^zyI$ø.e0SS3뙚OncM~Efgݙ!~)J IDATiҲlbJu%q](!" =p Y)p}e$S/ CkkBk׮:VOO7Fg9zW''^ඃNExq6CoO/n_A@ZD#0"KyDQdkmo?J$G^ӗ0zJL:a/ ZΜP,.r I^yݱsǝY!J2\`)_ ZhT]CO6G@ *-FYEzdYuo2Y|$śnee ElӲ=v tMn]vC`cu2, '8ud;Ocfb};Gz]eumuch4(y DCe$ &ld!x,ћ{>=YʕMU# $ U\'"E^XehU hxY-RIB *cex. c-f'=R, Ad˺t 2 i Aml " +9.!M'߬ߓq\AA-:DF֕V6>i"224dY´mC!! ɐڇYY#I2aOhn )O/ "#|:, HHޢ;E|FBZҔu΍5HţDB:ETVfB"l>zIETM-;z2@&#a ,R(UIEѣ"KySaz$IdMwo/`[XN@"z:*-\ZU[_/[׫L/#" Ah ("n4]djm8HIBEW1>t(n' 𽀙y\jBO:i*n#tdҝQ)Ux'z2xtg(>bP8/&bZ&cK?r Ihl|$T-@OF6O_DVի=ʻUI`~ȂDWĠ+RlCE6!(6{7YR[ .΁06ċc_Y R,7e|~v.z<öp*(k2$X$M˧pxӝK}JevtRV"翃H iږH<lןwm33KA@6C[O00CwWwqH*8_wطFR=FRģa@wX2kmO^&nl'ɤLΐNDvmkd#}T+ha\bAFz{PL0==GZpn̜2~Oq^tRIRA;cy.eIo:JowK׷5mQ|u,,X/p8;yױM.p8l~Ļ?Ish7I0e J2}]Pf(^F:EVdvc6ڕ O>wKW˥2cHɮ=;H5{yÇ/^~j VSu$ErmWqc/c"OO}d/i9tX BQ>V *+Xt6F]U$ŵےawXXОݼxi۲16cxF CN(!:E QG 2(!UDEmUSYXc3p-|pR5eA'GѤ\QX,TjMh\6"jMCaۢj86B5BUl$Jd*x:~(ilĵmdUE& m_?=GnP\\6T 0ͼ#9~ Us/g^`T%|Q@D@@Sd0Ͽ|nCTX0)k/ %sGDL$bp#:[{vI&DNO3#K3Zc\G%!$IH; A:+FC[-U,NGEtܣO )YtPEui01C)c uEQHH[7+XOʹɄE"WE DIfq یwQo;l&NՅ V/|?]|O mP*GeTEEdlFD\#Qr*4*+OcH/]<] ɝQ?'>'?^ 't;{oG,8ql0.o&J[]Ǿ ?HĎ@94oF),:.~>~3\" unwػ:N2fPM왫8A@-JlDP42Z Q"shVN/َ$BVw,zfS鎝ɱ\YS \}EΌ/lR'$)8~t?K+yfׅ Hh"K-X&C=DqְMPEհ-z(QM6m6 M f0#c!Kh*=RN˲ql<>P\'OƒUlϣT(  ZFEV04H$D_p%DA`q~EHg;K_o*o;q)IETIWaaa3j\]fKkE U'`l?O-婗.؀j 0(uMe yg~'MjTC%%s1 CAؕ:/hJ*hqt]' Z&e,Zz(JFjBVEQ5TM#`XmT:XYF\k[s8Xʼnwǎ/0JX]twgI#VKEqصkZjtzKuzY]OI$.c|'ۻ^x\/[턣azm]H~a;OHR>aMٲJSoQ$[yXZz+/R;#β{PD> "*úH' -$Q5>Q"tzEibZY@HWq]vt٬s$ E 85=9A("iHrjk{t sL_:ף36_c'"v=eӤ2 ~S1œ/ga̱cIskL(1^i.;vq}A,OL'm[,o/o5|NN_sSdhлcRMo&bgZ@Us|2F`/w?N2:176] i*bЉ `gmUo&ECN|AF%;(Ac3Lʑ٪ssuX[fbjY dUGeeuqۆI&m E%yiٿw]#\}\Gwñ+'e4mCؿes⅗RP_XxjoLh$ x<;r{gl֩7H]<]8ř<'VBqIDAT]Zɯ1.iiK6"Ne kRf4 `e5$zۣΑ̕,f+ :bL$1tsYZ\ dp~?҅qHȊZL6EWWZjvhͶmCHB@PZ# YX"102HowB(p=E,d@`͑x&XZE4$)$c1R}g;*w;Wd %EiY& >N3gID5r1XTevE&-%a(R>}?cmET$fua,_c.uӤRoQkѫӼ{.UkJ 'e~qŹ5[hsK/AnKxRƥWS\+w_~7p 7Vm% CmQk6YPsDBmۨLtho`KsH&efG<+ Y" n[؍[M%^,>{'_Y)VdL=>_]qV*bc6J**>cw39.5 _3ӀJw;S~e=#;,k|(j uCq%@FYeYӵOaNN7d{BPxNip>\g [@.Rj٦&!5"!+L"dYHIVz0BuRJEiG"Xd+W+]WJEǓnhr@Be0Z Ό,X*qRjQJy[ȧIENDB`mu-0.9.12/www/mu4e-splitview.png000066400000000000000000010256301250406103100164530ustar00rootroot00000000000000PNG  IHDRgsRGBbKGD pHYs u N{MtIME /yxtEXtCommentCreated with GIMPW IDATxymYUk}}ޢ^"c8$AxƘQ0'0jFA $6B)( [_w֜vswZ3=>Y +VXbŊ+VXbŊ+VXbŊ+VXF+VXbŊ+VXb1LW* XbŊ+VXbŊ=`@0[XbŊ+VXb(|*L3c?}8vbŊ+VXbŊ+6de)m[XbŊ+VXb+_x_z׻"H{D!%Ɠ /߽"@ C~_z~ﲟ=ݳS~n"=tu0+?'C ΋x@T]?^R?G]+q5d=lRk;\{o7d|\;x̌KO~ \r8_-9t:kظMo:Jםk:fұΓMkCvn~H`(~kܳա 疎pJ)҈^{ckCs<~|8O{Zc`ǫʿT\uH# =޴ǦLlwըԙ3csi 5&݃5o=+:3;؍'vw ~dCV8wJ ~DxJjOVrcݟ1rw]qh-P_"UӠi,Vָ^)[H> K{Uc`4`浾=ZU{k8ߑ~G0S^5׿M=c&{Xo[IU-ϘCmPlAM )ޚӀ!y94 zl;qmѦ4t\8 fpI&A68VXŒ"ΡX[$ ZP7 N3{ZQGiyІvN `fuSZ&ui=d2(?^k\Ox-%"0m8&Eÿ;7T7*%3}#MglϚNJ"8Q T:B  ߑ5/+!@nv=cAбM~_wZ%* 9uк@͓ k8t%q`Elp}ُLl_wb-QwH9гrچF )w=NwZ~5, uغ-,N}QBnjZsWwmȂrIbCV:~uW 0T9qwAV:w~ݾ&(qó"J N vAPwSFT;wzkla &I*qCd\/u *ְkkE2B&] SƳ_ %z04>T) Rϝ B5XkD\i48umֺR1UuyGVFFwթ'Cy~}R癆й_Ê.&{"ޣMB}TZӦ5!81NtYT ˙?i  "@Z5M o*Yp=wvL%ҝy d[h71]Ӻk[2Y&|$[1H,fyx &`u :LDfnbnDG"c ,v>c7CXN2~Ϗ o|N~~\z~aQ,"rU)`8n;_w=f}qpb,8C@$X?xtoc aoOf֭!!P&þ``Ȥe,S~㊡Y굆8d@YI=竵& Z@=g3:LLwb:("V M× lO !{5cl,ҿ2!;;5xYeLP9h@E)"͔mZU66!uzs.[mH-l$lU7a0Pk7s]:6[7/΄3JmY12B1 X$cw!RstCs1#$ҽ)i:;]'9iFPh#"HX&8 ;ں5!X(К怬;IYl`䌁R-\([W}pݽ- :Y lEJ63~T0ai(аr'u^I<9 \GFߵ ,gC>q@8Ev2LK5{eM/G/`!~6y1d,(]KuYZnDž'PgItN[&NyT I-Rp|8ij쓟XVX!41-*PWnqw踋cxfWӮU6F6:68Rs7~vW\+l͜iw:G!N[)pj= xJI{zҏR:pQnQ?T54mZ0ŽZtTV5`XK[08 <0yb8O t!Qa1ثmkƘ?hQ ή$v6(c" Iʇ<2ԧ9)uCK?8')Tha;I+0s =7+X TJz R;@4q0VPiR 4%3Կ1Gk%+ D:9p-u< Z%/9s(irBYͲJ֘l}锊 2(Fi s"!ϲ)FkԆh4 RC2%2W)KW.c^+Hw ZkPG"G#YME,Aty u{.rK&y5(%O{B̡aSA` c^fu.koeL[4(E]ZxZj B DE1b6.kmtPkeIAe92Cu b̘kfݍe8 2׌(77/ BI O/\R̆%z ;!ϦqLwSA^e%eS9> #KbxwY5ݺ!py(XXK4[4M*`c;bƴbkݠDE V 8!3U *7!j:kDqvXU 1Xa/GA !imU ;SZ?R0XF aVU!zB̓]Yz|eE, :3xBY &.25tq;Rk9u(Y电벸\Djh M>g=Nv֝cp}1?ފQ;ml lLN\W:)c5cT<6#kbfjtBRCɡ1f%4e:J2Dmr,+sQxmL+*@im:P'Y+WK9DQ)#f|@YJ\ [Ծ.͢_X+ݣҌǴ>eωYMYC:!̌i|MjgTUƄʘ'{ X\n=RsACk1+,g6Z}!kg)Ww![wJAd4+z1e>1ZV@ݼ&{ƴo4qk5dwA~# L8EK^g0RZX^'ҲaOKWPԕ V+F3$#: զYz69`Bo]5}rc ,Q\澚q}eE)IŤZW`hKC_y=Y>5YAp\69C iNp'Y'ʔ5u(jV}t]%&m4[*Y5 MR)"V0!QRWbϢ+Q1L7P0gjzBsnr]bظEEBɗjR(iq䳉.NhO7j:R79!R\‚ܯ7@$wąhMqq[3֬3ɴndBv7^h%VIUѱ׺YKۦ x#ҩK3a-]xUŌO|2P w֌1 iXZ(7!g.t5eO}Ԉo'bG3! zF2lgk=z]t9 :DG\IxEmF<6k.^ S`4.:T! wX n0W9FeeWg̱+mo^Zc{6Xnh/| bMx:5՚  4H:yB;VQiyA h` q?9rBF+XT D6Wae{/#\U]RZxP=d "_x0F@%4`4A$X:RqB fNJ|ϙ T Ao}bgeM[O[U 40M;~i ( ֺZfZCu]CW >cl[4l%cfqȫrG"Hlp^6VKئ8D6B{P`ˠvR݁j@#[">4Fl w+{VJ%ƌ/uMA58~UTaW% mLzq'*M0:J$ V5鄎du*oV4HE9`Ȋk^ΐ8'vעjjO{ N_Ms]NBtucz+>]g6QQh "=wĔrJ!GN>͙}x&c=#!c T8.ulu;NC<`[kMDpBV=uŒy"Y,ul5dSZZLhV-RU,wH(vl˺^1cB=m# SA0ƜFI~,3 !duiiH!7͘8JFz_ZCV-gϡu2oNRG#+)a~rIǸ` !,ˤyFUvˉb1)־[H2.%,Z%Lh\$0Y1=ਅ^N7IX0ƒאa7tnY4zH]ypI3^R*]iĹal&`Sz حՓ ~88HV?-'ǰ (򢔈#XmJ҃-fA&VJEJP>3X*nO^Me d,vZp)km̈@  jH=sys׳۶h>|Rtl-l.H 0So'rSᶘYO?A J`pt\% IDATj&jL>:f^zh<ìL fƤڢj[xIPi5 8޾2 cR*,1#DȚbe78xkdm!Ӌ8Um .Fg}KZ2qܖ.SbF7aJ(L; eJQekc}qاҞ)(nL}&gzdqyI7[g9{> Lm(>㾬t 4{ke@H"F"Ո?LM:_fESdoz 4q+J]h/U%*R)%/+}m$w 蜓ڞFh_"A P#B'ϵ!(T *SiEOZcl}}h Y JF[Й5DZs4;PyqBZiOy,֕UH'|~3cZa\bZajS4En¸uU;_8!M ).DV^1e{ТC pNM2KN.( QaNV-}T2:ty")j-# :/X@Q>r̵LiK) "RJ`X{p>2eF?*4Y(]f`k *~̔8M5jͽv8nLfI6MaP2sKҀg9=۶.7%O$;Փ%u*sa {An;/ezu[JCbaqbVUzA(vTJ#C$wLV }PA])_Ky'j@F ,rr`DZ鱑7Wڌ =nP n՚;\ dYjȲD2.Є̱eXUF]7ޏq ٘Ɔ}uJU0Z@_56`jƜ$9O&'4WP'gwngjzZW o6 Vƶ<>1 ld*Lt0&s&*ճXq,q*".,5q$.~ұ@!MwuNJo%+Xt P[9'K.=^MIlN24R{v!p [a);7O JzԊxta m^5XQnLH66Dɂ8 8?Sm2Ae.. =}U: \7)5 úɜ8vv@-;dH [hhg!,'KQGgxi-Zx:du^AR5.IBE{SZ(@8|m˭jIg f1ƶ5ulN v,ffBHX,01MO-.#FV)V~`X"U]a+c 5-aʱYJ;ʎ֞:a)"%`_tR3,@S; ;s*y5;(ʧZ1Ng"TGOR犆m1t5WJz R}0w[MѡjҘSq΍-(b" XfG*j,bvL`*:I!Y=ܪ*6Ƴ6lTI;tr{J誂JZM ڻ +5JLS2NkX -m`ec-{#.ڑI ZezoPŸ7)>GcMTe5B+ks1A}L$ׯ==*0#NfH.pA۳UuS_{yǐsVA+W7Ǜr:ks6βRu*fouU)Z9ZNljn!S)d9Tkָ9@*RV\(~i`,ce-x8iCumXGUiǤ(ІٺPƯUj+('jk#46hMY]|j LuH}I$xKA}\nQ?nҒjcVjTVX`k(m;:ۖZjn뾛 )mM.)IIw^l4xITni6/޸d{k 8ĵЫhc( sکBc<)O|JgRt)句L5nKa 5n lRfFǩ Ze1(}ʁmۛ2!&-~XC3˖{)J)Tªrl`0gGb3!aJ+04X+oBl{:[oTRKZ[/2JZ/(.2Ɯon}QZ cӴ5KnO BB\OyP+nMsIЪro}^V eURjdeg5JuOʣ݀Zg8O@;ԡ7mgD $S'(g&9к$Pr]pi+xWK p tUQqh*h]b%MllyѨT'^ؗTP*P'ij1&J굝6TlQwFS̥juaSᇕc48pj (^ ɐݲ"c@Kza&3LF4>xM;e{A+'._Id, AsZꢤ0 uJ- =8q||zNjf%J݌@iE7fHKLR'jXi$hi -596XJHs[c .5H&(lOlkkiם$ 31RN2YkfXVZz${Pѹw6,YP2u@@{DYRiL^FE\A%JC+D$ekᲗ'R-@qPۖ֞q;>@U"x6q9/ㄸl'KH=C9:8 ,WXu`%*ӲAFИtQeƠO>aZ@JH(Ԋg[qL𪪲b4/it;s;V[qݓ%cu]C-UlYkƶ/T9hŐ9t8CW~iM:if8(JRī -'ɤD`X8kqUoJ$sBwk`R*au(5mQ{F@\3U"k_=Bb0mJh!Fu+I2oMif'o JW1(f*IPcx(Aк%A?uZOGWC3$Z0YGl%cI@on{#4Tgb S1H!V JKlWQBIb`Z lW)i dƆ> 32bX0}Z<`VU tgƮp>ftX./6O-LX5iU1-JBc DPv+iXNhݵ!Ae>tk4tacR{8PSU N ?}.Hk`kcc/c\Zi@t Ȅ hQ\xaGk\cRU @lb+X+2v\Mdrۂdr>DDyVK`;&9*6) #|76]N!$ZeIy*׉P[+@4z2[`Y,53vsk vaˋϐkH~-H["5\%[K#j|k<H蝞W3*]|<]#Ը چϟǣn#_X![V{`$5(L&թkr</7XbŊ+VoYk\pxtcLjӲR i`WwqD``}r 9k!ƃZzQ *m"v_*hkSI^sz}6D+gxdRFdbdn_=?߃O}>b2gt6ch\.q]wqG?e`{l}+f+VXb -7v> p^03CW&)fYҾ+J T-`fTa4k#GMoZYqd{;9ևA=5[+ڬgʅ@ۇo\b\b~-V~d2t6ra5g|䠚fbŊ+V؃ɔRv8::֕U Zҵo9)XD,f꺎B]U|NCvJ?ٜ(huj^eX!92UHh[ 2\@ DgL,V +5 T]U: +VXbŊ=Xd|m{k)SU5&Av@{0|er7M hq mE+Dqg= /e&:ϤC rg\mš|[̮ _E5\k6FTbŊ+VXWnZj_)b{t:RarV Ԯ{*F0|^R\_gg]>fjE!{^V5[<zUh]cԱXFk4곀z)'X6ƷUPZ^Y!Nƒ|Nj#+Lg؞V^![5 0ĖaibŊ{03+oPCbJwZNJB]O1% Ar`X(]6Mdk h( 00lR&&V&WUB+o hMk^iFe4z|' #V`bqRV :qzs5`{Z2WLEq[Xb/{W~ݟKXpΣkV~&|Ŋ}ZiW ^-!?$:BۣlZԨm Y0"kd2V@AO4f"10h q`;pN/aE8 dB9-L'@ځ_ H+Gfȩbg{ !qo{r)`{Pom-(w~|2ηXbMqO|ճp1?7gk?#;~ڿGUp ?W>abz0}S:wq_U'B)|`f!qf' t0#̏`i{l,~ߘm:/hl]T`Vs{4s9>fs,޵?D߸Es,}%̌.AD89>1l]ڒ+Vؘ}wovaRk|鳞/z^gs7? _ /ow G/Grg}s_5ޅ?~^?/xl2⎷ozG>ֹ?-_S{oO> ׾?{YǾwDo~?uu? /{7ܧk ^ٷ?1~ގX`G<} o W-؃^-͓tNsrǏ~YSe|5X- d3{Jc\bZՀfՀA`c`aƠUluAJWNHBX\6M 'lnvF!*Ⲿo RܤZ`4VxX⤱8Y>m,Xblg;Ӽ6rwg勲g;;8Y99gh5x`'?3}Cx IDAT`>6ȁ)Vus/u@rzn\Xu뎈 D1smWC7b|ݷ?}_ONX,w>x:^OeM'+"Я}?MO&=W:?'?˧?{?諾 w>>x~p >[w~=_t cuw??m ~g蟿ozOl|e (V/}VP_W^җ:x x;<'6Mo7x2֘T"Xf\<lcq#)7m/~}`/3p~*X.縻 ǟ՘7 xϰ D)~RPR)Oy 򔧴~8~UUϾG)[ouw14m0*!Ԥno{)̶qngwг'?듞sgvDOVG'xx{q||,={{ؙNUk3V/;i$SYa ZŶ*098<>'4&-u GU2LiEFΣMuxx #Ι}j @pυ{bVa9z7H%RPʘZQ^ug> koO=+0:୭9iFxc_zOoO?8Z4̮>5?yNrz\.Z6XW1L=Ko^BոW9O>. b&KK\__ WU//tkI |;ؘU^clmARZ\;m u`PӺvd lSvB e!{pZ"ִj`nI9P.2ڳ(/UM[1[_,8Y.X,{.a6bRWPD`jWVG ǚ[W{YoS;[.48:Y`o}Κg/37d+ ë~_z Owf89dޠޮW+ǎ<۳٩!"h+Vؘ5~Oŭ^! ,tx[{oWxQWxԬOs?s>Tr/}8 =a»q)`}!1_b/?z>??x/8S?bPO;Esc6~vгft ޻BHឋ3qk 2NNX5>z'lT̮n+J) ,IF>`7(7J7.za@]3cl,Nq\k=ag4Q=yYֳ3أ9N \xAW5fdŋs 6יm,ptp ^ n/bUma8^vJ;;"N21ԒKob zTG ^?|? ޺;>> I- vi{;o _:7l{x/ww+~E_Wݧ;#??xϕO-Cb2݋b7P,$>y>wZছnb_j'luAaNUak4\].i  FӬ ¹3X,X"ɕR)VTQQh#M$ JB$)Mp̙ߴ[2W3&6뎧s 3?il4~&|%OC+V󻟏Wm嗿u{{]{CǹY߅?Րze_a2r~r4/'ގW~?]s#s?&]s%/_גKv#~~` nyS@YRG _&"Wا=~UUx?7|s:[5 TU910̙=L ,ZWPZcZe䣴`V 5ל%*׮n2kϿ~-8J^N-VMb9NN899Ɨ<)xғY=YN,V쁶??_<zֳVXݶIGGGZM@;zI]A+-j>MήCfD=pդ-hnskL&5v:f@c -Vl{{Wiٳgq*}&~oqk|tL&5tgYL0m u]Z'%ظZ㺮Q-;ZZhM`2_>9#E#ѠE'N(? ^ @ ҵCRJ **xbWXqΖobŮާĬpbV{}>Z¬W7{:,h:M7t` y+Vlg+}P?[gqӍ7ÃX"iɬAa6x?1?Œ`+\wv'G==a|.68ZbRh~u;lVu&h)4hdr *>+i%[o)~abŊ+V؃vvvpYXkxeV >>tMq R8{Gbogs2mV+f K yGX, kL3{OA!5aec@0'Pxŭ"`6>u{Nvlbl]&Z!WYXվ[7HUXbŊ+`kwy'ӐdZ٫}|xOnpͷ`{:|~5U>Ilm>YӟvJ9ãc|OަiX>q<170 '>?(R1 }<]"4x50bi VlJObŮY[,KƷXbŊ+`w}7vvv0NsU3Wp|rZmmc\ҽ=^!y.q%c0la2Npt8מCvp|03*@Jῼ!{{ↇ^5?x  aX]uGAsDb0Ң(bae- 8+VJ)/8ܬ*XbŊ+jn {{{>K.½Gm';۸a8<>k-KOg7bg{( 1ݚPM'L8\.G#wΏ}}V0ƀZ]]݂WʄtBw6Hu[p2)x&r/e+v%'ҨQen+VXbŊ6ϱ//*7ۛflMkn|#g3`r{g?|c>{̦5mG?fGGvC[nƅKƴ@]qí\`+2@ؕ@x"܄nbŊ_ , ksbŊ+VXז%e]y;Ƭf7r+[\Z<ᱏ x w~쯡 ?KLJwa4,x>7p- ]s->;Oލ\N?2ㅧR"P>CKp=YR`%V`0&𲗽l\w0T3g\ ojJ-&bŊ+VXbxĥOx>tk;[.nwsKK\xZkLwp1~Z8Jkt]?}?f3r/\P9tõ89yUY6mȃԩ%h_QÙJ*ܧ=ie$+v4 Z[XbŊ+V4vU=.[ۘlmaw{zRd1T0G0,M8 +, j5in=Adrec?{ھəWi(Hϡ&`g޿Z*܀oU?ȃ>wU:1*6DM}FIFĂ5 b|-J R2Lgnzf3g朽9k>m2t=ѕqA J{A*}7^DDDDDDDDDD/H&FT]!x*x.f 4(WkKaR+q4@A,2 |קjxA@@X)ni&Xn!& EJ)1"lX O L+Pj$|Ga*,]T3NZՊ+zbչms>Z,+N|#CC* /"::wB=S7.? |wDѬ4BJ00b?m9=cmե'Y| %uq4+"fufOt*o YO =峽}Oetp|r|}"p"5cDDDsFBb!+0MqFW ! &A}ÆJ9qlݰPфX t]G(a{Sõݻ*/"뱜rnD%xխ岵[?PK% x!Dl_7r(w홾^4'쾏/ܻ:Wͥ~7&"3cٚs5'#ߌܴ(wϕυ1iy\yiҒk"fZCE TL$&X%lYz[T&W:R'lxR) |N"|5!q"wz9:lB P4@qbqQXe5͛zǹ~Z}eUR5 nyJ%<](71:6|.X{8ѹ<x]xw\y.+Gגº;~§2>|79;g)jA{_|}Gt|W$z=F(""""⏍b>s0b1bHa0IeZ 0 턁T4Th%li1sPENo)dO*)k ;RH!'JPw~^6^vYuo ['h}R_|2 |ecϋ̈8&X 7uik=,.#bv\WZn~뒕g_z;-ɨ ߿o׼ힰM;[b|ַ}w\N.z7k}/5N>DRCm9W~t ?$9}#iML^WWr5|yE(!Mx"N܊!tk Y&$ReW4~l &I6u4'uBas갮κ!Q7pC(<'z29>Pl|墫̵o*p\aH\|DG>_6s7n3m>Npt\!?{~ hn>t6yV0 T@Vf ub1+ 9, fbABI;B:8of  QhziӞN_Co()gi 4Do9_qX˯]_:6 >͂ $w C|]\/Н6.Mc6F;>KbOE޻×L~o__wD_i߁>?ϧx=VW77&`lp/c,M]pVUGr}?3>5s#c#Ivsi{gҏ^\pDs8q^"|F0 L+A2"f.R@Hꮇ=3JfX M&cuu]ǴtR>WJI,q> sTiFKЄy VBo$| ^kl IDAT!d k C Sw]lqi .℧V'p[z㖁xHXE\Wu߳qӧ>>7Jg5^W\k&w=ynǜ]^u3/u1>:;9+=l)77c:@ɼ8^O߷hibVX}ijخMZ!# |L#6z 4CJ+, :ӊ#~J WJL2J5sbX*$CHC 4fzV smlۦno{ȶ(Y#N_ԧpBzD9jM]=qwcVru,;Ny!B>ƛ=޸"7nluWno%GܶqNz|3|/;/9+y}R T5ۻ:;}>1:1%ιCeJ?;[|[ pUd;ٯq.?do. /~ݟ FYIxv(5=򽋚c\u}Eet7_9Ύedf u۟9/X,N2G_Q˜Ƶ8Ӽx6f3IL0 Pص2ڊaƨjd)V.]gˋN]k^B^xZBQ|mY.Ӌhc nR( oJ!%̛ü9Wj[DX$R S@SٜC!+PTj]אZ>'c. ߥ|wk|w{J~ g;f]3|ͨx+/|{ၫ9xy=Eۛ$W\ҿ~K:gq嗾ƹXݪA>K%'g|]mkY.p٥B翊/#nζ/㉽e{9#JxY| SY|'ȫ9B _^Cszuvf u۟9U*6X!K^.Ք=|.l\3g#B/YD"bxfejH!|X,a@Q/ I2tvI%Vk<&,+Ο%$?ꞏxkN?~j5Oy/nI&K.5t#FIv]\_$W!`.RBby;C64ru8ޤYn7=%}\nF^y_+_T)# Q׈hGDDD<۹~N.%HĖ4M&R4B)b,:ZhknV)xٺ}#c @H$IXh1nxHrz3WDKSG7

xŝԁP 1`{~qnCNXVUofyafaB:.aH hH TKMi2L$c&1ˈMj%ҩ'KR? nc;uï)c wjJlARD"m;Jz׮K$HII2Yۉnذi[ZؽoS#kTC4~qh%$LjN7uS˾R%ô'@KͱBz*".B&ѕq’޻93&37)Bn~wup55~&ѥ >f`P;+5ʕ:F624:ĝw݉Gyf @OI >DѼx.PJ"w}X4`=txaaaIP4BuD$8OqS8GA\0(8Tm{QV)%-Oτ=C$ FDDDDDDDDt2AKsMtWJm*'hjc;jZ8}۷eNVXJo|q I@Xı=lƲ,l;`X$DBhGPu):u}WxR5Wѥ$0el|@%'+-%/ۿa)[KZUsCFaG"``xӴhok#KӔJhm8n*t]44ܸr_ ܺ0$R), T4JSW0WqBzb S ]F\D>#iX&o$ 0ó&0hJEY  ‹:UPahdic_Q+WV|R㛚|}mEqo`V]DZcA,f&zLDZ=\*G:B Z&#c=G"qvE:Pgrnqo4 DMPpD" I)Ѵ0nC!TT I|M^Wa1SC(zLt"WbK_ҧ9r޹q'|[[%cFPO쎎Nnjj /$"""""""nA3,R4/2\K\6C>#JS*6ii7mfHӜM 'v!H$bxLM5Nϊ&nX'MigqQ(nTDK7u!BA>ayxzSLu"fTjQbĉd٧}|:Ʃ('v'ޟoDDDDDDDDĉG̊188LT&T 5)  #dx| RJ-&M344JJaT S&dI*OmLx|&|_?M&0 4&E阦9Y7u]jCݶq]? fB˰% @"  0 3fVtED"њE426?i ((%&mhUa` RGk̢y\Γuަ# ?͸m[%-[&EA4xW/VMއF\}ߝ`u7w j'?:kx!sDDDDDDDDL,^t:CKK \l6C*%ΑKI:b 3#L8nlcxhxԫ͙}{<({AM lQf˾ Xܑ`4g,pl4"NXG5\Ų&Mͭ4esc:M9,3N\&J&&hjnH)µǾ}"|ס\gdx!U=+A0J&$R Z)l 4.SГ?j{>'L(e;<'vcKSY)?,~mT'w&yqn{UWs~qp jZ&Blcc4#Bvp2W8sɺA4 X!p*SS|)8}V~w݊=y_ˊkXl۱f:} %B44ΙO?^4MMsUĢw+P@u%i9<@gg7x"PbqY?+x9cTG­H."rRKF'((̣\)366J>t}7on0^(̚5/n1>GYie]~ChI{s'g"qKy|0^stg,k&E E cn4òL1]hnhP W(G 3q)W6-x|$CCCTkޮn qkqߢ\?ÛJ Bk6:񘆮 j" |_h+GV'j|63ȷ;w+ߺwQs/?͸Y!ߜoXZ\:s'-JM\8㺿&e{m*|fΝuY\?9[gZg?)zz{y[N.LH&SÒ kA,ܮ`R"ʠ _wF(ވk20Ց{I4!j^xGǡPgujZ?h|=(Ra0h,m,9w?@*ӄվ 5\- F"TRP/8;5eLi4m:2E'њ;!`PG7u sKNoM}Ad80 ''a,?Wįj˲eÓ靿av ҙWt*m}v%|  >ۣ-5Q )mus ~T"NPB/P`ke;@ =|!Y3ADWZD,"8[oϕsJhVAٷof<(Kȶxۨ ~kMm_EN}F}--1onB0)/u]\u\ϥT*G&ݟg}{r_K,2M>k]9)~+ gf p"~_j[J:cc"N;]֔"!Ɖ#zH7[!cԊ0SˢQbu#}:;xhmm/Rt:CZix1mYYp\ {Qj*ޱ*P>*>xcZZc<9Cyϣ/^ɋfR$MM451 l6nJt! Tt=]ޜȋֹbteTXpu0wř н nͧ6m @_:)|Pa +fhiIάE)֪H>< oOeҥˆ'6'Yh ݽ=KZ%Yk֬a۶,[{`B\k}>-==,^ݻwMYj!ɤRH2$>Q*+B-;JT*;᫂^խQ5qǞ6݊'4yVA6Ų0wM>LhmmG6swe!>;' my$qrMMfXG LӤ3L0ltF)]H* J|B1F:یexlށ~ â{vYdRuy_[އWFgyC8 P0vŦ_^$bVfhT*UtMGJc=ƛ.KnO׿ւzT*5:5X ǢŋB>yXt {즧>W͍mXSMH)qۮN=t9mHvkO[A2={+s~{h޼Ufވ4:Z3lk|]U<{SAmZZZٻo/rT*\4)1 aV,_97PE·N:n c$'8NGKZOpMd3YdZ:JnM<5M-4Mglldf Z!59 F)$j ]ӐRchin]ӬӶm\/ !g]& >>RtdLX`>{7of׮]|$ݧJ)-:C{+f1?{+Ogx=`At?w{{1BA7b46U:˭eFKxJuzJR~?gr*?OB*voY϶}C}A+bc 3f ~oBfږQ꿝LEGݎs;g,ϓ;R}deґh+'fJhwK90NS<,_q2<YzHRX1k2":B(E2e0]}E?18a0::J^gp`t*M&Ed>dzr9 ۶)xGK )%zC I64M|ߧP(0׏\ڶMoG iuRIb\t"i %˻۩l(nޔ^*u ?tUS9quniPVL_"1t3vR _h~iu54̳<5of~ًEh>kc1aɬU(Bun'^&iim 3 Vh6 L>-RҥKGWgBJbӦM޵I&gExwoYг]پYGVNYY[A!q{7ވg[G/H/z$ ٳGmr?V>T,l6ܿ߱cgW];y]?.N! ,bkf-X]nI߶7-V1:6e'j&BH,Y"Lv@s5m^:6Th8"+4 0/`$O_w`j?O7i=|4&GS?D|WrR{ ԏı>pѬTU4)#Wtƶ7C.a;E6-̸m>F>obqK˴@iK.~ҧՖcGWwB(:{APL -$.{xE=tY޺om9p5;ΣncH ΋K̏Kq\|fF +rG#.AR+&hܝ[ޮگR + q٭wc۔[p0xΕ`B0",vFI:}>WY۹# LnJ*[v'2#)fj5\Բ|x?y Qj>滨nh"a-|36NߍP1 >O>He=5p8fŝ=cGq{ ﺚ.hQYov7=wAC+b5li߾vzzlٺfuv?gLGdd;ΣF9^un,gORGBxX~iή{IY0>(vz,_A@K=ǢuQa$ay#ĢQd2ۺX~3H8j|9zFG(L4fl(A466RQeQ!fc:6tf&J*iEǣ t ,G&SpQdL*q]5(.]QeM30LL$_rpc,\D7y5/@ý#{gʼn2Ko/R.הJcrMعt~s d9p>W*-޷o2;ﻅ[o;J6ѶP@Rܶm9ph )#Mc66@]x^ zn#'`yhܹ, >T28s.{9H4W匊/< \.:ZZ[hljDu9zhhfLW|N<䏣~̲u;ye>ߨy )6uP(hoiysQUhui pL:O&gÖt"dP$W#(VUel" #ATVT U=?^ _"p-F\l`%ySr<Zk=B$"DP@³ȲjHH%HpB\ P$U.U9S?{mO^mk}ٵȍtqWW[|;2,;SeQ++㒅@rAyGÓFiZ߰[nu--UgUoI8db>|օe0zG:!043>&DKoK;8f4Ȗkh+/6q3t}\5uxurciR8|&>>o# xxNAjEUvGk4Zٴ=VI &I U46a٥Qbr&3$9m}lxmS' Egsg$MbSHHȻugR?_|dZu̟;q6f:5:nBƍN!yK&4KضMOw/՝c{9\~߽vdY!N8_(qGKK tvn͛7I,iꢵ!{\OV-hlnql߶Zv9bZ'ڵk3XWrbyye 3T=D}_v*~ems뱶%ںUZfmvkjf~[I(>xJvi؇kĈDVk(KL Z@5/- 6,_ykӛA P2v&.ukW3RY)Sֆ$ I }GEuOə3DŽqjvvM\Vm7w?ǬYscd _]Lv_/=?_ĔTxy)WV'3Y۶eyeV*iXBqӵoo_>o \!=]Ies 9[0hm 004¦X:6u}I{`ttzZtvwa][BPb(y,KuPocXl/D$"RAUcϦIߎi<!ɚEsA$!Ex 4%hD 嬄L@*,1ru,;y?Ε\}'/ ~-N 8̋]]?zD~Ô2l7mV%瘟|3N1ll)i˗obNc 0Gq0a$F]~CdGzxr͝->,A9LMVgo̸<*8#cq, /<;;VIvdb{( 1h;.;լzu5=/|]L&C4q yBP*ɤ33gO&oR~@- ;Ecd&]oUXNIHN:\ݹ5,Ow?o>Oƅ[Rd[g7bcq6w( ϼT&CX$ɒH֢"C]S+}zY24m3MyR -Hʲ@2$# \?,+.`ӪIo RdasY(CE.#SJE,%[72Bg }B2]@U,'loYa_[?w'6]v;Iw~wmC!=n{>|瞛Y5R79GZper͝gS[|,hb2zoM3esfGu]WA3&joe٤3]H*2PxM+;$6}繤l zzw[HUUf͜EPLخ3+B^|N:$oNcclSXrO\i ng< w´/Wp]B#TUU(|hle4qPgw纀pG2Eǣ+ DOv (]˗rEh8}ui^t(/7z$Y@48 ROyVR1*zd2B0n0}p1ǡkcYVQ(Lt:{)̜162c l֎>\\ץP(:B;|~u/l3I.##`!+Vf͚1kJx ߽R \k/SU{$Nzi4d!P5 PNQ ˶QB!pehj$Uˊ>Kɚ S9m*x #>=UUU|ɸ;+I0MsD"A(faK%#,k5Mbsݔr}V9d0~"h cxl7RS]MoPSWG&'hBYd"_P@M"AX$4s K?mFdSD+V6헀csļQDT۲,L>Ky%CQ4\_I[['9PXv=z0EQh, ȊFw [{zNdK@׉VTx# q(Nm2Y.h٤2Cvrm)d»X# p vp@eέT4!꡿3 ogp cbK"+{ %X~8cM$*|vȊ *' Wg۶od, SqZiy?cu_$?>&Uy]ש?m<3 E>s!>o]6w ;G3 `\]HJL I@U0;(Y&|#Dx(Dbq#peiq@UhnmEe\IvrA!+e]}=$\sqgyTB̙9!"XBHC)QQXX YStZ)r!Yqhi@I?~7TZi`DS{Ck<~RzYr; di,;YΒ$QUUŜ9s1cEf FI MU.^#|CGy~{.HD(/T`0HUe!==<aP_׀$IS@4jÚ1˲p]]4mX4qJ$I1iB1Q=;C,ܫ6MT@M*m۶q'|SOrN稣`aM"Ϡ$XWǴm F$n 7QS R)l!_,Wc({uaBz * t] 4 DH < Mi3CndUY.Eˣ2k֮CQ$ UXEFaqwu #>^8Ni+Ni&x]s)L0\yl*\u]yo #bAiZ8W!R{DQBx"eYޫMx;qcLga$<}occ#=}Uf5WR2s6pQ/oĕ‘ :6ݍظY0 `(VU*h]בOP04P!Q,dm6 _M HxSTNUŃM#&Z4Yjҙ!H HƦv.IHm{8T1ɿSy|!w}t'~.J ]íyFD/ VU <{>χn=(qE ~scRBU)4yN.pi7% ٬J|Պ7l˵Lno^7aUyq勔c&S7>>>>>>>>o$k5X,H8CG#idY&`(4ו "826k38p?fVzWiRl⑩ xWTU-9cǒ dZpłpX +sĴ66v/S&vV-}-DqO !_(<M+8܉$ |+my;݇q_?x?'g?9g-}Mm_ɫorͽ".jb2i rbruykvF"0P C#꫓Ղ#D#UC$G'Ų,Ȥ8 L \&xI(^B8TB65Pn O@ɱL4lBajW|o[/|lo^,γ4 qɒ7?n*Iq  m f($h pȓqș:YU =Ӹi0ި-=1ɻOn6j_a-1ϞH&M֐H&$ kԵ XE)6%Hdx GR)TUX`A,OcA(]g$mR(9_?#|Eg2G 8q,sYU%m4 b躊8X%WUU!0A4B֟KbM͙K1? g7crb%?zD~Ô2l7mVw#^|4-oIp71YBao~w|y?'V^]ŇKwxoԖWL'AȎ/;WS[|&|>G.[D31cYIk̙9ac m[~;F 6̤{eQllCeB@QofxxTZ$Q(p1-slyƤkN&]vo`՜n͚_ ePeo}vicΉDb]}|||||||Ħm*8@6dQ N?b(,4EA:쉓?@@GuD!00"+zkUB4EAQpfH,LdݦP:k[E±̲ rBJ>BdkxغmXcrQ1MMd ! $_BAH f0BZ||&u5_ V{b_^q)nHO{ؠIo7|sk#>\!0Gmԇu].MS?^ϓO܄iKaѧ)&8P뽿,,m|?/7p#N(n 7)߰uyWc?b||YW "%x瑏xʨ$Kͦ;Xᄓi e'._xnEփ-4h(22E^+&ܞSTDTTE ޲[X8e?wp2HjLQ&16ޱmE γP( x0 d-]*O^ >g7 @P1[=D ;Wl#pTT(O ǝLkHmoLń#3[)YNE@o6\!$}pZ҃A0^kQms(8px@! ~}4W?]Zo,KYxS?7/;XTVN!fk?l< ' kxckWfݎ;װOp9Mlb}Լzt UTkuGǑJ 9G1" I <{׌w}{N+A]bײB'x9A\_QEp*y=yOℋ1w<;@u~kZ>xZN~>;fNuHM<GCG LU.U9S?cJzz$]׾@{݇Wnom_N$Q])ϛ$vuXZTdduS0dp@%̢>O\+mឍx Q 7A>[9>Oy>^];x}5,[x߅ɲg#l,z1*f5oyE/=Z]}x $8yf_`I}h9'Tqh<8*xgwEj g^xeh,/`jWWűbC,MI%V4S";2Ppm H@ъ(B\.C:fNńO'c8B=/ sD!;pe C#dyq"|ʚ38f "SPe4 ^\-h pȓqșNa8x(¦u:nb~wuYdIs߫>#i /@zx{?&Vcj* lLrvAݳU6}퐽k;`y~is{W|>5<`lzx/GXtB#Nlfޢaލ/ā/ykK~r Ki>^=MLJAZ;:ѳC, ^ &+2}Z\ؤizŝE~5<UPTtYpCXp&C}<_ ?{gfU>ϩ۵] 6 J(b؊b`7"_n1E \`aٮ{(?ٻK Νwf3sfwʏtK/vM'#'E(AAVvDGJwA$ 8э:\vH Sy\Ȓ.Kt#;"B d. `ke%H0 EK/.e7zC=y .ڇg¬ψL$?BׂlDZf>9fy7=7{?z2fx#GLjB/n⼿0>~:N| m(6:8T"Ӟ wn|ѷ|{& b;rd>'eUjm 'IuW&1I ;3Rʝ{tsא#8Nd,;~a ]_Fmju3%{wJz(yKW|c-L*6z^pU݉UXj~,{s5'R8#)`h*eKZrzߖN]?5+U|> )5O՝d4Jagw # jRceo!6?+0ք[l+X{I>0 H* $a !(NZי!woV6w,yc '͆0-dvq2eda_CS Ht8((z([_6ݹt{?ҳIh xi Cثg7~A" TUyk9SG\ϽΪp|N.<չYM;Z2i膁hB!œys1%GVA,i"H"b")"NNEm(#J&Fб: rȴj6p)Z+SxKu~M'= KxmaKʜ~o$ycshHU} 6xGK__ʚs.-|p >_STaz\AM'TSCTdT-w|AF Z)߷&rnWPD ŗ1`?G_7&j$ίZpYXl Od-dV۲w{dF}U=kVlptAwu( /ZE܂Ҳ21E~S]lrB5 :/G=h,|y*v;X =k^{$*+YK,A$Ias" d˾ ijPUns"&xd}Ǵŋ؁Pe%4' ()昣]E2t }+.!k8N2tܞ;4.'`8h2s0eOl7TʊLՐUR) _Y$dfhw #hHsp{WNN(&G tj]>HSP0$)a~o?7nXE%«tRz_b#p |_x0gY}nJbyPqXEԖӮsG=%ɠcvԑEKAV F6P^^NUMP~Wm.( "PWWUq&߇Z"> _MSNqPB(>n'##ںI2Ns((I4X潯搓ݻgLw%8%ӗA,YsXWRAY͎9]nYEH1ԄFu )+gO{=%D"7e%Ԇ"hh$pX,'m[1`u&ԗ7C|,cO?ˎ |Ӝs)ò;n?9^$I$'nc{z&2mvDaǜNioz K.]—/f-?oqq@M:g3ᦓot:,%Rfp%WUf~)>x-3\ǽ3֤̏ͦǑ읈HaT ']F{ ɤJ(%/3}|5bڻe| ߼u'H[SF0EמGnqwY4nGZ,3yޓd'v5qOQ{ wS3 ~\ɻwLj}KKq ¼Zln{΁}OG^`vI$& ڵΥ{BJC,) (%` n`(LmMߏðKfMS4IƓhIu;gj܏)y'<aHPtv,UXp` _iLd/-\{Q,'NR48ɭKSLGٍA;RۑRU$Q ӹRe.;g|~pƷ\7Ž3Ont7ۆZGC.sO!TYVXk_5E%(_ 31x쓵Yү ~Ⓡ`lTJF<|kd[PT2y $36ݙy?GzE:;ΣX~ؔ2w>wA&@!bЃ<3\1\qsOJgSX[P梾 瞽xAtpԗ8 HM]8D{+sǞJ]/o̡xH6+u*CTu(.*=nlr^ xy&z 9֊ٳB@Úy5qY7(\{SHQhw1]!ϯzYn1+6,&*X5|-6z~?=O>d÷H4_[ۮ֖i6;vaP%hAFV&n1ǟX]o}ߩ Ѯ}kۓX(Jyu (Zc _1UB4r2@2!C+ˆD"ɤCqеS{EHT#S6bqd)/MU-ԃj囵;83xw&HȎdg[4$%_=f. Is-t k*эp˴sUQcKQc*Gwh#ږǪēēhTg6߁/Gb#p$dzݺ(!"n'uL l&+ÉP14xp"MVzbˍW4ԅD"Oٰ+}a݇5S | DԓJxem۔(YD>:J {###`mjc8[,lוۏ'onau_KhܔՔT4ͷ/go-b -G2*"M uya}:>4voκ_q"h݅d8x<8Tp]{H1M7>)ŵ1SΝmY tǸ/`߶}u IDAT(HqOz=}= Yи0̘gRl>Te\إd~a IuM:D/`5LE\ͽ3(ݝ7M}=ϔ s΃pfSck?HCh2}]IugA}wuҫc8U

̜Ɏ-_z^~~{uAV@4TUEm oחQOb<v6EC${#;uDɁөWU` g{-B-qCgZ6N$?.jqkA6h-3O4p3exagCT&T~5.;4v) u|D&V%ga!v댉84.o @I3/އ')ޝM~JnpQm" nmn8NMW|vioLTsv̭C2t `1l;sy71uk_c;qts2IZ6ÆiDXS-^FnYўUSΣ2K.2Om޽|0 hmJFSDi bu1n]Q|qd#*vGcSe㷗nwm@0 o;f;c8%]▣sEdm,\nޡo(\'x5ϰ&!o@M^bN}CIMIQ R&Cӹ6BiE%Z6lUy]evj_@},Q^Y-vMX,?͋ vBDqD$ Tz^Pv 7m<$3aZt%w^~.ہvgl6V7I |e^o=uq޸t>kwOഫ'2*/qB߁}8v(O-1yGgKH^*&} ,Z I0h׺`~D#@xzy9GX0&V=~]b6Lߊ#̯n۟H XF? (mEqTH imFna(*i!:JG0 Cd1~nYyxʽnrRrw]D[˺c9c4\4f`$U|3:ny r:0+$I ɷ7GSޫ&={ǯ;{>D&ْES.;㖵-;/o i PGN޹Uƻv0&}0pu~GxN=Gg0utbS+PUSl2^Ǎ +t@>7@dE2Xj1ʪ*VXÝbp8YYZuWQxU,Y ~D5n$%@|jG^EU.~I%rxb- MׇbQIzz,48l~-Y!ut~Z@eknFX|9#}D@tP\\BMuA9弳fI Ā0߅7.!JOO=$)g|wnE\qy|l^9yyDIN2*{tuLB.7%aHh,)j 4IKOG DQ"P"=ݫ#2}gNfOj>wN [|@殡Σ>XJPQYQWW ʲU3Z%XQFF ݎ LRj ͫjO,_ӣg/ߗP_UNJ5%TT\uDQunG$P Ht*Tw~Ro_j)lOWqܼާ\NZTcQ$IQB$ -(ԅCA$E&m)*ȲHvJ"؅Ӽ{'1IV˨ b ejy-,,,,vk5;X,J ̌ưhc(BZfօ8}ih̬~|ztJ7@z`w {}jFMD! d3E(,;b0KϽa(Xʢ$:YYY>n\vNn{uuۆSU]ϊV)aMq kŰh.hZIw!:`b*`\АM EQ0AT0$YUS-,##2Yʰh]|>H2vC.GI L|v$۰c: 33uլl6t/y&^)ӻg/inʫ݄jC,YcO(OiVVV*dС]>#H&UuAqX,F6m0 $ED%40LLS@7u $44@"IplG^VM؎h)9Uw^ám(.xbv_/g?r=:/+cmh!e%ݧ_e|5޶l*k/̚5R?tcVmβ+!K]_=9+jswP\,x;ymz6:RDD/p&9}q5 N'%?$!H"YǗd2(6JTKjB.]!NnP 1BK8^Ҳ@%DAG5D4 _ Il,/YnSkM<\NITJ>-Ɲw$߼y7OH[8": ֋ {O! Hf*Jo[d6Oцڇ9YMg%w)0)<މͿЛ˓F0!̭<N郆mӳ[a<[-2oK|Q̫I veh{ʽ\W;n)ܱ꟟㚟,[qGm|ߞUkh)3. {|-8]? ޹~o?W6Zi*ц{wߋtA!";7l#K*j4z-/v&Jԅ`E%%tkH8i3iG FpЁL}]cI4XRAClxh"oSDPUFq*(# :lG@ttDYܼ,,,˪>=wJzzb-olub?!*Wl~LxWϒ{@NI%]e2STz"|ʎ6DnW%SnY?;3O`q'P-F_8|fe63}4`1|A ذ)f/7O LuO=v)ٰgp8M XYǜAI3*.t@CK?–Ҟ;^yZ `m'q>g9M;cMLNA1ry>.KUS~#;Důq4[g3v"l/_ArsCƛasmw^Գ~x~ا 9֊ٳpLat3e}QܝmC,}ZnC986csL [v (nٺ+ 򦦠1LP0HB՛l\n7к ,cs(tlZB$ SrbE]UC!ύ7'M>G9Ji~&(n$$ 膀DlIp. 8.슂4"蚌ls7Xx|]0~|tze6vjl)/MFQ.&6Ù|Vor5\{u~Ozڒr^x5]u>fe6$#ar:~Ֆ4oƠ3xw~+4;%66{yx8~_I!7#'0༷ C[Ǟq GgsuYgV?f~c-6 YNN(nz{4κwޑ|];|~Ϟj6_?|9 4ggT:Uݝ0| @Q\ǓnkQj̨^淟lliwEh_36[v睷柆\t.d?f+MAi7i&]Cwl4PS՗SL`ƍFxHXHkʒ9&-2z\kد~Onn)`}cSlw銤H.dAҊr\n7~ b" *1:,<.'ZQ-F #S0id QR%Eo"v _-9YL'kJh*$#+#/9QQdYSVC"@DĤ١ 3]#Ѵv؎۽Hm[~/Η1\澻:yesy$| ]ݳ,zAO>~uoĨGVeғHg?4//o57둡l^/y}O5K!9h'ng}3Ɠ$@_Lz=ϑwetǘ+O}49-^gƳ/dz_㵙 RB{AY.| ">/"]j 1w7-y}-/t proI7 ĸ_#,㨫{{vjJϢsfŕD ONm-@M&ͺ2Nl PiU؊pšI2|>g#ڐ?PZ8$I$ PCOFRvMJ0tU W*-i4#'P IZz6yYԚX,I2.KhB"#I$_b$1g؎Y_iGvމ鲻3p覧I˩KDEqvE,f :lATVf*tdŬ'p!0E2S律bA,j|ĸwئ|e| 3$מ5ECWrf˨EaW^8_eą8OeFp9RX70eg4lOͯ n]۴>M{>}6.,Aᷟf(^z~}pGsvb*UwL]Q)-z.<9L 7&Pœ,LA ixTQ& l8%oH =:~_L^=1TRh4ANԅBV`a(Z>ݺD%^w5|?m%ՆK.T}ue9fn>nSm̦p,/2zEmYnB*c]@ķGOn8h./AGfITÑUV5+9m, {L;Di*kjWN8ZEvxLv?ڧsՁY3/!V|Em,s!+b-6urT{*ׄx+(l؋IS3e6nOi;^7UwAKg}Oiu׋n@RqB(5MiHt}>n ^W˺56<.'dD2!ZXީ#9.&rt+XyWGXyGu8sа#fh*m{#zi z]۬' DQ IDATb*r7#SP2h׶EdZrMO3gB3K!5Y,M<~/x/nQzuSuWCEpx7=7ߞNiY-ne5U9nIoK"4ѷԷyNncx\H"[ÿ-{q w PU[a 2t ̝?*r1y=Y" as:1EL uK)f8( 5tԙL;w|'4Uӹ^ vh!j+Bjc35jkH1q:~CN;p1v'+J ]dp̱d^SBv|[c"nG|pÈ$Շ%5 G\"i-صx*o:ldkt 9OO8j(ӦMC!Rpؚyh?8+jqewENcakK/)\٧l֢{<6~\z߿ug%4Oұquŋpxi(vI3iHtE). -b~P/@CҤHeKWgs>7#4a&SHb&c`˜ $8@pÿ:~~x*?Qdg`dTIB$LAQ i覉&ಙ >KZ(}*H6~_V.[Ayu5.";$XXl'N(&f6_oᑫ |EV3z 9cX GԴT|2QES~6*e c#fׁN?}H­EexeUkSSY)xB MGMH8bXD ]T54 mՒDVVĤ4XO$!r8INL"THI3GCq٭ĸMcrV galצ]~|q?SXTыfIO #+QS]˫S/VPj!"%#ɢE߱r21z8#qp͉Tc|;ې_CC7uQe%x\.rҲ\ ,7ȵ!}ظ [ gdRPTQ5tMUili-Ph5_ >S|ߢ]e1gN5l>e,ٓL#M8&&&&&&&&&defpIKK'#3||>/|9'Sxϥp[ 7=dYDADtL6nE+9K_"ah+* KJ[R]KnJ2^5E hFt3h(}HIAtiAl%87;%$A >>@$BQqs{˶Cp헲{rNEytxMdey[$,|FN`q‡ ]#;$ڹ5h8ӯL>[,ف;NznQX5F_W2Lw&){yl~Aٳ|3KʖB/k{FЍgtl%?=)?eZ wWCx=Ϙ;lҒz_Ɲve'w_rs?W0mm~xvаڝ~3Xvsh2lj߰zb6n]e:oOy;ǃ2x}zuFt$Y=m^6)Iagcs#""vt"~RHCW7SZQJ=Y5V2w,>E!)ٱ "(6ka$"#c(b.$I"rڑDEe2R"ʽd9閟DLb ܎(jTaӆ$&%_B)\~.fr4beDh_o%l9"̦-!%0|L>) =A7`{{x1.PV5;CaJMvޙe 09P]% tU䃇9Qvd?(R g0ڂ9hcՑIOB1xS9h[|)o.f e;{4|<8a>R8 bs[Yd e_/[$\h׽vZ>!U$\4n`%t{Ǟ)6f}?8Q-,_t{Cxbyr̅ڔ'Rs63Nњ¯AiԌfۋՁ;>)VƆ&rgexwՒQ4Fo#)iOym='f!KD!V;`Aq{TaÖ "2` ".SVX@q6RR-0*FVկYfFT ." Be}=qJ0gnQU^Nmm=9Ao^Ic]I-V 64m'.%ŕN*@sqaH33ilEA~gۢ)ZbGkvLpy#9u}w~Y [ɢfTuwɬ 6 fJhO]i[O7wvIת ռϹAx䟽I x=&soEbym_sBk1wCcΜwYOp}diڧ.gUdx o>}/ϴ<|-O}׆%zffcG@}3M }+Zd} l:|Wi,;] @͊w(FWӘ]Xţ=i|ˇ0f` ktMMVXkv}3Nt>z:#nYp%W4ks叏"="ԫ'߈hܱg!n-&?E}k2=v$_5KaE6'Hu}5+6lYaۈжM)Iv67z5D,(Y0AP zkz+nG|l5^_-z:+J+|>:͡"$Q)* hzvah!@'@TU|aTEG54wȣK>؟gmeI}$~ F~ H3g=;n` Kb{Z}vfrHYb5I &'^;m ߒg-ح thdhyR4H9c&]4(+ݥwsL0t~?ܳ̽<_Noz$oxf|:K`89lV .')1 b8+~| gZ[Zz<Ϻ\%Or~-f< .Nm`) ͯwz䛦:/:e w;xa R[\6o5̘ŬO{5o _Av2_Dhl+L[ip[Dn]ZÈ ]y5-j/Wwcw}G֤ŵuD*)J%+*xbIMItT-۶e*D1BeE2s2[iNZf.ymIMKIY;5\]#$ A1@"$ |>m/bzu大8+^j73oC!cCr8 25[y:,=+$ %;=$Yՠk8!OCp!cl]359$#/;):yTүaae@X{2θ~ŗSIלRxM5wl ~CȞzv=ضEl )R[d,j*<3boA1V>kݬŌNw}</kQ#LurٞWxw\q7  ݢ|SC~.:>>;CxL3{Iϥ=zUt-yEE6ۙ?JFtd-A]}-յn:ah:!M!m'ГN򈉍') J4o}#^%!.[ʵdw$lӺp6NDvc u9u9Q"QRbwd'Ҿ];ӹwOnlEHZg ӎf@T lpеStM75RG_Tq$F5%T0Gtwgo7|8Dߤrŵ|05&.S8BP5W-s~Wv#9ފ @4雍U;Y[Y@^va)O$2pկ-_òfnmϸgO@kN8=>Fb|ޢH]9TR?P-ao2gOK'ZQ$'ʏ~$nr kٓP:=^~s3L|,؋?"xѢ H5]P<ۇ1h)n 4ڧg-[Ǘc(]TH$$Y M5NS`9 -|h[% Z9N?/9cN:lq{yy;:О׷";C ;`*" Vy/_.#Qkh܃`Imn]kϛS3H9w]9i'KvOOTpǕ2gϔ%NVUH|y|hĦ^挽)O=œ Rx;TҬ$YD^ 0+?|o8wӪ~%7M`C2(SaO[e&ig,7/m߷{#q|)Bd6θ/i6ħ>ᱹoPtl[\[$7x]ApXme[_]ssgPV^Ǐwݾk,oߚĞ~TLKq ngGs+x~\{Z2̿l|l2@yxA ʪq.ӥ}>NF2ns2)tN' ]pV}zI=q8lZ^}zuDRj\'%@p+- ݻ tCY} uu MKGuR >7$ Zl]# U((JX0 * W_}9a6%a<!7c mZ迏>(Y̢K.֌hhhD745ӧ#'ߜy1117]7yp&⻏'ѻ9aL`.vRֺ\v-k}c(5ҩSGRX~=qI񤦤iX6,v7nǃ/Cj7\T|55ddP]YMPm%ƃA.d5&'/XD `c5ՕxQR"[VPHcG]阛OYYO=I"d=h5u|b]>cbD"A2RHOHUp H2ae V]׈f!"*(c(:EGS DA"fJV߈lҥ]i C O}ă_'W >N(&f0ip(0ԫ#YYV+"2+eZ A(툡]8mUA'!/˖eެϑX- *ޚ2*3)ItjCZUf=A}RVUl`04I H8B(r!KbM5MDQ$@tTMC 4C5 4C5[!sv|h I1LLL9x30`bb.(!q11d<ԴdrDBL~a o& )) Ӊ&A@Ӹhg&GHKOGtG.˹ 'R[QN0rJ8tsߜGSdп9 H[KmM=:ttQQ4YhLШnB7DT@5DQD4,z[ȳb IDAT1tCבd!Uꢷ>ʉxO{ig$' n;6 Ӕ rNѮ=zs$A w{E;[, &ϖfG쥿ah9)f29,5xQ jD1abbbbbbbbo]7@MkW{v %!$Bv343'x4wߙ~.;y7 p;VEi,ylԢMfʀ[37u>L<'k ]vvfŒk~5DuuV-KF3cyg } M# t˦ %[*IB;ގ=6 }{t[\H57mCĥe`qd> ?/^]:ѵgOJnIg8=ڵ1G$ڦh*"D5$$0r S^^Jrr E"bi EB ;4gE)"3-p{L[XdPLy,BoSo ֘;!U$\4nE2]xrxul]2~˿& <rlhͦ5ǖ.|qV1vH>O,Oov6oy1d;_?}++$;$N1X~m1@PZ٭9$04 "^NPKk7o)ry]2zJW,OF^x|4b6rb(e ĺhE?%?P~qֽom-tVW6 laSQmPuo=i Q:uNl\ NHzjH"VICAA DBD(QJʧ?-%wV&ېՠH~>zjigu' &U|4|4]+H]uҽOHRvNuSV!VDɂvg ꟟ݱ.u,-J[rUT/)cm-Rݜ &M(dwHk ,Ϲ;`uߓLAtzz[Fܤ,(ӟ8Knj:os˒- Gx>4{cɿ>hrjV˕@1n~jSh^=,%8Y?bő~<.[TOOyDsi18*K.)ܯiquьT+4͝ă.77[euNGzy(\p:{Z|_E+ Y Okt ~{qᯮ!MVb6>  B(u銿r;,V,m<Ĺll+,$1-~FME5im~Vu(x!t VP4ł*p9T~d&?z8uE *!EGTQ! QtCշDyλYuk.0re?d0,O>TZ>5`Ti W'¶ؒSڰ{E I"u^$I::-\ւ?AaLJ}L>̃<{7FYhʉ-{bٌ̂wsL0t~?~t2!}w'7h<3_׍gswr s`89lVfQh-vop9I >]X@$:e w;xa Rtv쬵_mq=vgKkv8X(||S @ d=n:ҿ}0ZG@ :Ѿg?bp] #*,L}.O>!eim3qHxdgsam} @-  -$zpIM٢*D"*~6v3>qg^j e Cz>KboIqG *z^ aS9uܕTZ[w<(E"q\$??W1ByEr2[O>g. /UYW찰2`qtB mQwg\{g8cweq75@ _NiTߧ̝ly}<"לְYs͡p-~[˵D)Ig3ah1ٟ"~IZ3Ï/Fc峆o9fJMf>p!`a#ڎ\weT+?W=vO} ݥ>Ѝyv`ogvw|u ,X4َ(bR;lȒ ͆ $ɡq}  G"mV\V@v6k6`;?>G\.]Jyikmc@peDtgjү{V $9-,IGT]CuI04YĤHJp!^lREz^ycz>5k6":{>nia>X뫙g+({ Xռvl ~=sחs.Y]e9Lk7/j||4R2tzM|EAaUյ|x.(D&/>C UaMڞq9UlQ.rQ|5G۠L{N){&n)os_Ef̫a{J49,卞2a͏L˳M.V?3wG-LIKz+/ bd&_ݢMfyxuj)+'~!s_v,"o{UY=궾̺b1׏DKirtsݽ$7x]ApXm-1?2'TÖ%̯HcW]8dmp?`^I}xܞCz0,'6b'݉*V !bӶvΧâ!.K~dUZ‚<ګDQn:gnڷS?w~qP!8IIUn͎7؈$ID洓GZj5ڤDac] םX> pPX\馱n鱌*?n?:ʛ1c6cn/YsfѥCjkk{pHVQ>}: 4hvĤuXbanbGv5h}')1idx^;, IMˋ׮]Kaz>=Ajz;4tB~^&r;$6Q&]. "l; >  vLhTs^{ShxilEW]a za;~ޑOehƎg|(Qkj'`$첄1DI tQаQm˱[d %Kiҕ.΢՛X@0LL!pb4abbbbbbbrLu]5 /DQ=pI]nVPрa5|nhESDӓHHAA' CBdzwD/A0TWQ &Dn+k e͚u|ӓSJ"aDu@rb$T*kUܜ6tRHKI! "GeE-" ;E6Jw_3q&cΒMiy%I1LLLLLLLL1DQ$5)u끢9[CUdQ);),:aSUSGqI n%ңmN88<25ul޲ U3lӆܬ ]d_X[R/؈ndgsr|=N>VFYM .tmH'\Ol!J$!1M>.[*:u\"l7[!ч7GW5&8̇V&&N:4AeI]@LS@ lo`SA=Uut=/%3_mdC61n,ի, +w>P}{ҥC{3$WnUu|WoLJ|9l..%.CjR*nEp:haM!t۱X:QIֵ=ш&P0tPm+'wwlY%b6 #Bˆnᓌ˃ǔα}I}?/y;컥틌C}@o`_FKk]W`p;3ǴۯkymQ45111111117l!XWӦM[ۤP%+6vQSS3s=b%jj14lAs "j;dxn/fKQ}Y7 "l :EP(iI ! EFwq E0"$]3NlA54A:d bs]0jW{~r)mIjCoԬx^tӮiǴ}1t\gJ z70ew5{!Z.&Oy o<kw}_ﯨ? 9?!ѧ1(E(OXT>jšCkjsJ=7[׬6;1k8=:vECe=,%XL,55^*}<ADp2EG%SLO_aSa 5^/FLCrr"KWa5Ÿ1?8XpdY az뀈(tmiҒ( Ȣ磼 _0H]TY0'PS~g|M2-7k{]xDt_?dkO1$ Fժ9I>R\S=HH_Է7ɹNi@^ @D!PA`E9Y1Ag RQUGRE9(}'_&OtJ=qg:&&&&&&.Hc}+7l5<VÀX5L @3tID_cw kRM}>`E+6rqFr:qpA G[Uf6)IDUAEtC@JW#Iq1!"i)H( (޸EՐ% Aq "2U" b ՘˿YЙp}foC>}S1;JӕzrOx2bh>pv䧯fӘ|E3m2qh5ը ..&NBIQW.P#L(prڨǝϷtIqpYݧ,X4liyh5);9ܓ DHӠ=d('" GwSfe Ў(R7?nMf9I@?5MߕՇb,V re 7^w&.sbDv;|-am$N=sMc>ظOg6kۻ-vsh琹7O>䫚>31111119zQA ߾:dp;7 i:^e&PX #q޹琒!)/…O V_/N:? iCAkhfzv[$9Tգi)IɄtACbPZU۞ Hmm=l,`kv!`i'߆EjՖnr@4^/O={`9@m &b@َX#8;H<:Kr=Y"J$ H(j-2Pgã*;u{6=APJ/\; X *(6,W E;*E.U)ӳnsc $=O)3̻3μ.Obwp:)|>GtJmsZQH:²U $01u~W/6o޽i Ȗ$3â߾#--tSC ώ̌m)2my!`Y8ncjg]qhJgò{7wV9oMbp|vB^:S7V+px7ɋC =o/S,%ʂm"tt:Y۽ʻq]Is+dջ4ܕudznQq\4Nf My7y6666666`YEʰGi5M\ˡLAh۶-l-(ĥ;P*$!`6S_I!lDQZe+h21 ҺE6mۈqDT(}cy=Iڹ{"ݳSʾ숣emcccccc<b ۣ@~B2Ȟh׹?B48Z.+|OvјzUN*;$_:ہEetҊ UW1VYIn^cz_թG!ptx CpPP\!ϩSQYA0@ q9Qĭ8` haIEshfha ! Ӎ~s}2o" qg=2㗏wnDm9cJо-ٮsnƠLȈv}y<{:Z;rCaljc g[ Ʊ<ydTa@K}{Iů[ `2C~QnIb,E'X  Qjcȟ #th*&akLsWSYi"mFMto{tt3ҳA(ӣ$SKt4݉eU e5U|7##hF$Zd85y[,R\ZNE4Jy%c_}+| 6#>Z@WZNv?qҸ]ux9ፉu^{d7^n^2^{)t9|ӹֺlrEUe=Ɔ3Z8.8+)ڰM)=7W9p 4?b?3cXodo17 VrUظ/m=+CCed^.]v!}ʷ%;:A{k:V=u :@y gcccccc%ѻk6osNњҊ "sѺE&,BalV̷vcko`„>|xtޜ'1bAV/e_7}trWrK){P\\̃h4HVz*g{>SLAAbϵ]K6qҥ^DaZSXE2q8$v: ?!)) ˃&Xu]GUU|eB<:oE4acccccccC' n? K.aY&(*D2 +*P E+7i~rZEFDZ2-i"L3B*<^SӉx.4LĒ,Hld]hh4*d&!~ˊk|-"ccbeWAo:{nGR?n\ 7ꨧo3v8nw$FUMZiXj~ƈajTZc<Nt+Ob \/ ~)2em[mRSma۱yODQ.]Ln=8u 0+$/ʉhLzF*I>TUt]Ȓ D& +faq @b(!2aI)~?ID()~/BAxEK(J+w0_wmHOI((gՊ5x=֙),Zju1+UMnnFY'jYlۮ,ݳ(CW0)E1}ΥcEXƿ_۫x⑳#hb^"C=`'P⦬W @2kwwsزa#N ΡŸ޶̄U8qMہ+gSOh?t8Dc /(ݺjTzSXHf+iɸ\NIN]Q$Jy *CQF#GT٠Z@QdEFd$,B5$!\Wq9 i@^:kV p4  ֲnߎ^#i xļ^Zn-"rhO&0?`q-[eitk;I %ϊk)mC@‚QTM+)#f] H\ýaq }OIwy`' 8|b@!`Z,lllllllljʯ4 ׿i%I!%#t|^fp0,@%bPdYƲbH2L0W;rU $55,P d R<Βl>B7mez:AY0竫Y8^8\Q|eZdfԱ wJvm)-.k(.`[QeKWи05c 3W^& ཀྵ#6s1te-sr}o1}sIv_o0MI4x[] :kt__S'0r~`fӥ2,/xs/7CgkNT82aٔG tt*$fj$IGE/_w?ȥGs[(DVf:MY00QQ σѐ\!4 Iޝ4ȲeY(BJRf-D`bBDL4L`8@0BdR}~23R8T>B1GҥC;j*WFgor3cTEuҵK{\:+ˉ〉7 ^HnP&o=JQf"W0 [[F?9x^7Hy8‰/G3_{:M9m6]>wLiڷ%#6U߼I$X'Ӟ`җ[ q״L/+;yńOshN&%|v'rd;_C*J_@UwTU&%:XH<'!pݸU4Vr!!Ljۥ$Жi $ YH(("aj Ye&X31b&ZѲM*N$ŔBBWb9S0f6Y$;VID&XJAA0Ta,L^~f<ÇoV;c_*$KB)5i~ӧO we. dz뽷ڹ+L4h2.)S쫨`0llllllllWǒ|LӤO>Q@MJ`ҥ\旓݂.v&''mINBC*)ETɉ@5L0@4> J¤ٴvp\%ؾ,I/s>"@R`}tq̚U9?wg(`uZ*:FXRزn3Vn kd8pȥff2ƿ)^ˌvϫ(#f1-^ъK'Yg^6ˮyk <0 v`׸0,ۭ2=J $[Mey+~ϿHNM#93={c$;5b!KlڴxYH -- p\8Zb;@Ӵ*IFl^^7dD0 .a#)d2YY6h y"iz? y1ҽi Yix=nJJ+aƣ(ao|1L۱MPZVh,f  YNb;+BVv _rӉPRZJQ~!q` x)a(BwIuiNNt:iIx|x^ܩXX JJv# 4Q:e9t⛑u( PM_rݻw#)%-7Y)FZj:X0aeV] 2&AlbJQ"!_\rS E* UTƦA–H")ǡ]KqeZXm (--aڍXٳ* Vp&qMN٤z=x=i߆-[вER5ˈ`@yҲ֫@F]E0PNQ&>vN 7Ce:v)d@r mC ѾKgˌ" MX7֭@iiYiXb]lliO!p9 2r-W666666666.>&s<#!J$eIσB4[pdT@H ʉ]@J% ]18t}EddӶu.f>ѥS;2;u#3XB?pQ4ălH,Rp&-9 .IKEs'.-ai֋պ C8XIi~ I ur.苙!ᬧaT$iY7?mɝ$Cx{;bxA 82v`U7Z}tL8ڦl׏1A=7)÷S[᭦i6FMߺǒp?xFv6y/SPTF4MVF|k}=#cZ2ڝ?{L\|ALX-goР\#@?d;2[1ۨ)]s;qQGRI{]2;.˹_qcy<>6F&mMQQ߳׭߀@w ~foQ 片oInɉ] sĚqPIy]+| '͍)`P=_L$euJbgm;f8İv[rO]y6&C*vxiXvvg{pX&!,Aنw8=t ]θۦq:/^'.~ޞ|u}E!%3GT.Y ,K5M2MVd]QEIXqEò FPaQ4͉Bn.lz"nʼnVD"SRVg{`3N9ӎKIy%r,Jzf+$,YEvjh._U0$ XH,dDU$ɒ@T6w1+UMnnFY'jYlۮ,ݳ(CW0)E1}ΥcEXƿ_۫x⑳#8a+/NA۲BW!Tk& IDATx7p$ɤQE {,pȎl(IqSVP+ͅ<,i.m[|LXWf~"EATF,R‡ k7O |m8=; !{q%db2ң߬jwQO<ZLN:k}~9;N*+~u}3# W̺Js?`Bve6][N3,;+YK21MjC@14[G%,x{L}כJ8dȽ ᆑ_c!'yK{Ɔf++h“NRo\#`!Œ#, L9K9Wg5PSm᠋;]+v8vI6*(糃8κkOexfk){BH(RdX >o_pКU.Y\IX,K` !DbEXd )2e daLT]&X"7PU0TY&)^:O6k%U =1 anWzRryqL+F4"``X I8PyT[oڨ4'8/K\=HJM(]Vv^|Lo3:x¬w'/"o:^9}Wnʠ{`mg1;ʃO_s^gAGfK&;pz#KJr}unru;K}2<]01+θ|I7OIJFbηvFx4w^5L "N17Λ²ʄ+zYO>G󷳻k!PR#YO簣&;@X\xz~]5_іp0,,Wψ vYX⢱C٤Jtڹuֱ7ҧC:lQ^k|/^ #`˓:gsyt?7H47, 1'y2CW1ͅ*f<{nd=-*YI.*Ky;YPϥ#y:fw|$RB<{nQ̐x~O-tÏmMĔnɘ_˓D|{<;oA]vY#};$\“Z?P?.7,24&=rX +٭R_ܲ4_Ӊ}_s3SSoefsϭf\y;8u(VyZtMy/?r{jH~7zG#JSnnal-KHs#DH~+8Ԗ|2zו>}?<}OG_`[n^5f)_8S{o -yۧ8ןJ,[6|!/'9^?JH>KzOӕ λvU0G]p4wƨۿg+CO>1IN3Gk·|ȃ|XEjO_ogӻʥ>h<^{;x SyU;)L[ԁY\)ߚu K`<:S>൚Ux5iʶyND9ߩL|@%m!aqeieɞ v9 5+0H9<$dAI~>mx<i>]Gol܈BQe2b`1}.zIxN] F$я<ii ˔gƐ.ó#0,<đ$f,Q_5c\ڳ12 0DMi"KMMSP՟Megj۴+F݀!d _. $VW7q#q}V}IO"+?HiGv}&5ʽj ̸fwW0)//oYHk0Ϥs*UPﳲH%8QY=X??g97oSo%{%릠ɛF i᥍A71I:S"a*YMnT{ Vŝ|r/'T&MpgR8DZ5 C{aI)vmS:Bu{?v`&3YGCRy<7!]>m0ݝ\mW #2_uMťre'>?bA 8(?RéNeG p$5In;y4)> qI ݣw8$#Y`F.UTr894?>B,j>:Q G%~YZּIEXt栔IJ@M!0AF_;( uW ,2;daA YAdY/HU|C%nr#0.9JvÐ54e(FԈQdF} eT2[aۚe2doGP0QncO8|qToK9r";_(W)}"t验&/ hw-2 Y}<0cc{Ñi^EefA^2ʕTl݄jr_jX(@Gš$2]6uu"HTI";"9ox p5=op_y=~z4oZ>X!w[om߫ܓyiqːk(${-/1=XFաQ~+^9 ^ϙ'cM"=^jżb,S/hBlHG^9P|c$\j2kÓO繱[i [gkoo b<pߍK @Rwi5)hswm=ك/S:״=`G(9 [԰RBqj|[5y'=-#vt-„b[FH'U5.<f8;WlO2 ,-MZ|7 _fm+c}yh2,dm19vi~etK&㙍s?~:7L{| Fr>Kfrur۸H;RT;2W,蟤q{qwMJfnr:V]חg&dm켮0[bgmnI}k%dI6I\Wa [BAO6ٻ`i;eKXf?pcX$+drQ=bh5DS38 }͑"'Ӷ0Re#7tqu\ͷfwJiIcOßem7}eU9W|7MCٴ*@~9mZgBiݎ=N]0EHl$uV9C*]@SDٰ~#9[ 8>d3s/ +Knq^7&rnҽpݼeS<_Ψ.+$:N:uk~E{'Ogds;.O[U^⭇_Vh O{uK,XJ0kx`Óp_(aD,Ю/ |c+SK)$Ç_q8q8.TL nJثTo_ DHB?r9Uʪ%v5 Vtk^YIsіv\}W^p|oDΡ7% S(- 0/y#LvG]x>dJCA^69ŀ=J>,B-&;ŏWvr0|f+9OcĂ^6ˮ9<VSR)@[j 3G>I>}:+sd%[E](..s.x,d6n?_:EfԻv [ 6Ӆ昽Ui_+6˙ǣO'^=Czaт)gךt8Gi,‚5xytNοVҮҠέ&\S_loْ`4vgp\]dK7< ZUtG׳."sB;̇S~Bw\:%Ѹw&`AsJZK@-TBG(\7_Z1=X<㾾zCf|KxnT1o M#߆5M?],Z{w ?7bwpOi7qW6N{cDx#ώvd+y Hc1x_-a$';֊²"Y! D6 H"r+(MGbKڒ/cьew0+rǤTf\@,}u5waAi0ݛQV;q=IXY_w*ˉ"& ,~՝% Tzv3aKa& g-CIyhcDF~9(+oq&Th d{ =מX*gvcrE)>ylx^ LT'[ D*#L ׽9pV;.EJw0{A*;يݦL娤ǶSM4>s/ i]2nI6a.'? 8\4=N?6}ȵtv`"%t%NI>;\7'_[7.؄sGOFhm.6-z^y02=٢trig0{@ۚ[[6$dQ/7 Fm@$bye,7]BߑbSq{a?)i'1igŴpD\7>E6 Е؛9S r7CώrΒ :nj^=J2o^ě$Mр,˔½xԽRD 쳣IBQh֍۩K) |PM, ѕ'97~BQpXMU 1i ٢Pi Uz5&y[pĀg{\Ŭ<-3B#U<3%_l<Đt_L`NMɝX8qw%ILn Oꁐ$: ֤ֆhiǿA[=:qخ !cgۋ7X^5ڟȸ$/`Q!sي;Б7ˊqmŏp8Te'LuN>9 ZgJm/_ѯ=7¶szo`J*Kn\ϥN"\ZJ(+PxHpu |sZ:Nz0}yS;0bBU@kYdWԥ^lgڿl(Shghf.qeg(ltl]RNfݧ =hU爐kF>(3~x )PycZg8b Sр !O͕V#s J\eJ uBW{[cxucתq03r"~_cѦ}33г{WG-[CJJ e72Ц}Gy9CP_H/nhr% iBp*K_Yz II{1$K똼u@|ip K~A7? oEa+ Trf,iGt͈ś{x^ V"7Ե^ X Pw[$kiA,jم yI+^['L&)A k"My%H&6Ap0~**J\cd݌+7EkCCi0} *aZ͟H}eA4?݉vl.?9=5D/@^ 4S|6{ڷ" OrITI̭fW+ e]rs۝Ye>k6pJǫ"u)_۳=_~ r|Uf }6>Qҁh9=tK`_7j`5>·h&ve%29$&&o.Kn8 88X~!#+\ۻ77na.DӉj!.mh?u5,toxF=p.|& Et:g!7)f}]!ptJ2kvt /-rv %\(y`t͒ t7Ϻt @aU$!!tд F;՛p 6'%.-mQ|@V ֎]! a{][B!}o؄l}d3A_Pswhk )2sՃҟe,,oG(ZJwlk7gC,eivN=G2 "݌"!q(/E>fPIIruqve.o;Y- 3qtnWx|?)}صbm xS=zƶ붱ts!R'rʈ.}GQUth:[b>Xy>ho੸\|clb˚ ,q&[7oc-絉km$RM$) -{1t ,ksĴ4ʊKشa %%-nș^Fw7f?5Y~|?_o%Ylo4?, ?b~id5z3~.9<3Mߟ6[x&յsF:)HO@%~ʥQWw< wM9%}V,Gti="Toa'oqWfr,Wp'A=!nmc!Mwʽ\Mk;?{ (t*;nG:O/pq5?FnW)zQ瀶jZ %ѮUƷ%~&׃_Q[tMcg[ysK_Hpt%K;|b?ֱf"Ve[6M.oNsGʴN#6d~Mǧ J;~{! |r6ckY]|kr}|aZu KK4_QǶ6PX6k^#bJJ[kuqV-$xO44M8Z/M "?ۑu?dĢu̒j!.# [͘šK)/ 0AS[j!rB0_ upf@>]uӢy zӦ7oCcxm뼧vyxW?/gݼ:i}!2}jzq"<(*/byJQ$I=͐5m7~H=" ]PʘL=z1Nx>Icrޜgwd+Pg樚m~1C'WRk~q=¯2ivdfb)Q#Έ6'|q+~U>֏Iʓ*. d3ƷF|,}W+f ݸpe{=Mn;6Ov dΜ\ߖk87f'9w)xx7(ߥ>uEouŢ`{;kу\8 7ܩ#fgop1d<߹3^=h66zD3Hgg,( &i ʸNs"Ye$I!za ڈZ+]V2:eܩ#rz( L"fO^۲i:aMŬzX[?B|OyPLOo?h ]0x'᠛sWI*J q.V-olܺkEe}W9VTTĕW]a31~sM:q)`AݳsNКpu[ ))2ZH#\~d``5%{~qqqȲbiqÆ ]Ӊ$6.Aݎjlb6+L*Q[lmsm\Xo{{" \JҊW"_0^/ pX쨺FjaOHq{ٗGF 2}Ğ>$"M&d2 ڱX,B*O?vVc& ZDZOE%V‚F@ a-:0Lfn/MLЛm(/s<|rc+GiɄ>J]͏A[ۑ*]i)&`ŋF] h|>/ UWArRmJGRN *VUZsWPZZʮݙHQTUCzIgKz۝N8ۃ 4aBrHLS 2/%$35/lI XY1v;M蔞QW AJrBE8 ^!%I *շ:7$+_Ze6[ѥ[gJ]hzAI!X3 xrr/p8LP!͌df~d㘶iYVm_`Y恫`wn>W3<}W il\7?:xGxqU%F_큚Π*&جuҢz5tIr A8X?;*l2 R ]ksu[ۻs .n+A>|b6*|Ր Æ,˄!0@ۍN;an!RXLfa Y"Iڰ}{x}Zwj ĝʿl{vk\uç>KR; k J Wݷ|T%īSyƷh_aڶW0:h'S-wu݌E .ɿ ϫ|;zp[ _kzgkbzZ\e՗@9XߗpqJfs}ΜZ֌xλ/$ d}E?zK]2 ITZueX}حU6 ]C1f3m۷CZp "hzxG,6ł(Hh($BHeI`dJJ1& +&dSY{;pM}I>dKiw|O@؝_,X.2/$tiFXdY.b{2Ħ+ i@-"fV9yV,~ɜrc2<{ np#7s}}Y27 EW^Solg].EDXs(ݑƴ_!h{V29 ?EU?j 5IW3h>i\2h:}ζvwFe"!*Dm-kn}a%nuFZJ;:έ4U`Nk *:XLIu߅5$Yl$p躊Ic0d4]Pbhld'O\hFFgH) I3ۏ .1^ 4 WWCL<ϸنL-Rb&5 np'?$E!$"]xq:0었W8a6KgŠdC֩kCezyz)2e#<~?ٖhn ̩ 5 gn`mY|z5L Q]x]\"%aʇwc> {-j_%i|I, {V(l{q6rw݀] )~}3ݎ׶'LuN>9Z4qQ(Z u=M7\0%%7i'.-%(:' [UyEz']3sFc``pH?~_~U?_ N?6WߙCWjDDU"XT9ϒjE}*DBYĚST :P4UQHJJsF&łjf)'af-UIa؉IjCHڕ]Z1?ԗ=˱g+HRW֭^BRR^|g ;:&*`ݪ 7P/_{?ҟ_dOCB- c[v>VńfkrSB}ȪY4?>iZN]*ɹ#IJ:HfA_( Yӫi` (!wbl M;йtjއT 4S,\请hl#;ܫaIh^Gm^9=tK`_CLJb ,ZP(Ԭ$sZ{ee@edYcGmYC֭6D0xRۤS3!- 66؄xZF1IƐܦ~‚fYHNih/ jc鼟(/+'9ށE=ZG Ca+|>%0s&K6I(VlΧ9佉O1xȤtJ2kvt /;{'&{|6L"uJJKZܺKgv/+!*eЁlb  N:ѐO8 HXh,ߵArP;G綸%v,׷H5_ao93t = IDATwmBٌ.D֪g2J 3($9Ђ:8;FǽosHK-͟A%z=״,AX.9kWLއ精O-,bl;;gbHOoœ{5ODt^F+gW;d<[ymߛUZ'A+[X.{=hS1x!X®Rڎlü:]uzؒw1ͪkCeس-2pF:NĚ-[f/$1:]nx9SwbV/d5xi'˓l.j4Osƭ٧1$Bغi/۩r3gG*WAVfm2?ZʅϦS {=q_] ?/{ ̼&\sKX1ywڢ2ճkz ֆN#Sy=d1wF{^qVh:@a((2C4 )uͻ)#AjJjۋEuo _T}iU⵾8uCqUj|H_װ8djKb=@HXJA\9(a0jh_|.Ec1T|rڴHZ[2IЅQ\\LK֬3隟/gכ60|l_=5͵Ç7r#;"b|ȅjDmppq8XJ;}jzq"<(*/buDFtf~JZOgQe䔢0e [\:'blw? z$DnOZVƴg"'?A;4g{ 9֊]\1nƾ{;e.z+]GX߫k&{\&5.]M쁎B7n(\YjkI';r?@d2gN j=--畽n:fS{Í3p|nSGުr\Z?f')Ovƫ,([W=h6'tUe߀v\/ vʼ1;J-Huޢ{zFcyƏaM׸肋r)(* 5eXԎ[q?Y;缪yo<ېFz] H̿#˟q\I,]sfeŊ=#g?%ƙJS/{$Xi">k^z J+ $nwپi>׎|创Oz L4`Tz>Bp:ƶjy'O`֍\s5-*ᄁW)"fNB秼>7|J\}nr'kF6 $9t^=o1GNκ--G.|R~Rf4h``p%+encCp:TNsgӣk$Æ XvNXXbvl6ٌɤ`25<3~-k;گE Ϻӣ |pU+J+_s|!"ʕ*GQ-F]AjJ,X,3;R.!Sl۸qo}n=6SRMYS^Nc7>-ͪXlJ\-<5Dfo ZT㴳5:}`eUт!S;]yuRA X:bQ5^kDmPFӫou~k/ ݄!JƷף}q_4t`OZTvd呛FR̘6{阖FIy;e+;QumZPudLރšܲ,Z;h,*2hP?|#iÞ:fF~Ti````p՞煀jgwӭOVt~Uh]zq!~_~QrD$U&&JrJ? !5H $OmcHM=Mf>r ]*aMM[6{1yn6o_K/d6V"&fUql>h4"|fVpvW$d$]F:r5( junm[_ [6*|V6i{rhtI c!/޺o.49{{W&܇bORǓ`iF] kvG,Vu\ 8]1݂tqk㹷ѻ{Ns OA5-g|l:iH=^yZ$9 ,cܟ#:<~"qv F aR0fd*"M UĊ]P#QmWnmчUȎ?We!++g׾6gaWVqRJs-'&+˗!7s75]cĠpҾc;uJuĝʿl{vukv>_!&F{lꂷq-UT/-ot1Bpgqm߳}p&| =Yd65;oJZɠUA!fUѽ/"A-^*쐑@vs dqtb2/ʦ4>K: 73Ci|S0&boѷ;NIےp( `],I``````pCQLN#l1i:+Yk ךVgr|T~Lʎ}%L^ SldKiw|O@؝_,X.2/$tiFXdY.b{2Ħ+ ih*B`oA Exu{rNsFL_n?\Q۰|+H v(K4q7\BW0jb3o<ΠiemXm-~̙Mu:c;A!",D^G~JFMnŠyB 3ɧ$_څa=kJ<$_G W?/#֓UaJ/MQy߅똜 2s%]!%ęI6,B}2H7GV^~~\kY[e,1ϸpngvcrE)>yj)Wbdpb>x0"JS;%p&~1r)9[SxN$w6Fƥpw%?:%8`܆K=T@˸ B6[GI~!oLHR%T-!!aMhb9]UUDlsBL&SzMN(Ģ TUkc)+%-#=dO=d7HJ .΁*n_-( 6rw9aViLYc1m,$3&>V/6 F9M1t%%u|A<3fN"lIcꫜd:ï[WϹ埌a+Qa~^V>vGxM&h@eJK#vp RG 1U!A/}]ܬ] =dS&+˚?3ԕ9Ϡ k\|(& Hdy̞"zRtaX';ΪΟ2Ni ɢ@&M=mE<{Otfr Ci,݃#z&L_3 |zڤ|?oK^ô;v5)_ όvQyMn!vSshºFX0,!t=¨Q=Qunu pm$IJ8Y̯}X .ɉ 9bN)-.07}{1F5 `s ꀄ I@2G|9𖸂 ,+e%$%%6wƐ,c֭ZpX'jh_48?Ƞ aP_W BAqF<ȃI ]Q3ڵk#fsq\5gvbaRtAdY$Ll(",!#! D7hB-5h?u5,t5Io{|V%$+6чd2>DYczݘԬf|%g>||`.?2@giU]8s^n{yf̛?m=MktRRۑf?JϽ4K4x3}Es[ܕfo wM9%}V,GD|ra 3^;Ja=8w)??Y0f5"쬶ªp8b9 :cdڤv-$td5!H՟BWf5 v4rM'Ck7ĝ矽]j%E~m"̸ir>v.)itέG${Y~/wK>F!L&Ny,7)7HPb{FJĸ FCa o(@ l`BXS9(ԪjJDat]Ca0Pp8\=iaAm^tYm2iaBa5d@BQPLLd@UX,f¡e% _d`?/bm-nȬ3隟/gכ60|l_=5͵ÇG嬷׆hmn牻nz<{p !`]3y+aGBG雮kK/-n>7gȼ`MZ^]”+1$9y}!ۆϊ8zjKOtMfQrӟ+9'"$2:H@+윙F!ӓS”/d}xzژvyͅ=/eN0 Ǵk:JL ]ف+Ā fPM{jC;$Y8vW.nU t`9fѨ{u $A,Ȳj v#%#I&rVjάtTUCZuk.~+ FQ!TU̺*QR\ihPU\$I9K*_w63Lđ S_JlZdQQ1x'~Mv+Gd{x\xlgaj;̍\޴: yqb @ ̢+7fq[\E3̽?F[yN=+^Z{77+ws6Kri~횰+U6&:qketY#t*eTiV?+DYrV᛿;Ibh2mb9kM"{o>ZCqi9v_WеjCbYr^U`a )- KxPZ##ogq:TBa/6C%tIG/½7Kb,I,ia`c+YQX*RGZ!aq:r.E TvoN0oaƵ<-WGyi!a{l"odc:L׍N8Wpd`TrIaȺD8#q;̟s\~\%at]ֶV>W[}ks۞W'#46QZdYPKlccE9 KaImJ0m:aqagoV6vc2(97w#CT" FӪ(]uUw] . 5s!ܷo:3='|x'qalh> X, %$%b0 ƍ}8nx!hFjUCќ\۳ֺ,owCNd(VJ4T*|o?|'I8sm-msYEǐHdFP}z6raӑ,]|9[?jq`_|ֻRb~/x3 tP`ky||v娋egrၢ6[mTgd:j4V[}2fh.w,QXabX,e'F#@J@rB8j VCwV|߯ !Kٿߦ&R^`cG.!#ڛR)q:^Ƿ2vdKsl.XA@bguLM 'qs V(w雏{}Sn=9u;P' }s\@S|q\8O&&5rsϧVS?b_&ɬr IDATRX,Õ $J( ʕ }(b/$@)85+n5ּ<=Dzݯ߉&1fi8Sfa.<֭gh8\/q\ 4F*0R%0Im0zRĨ ;vNO)ךի6?3Tb| %tp#_~;oyLoϡR_[_3~LLeu4}]|C`f0N! , >Iy>ip)Z-b(bPD&&!qR$Fqf eAi + \QA& _YYp!&Yp19c?K>]J.q1" .ؔ@ ) l'McPFBcpl:CSX|ʳ<_NyGc5_}Ve^0ꚪ\L9v /xCp{x~ť.Yn^ҏ? #{{ xɧ1E]U屿|ʷWW:Uks4\<%@RbAGڨs$qc ~rMn\fy gt.}Οw?M!q*VיzdN}[(pu'V2iNZsy{+,aew_`hJՑp\|3c06H!Jި _=R:c<i~L}N>+z!٥1}_~t_[w2&R#߭H8W55ub]˿L3y-j֯s<^2o맃]:xaePtNY c-7fL2>`6}J.c^^-j&_FyH|m\#w ML/f|7#SKFcd-eՔ,G`6 s'Q*;[eY,by _ 6rpOTFq$&Ґ LH4P!ra5Q",cÐJR h5hM$h0 뺵4FC&r® *>:>ڑAFW "E*RM1=ȄλN}+ZKJj-1q0Q0 x oBfpޥzo?,@éӏW,~o; ~~~C쫺low*|`Sgw\MLkx&Iʇ1[ٳT^ ^MWTN >seZd 8qK=+hj8nX~s]<9~1 I}FP^Y\C搖aO-8ىjv( bX,˫OHRG&s Gt$qUXI#^:KEJ%(:R)rLqJBE5kp"`T*W7Wa J$%c13'LP}+$_!2-m~z`&n#4qdH&*FĸPԯ=OqݻQ 巶.)^~{6opvկqn3|[绻c{a|_yH踺?qpV˦`tTwoO.r4 _s:AGȵq@&P|Z0gVu:e2v!sw~Ay76$A$/|:/~y9ߚć/%Ym?.lgh~o+}w bX,˫ D0U242# <'A2 تZt1DQCV]!`4exxbKQ \*A#$N`(сSp4F@PQ"1I$=NTJ% az],] tɏ^q7^YL#berA]7RnޛzSvdzЏY39ggnN8i]u޼LTEB~ۭe].ѩsF[e3\íڈbX^}UJ$Y!%)%i!E"PA5}}}CMؘ=(j"q>jhmjmR0Ҕ+>2G %'2HG+ič#dWAx^r[Q_i|O}?gc#=VBT2EC0^S>.I\2k'ۍbX^}$)%)QWiH(FhSz4ښ5r j*ŠWmf ) NqgFud2P BtdD R%1Ba})TȠ@"rF(Hbcu/"}Y,b\Z}X ~{ʊnh~n{",bj(0 451 |D6IӦ2 7D]=%(`UUdٚȍFk]CVaqjw·X,n"EI&2BGgtKiDI&=R$A G)aCgWf3n]i("aiy_/ߕ>ĵX,+1v:ɓ6t.EZJPjrDD(&B5 UT:\.S*B UJR < ־QR455w[ǫg\[GR"B:F+8ÈBH ҡP 1!aYT+bEVV[Y,bXvcN;R *&bժ$]HdiH'Dp$RZ*c reݚL\uy,2߯U;j B~XDHaH{ W*rzBP.ycu0OOOAdq A,ʑ#Q l2APPTe()rɼc!|+vS,|Mw->UkoS;yק97Q=9na:ּ Hfyיq8R7O̓#~{Bvuq%]51wG{#3q4*m>cg̺EVjfSsmX,˫ #2z3Yښs,X2}$0 @!ܗ7RJb#1f!jǩ7޻pDSp e|? k@q=R^Zښ9MGl(B8 1a[I8a%1nL;cJXFRcB~Ze~Cr:yL<0Ʀi\S9S)?Ln?$>__uuqzԓ3xәq`@\~v;i}*usSW> mE/w͑?c޵\#yDΰ'cw}rvoJ#~ط6|ɗd*Y ebymR_[gܗ5+ Id4AM:(E5.DuB8y^MJ)QJn-fDo2A)Iԉ1!J 9䈃9崓=E8B$RJrFrY֤{Lp 8bX|e<` @Oj.5Ƅs߻=G͏:/?&}_'~5n\øjrޙE~uLaƤ%KնR n9#)\6|CwMQa?X•;ؓ#]3q|{877T}Ky|Cò;7ww]^ bX^q59`M%ˢCM2#p11&$Lբ2@UJڞ^#731$7gH78TGaQ],MXnv"(1Q#=ˢ7tR;J1"4[dJ!vY,FyKfӅ5#s(s?Q/ǝ6.!a}]>;}9Hmhη|Yy-ws9W0~m'L1|GXx=^7;{~C[|l<7惘7sw\Y 9fNPn],؋bX^sZ3~B+m 8$uTF_uI7I*#Dc,PZk^_]Fq<,UM$ϨaJ&i?ή>J+ TD&@HͲKY6XJ/QǴyvi:~zb0rI$svY,HUlf1&O>f7=03wZcOL8yEtiK  *&.wL朋b.mqEdMeYW4&@o`xѪxU^='g:~m|- so^ǼN_ڴ]by){'IO~xBLfϐIeH%$\8&cV6 Y)eU |y}!vw^>|-MCGRKf]}F%\GZTbtH\wx(T"x !\ztbXR5o2U~mT%XoಉYj\u/h@6Qw,B>/S a,3X̂[ޖ/wTh| Owq]w{uqmiwxNKrσ2oh#ݳղ8^ʟyyrOęT{2T4z{'$bX,M:a@.=DK{t xLR*FNAԬ"AK6`4F R~uuTJ8R"1$Dǚ0@B0C9崷ԃM`jюc~zcFJb0nEㄆtS]ihq߹o}_8^Sqաc{*tŚ\y@ 7tzgRĝ=Á[:DϜ5/竂ML2W;;}S9h<|n[tbۿR6Gbwۓ6}{9S;~P x=Scw\&%s{rp|mޚOwE+Y?Nф> /b&k#N3 G9 7D$x& ٍuT[KYW7MQ1ֵ$tXF1Ƹ$Iy^xf.)#b@ 01Dt"ɀJqƠvY,лe=Ollђ9ɋ-vgǣh{S}7'tNcs_ ~~mluGm>wM]p,vgQo?}W.{ޚ]MX,J.s.~1$N!(D)IVݛ]?wH ͏m%&`b^@_?i"ȈBBr~Ćпf ^BA &\C%S$RIR T.bt㺸2}q,QpۄbX,U@|dci TxI +>!%Z\'QO)Zg6[-=$7h)vs kV``Ҁ!F ģӮ4eto7X,bXvر8tV.}<u_24mЄea bТ[ễeWwo_E`$Mp:.ch4|5+WTT00d ,]BbCxYkJXFQʬ EjyG z9Wt8}|S89ö=jsos^)B3sYPw~:ɼ$F)* _;*qՓ+# g1'mXkd|V:=ANWٙ7RH\>H:[/Ƀv|wu^,b̤@cK;xiKP_Ok:G4k}LbnC9{E|aDpsޡ;}u9rNDy51ذx!|/:ĎD!Fz 6&s( tl$,q4ػeL rF,WSv޶iORҚUj~\vTFx4NQঋWhF\wŹcnU^ Wz+Tk_IbX,QJ,uh!yaho#"\ҒriN7MD:F)UsqR\nx6#+lR'"tc ʐkqoMo:\S+Fh:zl+\7QͬE _ &$Cɋ~lWŲ~7{Vss_]wY|dv'JR}6]W%Xl}\x̚(wy.^v;]zɢ>2ɰ4y,scʊ/vO.7_-_៞f#|>[+7'^6y8}ggUQ- ;JbX^/ 4!&xxB "T T*Nhk"xHۼl(یh4m*auPɈRhJ%~EX.+i_O)1L馈bJ_"#Ba>4jrJXFWnH]S6olGȜ#J 2rKHup-lw֯ue0k hC2M.??Zyg|pAymLx߮'.,.0Yx9x́(=wbX^/[9h[KuV5S$ƃ稷8o,y~r4t.8(O]~+K FV?WX,zocT||DI_T1غzێ1p1KVjvHJ)km ۡ[:ow ?Wx!*HV;И#B0!I&8F HLpB!Hەfj}Cf$ 5K -3qr)F>֚WQDݱ ćLQ:Ƶ1#wMM?2tK%{ƅKBOOV@GVq~XqN~~CC#8D c%m~Peps7r_@H:{zI(E{k41` X|e,R:$~X nCם-M% z7nDJCK[A6%37搡O&Yi3Hs( 8@!l1c%/!F(JXFQ{=k-?)gYa,}7*DGіH[(| R8c0B ECהE0z#^||PgW7LL!?%(J9X\.`SG J%D*_(DFubXjX,mqYgq'rWGwvzq1+_& "S-4Zkƍm'zHmuy!:"7t`Z,7O@T,EGO@8$hI%WDՋ0"!%Ug1h aα78ֆ/᯳ݫqo"Ajt(1a)tZƴSH/"4C.fE!Hԣ# ?:XFُbX, 駟+_ _WyoZ,C-^Pױ1$,tUʬ>fbCu:f x`l4Zk8{)QB\I2IRd岅lXfVܹW7׫\CRaC_:<9_ibFC\B&<XL> 'Oc ±b bX,3$zOJeZ & 8"Og2ZGH I R?)A.ٕf5V[,eDDc=5\5\CXGby8._9c57r$ŒOe0eS|_};Id["H5RB4`DqL:dقg~y THxyqTK*q<mP+  JV(G>&)Bt$đOΤ;n,/>?|)-bX,!߇~d2O~.R|I+~->8Y*2g* J:OJg7bB3 N& ĘVq@I]ōcٺ<=>F&8wWm7CƵ(R1]+W" )%PH:2X!CQL7V֭1Bb],b5A1v'O\u馛 ydY,q0=$-ichNH@w>@skz)FN$!%P5jmD8Dzwk̸1)}DA+;P3sCd˔p":P!@J "A"cv_ȼ &M%JXX,>f̘1y~7ᓟ$g}vp1(2kX^㄁ƍ4t2Mwi!u98tzGhdnhUh p=ʓPHR4LôFraқ/u3i.T0 HnB`( Pb 3P*⺊TO;_zIM8F]\Jc0--$HGB)a$: Q(A]]]i(^f&MMaX,U&NȓO>ɜ9s8'>u] 7-8 VZ,#s{I|_VR,&֒8L3{g?CۛNu]¨Ge>@NfD~@0N@ww'RƓͥɤ{u|#T ML0EG#Z +8 9;_ R$h(`b-%RIrѮ4e4_X,˶xy駹K/87p&MC -1tO<ϝf΢TEʥMM:y|bnwUߙrݻ&M/JRHE&E}՗"6D"-Ԅl)l!rwl~>ə;gs3g3+*!TvLŶm 3E,v] .ꉲx :::dRhhӴI ix5EplCm#T=,Ĵ-,7M3qkq2fH%1;|W-5k~[}'W$I4|/]v^W^yn.bYz5su]JEd2lkIq(LQO0J$ ! Yy3ۻ;;0 (k6pV E޾(RSSC*um0;v7WԹx"C5%.qIQUGQi:0AWᆯ3FѲ}ۺz_I/鯻|}#pOsI$I#o|Ϸ>ޛn;;'rM7 %IaTvCč,Bڪ"#Fd&Q]Ө@im5Qi{N_5㌳F`㲣PT!k)++aZI| f$ɤx,=Q:AuM4Ml`/HA>@xOUr/u>fNoDڔ&I#=ȞםϏ=G^ ސEi%Ia?^}ǤU?<]tGqwٺu+{|%?cȘBTi`gw7#ܣ0vR Wgs?1'%J {aE D5&Oa6a*T_gx'LQT'їɒN%Pmh<dt*L"T/W#5ɶ%Mp|r$!'FqIűKjW4IA[w3#xǷW.v/+s/ zpo>7>r(I5=!^p!o1#7$%B7dI_۰(:FH+3!9b>u:;-tLӠQ_UUt݅_ߪ%ݍ&xxqTC>LLpL DrOa!#ݼlmnXmmL9 m4;~0g}=ˌ &+Nctċ麨2E$IFp#$I`&m6%ГTF Q|A4b%PuT70 k 9VT2($RIV]Ǹq x|~6mNڿs L|Ϝ7Wm#?/HWG+A&e`@ \T:;Ѽ G8*`;%&5RS]L}]III4IѼwչWͳWחjً$I$I>BT݃)x|>:qtU2/DmyBBT6]P>~e`\430׌pp#̸8q(AaT=UYbI|'DiJoaUgڄѴ M#\W|C~,kL}>nkN_W*Յ͸ 4BYa>RZR$#MFG((l$I$IIgUeL]%*J.ɤ6$F/Ͻ(*s?h\1p Z!fM` E!`;&~5U}9ߚxW鎓u4 TU(xY#M:--q=PSÌI)yg/-YMseIg#y? -4h?z?=qZ?[nfFH߳L3}A<\zXI$I$ H1q\B&Xa Auy44]%ؙGT*;p5(BltUC2b\O\۴1,Z7.;pX#LE0ߞ۷1|,Y|^0eNغa5|Rں1T*ݝG ɯ6::eIH{{淿~o0c7sƒoO^s-K\eqOˤW$I$i__ח)̣CqqpT(+cTG[~>C"E.XYFVp]QKUI8i󑈧8Ut3H"'Σfq-](+ٵPT?^-yO,sGNz IDAT8"6w:O(:K4 էc !I$I˶ tTڒI|8/"FUS ^oކs, fibɤqRkbMV݈? R\*xua&M/n"iYD +@QZ0SRS!)HIU~T!=z4?ZwqσOP_HI4mFcQR$U2$i?pݏw<,I$I,{Q*GL*P]T)$in I&{R̝r08i^uuAȷ m`:.$l'0Xy3J$}FF=$P8 ͶXljh:+0Yd"5Mea0zu!ޓ1jK [SWU+_F$whWnNI$Ikk9rJQlh;{I'(Pp!'qع}# Pu.ExPt hfmzqZQ<z Cx4%eS~͇ kx7mfQP><@<7 aƥ9=x:Q7ئ ӛdI҈I$I$I#GUT8PxX8TPZ:zhmˡ R|^P\aDK+.ly\+G 4-_̮-V2Bh-A:k9 >S".J~o7bb6!J*kЗLPMr!cg!m!{Ν(jQx*~jxå( D ۑ,I#ʴW$I$cxϤՍF5ޙhZE]L($1U!R./e'+".D6vl\O{fz;0L=ec*ZXl Y$OoJW5|>I3rp\ c P=(M*4hDćM%+fpg.[ZȚiThw-ֱeC3ӎ,I#E$I$IJIa ]],xun**y3umtҝJ(6eLoOe-yĻ[y񩿳s*>"ŔjdS1j GC8W@EDlECc֣7UUE_Y!5BAUTU  qpa:Yl{` "\ E<Fr^y=Sf/3sT"IFN!WC+7$I$IǍ('yٌIc]k)󣸂T:MOL>jǝq&6lbi4PQ1*`eu Ӂp\bk6%j\T>drz=(( M,$I#vW,K,]-t[ɟ] h_<&cj9jvu z\$I$I$I@ȧ䳳xFPӈ^0uAL+Ɲ,w3e+I$I$It c),]ôJ^|PU$wxM[{):77N&\X@Ok n* ?@IHϏ6p1"zw#lco{:F%m$2&~tRY(*{JðcxK;(B52hG`z=dYi4\1p˄B$I$I*E%m;$ !TR[4aݖX1¡ r K2pj;)%7*&P*U4 $W \}t/%J AjWŲU\=f޾6\1qT%6uiDTנd:Iiq9%K68_IY{&7J$I$ITR[_ɘbB~EGOwh:Sz*>1 b`r*I$kgkoǖBq KCwKe Hǘ1He1iס7ŢVP[[O}Y!ysNB棧JJ`8D^A1a7C&&I$I$ItP^Q0 6"ޛċMESDTD2͆]tE tWd1c}8F: \<~?Voˣ#@1+>;7AcC)Ed.dtu;hwuR_ȸVmn/MiΧt*FW_/]}(C2&>P@F$I$I$IcԄD~!4%8tdTkun'enҽ1|y $MZD"APXe^ẸqF3sgW]L3Or~]lhogWO§PVP{}D KDuLT/ ]ݤS4MCp-d:E<&I#}OI$I$I;0\A~0a[lP_UQDJV^|mz]G$`ۺ>Ϋs\1U@Q(B tߋl+Q0'h@u'.224utI~LSdzh᳦aZ6;vaX&%O=_^I$I$IX<` Rʋ) ҟҴe3>y4TP;{^to^GY@a!~(>=]*gMbW֋ڑ*/G%d<ĵ,\EMP\9Ѻmc/#ϧPSUN6Gǎ ,}%J+M\wϰ_ '@A^~2H!iq,JH/SDKaM!I$I梨F6eiS TRp\ Bg3OatlEdW2G{2᳦vK 0} M;c_ۆI&i8 #ր/A:3}'4%X5Mb1V~uF4N~IHe>}:?7- F}]'1Xē-ʫH%tuuy2$I@K$I$IH,dm_vFs?y_#a:Kݝ̞=UX76ma'O8 U iK ׶"EB4h/2BG5l%xQY3&e&eraWUsWz\u ~K+ ZH{g7kWog: jt.>3'P).ȣ?@xCGg;B~,įeI0AߛU¸cشnkwlSW?fy֌y=ߺ#fPֽx|IlN?#[' _ƄlWJaZ/T{ ?)Q+o%;x-WDrд4wԖN+NDh w},9$Iҿ M[+0cL'I "NyY!`)^E[;x'=DB>">jٱ -d]WxKqQǣ0yB=Oߏ 30}$Ok^]=~ Yj#E%rF<5EDOOƺi6~]ql.,l:HOzwiv'Eg}[mXCmH&_KCJ|44& 3mfg~/=y w]ǒ9߬o2hnN>{2'~ݳY'~-3gn&;O$I>t̚.; J >"J"bCkU$8дe'Vr}ll?oD E⠩SpLiHzL5`T̹? '\S͂o&fqOqy ܳ+z #ƍwV~ko m)d?8S^XiStv/^ˏm gKс Ӿ<}~ yXt񑦜lٝ_ ˖Er!W̓KB?jg{3Xh1w~{aSy%>`SQ;߭|z2 +R[[.x֗ŧ8S$IG۸Qx}uXcYɌM)œiDqcz1i6nlvsfOaʄFMJ6|#MUq1[q@C^x3]aʋ"l3X$1y,b!3%0(<:i Z[q]׶Jٵgc`14glB=]W~偽f 8m)N׽\|ν@27q#gl`<ײaa%3ƉmDOp2~\C|p-`2%.e[zzxꐖ jp!eܹƣ/@u}|O.@x:{C/L\l~=vK2$I-TWLYlf6_() q,v` R MSc'oeQ]\{wh5NˤcbQclZ:l E1́9.uphps|mnh9E8c KKI8&7mV~<Y 7s }LWuQeCj_e~&ļݻ{ ;XmGՋkh}GkB:}BN8g94kpۗ^dC'|m˵̇Z\>H/x8p򨓾D[-?8f_lZ/x>Ԑ:lkzSL^:]Ͼg޼CyT]I$0OΜƬ񍘶łUiji' U25Si<&J \u\}ܼ~SWضP|$,wo=Ϗ=3;v+#B qwGz{^s~jewW}Z\䖛׿ 3}A}Ӻ/F6G6kڶLڢ8D?Ddٺm>s2M[wֆi4M2<Ƚ\zޟ X:.{`]:0Nc瞴&ɻ~sNAf{N$mQ}ެ$*='I$%x8֮]PSKrQ_džvfOi`V}"N?Pl+PvUAm/_S|p9} ^]%k[ɣ]So2@H?]bg2p~u#n6 ʸCf,Gu{Jz[x:*0q e%d2is_Xhk+ ۱QȰmy*\F\Z/j}*I$IŒIjhF \Q5eLjehc*3y ]B^A>a[LT 8 JagqҤ#oFGh" rxt6ݯ˙ߙtQl]*7O̙ԎO#e/Dgg- =diLA5T]#kr4 ؖG(8Iw:_qYYX{ $I$IO|V0B,eGk;+Wo|D7ɸ8P,T\g`Uk](7bNúUZdA<(:N\ݏ(Iq{^ ;R".$Ou],^ 7k33H(I5ؙ'j.`!6!t6C?΍4m1<pW ͮ^*DqsGngΠv)|<YL}f݇#' MBGsbz_3w=@yap0e_!ǟ;gyH,j=_B~@E0YV=| 5STļsFSZ!x` /4qG| IIe2U@"R@w?/H[xg(m:j+Ҽ<|f6q5W5 YSǰ],k4b qG?'`"WپPKk0]ֹ`;$uS :Zūb p<{,bߎvٗ}ޡl܁Mc2,\cJ̿o\8M,h3#ޜN?M/b+5gMbN-oN%0}sMK{,?ϸ>0&#yKM]MoKo&xTe<bӮ]s)M$'8 +/e3q+gYs;8d\=?ΨjtvMx4Qt dr/g,\˫0糖H]mѰm'T#4lʤt{'+^yIsrXP2E[w{U9iǝJQ(q}}X 6IrJЧ`^ǟ\N]`eߝٟ5lAߚDӂ4%ݟ?m`}{ИJNWIaXCU#ۗ.32s ݞ{ Hۻر^xw:.hu>v8xb/ge_|"pM+kIfeGrCpT5+3`yiQ2gY eݼ?=1o8mZ}H6x924omUXX~ȵ.bpu.C3>4pI;M-O %5@ķt +WT}8 N[dc0Y\cK_7є /,k9[Rΰϝ̤ *^;x4ʃqcGQSZLD ,[v*^,]D]QJ( .r&Q`5ࢳ`E33'M{gZc- ɴMaO0a3ӿ!ǝyMѷm;7`X#dIq]c5QN+Y+̥_cis`,|Q/[O1J6lf?|IPtNQ2C*|DJCOɥ u˴ 6>%O pye}W70e,v*B0 x%IqUs~o٘s;;JU vr%{_\k ݞwىkDk7X 7垁ߙuER劗mi| ؚVU n 2+`e-[ !_f͜\~,tgiz1y> Z'uرA@dz+uj3>{V|T7_vjg<a!2P|)cCPH8 KXnDVpasxđƵkXR8F88/^4(iP 抗m7TUcplL3~f&lf0?_Yq'WSUAU@є!aL+V2Ն}qs9Uh韬=  [FwkAz17/Ic3-k:3db) B44xپ4wػp#.≣8<>uI}jr}zZdbfaCk2$ҿ,[Bʂ~.RB]Q)k::ٖTV 0pMۨ.p'rIS3n,D/ۖ,?9}]&73fEx|T.xunײAiQSUTD&ѳ&qKۻ9]MW̦*H^ ͛hY""McAtLA d+I#Gap*ؽ!'/!ܽPz /W?;4AEl?=6l|| -9rw۱Oe186 /,fp${}Yk(9;ܸΟȧ $v4{^h;|pODp̿i="Wg<?5.RӺoG,}K|4/l<)f\ֳSE!S$DU i[3Q%EDiublY3#+S梹ӿeæMt91cWBV^F1iϠ6%HyAd蚊"T\ab&EUULf^<SF0E&sJB/~o_\5p2,vNвc+2$iNz?N."]sA-1]tx"6b`sU%xjE meᲸ͢*2l+muzofޱ7[M&+Rv/JuX<_f; _o^DێrK\em%W劗{?=W|w-m28Hp?ҽu/{.FpC%`oEX=AU;۲D&ZBO@,ƫjbuЪyk}e>$Wd/G{Z6R3u{{?ЈP:j\r|}PufUM4#|vˠ6x|'00J9녪.NLUIZLs 72+bawå۶%{( - "EHU"(EA H E$T-$r)\{m?.D.wI|s<3μ3J@YimPQZDfk 5Mm|1,#B4T-Q2$bS]k# =KQ;S 17H5&X*̼lIau$yD9:VQdN< < D^n-3cxГY6gXj*i)$cD#_Os1{1<%eD<7U>c;4ueky;nNPOzTOyew-_p8#{Ӹgmv|k|/]^}㧖p13Y2ڶUDc9 DcWn l XG|Dtt.됷mëo5sEz$tnӸ%V$Y娟8ܨ'jG!0Lozb y幆:{O|tUnE#r78kМmfї,X+W3ehy>d>+V,aC&2'Ӳv*tˉשP,ܚKe33\xNE+(+ vSeQRTH#ˡ2 t}g,\ʠCU- 9ds|UZ#2SWёoO>\MAm[< صo̙\iLۡf͞ȡ#;ﺷ1_|>f̘$I]nxH?~p{D@XB֦\4}`s Vy {:;g~cK~5~iVXS%1l𸙳5f#I`#uWb}G2O=ld#OZʂ>V_˨7b05te mtt#Mߒb"-<<@mc@ng-lI}uӛa0 B>Zԯ/4ҝ+*"-|lc Wd|1GjTuk"W%ee!() O%8z|ٲArx7_G@ U~{mmCA*V͏~rW:bań8e,P9a6 I)L"1zH_ qh(?w>]T.GA@N!@Awޚa_+?)l"X*13`O~.\B{ n a!%hln EsSU!6m.O<%3Okh%ɪ CHvc9|.MX@Уt{ۓdOYtuZExu}^@dgAhEK[AׇGVb[_BU] ,~ sc9Q= _@u]}B>./#m+dL`r:0imk7R5I =Ba8L<©$~GG*I"d*jԿU8Fj/*Xn"+ *#T$N Y\./VOqj* Vl(#PUQVK!L4MRi#- ffTRS*NZ1h K7$ ]]756JK+M( or1x|n-]I#MlTfqP.%)&.<Ƿ8ǥrΉ?dkoat ̑H'()(97 I*(Z/Z!fsmz'h{Ms4͛8dalA!@ l?_؏_UI$R|aўba#I,a& bD $Rq(Fw ?êwDVu̧SRۯ/$aʥ7A,6-ʋɧuSD8iU L/ɤ W1cFRW_ϒe@ `aY+7ԑMgѰ(x)tY1c&vb . ded,"Vz\<&p5o =':%TFM\x5eotNgǟ aʤ,Xacc  t B^|=ƍ`}m Dξ @狨"?8,amA!۟@ @) S¸F֣yLo8G~M>êPLt{ 7( Ee}hH$#Bi`<G cN$L.ٌ$Dc&¥m7ާoE%eC1,%a~pГ7n(>6D͙tX 67@ X%Hg38㎝Jcs4G΀Wf$@ %-RsPU64lE5'6?qcB(5i IDATs@.JGF.T'G}8BGF%dKGUl[_r Qn#fuosk+E0ö$Az XYɺMup_ 3O>hGqÇr×dtIFsYYz EQaZ=LKbX ^ mk!pQ[D&e׏[s~c-Vő2hP?\rJG I 3EYU0,KH5Fc: jZ2.A6gb˽kyxK>E>B~rKSIYxIhN-mm^~@ aBGQPd 7 6:ط ۟qqGS dȁe$Z(^lBHH XD )2K#j.v9=x|^$IPId3 (_y Ē%$r 2(躎$簑1#VdE0 2 t U  u;=KgK5Fϗ.es{Ny۪@ߕ,n~ˎ3] vdYzook|rs<53qrkkݟ}zA.+- زM }\N.‚r,G!յ1>x̘eW¯.? 8!P @Ұ- I(΂lR&Mms eȻ8#ȑN8NaC \,ea7b9KrQVRB4 6PU2l ۶,l SL ķ_+8uVi_~>8[^sM;U3*/_F&[lGleٝLe6gkm`NIj1`Re2054B*rjh^ !CƲy|6tJK;v,CGE8\Hae_$9۪8l S>j$4!`]QhG C˺~&m!K&e!* ~E˖cJ2CEFt\+ N҆J Kz؂1gp񽠓y /kIW)((v}Wq?ۯc섃¹s[QVDt[C{Ew=/nʙ u[mO\Fv=WYEڐlŏv}Ld|3_u{;knJ芓N7uWv8g q*ުMn)o7Jd 5yu֕>'6s* wsrf'd>=7=fm'eɧ ʏ'F+|M{.ӣ8{wr0LFImKҸޥ]w+/_uW;Bwڽ;l~g]ϖ{|68JO= g YS UCKLśI$xdq}00l,DA0Mt3K&g`9d4T|.w<U^gwaC"M!$0.H=1f*;CϚeۏ򕗯;ޝ|6_Gv z= ߄K 9(H}| "chJ8nR$) q䤽[Ͼ%s4ܵ{cr5?G4|=} ySWhiogTS܌&kHvd*C{4`H6I㨆? z{_\##3g1*mu%Fp\0<ĩ/Wo}#4/Y@‘!~7o^)I2i`hdʼn ;mY#,;GVFkʳ6n^lsBZɗo p?+fZ?ꪼl_W]^wRwu6/nUŖɘ:^0ZIdLlG cdP[PONxw1+Bkѓnâʥf~sO^G&:HJFd号R0c}`jIkcA[߻=w8S5m~3_ mζu+~vn./] ]v7@eqXbAu )SEBBmd TE%Jĉ$::OYx%brq0~}*7~MŤ} зiSf [-ڕڶnh oYEFϑHm2diV^Gyi9ߟ%Rt(vcYlӲ  u;3\rOvx`Lpc3D&-p}weyvm ~x򞳘0+AR|2v0WH;'8%BYԷ^u :q&䐩~\J}'~9bO_\+^8SŖU%7/.mZWyv{^Os$nlގ#mYLKxr_ogqלF!\zG/﹃ٗ(.zGQvDגV޿vn./_#6(nZWb5I2`KHe[HXE(4$aY S&a4܎*KX:hjmfx4g>H$,NER$,Xd^|wRVki<W{~y(%eEȲLk,mضCs:i"X C --1d"ۤZ0-g[,3@ @ U WNF7tW%&b*˲p(*Υ\[hq $#2bwʒ&dY6ll$ 4Eڰ, Mv]tVl4 .'ht\C#́cƲט!x|wh5Vl/ &@ 2g`C,Y|XB6!I$A8vlI) `&8!K֐–TE%glUlHqrH$}(B<W%d-fSFeH$~Hk"_nj-i=x~}J0$IA*ն_Ux}lBM$˦p8Hk[zz"1**T Ĉ>` ze,0`ann,K Z)Kԣ\yOؐ5?ۯ3g^#wr;N[ha+83t0gsU ̦Y/ﰜxng:}6Jj#_G;w \]f.Rsah ĚѱQ]}qK2NyOY¹s[QVD{]ŭ/n[~0wϸP/"g}Ld|3_ԥAwڶl?ȟ (?ô-5^^%ts;܏\qA'ݎ`\A|/gk/浱9[8e|önۏސ^O/~&}I~x wþM#0Z>n'ikb$mTpghIbz^ԓ.CrJ$)D7&QDȴ1mXg/.x SômTBm<ͫQ\XDA(DG*aضDYYٮ |NC N5 \N( aRഩ^d\:ǟ% dX]KYR I D7f|z= vN=휯]oElgz\'85vV|&.\KSpCY18zDi{cTx%Ow5opg[fr=79M2ͽ_ğ?O28 'QvgD;j߹9ymiLo|4n}lμj6v67~B>0txGs{OCOooxK9{%ߵmR;0o}r4,Jħwa;$)A+9eGQuN8 zڗ=ť=_WLX8:3_˗n?zCn>{G> ne/ 0O+<tRDk^m E$$qbӦ@ͼ1o92i(.Oh$EJB&Ԭ\GL~r&M110qY`~Ev$1,Y1lѫE[kЧ/76t$I%u5C^V3dLA83@I9^z#lKBU4ƒ@wOSGr)|m}m㷼OzYz}Aro s$I2[>6/Y@Ō ~ m)k =4r2D趍,;ޯ ~>[кe(pvוձQ햳^]ۖ-YF?MsR.~Kò\0<ĩ/Wo}z4zT۪C>9|;|>7d?vw.}]DWyu\9,;SJxQIG6Gqː$;Ep-/|Ke*HבYdC FVu1dx>%hRX STҶN) (6Ւge#ܼ_d Kd,NÆ##cʵhLָ'rLnpE4֯#1FtC&"ca`#"_`OoBeRr%|.-7k7O~k'ᰟȜO1RNb .ͱVA; Y~xڵr-T_7Sm}_29]T4"/1',UGdRqgY. 񝳙Ds[?_6Z|_Kӱ]~s9{*λd=]⽋ ?cǻ@峥T q)uY^G=ӏ<%ӘĕߺZ_%go뮮wfy]Qw}֎|wYwmww&H~LXatU1q8fVodZ.َ4K6a:]X+ )PI,dK{'^O0oU _N3GGG TɐY ̹W 7p--Kn^e=,`Ο9g_| 7s/Q25L=;g6_ @=\_^m/pOp^K/$YG"M%a+榿ܡOu4Imq1ەocƽ3;ʺyϲp;f+zKo}V[3z[+x ="ˬf3!LW:oH?*{ Ǿuz3v>7m[麫(o[Uugugݵɗw&rΒ[Tpg84Rh/ 'IN*5Ah2Ed,[-?O&ZTij#+Aۖ0M#I}aނޫz>Gf|ю, DڦV sȁh:VD0㐉FӴKvk7~,d0LL61N .3ˡߣ͜9kV2i;׬ٳ9t$mmmL9W 3f@:QG" [|xqm;| LxP䇼=[y}s3y1%M I7ƒ_{+V.%sRZXDƶ>CԷĩiF_aT1phTQܧ"do HzQYm3 LIb ޿0ld6G:c07>r@H /DĖe jw{__\*NEkG]aX:exhjogjn? vdiW Xa=@%gt q@ H_=%ؕt2>5 AtI|z廽\|2A3"P>Ty^Z70ǍB-.F&%i3fmO0ځPd7U=CSt3mX,˴s| \HiQK8 H,-R bb6ө(2 ipk\.`9LsZ[{**>8鄎C__#Pyĕq^HoB@'|me:xwxN7*Jk2q:$I4Y2m8.*++Yluo3յbY7>EQx^&ƚY&7b, r ee EQMsSz[FMI,u@ ]*KMY|p}vÅ'x]LLuVc r:JmjשúvlEBS;AD%9ġ ༁K0xWظlFOEF v3d`?6mB2 k֬9J0T@J4MC4$d@"u90  3WIf+gMl468;q/mZ8njo[h.o<޴ m}]fnzz~Fdz뛔Gqi^G/./U ʪ/7Q\\f,l[`60ũȒj(2,f|ݎߟ:'L3w_~>};gmY*^/2zv7>kCG_fLf:.z_. @  (B,XaJ2>}sj[y(ed2} ndsSomSAQic'劫.WE+q:\(XFsSdƲm$Y ||WߙǐŜ;8)gw{;FMlcM&M4* Xv (%Qc ҋ zV+~zdzS>|i]_īcܗyszwǰ,,'pQaalܺ u TG#G($$3sQ;9XOR0'?!ae.ghz5i#E]%$$7rO~ ׏9xv֓,bƯM u6 ޹ d̨depvC mY=z@b$i&^RUj AnCVTIWtaꃊˆqʺ6}fA5·78gx)&?UZ㈋Pj[/'w@a4onfY3o#O%wռ9m8?9h4كp=趭h4M2L0ɷ,6l  }ABO=\V֭e|VH8s_OÌֳfDʶTLCF6q] Qҝۺo}Zwp< E#c~ŗ]YgK~y?)ZINNEA ,um"8TWWQW_PJ!L84)>z>jq@:x¤;di\Pѡ32^u-Ջ/LQ)g7s]7v~tN?}ҕֳGq#Yw n{sfr3{s_Wߦ kԧ%(%Sr(wFqa2ƞo?ijv8LyC|35f8rF> ȴNڝVf02'@e=k4FӅi 551@#m -,_;oHlxqj{I6(,CbIO)8#3L0٥u|#f,*BvIEeSy)?9?3+og}&AJH:qFHx3h~uX.57/gGXC2 /F[v!Mߺ,_Ɣiw0eLfSd_}1%?\l<.zpaeIkb82o¾%BC9`/X;. t= k*Ck<"5\1=fwHesGU0Nf*N( RSiټ9Ӣj4FӅH۠8/LIn.y0gJ23 I8ds!{ryG[拯ai?Dm>+#Xy#;4xfS JKK)uoq񩩫WDcދ8;y"''7P^^Am]`H W$=7ݖ(׋5NGڻy"pgQ|=݉/㵋OaRTY_ X|˅|0p_N;^>a8GyJȟYϳ 37ok,²g2뒽]/{77.BVeh9]W/WbW3'%/'U GOȪ`lD h4+٣oOr)umK6bY/PFG!fTWis%oEa I0=٭_o~^~S*j#xB`g$,&Htc.pOV^C`6 MQ+ң ?˒\;~ y{ p6mb֍QK>}}0p[U*uЇ[i4vMWqv< )xu%FZ:g`=@Ɂt,B[oZܫ~Ovu0 ;Eq?m^҅xqmG^zlHOXZߪ q>"[|gE8Kúex{ΞPi4FijC98Ns9~ʖFTxPS>yٙ8<09`A;$\~w>`OL5Ib[e)յ5df1D%‚{ʗvK´gusEyX2D^^N,XR߰4uע!%M(WH9dLф'Ytci!wY e4r$9~M'N`e]̾Xaķ0 $0N0 h߸՟Fh40$1@ Q$\B"8wУ=뗯`%b*K$ L$6Iz>![+{B"}T>o6X55ɶ:kX{$ XBVRi&hKlw%3I^),."q**Kխ\/[Ml8`j%8ι瞻ӂ* kv1YYYԩSX*F4Ci͜=aCPSSè_C8qbj{A++"h͏Ch4?cgrJf\1p&r”d2e>b AJ+ۯs!ccE A!B^n&1漱d1Ս6h }`[e4ijR]WO]}#e@oH-ᖽig|?~w/8۲OV8Mi7H& ĝd+ 5Fh4Fӕ S%kjln9CC8KfVJ)Lƙr`UQKvf7ܤ&Zلly3-||ʷE"Rإu|F5MjpG)`pٳ'Ńܿ%KcmLu]K$'+N(S.q!H<~t2+5Fh48hr]PHP(\bIF TUWC 1ψEb]_|d& UL*AM{1ׇp8啻>x 5lj6Bc%,$C;Q7Fx+.D&c sֽ'x@&;3'F˩zFtVzFh4͏x$xP|PJ! Is\qF=X{zl,&3+hC3~'dC(S" S)Pnݺ/pP)o e1ո df6ۋY`5466"<=4 GHtUnSFh4o.v1@H< i.T lm8ٸzS=GEu#Q[*MG{@q'hxI,L}C%R:pqhK!A'p䗔\5uq0HaA1x jqp`xe i4Fh4.bxgoh1 &BICQQMQlވeɔʠJSzFh4lgSi%qGq>#RW t/̣nhb',YwWބs%R%JxʗضMϞ%4El8%{wѳo?,ˢI @EcySd.6MAum-4VDZi^I%rzVFh4Fhh-#i B_O<J7o%+$ܲ7^JzfQTKmQ>mcP L  egҮ/k5Yz y9M2.1 TՖRjP§/fH&Sv57DZh !R4ELh4FѴp|O !D))SU0p⸱fr-J8wGq̨ϰb٫痀)0U>az jR:΢AۼX,ASS=7l  f`e)$iںjzW>䄳hTIRH\<,D)Ї[i4Fh4.c靓Ò$={4JS.EYXū$ٽ<>d6>]t$1-Xu]4H:$s|/cUd` ;/E;{ѽw } a_aXP^YASSyBa6B5PR4DBg~5Fh4m!HrV*(!q1(PSWz.Ƿ[Ԕ7Sv(n1dP q+H'QZZJM,R*&%!}Ї:k4Fh4.2MIR !@D]$>$]{xǴ  $+W.5U^`&݋ 6|5;Bk4FF)}Ni/z@L|{XWwNA9ʦeRM=8#9~)S y0zI|% C0I4&x@(eu|i_߰Py(CԦgLc00MIFP55u45ԓE(..b+a  8ضM"jKh4Fh4]DIn4IzM(<@ +4l,Z|_6|0}LяfV/i { @UTx\ߧ^`HlaI8'=8Tkʹ 2LFð0M a-W(aװzr-ZDsp#l6W8,﮴߿eUN;5kogGdhvvWE~}_۟3sn`@p_ZזLt)T߈/ѤD@J2 %\%o%xĝfŅi_00M41 ÒIJTe9Jb @JZDij`u4"X:H8NYw_O>ߟ|&c'R߈iU/~Ҵ߹z̭ߙvwΔoW賽:|6Ƀ<;ggqkm~m4M籾Qn1]||PxOqQ>?ޟAydk{l~@qGw9&2-XmkP$R(s-(aH8TgHy)O>`vJ`Tjf8Ha'q]۶qf|G;p?}YrOKnϤd=;>doq7~G ;n 2r}34[ML^1m^O1zE\ raq(\,kj|'\~'ӝoM?UdnBJP2?4TcOfkOf{ٞZY{uN-_H̕7K_zv5ПPw_^m5ta#.3?JHHa3nY{JƌHVVy o7$;$d _|Ntadh-ysjh!E2-q)퐝}v#er̈́__t}A:,^k.t6h[-W b R(|a"&LF>ʲ 1 8rj"&@!9%Ki`gpˇU?JMS9ܩ;]&d3^o??IwӸjMCyge8ڴy.{e"'L̈́A%=&n#NOwl%䗼;nV{cx&NiNh4۫ךګtnn={g:qp0;<A3i4VKZ{8mB^9e*J9<9[g*g~tN?}jGdOG: KVv&C{m̡ 77|ݖN|_ϭ $E;sO^?nIU!k*hS9#e9"/uNk]A:h4-!*C D W!Q ".?wwD߇]% jKy}Rd9l(#1z>'H:xD,aaJP-.r| ၧfv%Ԟ_J}'<?rl CXO]m=yY ח¼\P8$xU0KP6W͝`c8%|.8,Ӄ*i_5x[˅gCO=ϥeEGi~k[/r! ܗξOp[@L_keIj~'_k¸ ,-zKkmd}-.:6wjKq>d{W}rL/)fcmu|?)Ga45h"֮o|DF Ȱ}8_RẢb>\Jn%ضM* L;l;"-%}%?2ҮE ,BP`&Ӷ CB<ƆLՇmBoj"-@s]LHIh4?hۢٯNN8{.g{;g\K7l:xٔtNvn5Fi qh7yɝ\w@jp')oNv&Lfvuaam~S?4n^:nc;gztΟ~3&<`ΘK+{&KT<͙JiY-^s%Ss5L+Ɲ?úN'3BK?Pgɿ֧Wu>ۛ_:uEԭ06uپ̹/^eeƸ~~̃m/]XhxE?OQƗ73&-ۊ1O:J<Ҩ& H[iM7]; dʹ_[mg]UΎeܭ<6=_xl>]h:e(#TjrR({Ha ([8";3LUu?Î-/R -}P@ H0dh}w}$azΠsiAX]MVVV7uTV}'uʙg2l0jjj8∣>2q=l짏D"'w<}xqo~4}ƚubǍ xQcKn?v6+Wײx[=kD /𕤠[6p!~~^{α>>zSPMr@d(‹|{aV^CyuYazv/|Hq D:"-ߋsHMZoMm>`OR}<7Hq<'@!<wQ(H!H)C('PH)FC =FE 0)5x KVh4]yyǧ\eoWh` J-RS#+R&/X%e 7o t+Cc~qE瓪~NF8̊Or~Ov|i_ qJ!pI.A)aM +`8͛B<?a6BV Ա؆Dh:-`ۄsӈEl}^O|74{ߟVFii#$ Lk7K"}\OQEΦ8#Azүw/z:K+G~~.e[YdqʗW`&a# (^3~2w*l֯\Ev0y7O80ʨqI/RKRC0 Oa7ԣu1"8(#D[C|_& rQJIx4#|ZĢ5OVVR*gze̮=/URJl ҉fRjjA"(dP8P(DϞ޽rkk`ix.!F헔h4Fh4) V+ mrr1 A<qCNN6Mf)p|jV%AB$-QJTMg$I<./3bƤ{ytyUq}`F&99Ȍ `! <ߧfu_cr),n}Dh4_!rz² Z(ba}(际aaYlm,i&Aq<>x84&"4cQ8k K l۴v[z1>[G4'Rأ3#H\Q[!J%vm!$cM~aamuSeLx:4WҪ^ۥ.י:@{+tݑ|_8YTŃpϿc_pЎg+s/NЖO⛸ϓpg\uM?>Khԟ*OA0dSƐ )| #lR ܱrWJӰ0M0RW&Cb,Dn#/RWI!Ov8>qX,A,} 7A<02$׮clڴ4, !PJa[&4|si4]A;V:g=R{q_Ng䧘{񟇽;.OM&rd6geGgά'3knS~-8_w73ʫ@SdFxv֓<;I#^g0xSx\u6K@>3mΚsO^YVL3o6DaθpәS̸6ۄ9'>ʋLeΜrTך>s/y%Ӧ=ܹM8ϭ%ecseSsE#i~'77QKxg6Zvr9좽zh3jWNB;3%kI8m L:c.nUCd;tm:mcAF'@ (*̦O t\,$1lI$k9@J@"¶ 2 !pp!+DжMSXw x8NncΤѼ[IwӸjMCyge8VKy}V2K~ɻsnϠvS\tS,[<ߟ|R9rTJ7s[Ng'L̈́A%=&N[tmgDw<29Jȧ.K+gCG>ηl'M/VRך>u~ok=~&Զ[+K:;;ml nrYmmrVο oSvD>F\߅8dO)Zrq_uk ߭徵 V}8$xt.w}jr꨽v6h4?|!<A,Gy$NnExuRYYB}! al.J`ZH$$!'A*$b$3lP a!$]~|Eݷz.6pӼs]֦;uO|]\/޺m-xtH')eȷi-y vI j4>5MQ, MP.!E,'3dO:66HxHS!@"(0B;[PxHD#L Avf;AqYfcʗvwrr3q]h,yҫh"U9xQ 0׉ĢdAp=< #)rq{o>=Zmiv,=[l @ h|.8,vqga E۵1qev)[X b٢:/_}fDYz"e Y8-}9 x)m#U9NDYhlp9jszَO)\rL;_5uOZmpExu²Mqv[kDxM4~<ķE}̵sN;mIհ9;͎6h4?kpmr231AM]M͵^>пG1C-bfwa!۷/ HRHZB>JlD;ޖu>٢Bd˳m3yeW=`sqOfmo<:g`WWo6Kǣk{`QKú]-7;; ׾Ve2HϰEEkc.GwPr9m.K:;(c+ wS:syyf?D[\= +Ϗ9u:4Kt[;ULܜǥ[ZU?ʍ'i@$osly&~;~ئ k/;k j4z2{!6ᐍdlˈg؋wpu;Dz2<) &ҰJy445PW[ ^30fθ[==3KK;[, IDATuz6ӉxeTlW8D ++*bDb)*($DuE5$DP0~.6MmincSt7\|3Zˌq( 5rwsmv60҉s q%sO3Qd;;~i7?xLe&=sfP_ĶyWmNg\-b #qOP$0N0 h߸;Aan:lAlwEy Z^8 HLkLDû,مL{2l1O:J<ҨӦic`fgXiKtxM[wfmii+mmP8tٲd 2 Il|h(Le K8 UC"P&XVD"Lb{3#閛Old4%ft|+lҲ3qO,Lz&O}&?0ʷqƱ?afc74 ؏ܜ|)oZE6(i|>h4IȎmZH0XqɕJ~a(y[s+ o?]"faW>($1j2xX4J$E5[yc$1)uoD4Uõ=2 %+ˤ`3\ Yь`݈d?V-Vn"I k|U݋mmg?s%L#Q)0q ϠBB27CP&2zX‘iDR&ȑĢ! < i0ĭs3Zħ:Dl#…_:uc 7sF]XK/}PLHXZ$YO > /JU±@$T<Meq&YK ({rzʢpo" EC|K|k%jŚ tϕ'ç("ɸfEP$LoK "aW= ײ+JMEO!Mx߼rDk"ZA1A-Esm#A"'Sy>F9/4o9xɘM0Y2mLz.矧hϫ b9u/5%iG Zǧ);/]x v/m$-P*Q<\DQT\f:Fi"_=EBt,Ǥt7ʘF4ݏ/ql4U$0y#AAa[M=3񈂚SQ@ }iէzzX{ᑇ`5z4&D?kpÕW˾Ov<( yk<<(*HG$  `K\qedRB$4EEUdY+&]'ˢ`1I %Y~}El3qW +)?G:KA/\E,jlbiR #tMFuwҵzY ~vq%;KCS3jF$iYM˗a*1ʬ璟zO1Fn_/_  `4BQ471~L#Xxɧh!)^ TT*E{{;3p<4C\S%|N,&C*2ROkV8 />$AA]K7fҚ6J9ﻧ0qP< U@Ved˘2u !؁v,>ƚ4P[SGKWf) yP4e2\t./"l?g樣/3~P,ʦ^mƎUhnM>WȘtK@plUIԤǺu¶m4E8'ScVoim&$瀃ϸ' c2itYߺ6-S0o{Ҝ8s )_~mȥםĈ sяeveƳ{Y\ۯ>s~cQ^jpyK2FH? pu[gG8SNɌ):  ؾF֬MoZN_?-S]י(5}w̆;5p$5-t:cG.$4U=MkW6efdjɫ_oW>=8_Gg8oSY6SfšC(mǎ<8-}ռ~IhEj}g_4貓n~~>gso|c'I7`y]0P|dN>yЂ  ?>]E$U0  ['Ig:CUxq\|f|dj|B*g)cXCKK7ZYcjy}C;Sٛ8;z{,{9i%=FU_!6#3vcQuKEZ;$|.hQR55T,^Lį딊EBA婉DOmW.,:ǞmKwϏ9~{bez/u\>o]@?j $ߘͯ;-T[vG.LoNfo}[yŦ⢳bl<n:$sD_A}їE4<nOaNrجx§9m f*yss:xh{YchFT䅥obttґ⸴|s |ƕs%cG2.{?'񨟚Lkw=]ijp+00a[Vm#F!)˨ʔ =MěsoW~#oy~Smy|IjH ym*롟fpigS9m.Av: qt[L-?3x;?xy8oQH? DAGR,WvMTUu05%q'3!UOrǼc?zx#okdkP EB E!,|}L;j$ʚ{mۤ6! *DQ|~l>GWWdSAUr!h(x[`A EQdC6b&Ch˴VV'~旹2M_~u|NʑǮ᾿_ɓGS=}4Z,7?Sx{5\(Mjto~>X@].Kk3Nrŋ&:m*]>lqAAi&O{J%Xv--ZIwsr& m%U_1s&c؃u+Vyw+T1u* _^GoH Y\> JMNjwآ 0N\5]|5xD$itw DBd;bY"܄ 4M$|>m~FTc28$I PYs.a6sۮ;f4O^Fk>ɭ'QIJ+@ĵ:^-w<|=4~-7>3>eVpVJ]16Y]\ͥg}* Opo9MǷwߏ~^J>y[_h3AA:6p 1uU,7~JvnKqc $ j4gNǴK[Ś 똿%^]Jog3%IVԊe9$aR(1@"`[X&XO{WLa9x=cӟ饯7m;HʀG&câ 6Op%  $ I1@$ɪ>J/JTGT EqxHj+}Vn  Q[di8dV㦻mUt$S,v^;e d jY|@:݋exNP Y۶!^#YL] GPtYS[4AR{E+  Γ6O $2**P.3ylӤXH%!t]BӂXCTDWU %Gn}k׬GUU>s?Bs}-+׮=KW;/=͚upl{%.ُhBCT\F@T.P(5͍ԥjظiE֑$E4Aڡz KA飒8 Ea e`H< Or- Orq̜1==$!0"Rq<邢bX1s$NG[1|&>"($xy86$bqGX5LkBϕdR@X$  (Vh"Cb"{N  m\l)@Ar37&]uMVtF64~S^_ÚneFj.N2#!S4lLa;%R 1s$Ǎa6'˧3'W ^.6VI|Ó%<Ǯa㳎'h'xe*xb<r-p($.E!&PZA_~{ʵ]MWVގuqmGb?𵑡5 ;>aAEV4$Yd5-ɔ= Dž~:{3lQ(VlZZxmz_? oݧE^ƔS펿ϯhͰW"Ix(+) yZ ۭPg- vNw0z8 =Dm$.$`hL4OwO\EQK&Y_j$y`KR*39ͦPvy\s@[5u$p p%iZE( ض) :paƍe 474Op+fM3EEr,WLn%Ck_3_:Db]cwLe#2r|WpWrz)?OtWݷp39`i:Z-8K;1sU+o3p|jbXznMb \r|,jpXsbUfk3宗BL*߹>_fd )[9$~,wg{V`A\ʽ}a.Eݦ}k9.Μ=p8Yߺ6-S0;xuT+ڲjRw\u=c >ݮ0rn?_|;.z3%>8Wlm Aޣ)Au!0 -|кή> K,)T~b4z8ʖK&/SP0T$pDu)f=5h$H<尃C}I|eٷ9vq%< Ipjەp=pG6'J*7\>Uy꒵h3 + saL1k}b+lh!pO{)WrͯyGuj^$~"?5/tjiLx8ܚW߼eKǞZ܃G.2<.>d,o|9X@v.+Q.>\崫ۇ$j<_ Is6*o箇3'sfsʬCXSv%w~om%jm].ʫZorg๞2FLv4ZLwR_\Sܳ"x-c bnO~J, K>J~^ii/GC2N8)AS]bر,Y: 5(Kbѿk6az8HeVF $u۰]H`v\ Uö]\Yqe|r$E@Of+}H$"46&}|J C\$ 劃3< Yw^k{6%Is%<ɕm>yH&k蚌ZT*~?$QT$IQp)KJ>Y5lW\+װ۾ ܇݌ ϏSgho$gx*О1;ÛwvsNEm i,[Đ fp.5<=Y}YdTIQP<0:n're\IlT\ّ EhF<&Hţ$"1|$ۢ?OGwXT<ʸi YjICFAe\ޒ{}<\Y܁Ӣ-ס6UOӈQm;xGٴHiζSaWPh{GMaBʽ`(>8eZ?<|ǵwL}8cH2/CB_[/; @l ;f)ѷf1U$5U+oTkǩu8Ty3v]wh0M{$q(F&#?P(Wb20w 3yқ?pxmZ.Ӊ~{/d_ӯCږeVigF=LI*Һjξ*oG\p|~{{YrsGζ7jλa~uѺ ?/{K3{JiVWhr D\ ɯsu?7X]GR_nXf?`_r~w}mVV޶cnjBSÒa>\_ GJ^7bU4,ē=Ml&R,tO@\D_\Dɢ3K_+Nx,|2Ve6'.2"I*.26Fg_~O/@H”SI$d$,u6Q )*|_V+ ,͙3+s1{ͽg.Sv@OOzooi˽.vrߪ^<Х5s b_r%j^[H, Lh$L8b`->Z7>F7$iIP\lCu* lh'G`%<,&gڏSt3ff|@ \~ʛ׳8浴od#2"6 HQQ[6b1'dEP%& rhCTDd$ GA75Ӗ@ 5☝QAN $1I45Ȗc%ApĂWrqT}=V!1' (4Mad]dB$/ӗ)ݗ%W,=<{Ǭi]t,\9U[nOSU< 2RT"g\,k,3a$5@\>Z88WAp rwP[RwhAa+ J86!Y*8APu+70>Ək& x6P@ OWDBH.!LHGscʔL_*yx'd?hLűlqə2%)֢h:nfڵtꚉ'hPeQ'? ®O(Cc韮S  ZVvknd„1g l߈ kqLseM Kgo ߏ߯ hc6cyik }Vmc+|hchWdOF|j"ˬpW{K^] *Dd4Uu]:;$i   ;IGC>A8#̾gU5 DuƏgdf tttsd $IJKWSFqpd N:cepЈ'|jgtc# xxfE 3v/>E[wQvPXH()G{gɸi0$  6wc>u2ُx,d.nE&ů+$C~fz0{~7l&NDet.[)U*%AAAvdnz䟌m,cG0~FP-|&m'RDc!A?>]' QoB&g "AY/GK[7vɗi&^=ֵu,]XeIsaȽy2qWAAܘ[ 0zt*YЏcõ}$ʮCG><#7MFIƢȲ,yD!5Aj$'ѓ΢ a3*f|sSFҗ-ϝ+E|&1C+wmlO7_U-ĄcgbCctJML.a]Y1ESDO!N{AAa[էF0y LߗhH#v~ݭ̝3s>5Bi? &Ҵui~ S[4xz1쑌Y<3+i֭ZɽKW#eVm褵= /H"ˤ ϗ¤{2Ky֬[M&@ "mJG&!z 1qWAAx/&:ѠHejB&e6'"biKX\6MۖK&}44MЏNceh̰D$+}:K|ZZ;i2)U0+..2}ɌnE%*EWG+u uuuް.ERNۛ&ylhK4A  tɳ7R&(RqT`.I1?u1zGԑ+MtO\t_$3߻Bbml,Tpdu|D6W&'u-d T*E%Tlth8dCW4O)CCB,  lz7EP'~lDM pr_T"L<C"0KF$ g||Ξ =}92劇VѐqcF6j]Yrey.|n%Ǎ%Q.U0#*"]gcI"d 6,(1~nte ke@FF5\<]e"RAӄY۵~.CUkSK$  6쓺i8Ls2e+"ztw FgH%4%uBh^bQ w"fɤֶ.6vٷ_.Ie0ZMWͭEC3!Y|Of2],HT4  ݊=y2эL>5 J`YyIƫxdswvc'ID8ΆbÄC!J lG"gJF1- 9އ|5,;LwEkhjLOcⲵ<x3{HJ؎M8CqQ)W*q=Pdh$J]N4  濴4%SHV*tBAF5  ϛ%FV4\$Jeb{ndb(8i>2<2W dpX!R5>O=n9耏=M8>V3Tҡ3.rc5_Ed\PPOm}-6l\EFTτa,SU`Os$1Y  Ck2b:ѰAafL@2yzbZ'C7 "#Kb>IdlڴC ܾȴ ˶I)i0v{=v-yfP`ْg:}k^ru׼k,Ֆ#~Ku̽|{]:՗>(ϟx?\}(l+j=z{/ayhB0Qu,W,kؚ,xK.zʦE,.wf>=&.*a;&74\ z9A$ MmkӗG"et]e( %$ñ=r,|: C<4Is<:6NP_[,+ )蚆W:U6R|{p|S>mp޹s}sߗKn;K}'ڲ/"mڃt~Zsmvᙗ>! sg+Krl<Et$\\CB'_IzՅ*kF".Im2JЯG 8eKdQtCEr]$`It2e:nT `&YFBAj1U1"Notffu(I$k %Ism$ˆ }5!'I∯ i?0 éG%L'Mp}H9o~Ir,zOMLK޶ޖ:>yJΟYߺ6-S0KL=K\崫۷y-PmYZ]k܆9|sLjQnG>j[kƐJp˜_c#ߦuk>nc)S函OvtOÑ$ddR%UaF\zy f$)(ǚmj'؎nݽL̦4᧟p>O[G7|1<]&[MZsX% APP̋~&CVz͓Ed)謣7<Ũۦv>+>+[ƭw[e X;b <,p3%~<3ksٰ#I̕nخվD 5O|P=alT7|vko]vّ=U-;c X7u|k92M@jyl`eGzdo=1fAc \p\8͍IK/KO_tLHTUep]0 JΊU+X$)Xʤ{ ttU TK|MɄ1c- UX:^{c5ZXMڙC0 {"޾幻姝ΒC 2 :H2caP.q\LШMG xI4=@6K[W~zz3XS$Oq=۶q ϓ6[;X9UVG8_qgO +Eԧ"\x>i 4S)P%bcWC3clG,C6 |z߶Te^z чֽsyr/N$GSHٻ0ɪ2scyBOb Ay\ kZDEEYEt#ɀHPT T0 St|,3q*gv݃]=i=+]Wn?LWLm+uq⹎=-}c;q߳Izw~tcL}w<î|vt;ֹڏr[Nѯw"[OQvP'#aDA w%薉/$# ahd:k7lZw ۈX<š,/ܟ\91u]_7H=`],ZQ6>M{w?#h!K:f¹y6$U6Ҭ77DF` 3y|v"e ?2ttMMn(%IG3.t-[ﻑ;&_xxSwd"O<}w򍛮bp?uLMtig󅋿čKtg g=ǡ)q;.itvw;w_[?vx]~]p3_N[xgD]<rGЕsiR"Js%m~z_h[n:YvY.l}B>tS?KMVC\{ sOb^g?gUteֻRg'/O{֭g}Hx~Ӎ!3B|d)ΜvIc&" 8hΡc[v,i,_׃l&FW@w)Kò,T"#B -קqоm&'44Nb[#eAkPEQN+:#!ò6Wmk Q({Y?>Rc(3lZu7=ĬAsnO'8ma;> vJ0,B% z66[,Z,]3;o%fZĬ\32² ZNt\Ly=vȈ@בRRVV+i\4 # (L3/D ;7?\Mc}l({Y?>Rc(3WM>&a:R(~oʗ8#-eȽXa@4[-$!&-dYXhDJ֤ tt t!NM-d~ýx%Nўp0uC$l#_=~4[.Fch:B"I:7Ӷ3VӣZu@ yQ(isqEQEQ} qo]$N@$)fFdŪ!,x{[p5<&&$cI!> @Tj 6d2&2ĜM|YŁKIg3ؖë 32^nLg;GyK<2qbTB!zv]t]G(3J~do9M\(HQEQeyE>cKV$Hi& =ھb` 'I⸕6mbb6a0$KWLav'0:`x-l:9;"\_&!s{D9`)fnȼe(f,cI \ˆJm6uj uhJQf&}~ϐNW((̲(D5j$014f4kNSȣE.<3w^7v#u6l`~)űtA.i^_r$K{',OTd~:`) _,cN_/\ Alذn4EC8` 4Ey3ޏJG8ȅt|{?ɥ|3YрW}Sxu 9Wfm~pxSsڴeӭ;;iG-"ok`EQEQ瘔:/bdbD"IR@w)O2Ԛ-6nZ& V"":S·,[d2IDì^?B*e[*ߓ#L3Zi׾f:>zk,-c@.gtl ]7wXUΤh{Ln#ÈZF$ϓ+ID0 x<"MQ" =/:.=z<~s 4Gç^NJN|Һ+nVP0|'Qu(( B#"<_" C&%-Fqʓ5F+]7ٲmQ BMpMOӑ^7kItwI%g6'6Meܿ1RijBgŪ 5:nSb&\L6h7 zAF:ҏqjtX,FD]h*e/GmRS헣ٯk=~)yoR((24!,!N)@haFS$Qы -h31٠RQln{ekxK.;4RNf5^[mvxl# ЄM²d*A4NRIOw]Y6m݊@SEJu#MJuM|ٽ@ ׅwqxxglEQEQfMIXi _t'~Ra8I.W`^_Bvǥ4mrmC!yZ TqÈvDNo1K2Bf**^ B1s`ZPk'kJP AaSɩ {-?+څutQ^ "t9SLe;[t̑i`:c*UEQEQE-&/b`:4#(Wϒͤd TR1C_fˡZoh:M (W+m0HCr\l7B 8֣ټq+u.Čٶmd"N{4۳|MwLth62R BhV 1-SETG)(tdh؅ 4MȈ14:hCh$bqr ݅,\]H'c$~1" ] C)%/oah4:MLmBy|6}+NEl[ZX8>lX':BQESuA2sZ9dS)8*EQEQEQfz2>`~.x 0t ! Cn ۦ90<:i[q2$taer&RJzKyz2,[Gܤ\i1<:NǃPCӠAj^`INJAMt`hԝ<EQEQEQm](F|"xE($2׋mƘORd$#$Aۥ0>VH.e||YHO,OF+r |b*IO6?/^DZIa`ZR@h[-It"Cw@i96l! B,BO;m @40nO:Bi(((D1>^wch^l?x!hB20Pd.mdZA8V"I$$a$N6hnnlux|#ߣg^oߴooo/BHlhUXiئ΂y}$4aGVm̛kp,94T*e:bQ$}i(((D:b.cFx*Ʉe*e)RMFLL6Qi"^RQ!ɐJ d@lڼR Njشe ]W(((2[. ( ?0R8R#DbC!:D|6C>'еuN )|Mr¶1Nnbpb lqzs,7x,@w?pFGCCt d"exx+v&ud>(((2[WNౕC"$HNM&a#8~]bx>ϧRnJ_v[#I%sTF')qz ]i-`ʕ&bq.eq{I&.Y2@ׯôtj tuT<@MD4VC*#Jy4EQEQEQYNE# !$$akx"K0G7 ܩj ӡliRJb1v:B 4 Mzy$9y >̙6=c-AZGm6m#z9:C:&YL',l;Fjʼߴg4}74$b1o"2N#DBD$Bb8+V{l sY&>+=aʹ,Ē)<)!80)vj MJ4"S(((2Kz8O:4H%bt ~@fpn/F>QB3pÈz!r n|P6tˤ@Ow O=6]?#w=ͣ_8W}#> " )Vqf鴚dK,1e=ciA %MEE^^n/e;O|_r5_w'/\ɧt{E_bz 0ۦS?zJIjޫ.;y_ex;_Ïx{eߎ2#󯼖7]qg7lLv=|.J0zNw)^/n ~'㋊ EQ tu tM hĝVH&cpn/AtHll]0Q Z$QЈlp_Mŭ[5hߴG|( 幉o[/6} ]^qy<\Qa_x Trry'z ,;JGǟ~oh*)xg%7c]ĥg]ަpGN[+Szʓ?NK˄&>Kk?6/xl1dfž/*&E[uSSפ + ƫMR\t2F& 3(e t7ZT-vǡxzAmt\<}d`x|Kvl_N>$3i]{wWw8EYv;mͣwpi/Y=霻ssg0ז퍜ϳQ9~rԞ#g|݋xhLW62rә0]乎 EQ5:.#"0(fǧQD,nSɤRdquڄQD+H%\ep3>Y 8aD' (n^!Z? ,Z 7@ t: :I$D>aB$:i&"@Jt]seysw|}1rN˦spc;Ƀ즫8Q^z!.>Fy> O:_*9!|_"w˙Oמ34FKK̫Nqcskw\{#|ʦC[n:}_TL(nI&.6hSoA6dL*mMuA!!=cU-ʕI|d"bEka1\/`-XmS[5Z-~σlCub"953 SL55^F>~tſso(kifkϽeW^}W^u=߶$,DZ?Yq'znb#;r>0oKnrtʀ2X4}/< N)xq3֟?B/qG=VZB;Ws*G?/ 1Pe!#[`|A5Z UjG|B::/?0VRIW0Â9ZkJ1G[GL6-DH0D_E٫lvC.c괷lʞow9e/|?-z!:p{ܵuI-s_t/Og< ɊK}=Fq÷>T'I3ֵտwxyoQv^i:z`<>wTP3`߸C-3 ٞgh}gي'NbSֲGH_J=ag;_CL}ݧʉvr`酟GUWOth˝qm|k,7zߍ1]z?+q\xᡟ^ipip/k>4|J~~zVpas.C3%>ǿ$7[sr}8o Xݿ1W;sg>;MWmݟ:GZ;O;/\%n<];[8|cbr{uL7AƦ[j>0]yP&aiR 66&Э*m$(RXI"i$Ro4[..R@'֐IB47d(##$IOh˖;>Lb6ne!VE=y BfvD<{XfJA-:t/N@@Ɉ_jN9gPu:2sjF\.sS˸Kӟ^l6UG?.veMD`pg87zsKK^/Fs_+oz;w=IrO>^olrC/*& ({L?\w`UB"huD K]ʓI'Ӝ !4 l6Hy#IR7(Z*&^! t|v'@m_kn}9?lrN 3~W|{04_0MZӥ֜Md*$Gic؆E$5X(ߣò6WmVOzzy)||ʳw~pVݞ}\qOxQ1(y8BB¶Da!4"V+8G:$M8 I.@_0 8JZjF٤b\# ,(8K1_p1ھ/"X@_W/63=[ ?h8nFI0@J5*#p5%\>j޿Yrn}V/*&v9&E#ehF݊l&A*$ZdEF 7ru0ٰq+00~0k7Qȥ)22Ir$}z󄡤nQֹ랉I|!#} ӡ:ČgӖ2+k|?1Meh o>hEF&''\|GVm[G$ 5S=<^jEQEQf_g8"-?4TܤI:mZL"`c?kw(W'Y,۠eт8xRBMjB95uAFe'[V6:[9l`ͦC(ظyZ +Qm D(p CE(((,I&,z$Dlm0YoI&Hmd"˲D#f~&ibYChQHۋ;>nbtNa$|jdy~}W"E# jeavGW4"MQEQEQeV ObrA,#J$-l& I7q8t2&=cr4m8V ʕIu6mF+Y:o&glݳh6f@LNM128Bdvж\B^_/`KվED N +\988㜿W`hcPVoPFc,)ɵF:tҜٹJTqCAxą)%y [&cѩii6`blb _KLPNbGgD$O-;vls7}.3.qqqBb%4FTL)A0WfpB FB٢jIr>Jf,RXk:'4qP cN=ٷ~z T˔K1R(<-kֱ m߹}{~g 135#(HNеiVRQN61z]SS C]s4y ?pqy"R =rh2-E=*=*8Dt*trErV`_ eFS&i0ҒYzzqsE/] )pFd-qP&&Z(P9Hmʥ V/q߸o돎09P/KWm~)kV_ȼaZ7qqq|&±F+r(R!QJ1lGu BG>a>'\ Z>4 cdF f#.(eiwR$9o\,qd(A1ҏg?|3({԰}b|0-aY2>{+䢥Fr$ܻ/}C69k=}7|;wc DE7ž3ޢz_}L+Bqq!u3Tzd!sa W cinmD%c* xP_Z"}E A:L7i&dy\@yK|w, zPxa4mK3;dӆ]|֯dRu\{5hŰ5-ֳ}^v;!E㜓uO~2Q/~s>qq9ykXS.Q*ɕl't4XXMo"JaVz+!+1FK q2{'Vf+N%}=}OwGj{[ گ_+:YƶMn2vYYx \9=ua91AtvKx_z$w/|7t$W_jĿ|wx߮)o}}<㙯w~#ۨ,%>=dr̫Gǽ88-k5hmr2gNQo mFhX#Xˀb1f%oiF?J2PUkCtBO[N=׏/q{#s([w~6י:LkA3x1}}\\ͲKVa.,`_o!c |M`}o;j7w~9gO)Ǘ9~͟coI>_sE)@f5n)j؋W4FooeO-\4{{/?qq|!ZV43aH!XJ9\𢡄9vN$I207y'ʁ jxs\9ıuտW?O{s03%L߿'˼?'o3ކߞC1_Vp §[mˇ}?sqy1`玽ںuvsMP )N,#UV]K/k /A ɡlؼ[x`;B T]:f-pfSixY'[^+LF??\y%ook^ gEa|6?\#V3em[j8qq񚘞X_r*cZV~=zcljwH3HrÓQSDAE>^QPjա*+{dbjŲnPtPbtZ4K\8Sg2oϺ{VJ-X)V?2KGW1x}U&k}_[߻?MH\dي%`5 V+q΢u]ZZǶ/[}[yxY9^_2=$yo}x5Sw|dz7f[Gؖ9sqq7=Zk'$Z = U$INk~NKCfrV O!zhmHM؜/ٗ_Iw;ږ9 +O;_?ozO rqj`B IDAT+hsbcRQ !INVRkk,F@;4[DQ)SuZR@KU< d)F/-R׋b\6 /˖g\Iw%fO3>YR)Q = DCEIȲ[$?9Z'\:Yt&Sqڻy~a|7/uwlS׾??^'WvmˏCo KzqyRRFC0RR-Ai3n'tRK*X1 *7L6i;q5iQ U JX<&cp$;tn:xߊ~P@K(J\#$gHtVv;ٜ$Rr D33="q'E?#w qyRl[zszI1.FRbS.P)ȴ(6NBDQ@B0$M2:ʂ֖z;ejơ#!brJ@J߾]')ΩTYcSOJ,Fk)VFAI5`2 HBlќ$49g.8$Com088d| J5ILE}UqDxa@EGRDQ.c( X2Bhwh4X+ig96>ke/}`J\*W!*B2[a%bڝIf;)!TNW*4320i\xe<+\9sڬuY88C}Ob  N'G_Gc~'0F$5vJ!>q!"2U#M4[)FfΊ%#Ih,Ȝ:D:1MiϺrOwIazirMg֪{FO/^:~%88O8BIJR9Bv'Óߗ 2RJ,×1q9`W4 vv+diTsL|oa…[p$-J-<OFx Msfg4 &NJ(!h$B n7㜃L;;Cq'Hgn?N1^VX7zy88<-}n23Mk<'(mHT-Z[XkBP),UxFDF@_ Rt)TLZT !BZlL|?}mLLt@Dđ #Q %AZqfk-N-F% <1ha -RX,YDk._q&!ȹ0oo:+wnz-}??C˟zw88“fqՈh41`=E"Ks:yFgX+I5b\2eȴK-Ld bL\VJAW]yiZ>՞kXc1֢2AxODQDQwwN|^;8㜦!&'{wm OzT{+Hc0Ƣ3l DaHE/ |^ݖbkKHa1zk,R+C; !iPX?0LFibtzi0k"W4j e X`!/~Ypf(enLB|c4bjE<ęN@$qc|qq')%=Bcրȕ@K'Ih9\!< Гbi=R rc}h')ҳhcֆ( F+lK/\T}w޽;Tٙ&="rCd*Gxen4餉49Ix& XQ9W]uRJv=qqqT>я׽X,`B'48ɕ& 4 ȕB$B(S&|,jPJqRJqH&Sݖ`.g5 g^O٬X|EV=J|Xw6> F\8 t҄#b O : V{\;I5# ػwo7'v883gŊ^C"֠AKI|'\K5Qe,?7Ëq*1g]rn>Dس1U ZL qɥܵ#N8gڹ|F[r0`&#A{&@C4Zk(<6^k-AѢ)JNRzH |0_+J z=ظy;q(X2,mOpB`ŗ#$R)l&\.& `KFʬ_g^64_W}fm ☾d쳌[ `dggNd8g398W4wo>޽iy\xCOtVc]g-^KZc֤lzkچ$q.n.Z#)<} ( V  [D/CZ^N>`5q`1,;ŭ $/\?(GȬ 0{γݹ VCjQbclBzqܽ~ *M(>V9 4)\L@nٿ>|޳|OHR_8Hmnr^!UcUiķj nҡyW-G BtLGYs_x%Go/]!ZH1@ ȳvpOqb\`I~!] wA4ĥ++'léַhr5=[\9Ú&5Y9qy"~KVPS8D}.wrYTaQ&3k%>4 ҂Q'~wr=d:Ufu;gzq,];p<,W_4Bf'# \1w>!];BY!,/S|Xw/ vM;EP\Ӭ-iT-5 brBq΄9b"kZF9<)H\u|Ov(8~ >aؽ65PBFwWy1 AA Zks{q[x#;1DA$uo@o?ҭ|ӟλS`Xx;[¯<}4jSB_ظe_vú_l90˚Ջ9|+l>`"eTc"Σ ќbׁswVMv=Hjl䴎_!|(?ӗS؂UKǾ;=|6';6 ۲=+Y34̶%c:6y~DEdG{6^W4s{7ф/=V1:ʺM{Xca1H炤6n"=|ɵW_L80ō۩.#Or%;ludz5ӏͺI,[]G= +>[늑%ƶ^6 Uф >'[:ҟ8sfm? zr F[N! \w1>֬#\ͷ>3u*2Zg+n-'rM[ kN~[ӏ=x_RgkWmsr=[IP۱lǙYV]Tl饀<>"IIV2 {q\~ -Zqמj5v%;>p|{ӏfftUVRy-i}dJ1h+,d_l\P8㜕Zh&)IiDԝHssSfcai2qK0!,҃r!>oiǹkl yOsϾBɭӜNPe֍޵/&"en\t%3/,'T;mg.< +{Cdz$闹2zBKQ<<]vwqc[BI`WQ9Wi_S \8y2!Eh:S$4 e QPΓ/Og'=HS1'U 4{vraU|vQ0*zJ}>aȗ?-cJdĸC k1i\4Ëhvl}:|f'PG?.p-}_w'Q*cq })֡5cl;Wl:TcϬH<'Qf`R_EhѢss<5Mch.So?q!|qH߃-ز= ftJ,TŖ}\/rrkiF~1c^-:Ȧ}sќ+Fb ٱ;=~YS<~+/^aS*,EC$,2|ȓ}q-K,dlyH ZW%qv}^6OŁG<^{,Icxki zDtc'E1MƠpK1ɦS\-Tq)qz73-318s6߹ڠsM&R٭y~g<:f {}h={MGBƴ &@k.# Hcٴ}|cMoz29WR9wyK^M||;3ѹVLItLȐ+Xʞw31+rٸaYqfŊ\oYv5e ژe^GJu>Ga!=(Q}D iX!=ڭV+e; Hjsfٹk=/[.[U?$(-[NKy~E7a#:׿7piN̲jZδk2tzPڰt0׷Aaqsv/qaM['⒅l|%ys?d}88|vmX@HIKXmGMwNe,[\sP) P/|op cLO53>uBR<3u1HCXonZHA_"iBWsY!E_/_+<8OF+Vx&_Üe)_ q}{߉wX5xR^7%Zֶb2`sX1iRoQqsc{nw&GK '7D"B<ח>v|wS4E$X4?0ġR E5< KEZkW\9K|E4si%F/P~@hBV6J[D[HRE'Ii"}?08DᲶ0q_< mUdu0tg +vss VX"Ċ|OZU]14Mut~`888s2<8¢e?X%Bf9Zل4M3zx^'$2@c_kPJ)& $i4H2K̵aPAz8޹>>ՃC!>}=%#EX0F`"ȭE7cCT`̛7ȚKVyN֮X߿.l9ֲ{88[ [XÑr1BXrc;3uOqtF[#ם}@>_9V@ԭ sE8OoZ!܅uwz bE lNfÊKXl{lpwv]s`:a .w8gK+ ]W qq78Ã#'^?Y''9q)dýH {{6e~°;vחB0c R<# < qHjϙp-!~ < Yz+>h6sTn$U)0&Ajer ._əI0v(m{zN'%O[kZ44T`tҭ;88Oa?:-MTQ,0o|zzE4є Z,`lxc0֐fym)ۢ0 Bx^cM4J):YRWB+,Y"LMΰddg]# G%:kq8dvCk'ViZiOh .ZLOEERH,EW%qqG;1} h|.^҅!{LAƀ'}^{߷h aya4N9`eT _9>m: ''iz/o Np'8 ^$!;7Hc!~~^Q:C(53{AE% "qUUH !qu珌aDe-Q0#7%@YYZuxijY]lЈ@m N-u)ˊdBC9 ~ڐ;'8 Np'g&1@$*ʒ, o_VVC1F@R'@id`,ZhST`*EU*bɄ,p fj|<řWrz6`ݤVD:l(5waLAoa0eՏ:'8 Np|\~PRj$굙nȋ*ISk28uC)ғ\kQR"ښ8k}0MCFt8l6y*=.xbKpj63K!EiiL5 *{![{dadaM(0 bfrB|Op"* = Np'ؐ^5T Tٙ6 s=f{D8%S&S9*ΗiK쬡ޯ`m֚0 }>X)o}6pA@=8 O O\Xrnn痻\<3@1gm{r9L.DRӋW3ܺɃCSS[=EU?{ ~79[9cB'8 Np|LH/pji^  ?ho2hy퓑{GDV/~~RR Np'8 N8$IHcI󔝃X!$SF*϶sQeJRM l]>ZHdAA s\U/Ȳ Ӵ'*IH9j70/1L ~ً<gsxv1\'縼`nş|_`)t_8w~w~&_җZ{tz> Np'8"ؙpۧ0$RedYA hZjX|Mie- HY$mn7A MP(n)X, *8Kkzp!$&M VYD8 KXclﲳHQl,9<8Zh4& 4GF"4[dY'^~vpN053W-Lzc2n3aiY[[ MJ3 cҏ0DTSlmz4k9?D3(-[{?NZ!FҟYf<UTxQVXkxxl8g:`~ĒXJcXbqa &ʔ*@+t *9[=FL1[a!RʲBJdz>Edk!NrҌ$wv^G% yQrƻoQZwlS\OH,{HZIR!GVJkPB}0"/ :nYYIjZ~SFyFU:$ ΁THhtU2D/L^\Au@G~~2\1tFX)@J@m׷9 Z)pRS3ssW: wnp<98<{$ӬY!5 aPCXs<SB#UaUDU8q HK Y0`=gTz:h~]i9{c+(ޜEhᬟ+Sm5280(4nk[l1΁giD!VsZ:a~3XDZEdyA*@$NK˄q8q7ys Aâ"7p!7MBϷ}Ksf:;GyKY!=hbQYCQDa@XC*0QVc,ZX[E{n Q/GkNjt&m=ZģƙPuՈ6"2Fiʤ4'ÀNg UQD,XLQ1 $F-%ZɄ4Ɋ ~9Y^0Js>a(uJNZ2):qD" 5X*cFh5x̨?$K$!i5 %J iVMݢ4u; ɳ Z>X:FfPlgUNZNζ"rSd~Y0h #LV6#V/^) vnz?'_;wnΧy嗹rFs (hE<0sBYqmyf24$S3.v>y\3D3>Ź{s9:bPP O^^`3Nqi&X~/upl߿F^s+5d5z<.wǕhzb{p`$؉ )sdžAjj+quP e)IhsTEIUX<$R=X;p&JE.  )iXTPXc(`IB T)1T|c}P }`u@FJAfy&ɺh ; 5i0Cd\j(YVqawsÔ1Cۥ!`fsO^56v~88p@ݡPyIP{@#acs,b2fBg]YdmcpLTEEVt-&6R f/mqșiT8>xϽƻrA8sf+s%֒2cmDù9P|w$$Q<+M2$4 SBpCW趚iJQ9x]fmqp0cJ\8೟|S|RX HA"DQDh4DAQ2pBb$ q TـVHF RV?H^vwiB8t#aaqxB{G#rqn9ݙ3C%}oRD.D$WVPIJЊBJcAV"hU ! њI^(IhuTeFb4B*SUFV!Rg>b:`:FkL4 d&V?Hh5\edlefC`z7g~=rQ k[czagl#pEpBק05= eȩ%nZ]%Yg?' 4tsL]2ܸFZ5=_!O" ǰ8 IqZJZ [5$"J6bzGu ф}͘888HZ͘v#a1i&Vͻ 2 A#1N+,:CbbbFg}{R(58&'Lj>kQ2'xEM0$ #QJL 4E"EJ+**T0 1eF*9K*QYOQٟqW$Qub>=F֓hOlB< & EY21!PXPZʼ@8A4AX"2琶UΔg} b #H, 3L2>q-ţ{:&YrDPJҒ8it:md Gec5 6 آ*K "iuALlh6@ks @:"ax,زĚ $BiԘv,ML24#gSynaTueeA(Il23fצ* 4coaۙ"*1Ec硪cϩ17 ld &߻Wg/_GNY*SЌN4X^npcv3Ɯ?ˋ}{C曂..>[x[;ʷy-{c~o{V{gCWV޽ |e6_][&/J ufx9=>NڇM `@ IZbG ОPklQ^&g}MHYum1 @)BMXLUz닩xC83HbJ{Yt1UUQog-:*["ZDDI5UU'qOh`vT,x5/VU(4CO \\KC&YZݘfG4N-\><3?ڈl KVF\箞c01u&A,R c]8CM}g V[)VlnֻBj6 u@LNyp/h51N:?Gh\Q)Bx3AP)l>RС&jvZkw) T1faes( D(@t198OE*80A"\O`H[ $* [SWGFʜz9}V[]g{sg O=qf2P)~?X} ,'J6V|(l1_aensӌ'f۠-^yG'7x8KXJ"0J)hNda(!0* V,h컜HY`IwDZCáeU$P'W-{HJc]>5̘9[i50@'ƪ:(s~sϽz,8L_l1N-)ZiV"b7!{;$4lsE~טp%)(`J0ͷ֘z%lY2}:$N)͉UMH!jCJ޻w4_>Yͮ&~{ZJuZ& ($ƫ>Ja@$Q+=94|7X+{:|KE "%YYUtm9qZ"tҌ4 ^e~M6FĈܺJ BzL?"vq>+BPJ> 0I0!2)*85ΡL 8I4$ƙ[eT~3RUj bTxղuJ Ss$hD<-|bw*Q/$NOqfRZV "M-%aAԈҌp V iQ.R dH9EhF_ckp[9E6U^ IDAT&"*se"!aPu[1jH{8贚Lw,$=J V HWӔ&8(* g}Tķh1MlOX7!>-Z*ɯ|*.>n(nRs+<}a/>1˯Ċ+v&?@lر*jl}} ޻q'u(.e|ΘZkْhBӤnc(?j_iÑI+ 3!5Κs)(:4{ VU~^ VB"@V(ۢ(*xE~rJ#F?݋Z9YfcLnqdk*C&WPXR::{G9eI"x}Uܼ}|aK,ϐ%7w92N **ڭ.טjZɀVKgЁl (Ph3ňbklfNatr<27u޼v"h$ wgqɋڳJAUߺ~]$LcqG-}~aF1v'/x[`,;Keo{1>tHO(*K(F@)qL $"**k8|R֩#=fg~{[ǒGv(h$ZIʓ_d@#I@ 4ϟ' n|xLj-HV*F"7=VՂPKx%8/ yejs ug̳OnZ`}}=F iyn+2(AAzl1DZCUfhaqmI0?q zGtZ Ny?LYg)dB&#IK:!lJIV:%Â0iZ —5a@ Q4A cLYS9(cVWZ!a([pz{dLYEgD#ʢ*+_lEԖlܣ:)u(RV;)z;E|O>w )|KS+FBhfbQljˬ-H}pR!rMmq *V8e?os:orvh:!$KE j 1kk;Ҕi>'y0yA6D1_5Ξ^mT8aIӜ.N+zϾm `)K\|ƇP\VQyJe  DPR(k)M5IYiu0t!B "€ W-3}o@idR0(+6x(74HiDh1kw*7޺;L:,9wtE[ ̑V~V]6=3Z*d)VHI+9k9Pf5EG!th24㲠'&F /pO?#},7uۛMBZFxbY֒S\Iև+7n(FJ0Fqc{T)֓%|aANPգ4F,QFi:f,8bqiUi*;=搻Gjyפԥ?Wn G1v,BP+dmVZ [V`,F ѡf|x;ز}͘.FLJ,- -,2$ՊPy%^f%ehVNCeHKSSW:qKko?eEMh4Y\g~a$n1jNp XC>0U_XA!) AE>P%[!+9R""C(BHAYiNGfF1:PRCcx ߺEs턪U\uU뵪 uC8$CYu opsn<;/~%g_$B$ 2);r8DO88Q7Rlm޻~w :KM*Sϼ,w3ΝYfݽhͱ.ffaS%tkzD'93]w ~M_Ouh5Gc2 i"D,9doqo |?ܥi)i:ƚ$iq¡ɑ"l\%!$`j/NqKVuAbaEew7wɲy48')woݻ;#4.qT@8k7qc+槧csi,#/ugט9{6FB "gQ:t0FL2mΞoLupiF!!?7-;DZ^; }5fr7 ?cX:B|gޛ᛫D+HB&XPBiAVn&B4f4@I :͘=Xuúo}ۼɟU?ۿƩ|p/_"PEhUP2rIm"n\>!M'LB:6xJ6q`.ӧf`«`N`mEX$M3b8Ɋ:, H)oG_7/@2*|A' xpXG|m`s$zgWΰA9aF$;ٯ].Y[[;<'Gio甂$J2k0ӣ4C@ь٧.clRY^env[fldnv MM 7޹ç^{_}DUx T gYu34␗? opkÝ*k9Sq j;q@ / G}ލdk^%?*5UihȆ" ^gܹ}V#" `f C8@HpgL2C#iY͓RDQ@:ɨdmJF0t;lݺ9ڳS12esk  F)ZGK\}<7j?{뮃D z4! Iآbs}|1ksJ`1 JZ7ۤv Ui Lu-&iFiD+ hjn#b{oe!tK#zi[@Ji@L:J:(YcfK%h8egsK\vd#>`@Ru:^P;ɀlg2g{atTFR9 fnEWj"ЊH0"Vppa%#FR4diALOuj'-ڝV }hu)j%QD*T*ZK 4ZO"DJo_+C:eݣ KYik>U ڠ9=J}+/\X"ӌ6"kw]6Sܸ?툣2?peƽ{>E5a:z ʅ]p:kgՈ7og.39o {..Bu޼;!Y@IG޺8~P\ogxݷx ܽw~uM\m4 aI4ȺC[y[sR 42ڪ(Q: i4ChBU1n& $AjҊi!T,,7qwH(k= }Fہ|pgkY:i:EAY^+)J*@+eQQ\||p{}o5yO}K c\!LT;&߸Ev{-οpq̥U>s|݇|o F(-HWϟ{:=3޼v|VyKOހFw?mr)fht"VϞFm3?9,1dݤ7f4ȹv ?nH"_ۅAuڌ!/y>/QɊLEQ)w!%|W_|n-py\wvε8'uDq@Y)&eTa!qkiM K3=gjf`0Hq&IHT] '1kQ'.1ӌy[<8Oi5X^m3q7x˜=@eKcVZqŕhQo,-gtgh]Zsd^ݝZNA C 3&{ sι}{}!AQ$l#[e[U'TIj&#x*-gr\-idk%Q(R\@Ď׷?DT&**tƻ,AIN.p۵JrLy`ˣۂg% yaVa<$ 8ɓ g?Y~9?O~?G9o}^ iЁŃɄ 1RdI#KsFjXB=q -C( }T1F^ݦ$|=f"D)"Pb]Ӎaף**\pymrt:q3Ǐ#+Uhk\c,U浛l U3⥕\.dz:B z @ {Ec]Bk.sgkӓ8EuZ4OIx^r&7nm`)t]rG]l\@-֮cYtLӧe*]+3E$q'%(DqSt,i"q]$#Itr [Hñ%~R{l8XEaY&G6AZ[dY6]<צ?|, BA--av67÷ycGqs3⩳h5+!QFH,$ .u˦g?<~#':?TJm1Zr=+{ V`bf6;Ø+}@( Q`iTTN\F"((3I*Rm[<$Ѹ08s-IJ7mTE1i-?>Tw3=Bٳ7̨lj-y~~~ ˿6S"V(鬡 SUPH~C(;a2,hW7n/]dwb8Whֽ^+3e %%%ww@`{0bmg@:=R!QjU,Y%2KC'qjڑX_~=5n\±UOһxM,+\d(zqwtD;;_߳RZ焮GG)[;]HlBE*#m\aӜi?N֚'3DB'R(9o}yg~| NϣýGGξp_f?!EaNB@[׆\z}{,1RIZ%\((0) X 1*{wwo\{i,לJ BQPz4!P}M[`qK2 'l#&Q6y I`glhJ%SQhto-I֚4)XYŷm(\^k;cNNˑ̤T,Hlowy)&z`DT 㲾 ϿVzF7G5CUayKi7qlh1RƣqIlF1$µ,5% Z,Gy:#W |jnwc(+߷xX(T>)\u|ߡV Bt)l:j%7K \:MesvoDIB8fkS(h^oD:2+)ʲp~Bݭ.~8JRxTk!FVF^¸교0kj,Kʸ)pHC彿0 z-WM:G.r`iC[[134i^re*1suMEeC)Mj8~}VEF{bjTT9y˯Jk #׏k-5Z|_Uv1?l5.]zŠ>%?:e|Ϟ՗"PyoR J\]Avl0d[JvwЪ$*KOe)3Ӫq`ɰ?be}$u4qIa&]Jl$'Og}8xn1?|O졍&s$ÑJ%DfN'^$h8pAv}[;l",ۧ^1GE;65.oQsH 1PjedQHaɴF%) J`;Qb`Z i|곾#+1u q vZBW Z*Wwtf÷m<Ñ5:!9EG~V+Wt&?w],µ7$Q\E2IV]P".^& \0s]@kMoEF3Xv/E`#NO2.8%CaDdY+Ȳ p(ZuS_˖8 ׆m71n\N)NݳunkkF-V*@$+{1LlgK($2Aǖ<ҲSA9׷Glx:nsu8uN,F(1(댭.{]|C6fzΠ#촩Yb߾}[\9/Ѩ vť|Z9QF8BzawkpDdnCYDzu-t2\W`ϖZ=eȳ϶Jʴe#풬lMwf"/ayeF&*01ZSm67}*$QB8 Rv0&e}hf.@jt4$IB-+|g$[dqD^d0DL{mՐf5ٮ1ӪlTh+4UI.@!"()ͮ;WKnc;6ұQiHۥJʢe;ϓ)^x2<'L؎bF!VyJ>I?כLc(b :/h%+E9BHqaII @#!]A%,jr8EX唇kBSe ӵјV"- l.Q-'J ߖf<.2[6aQzpRE.v<ɣ;یhcYe3R)'"-j3M\URM,瘢;G~fnʨp$ e:*lt3#C\ףUT=<0uXiuѢן02Qxk5vI ._gm04B9ߑ$@UIĸף7`$*EJ3MwoD;fyuwh6*v;54$b;$I jՐy-lNu-`RtVE h)xBSaͩD#:2Gy?6:}Fg? P81fx<}|_npiш;ʏ◟ɧgs}[(b sY,GlE&pw1?<Ͼ|_b߾E8.}FH)]dLsa_vT9DrU.S*~RsWO]Vi^BY;aƏĻM>Kϼo[9Gނg+_qcyAoLٿ$Qo[R p<ӧ򽏟F#*Az5B^̰,/>u={0+UtdI˖¶mPٔ},SqrU6SL92(,!&yNkFÔNѐ=4ŋXE##<dy?ۤZ((lvi v[mM$5Q)Hsm\=] +:'O3̖IH3lu|hKEH!TkT5ݒĘ clAYƔ:/'*㠄db4:/w| Q 굡נU͋qF3,(gӌav=B{L_WYؖ8;6/ڒ1N>ϼǹzk=8O>QF >ip79{:aD=zx۹ `ey}{N坏?=ibQhTi<,P zy^7.c܀Q޾+ \xk~9J<ؼEJjL, dߵ%Xoc0*dZ!JR|[ >q2S,}Z&!RJ°'o9ҁ9, #l5zɈBw8NMF`WY=z*GN|'Dw4!G>Znp4nÍgY[Ʊ}Wl߉cx?}*ŧ_fww[8^ϼΓgo9gNpa#F!3ݺ~')'u.4LLjSB,[ I.SۧLlYY0e.QSnIc)TD%48Hꞏ#db K(fsϾzT[2Oƽ%O.tx*__|4cv j@AYa )0 "k c8BF4$iʤxZYז|c PV~ԵX)(;L€89{cg{g]DO9Uom!sl*Wt>ySkp,v!~FgM b۳}ױ5>%JA^`;.Y&%*KR4òmlŲJd2)5$CZv FEJe4c/%0(uH !BUH]@1YNFnr-ZZ%UYyT1.)t,!`FsԿYc.^KDevDm,8fnn?.3|;3/_@*[ #(,Aj/̱Xe2/=/\FOA:sg8phW_gs{Ѩ`{RUh,Y:~y8uOҘW9R)VկzWRQ?xÏVZ[gv^+O>/5*ϋ3i,AX)(<ڕA p}ހKqj)x(eن8$Z֬lM wstK㩗VขNrvJ1 х@HT*_y>ʍ|?y}?gڭO3 &i Q IiU Cѓă ^:KU.]䛗M$ͥ})87] s33(G07['#m<6ý`TvwmUAK(oU~DaJprl\!sXBP R>8f Fa)%Ġ-r)JSAa [BЬԨ4vgpd|-:C,zo}1~n&ɣO ]ꕀa_oo|*?mԸ/bHE* .q1BOZyPzDUy`ti3um8NI\`r1Dqh7e ܔJ2ۨөVn^UG%sµ$IVoǣy# 8Mb&IStL# `Ah Eu,ROؙis~̵i.Qy I-nMbqF;H9v=N\`{d ؖĵI!Ҹ ~%ph5jX"l[)ZjCՕO%2U 4uϷT|<ϝ6噫lz癞 ʔ%3}lh1;JSUld{=&uW73C֦^ pmɸ7d{tz# $+4aY`MB!Ђ\,KRkw`6O`KK4BSmff KXģ1VM>e mayԥʳ^5,7 |sKPmCwxDۥfAG谆lT븮CTpQ'd EBH-pEAW6C4+|mGx{'ni7LF\pqۯм^gێ?w+% mvbؠeXma5nhyg)b]a.MRb8Ha9;~?ȑش//\Qxbs?w}>A~g~w_6g_L|%| 2;Dbb܂b/lf!2ohI Hj& Om3}?޸x1BaTc*%!iȲQf6zTS➾@^# =W[2ID4=Z _k5f4g˄n| y{ p2Y2єx7oCn4o3#,1vKr9^_q`&VDQ9ڰ/Ҵ7o*܈*2ot7E-\i*We){v9 IF}[|kOoNd:!IFoR.XkXP*W =KuN3,u8ϯ<:TƨhZz]*iQLq]!ls\[oǽu׭|?U#P kHEq޷䙧;gX75&M3ygGd|#d0/|~7F22L-pO~Ǒɟ|G*<$g_$6;u}PQd ÁCiK<|BZհ㲰4glG!~-vMT$d(-n>W摁C6Yo)Gjd8`jBOR{8z^kKM H:q`4} ? '_ٻw8tq9Z^NwgɑC7sۑyt.f1I?͠W&*9 ULTffK+z*a hje|-_NJcU[FE1ASյN<#!|:E)rgpq3V;.&P=*^v昮*$q! y%ϣf}Iq|M71J2- Nz+7$) CR}ƫ1^o,Vbc%l;|5fj k}` }*EJ))6y,u.__#cs|nd{_ A':%r̞[p hyu0 Kwdkz49ZG2ސNҧ?= P"/n n皭U4E!ӵ4`!A6<3 m{rߣr{ؿk sdK+V[\z__ev.>|?~3N`WO3s;'&ÇuǷs_gWغm3Z3ދ^dj)&G>$5̰Hs"a-4w'qюǴhm .$WEMJ |*]c,W$YNQ \iBWj#Y%ϔmĕx>sG$#B%63ʏ^X4Z"ߧ^/nn@Q\ A3 z{@ThKa$Jv:4%<ǥ{Ď$uQu6y`mdun{{պ6m#J8iDu!ͬℜ 'W ľxT4k<qFq:Cɿ1Y_^+x]K[n0Y:x׏Ӕ2V/)a9ZC>?wڧx ?y;SG "OH:mT)Een)}c{(&Hk[kqHȲ-&;olʱ1pl]+]lƬVaHku]A}zo{N<>ǗU^;yWVWXo!ȇǯyI#gip=(G"8N,\F}v{oXL0gj QXůM[Ͷ-JeD^j+~tg ѐ-vGY.]F䄮?yr2Q}j |%\]kA/x>n3R]Zeۮm+s<1@Jwu¥)MLη6Hm.|׃č .s^΃1?>_dǦM֨brTɋ\_(q~ϙsH R #"sWg@q'aLJqL=$Q9R:dYFX߿ځC8gN_=(MA3@k/r9F`W8.µE4 dB#,9&cm{'.ӬyaJ*p|=1à?djgi%aadrz^ /(K@@) (>n}N,ٵE\)K!q`fbv/X[ZB'Qڅe%claB"ǮԭRIB`r m|@7$MRVvp4FeU"[אx ƑxAGg1+ր8R#<zPׯr'yk4V,Somv*lٹf{ Up3Γ.w*qA+ ?F { d w$H)uHcҡ6! ȋzHkx$굂ZNo@?IQF#AHG{sGZo+$]ڽ.QJ#x)!D)MѨG(euL "w4ZanZ(=&>k>:Mwi yN{E{yRAEܺamسc ʑ%\A{7De"H\OVs: $>DZ>AdZ#pk(+ Vv{ Zzh TEFQrZS)qzMqpO:xR縖 AaH"S֛ضB [8.wutG}V݀} zm{ﺋ{G\]pD &%]r|GDA@$w ,y!e!7yW8E IjGDžbmPq;}v IDATWHzK# dh ?|El"R/IC56^T>޲oǯpim3w< g3V& |G߶vOŹ5 9G1v/oe}Gސ&d&G 0)z6Et jT4hrH$p..p]7q}!e4R_Dž!ri=74a'c7M4ٿsi2BV;I dLOobyenE{mZB]j'c~(BMTir1A9 H(bb iUֻbL}yEz.1w5N;ω..yշzrYѬNh/6yl<|Ó^Ehv$bFHxE T0 0Y^[g4Ș4εI!\*n`@21IBm  <(ͳO<uN*#4зG8 |;瓟NO!2&|H c&#P')#YXXZIɔxߧ\w9|=yπ҉׹BTvq6i~ssC~}9p0wkm̑Sl޶4K&C*&{ ?:=ͨ#]p_wc?+9 W8)p5:'ص}x9=ÁO_ee'%(Tn=YV9&UW2\ dΛ.<'7,Ϭ٬ŢtP<b%F %bB_bnmJ3QoR[ hFCv ]x< 7,eyEӧ9df0V,"X!2VPUȜ-ny=߱1y`8$ ؊f_EڐfAX+q໖- [m\QH},"t\@אA21HR|Zrr]и ˓11]x}pl23wQy &W,KZZ8aiqkW[ncR^i;sfo1ޤ\N8“ף'$Qk2TN8IzVG$n:>N}u{#rzF٤٨3լѨl<%bߣ$ 5:ap$js,JBF p@kesq}(G!cccUH_2-$9(В_DnIqݓo&ш$m[fb,&|zE**# [-Zy^yJ$҅ЧVo軍^"sr,v8oH Ӹ0<E Jes S<=w-wҳ8uOzt#!$4K,,_Y%v4ˈk.^Pf,K:Qh_on;9)QM̶@!is/=ٻe/az5V#JSiL4 \d4gd R"#-f%~ћ=qsG7?+^iђEٗ=[u8P#|Y \&%XY(Í\j*FZ9b8ngaWS %Y眼0Oz/퉭T^4h4aqXX!r%\\e>(cܰsV~lmV{ |Z܈UNxL7f)zEvxxm̆ߴ3HY[kc Xn.q,Sd} `@j2zոA⠔úA#F+yMveW2wmn h*q^§?߶[ @uN][jt :C,^}Oډdjڝ>gO`{8ha>J[WnDe#L4Iؽukz 'nҌBHCRPu114E( %`0ؠV"7+#r@Qda);Msz*[##Fy99ibdJ'$b8a!/.d ÍיκaVJٻnQ1v鮯tY ϩTjTe$aGӥ7 KÐ*kFi|"Äj\,<Zo\Ae Cdy }S9TJG(qđ7< a$&Ϭ Oݰi@$M)U*?4IeATvo15`YZ.QI9\:uF !\*S nZ-+|s8w:?2֘19Es[eVf7ofllRa/O(ϧ~+9fg# 2u\SCA縞&a{LlJ2:>=w_ ǎӄ]*ܲA2Qm9u.f!B:4ƪdJ3h VA #J6&7Oy|cg#g~O~}s|꓿k3p$`(#0c%T(xJ8"\(89F{g ǮO\ <%I4;7ۡqeF9j$#sZR$K$雚Rq۵Nk)>D'G>C__$kuzvZ _xn Д+rAbe%aSҀB"PCkpd*ڽ(6IA R 5V[C~4$e[qĆ q< m;w|P"q9Wzcv̶m9sRG')VOLƵ+,:&tJ^̵O(ou?o{{GnǫЃ5H,!xh ULoꬡLΨaݣ4hmӹQ1E rCՓ+$yL$ƹ9:c=g_DQÏώ_[g^dqye$yEZrHU[5gia1ؿISvsdu0u|J2n@;'MS4(G菆HϧѬ39>F\"K*:O +7⡊Ө҅,\ \ ǨsRMt;`2? d9^%rw-m5f \6!z)f7wvvn\ (98zD:#Y Èx2`8ah 3^P0Q-35Vgjj/ר>j/%OМԦLИ`rzZ)u$*,ԑ.hh )TyF2t"N6x=٨ JZ19Qcbbh7W:"V-lhp=Q1ȓ 3T.1dt}۸9wË? _y=wtzufvr,N{/c_#;yiLcvN-^3smeZXMy'RQb|qLӦ 'F$xeUB-Ab(*nz; IDATyÛ?:㎙]Yvp,C+ /_8˥!\M|IZ|6;6M™++Cur4Kw_+طk8f,3ػ{o'][+ ,WbD J0B3 <-o%Pr0!˯rszq•c77@R8('\[`,A +} ϵQHYT̤rT$(^ɺr1.m^prPˆR+kkq,,ƘpU^17"mǞl27hmm3 ֖{1ͮ?¨&)[~tbpK\9v]y֞ H癢D,JfKdٲI;QҩtwRuҊزeIeILI)H  \y8L{^{u%W*s{['h|*((]()y$6r.<O<#?{wr7˦f$#{o߲,Q̝"v},({K6 "+5~P6D,[ۍ$(rCnD$Q> q38öݽh j@VIz, kBfԛjŜJ-m"$H^~R R iخo|(J회ni% hl"cnfW^| 6GhZ4`jEkI-8Ml45Z j#LnDY' *Bt XS|a24~JeUBRHIQA~Q}/ɐJ6Z8Okl3AkԦ j*cU~"zk+tWY^찲rM8*hwzqBP㌍GilNb@X"]v~]"wc2F^TRe.U]SHzII=׸si~ m]}&%}ڝU\奧_Ef1ݠٳ{+wT;vsP{8vq໎v6R)m6m)]ߛ%qm-!k!j-N6s;yߑnn{/x[ܥ5괕(MGXHer 㓣 _?gQ.,S(pTkgGH 5DZ6BBb6qh@2j]v4}rw"l2Si^#r$*EQu1YRna|aFS |<"aT$?(PQLHg5EV8M|$qLe8gm8̆ z"̖a#P`ik#OYpm鸸ă.ХMUc;x"Y\rQ:IQξ2s囸f^>r|A_sõyyX,/u0m7]BӍbݿ~m<+F}ml iE5-s\gMFԜ5zy2ĦfǴ{dcqrixKM/$F[PqAq>#Mpo|/Pm)\ ʒٕE<ò%ZṖe(eEق|BKJ#R֚uA?f%%ALZ<5Ï_AUk/=7nu?Z@ROX=<6v3K=o ٠ڶcKi|nLKطxϽ?oGx/op\A(!f=̙ї^BN&[cn 2KgaRQBS]/VC?(͖[`FЬ7V:l޼sgΚ =wws}aK|EMju:ZiPL-a|Q^ec'( Y $]w >;q],3$2:W l5{^9)$e^ KI^ /,ٺw֨s]tnYL%y&]X]/Bh\AcUm ϗ6[k;}_]h33]{.wla tBr~'<3'f)DH01}t{|n~Wy_?m'^i& CS-_b>?g2ҧFB~>έ~E[GXQnNt+ o&=ŏřڧGxx9q+eTaa9i7uz4}ů}^Ч3h"y>刯>OVk^ 3\Xi3aF&L<^~ymOl*23&N T%*)_^ZX>/4TKK-) Ŏv(%)Asʜ󺠔ܐ ht32[^|Q" y䡇y⧏ ~md=w|C~irH͐ A4Eȍg 49ب0K77 ~Fݡް%+*ǎǬD a d*<'/Ǎ;6H-5M#H0QDHJP(3aGN{q fXN©xCf 5Z 34+!;J3&\'ogrj;vL3F6Pr_43%J8C¥glYBQmI` ,ʲk2=HFr=+Q IyF'Z8~Ny+y-x|'A 70bL kH]L R;xfd6n{c4yu,;dMuY)u3w܁Mʩ8`sf+>J]DW=tJ6ia'tr,Vbau@=WyO] 7^ ɤض*T40rL̍=ȰCoD$y&Ӻ$ {$Nm ȡ(u n/GNFBg9BUZQ9qNw`?HQR{SVgjj1&ƚl2AXR -<]w66K)EF6ut-.1=3G7іp|6JA+ X]-MfV$4*)*Ȳ jUS YFq\Z> \GٕXA2A~!)2j v&6IgU(`!T\6Vsh˟}<5ܷˎхFjKY;+$ Ԧv2},)vLm6$F6J +wq BKL 3[?.֭S/t lPW%L#d^.<󎃯q'_Uy',;[$9?͠5p +>FGH{:?K^h*m$-dh&t2-pA5tK844j.ܸ}7aX JN<WzͪoxE"o/c!x9~X}WQXnSiR`ڵ͓SX6>Nn..,=p<6/o[?~T>o`a ²@8~8˼|U;a XB5'ٺIBoin4ߏvHRX -Z0$Ej,ˡYoRoˮ-}2:2sgr b(@Ӌ ){ojlxK(ƶWk8.-l4<ϡ,eQK X#nR,C`&%juHhU dIQgbj.;`0@+A>qz5Tnd(#ym/j K!ʍ]Cp&AePZ 4g(Y؊|7 O3rU!yiH[mÖ/%)e*D r, ʳI*i ˰`Uc(Nõ\g> |g~{6lfFּ{r;7oY퓤%iRml۳/Jg<OM2*~]t>^>|GGkzۿbC?yhC<5DVyI.96]hւS3}vngS-w=V]{KF{In}kxc&B,UfD/hfߘ$K"~^[BS1Һ47emBGy e TӋ؎+sqCi"Cu~[ǡ3OwLmY%"S6j hi]rKH-%]9u{35* uk{vq{<^!]]_}_y(ǵ O[XFTMF] TY9P/ QRpY#&cbKM7#mȴTaXrBr,l7M ?6mXynš5B{2gB7nXEl'KrT35`|cc$7MӪQS%RZXız25֭Ji :ߵ0a^}uHiQ <"#O2Z2>dlA%c0Ҧ(]R,A RJlעY9iFij*Vj'SΝaz~JJբi3۷ Vp,MR$" H - \̇w7ݱ7T5lۀx@mZX-[TA2  {.#Q&-Pʌ`~~'c\>aU:49e%vP1)2QTV& u-H}6mUȰX_"9.d1(mj.kiNx芏#"KI>* ^ $yOpo|w>,߼ ]sj'/i*OxΪHEQH.:I_] IDAT E*5 G^e ؽc''Ocff昍 ʺWE9iŐcS@i[Z?F62(rk$g#nO?|/=xg~i4+dYF~O.9nW=FR%PiTDmjstګ|EQEJG0ᶷ}36RC ڼ|OxW83K:]뮜;^Sw_xcQq\2G3v4 z!>>} XZHm]<%/sdQUХRT]JlrPPd˖iLRFGo[t4wkwx聟8;pT^9*cl۾j+06RQ]$K/.]~^4H-^z:/K?auhSȻ'i?,/? >̫g-Llta3;wK8!XZ&O *oRTZ}NŠKv\z(Ta\aFŐv>q=#Et__ d^/e! x=&YL8mxɚ\ӛmJh~]*~dWB\cyn %0w7b-w=RnR838c+V xN`)^g EmlxuT*XCr[H94#sSlv \w7Jj-V;$IfLP2P( ]d96dEUdj5:(aǖQz'h6X5rygOs#b7_l?N~ پ{_u$Ks >z}ӧ.s[?qgg8{>8iTt"ڽA9:B؊];G _w84.y|呇'^?z-|o!߇;v;ǹ$ILEU7FQ&͕Q)_P۲{\]o?H^MHK1>>2q%X9pYb"{Sϒ>(# db; ",,!emK褠9HVc 7B i\p+hDk|J!xcSGﺝ*ܽ珟 (fz)\pڐfaC_h. I&;smWt.0J@pjٔ%K`kRb tNIvx;ضy"/V~( ^bjP4ţ'p ,/h:?7xEfWssc;O̰kfzU ՚BP6\o%s f1X˸0DϳiNfe%FI %5ټTIhw&Tp;hSG{ǒ"(9-4 Ox>ni?R"fFwsd-| tsCv0CrUE+ЊcRvХQq46)%4Gk J'pV\}?rgzS z}ANHs]4pm\}`d S's/E\gV9BcyF&X%R>j[XiFPw:,^:6aIa;z`ee@NiY6yȓv#co9u~A8w|3콒[|(;.ȵDDz׫X@IwfJ] I㛦?5lK=7^.b;y|WyI],/dT;o-&!sg97C/̒f6&xYh\Bc"_g?pᅦ?+4ª3Rk 3.RFoQP0u]\$ h8T+JqIӔ(Y]ը32=HQ% Kk+s"zcȆR2BCg/&K?yw}#Xv8=ϼDp Q4|dgNpݐo Ŗ+ )W8KVV9gsWh"cmu4əS.PA#6D4_5 /E 6&6t= q-/b0 /IȜeYBZ8悤mͦMS\EZdILE @IF^Mn6#SSj/~ؼe*=QN?S{)~䫼4_Է[[sgi,w3rװ8?O){e7oJlxY2F8!Vq533{jVcI;bVR"[b{w^k>>$cҸ=W"{EXڱʪ%J^7dǖ)yi9xՕmx[8Wﲼ֥U(X؎Ɠ=0[)Aͭ7$En|{^k$(B+Kj͗ʂ9]#-4[c:e $C,YB^8ZYj˜AQ\nb eѠPuܳ|2QcQ~'N2ԥR ɕ@h*6$GۀUggٺ ؞kvbw_QJbLQ, #FHJX)^8rT~-l,Gp .̵y׸n+\U5iNLM'TEd26 Ft(ݡHjU3\G?7iGH9Vݣ?IrtlB'Q kk<h@\p2n%>P.%()J hXҤymi>bB҈eE;S/B%c!Rۛo(M&pw]z\h5A]m Ufjc&:peqBRå6* K2z }[B\ h4!N p iqf"7M948TBZJ5 |.jȋvg n_bff'fXr `dlFGUzxxAJFqS[&vhuoxDBQ`KJ¼BQR˂,MQYB4^Xv\8fei%..d%a7fPp()ҌAGEt,Iټev`;:9ڝkk=XFiB{J8NɳLt NDHF[Z BסڡaqMen~z-VPWk8s"s* e\Ϧ^ q , '? ~ͼY<}PBfW('W+x-]8buu]@=z>dsǏ~³yVTƛھ[}{vYziΟ!bbA‰>K’9@%@7^Nr}_ݚ+P ض۱]XRQ9(ڀCRA4|ߢQi2ǫ$N W"qwD-HӔ%ӍXY0Ơ1U]ia2.]lo[?-|M£5I#;6 28f:TŰ.؉P B,e`d%\Ke)6}*RBf\[U3ېߋwȳ(!8^v˲gH`Hj4ul7]hn̓8u]%JIs\-NhdiA ("$<,g +6q1s|И{_P)n&wu n$QLۈpɾCʡqp&BQ"YI!Ξ |xxlD=~ȇh(Kh5]ͯ}8qqG˿)yN{3\x|z/Y4I'wPi ѪU_o뜫'^W *x n%bxoקxWXXS]Y}w;8'nwKR  Z$ ?{+ BV@PI4NlvR{~=+?3xNJ x^ь,9|σ}_vs3Oȅ0ysh^2a0}'s勜>u/? s3kýSS=0Aԉx5  /Kzho=>y,/>#8dX%PKk8*O^!2 {Q惏AMUFFntG>ȃI}|G_\`va'NJm1ŤcvzS,|zŽE |nvߥz89[ĚE`|XCz%RܑG9b04z:SGu gxe6ʛIGiiI!aP'2Kj|49]iX)0еˁ/Jm'&*̮wMn=BO积Os}X P`b0Ѩ9oPsL^{s}i3i^oQ;w+>WEVZ^wm\^X vM\HjDM\,nJ3/-stXR|4n0\O[dDG<>t]#;4#}Ta(t}H4 -NgBED2Ǖ0B+8u.zQ? aj&IMIȌM>ЃxϟO- x* v.G4}}z/kMZzJqm{&ǙE>XRWj"( TaF҉? ;1'+-aLe{6Ŷ n0bjNHdbjFar& Jcʌsx!K+\[ͅ&+D~EIC487nǙkP*lXkxJVR)SoTXstG켍ʘ:~NKG =g}F&"#o0 b88Xԣ(39q&ٳkBUNm%}*H'bkhı zhzkl,d٣9 0Z![*(jPS(=p8D7osw|cW\#_(Oӧi4Mul=MeUIUS|5=SSznR%WchV DZQ$n İt4[ݸٞA JQY6%oGl 1:D1蚆AD4^cTey|-.pKG>i>x9^z :\HkEoСoSqq~01{x_-rV s~>ྻbbLo p50C:(ŽcFXhz[Z2t0D|?hԁ*kG^C"0u4E>6Aqv7 X8N#˯!g.q ,-bcG+F>.]ltq]C%8iaX3У0ғǑ""(Ban`0x=G(:eZs4#EC d i#]:!? \2,5R5=Fs="6z=:]gzKKu "I$Hm.Ғa7Fk`l8m|ߜݛ, Y)ˑY^^#F v(ڨquz5 `0`Zzzs/VNBi'/766 p]ma9nH{:FrfTjdC@*z-˺q,oB@b4&{}FN˲eш8LmZZX1.^q\Re!F{#?ihŨ0@#^?_9u=1r?3k`^쓓T_du׾C1<{䍙i'IbF? /?o‡N<t:.ˁ].]fz~WT]"6,t-?پåy*L`dԍEiFi+fE1/_"gTK69dbOע6{7Qa@}| j,-,ƞSC)ʥc3 iۄApcXLr.4d/ pzHhLsQPETl1F?מ=ϋW;6FO鄺ߏ0to^I.Ϯ\779ihͭr,^#JêyG峟 ._s(Mz(y){ z!!W-1"'p"GGsjodZ*8ئt#›{6d$‘Ա#\]jo(tfX >hq5 \~dWAC3ш]?'xWbRUnIő?p='ؿ{g;0 $ (b4,sm} uRja鸣ח:4C =خz5{vMY\i% 1dɳ5OF rE#uNkp@xء<O0!~tAn;B C|-&4P:AmyW)}K~͢* #(d8&/HÓ89Hi'b%rjMӰl; j0<(d~19NV̽fd+Cz>&3AKVۄw~6f/&I+{!^za&fxB!B9+7;$ͫgo%N{|ldBocs@j+X!B!$ɒ[fn\gcmBoM8؈[Ic+!B!;٭CnީM uǼLݮ_!B![iĭݝwڛmvu]ֲs6ƙf&T+B!"TwfWoV߭|)iopqbnz% !B!]] ~f v}e>nunۻ^!B!ۅl #9g67{U*|B!B)nF:v˜p>V˼PlJBB!Bۅߘ' tlmNt|&j[>OBB!Bw~qEі1:5gV!B!)n}vw|`NeWBB!Bw~Z ~ۡS6B!B!v 1p*]!B![B/\կ{ !B!$ vB!BO`!B!B!B!g7uT IENDB`mu-0.9.12/www/mu4e.org000066400000000000000000000044271250406103100144320ustar00rootroot00000000000000#+title: mu4e: an e-mail client for emacs #+style: #+options: skip t Starting with version 0.9.8, [[http://www.djcbsoftware.nl/code/mu][mu]] provides an emacs-based e-mail client which uses =mu= as its back-end: *mu4e*. Through =mu=, =mu4e= sits on top of your Maildir (which you update with e.g. =offlineimap= or =fetchmail=). =mu4e= is designed to enable super-efficient handling of e-mail; searching, reading, replying, moving, deleting. The overall 'feel' is a bit of a mix of =dired= and [[http://www.gohome.org/wl/][Wanderlust]]. Features include: - Fully search-based: there are no folders, only queries - UI optimized for speed with quick key strokes for common actions - Fully documented, with example configurations - Asynchronous: heavy actions never block emacs - Write rich-text e-mails using ~org-mode~ (experimental) - Address auto-completion based on your messages -- no need for managing address books - Extendable in many places using custom actions For all the details, please see the [[file:mu4e/index.html][manual]], or check the screenshots below. ~mu4e~ is part of the normal [[http://code.google.com/p/mu0/downloads/list][mu source package]] and also [[https://github.com/djcb/mu][available on Github]]. ** Screenshots Here are some screenshots. *** The main view [[file:mu4e-1.png]] *** The headers view [[file:mu4e-2.png]] *** The message view [[file:mu4e-3.png]] *** The message/headers split view (0.9.8.4) [[file:mu4e-splitview.png]] The message/headers split view, and speedbar support. *** View message as pdf (0.9.8.4) [[file:mu4egraph.png]] ** License & Copyright *mu4e* was designed and implemented by Dirk-Jan C. Binnema, and is Free Software, licensed under the GNU GPLv3 #+html:
© 2011-2012 Dirk-Jan C. Binnema
#+begin_html #+end_html mu-0.9.12/www/mu4egraph.png000066400000000000000000013135021250406103100154470ustar00rootroot00000000000000PNG  IHDRpHv@sRGBbKGD pHYs u N{MtIME 4RtEXtCommentCreated with GIMPW IDATxy]Gy{[%nΎv! @ 1°abd# 2Ll! `klJI}=,|s{j*ѣG=zѣG=zѣǡѣG=z"ܙLdG=z㠒"sۓ=zѣGGEݣG=zqw"iaE"Yb &V!p=ѣG=zm.A8*#2()RR<+oJe8 ,!{L&Xk )%,c2eJ*wxGgǃ(֒ g覉1`}!8)3$S7)B蘖( )%r@ML`e RJDLeXAJ4T"sx! !` ZkFy膪b(s㳕RdDW*^ P<29( <z]|h6ܼy ZyR"O u J2xok ; p.Og9!{نx caM'SɸB2 >P!$OyΆ lܴ{p<(Nm% [7mׯ#ea kcC<:@&q#yr소3y4' BJ4oz~pN~&*g@d2E99~qN"2c b%Z>|ٽ1qkCj?¶҅@!!u4MMBc―),{6cv+/{G=2dRD $"Ʉ(RGG) eQD2SHR,F1=vv+ |ee9o@^d4Z}X@@|Ig E<.>/rɔ,1b^;0z(}m!{ۆFȤ" cH&} Ƙԟe"dRJsHRtu*@k4{b]WIy(rRXk#)3@=eYXH)){>`Ed دnjHIQ{TܴfL]eˑ3ѐbkZmÆ s(vXKgdJ~,rA ѐQav,W,z`u΢ FqsyH =y1M6ƾXg9yQ㘍u3BxP()P ̸i" |5Yt]Q5KmytSy)'0|4jPirAP;`VˉR w㢋!sUՔ< EQ\"^2ZGh<#@^ &Zi 0\a"YYEZM]Gr5Yǎ\Qa ipEB"%XMYy0D$⭣(Ktt8k!Dj6} "ZjdE|"- U]5(F3O8㏘TIG V.=1LX R?|WZˏDNZ^ \wY}Ѕ"Irm#y֚Ak:ШD5*{MI2xH4>଍UI5# ;7QI LJBJHSYՖn"l\RR =d*?X/Ϣ)ȤLfcM͒"7L\.85TftZE=)ƙH*@(q!RXy hkɅPȬ5 %3F3(M)u$Rr@S1lkl(wK!ȓպE{>VFpޥKV=LmY #˳x,:e,QY36vZm k-Z]y9./m^TOA%ri:Y3lT \GTr6M(cZ]ɤdN+a4AYRq6< &. !΢Kje$klSwiji,C" PDEGH.6]RrƆ檮A%Qja xΖ`LR咛\p.)PSZdf1! BTZ7,a-{N,{'0St*s)i3:2u<DrThdF-O8H]'eZun4BH*$+H*M0*6Tu0! s>)$DgyT bT%7ofnvCT{u4wy77R%V<T{(c0,9O9zLj'9Hu$>"{TҧTRKErCKDe0P%U]QxBHAB.kzmt$qLw.dUTE B#uhkhNDD803,,gnvg-jDU;˙R9h͛ԇzx\i=W, ߻&żq6zd`0bӦ#;wda~l {M6sћ8)spX2367))s8%՘6ofnT %NjȬ[1D"?@=IruI~Bg25gEl!6ц)!U5VTTH*ԦaIȕJ|:-*INR* &!xP75"OM^IbL$REQD& OOò,X(RD9I\ +6NcmTF$5EBOVwtyFIxCnC)w& Rl.&7p8LHV5Lر@DdNtdaaaMv`lr!ۂO.X!x}"g,u5XRszb3|R"<͑&\ym߹BC ]ާ8]8<Nn)V(m}͓1D@ve5|RϙVS<ȊDiN&ɍ󤭧؎i,d j63*#jB]( mjWQ̳N.1 !6 4:ARJDTc")CqNJ*Cy ࢒kNnHZ rX.\hx=NR:,KT1FQZױW*4pxh6[oFkCtڦ(n|#x"3Ɇf RRRHl˴aY+dF,"/ȅ۰MQ[dG0 W#bh(ϱ6ƌ뷳mטy+;?(P"zvq0*6!m8nGq/ 8솎I}ӱ/? IV0-YNV:rPꪆ@$Eu6>nHBöʥݚp1N{(uSEc0b @JEQi"ىIˈ~I109lI%t=&[ T>ʵ%٤$ 5uQɈjot<~Y$D)p!S_bYR}պYF#h;plltZwU,ƎR\g8L0M\-Jgku3Nc\XhYŻy8wBu2cykUmcm Dcͮݻ=kL/F71ZhMgC}hIOS,VkTuՔA9ޟ&i*[(LJHx@/TD0m Uuڪ)޻fM㟔5@QF򩵡/RҸ)@4ktdzp7ඝN,,,}`0D"Z7Tuvl="Qƹf156D*wq<^ƱᨭgM23;Gb(Oq[xk)%1`aqs5m)߽FqM&7;(33%r}O礭䢛XK1lBDBÂ#=cVW.[m@!drkxmYD1F7`ZJ)LjTV`_;ÚFe]QY%B ME,HGS 80gDSfpjjΦi0ɝb6x>]|nKt Ew#4JDFDL 6`̪Qj؉/ϓm{Q#,j/$6ZgbV6Ae24 E^t=!&;בP6"iq]0RP18$UyF]McL"nU҄+SɌgZOBg 1 d BR*]|ĠD+06\dXn)FS)X ";DYb?9"=u.s=NŎɥL#m AQc)n T2`C]tp6]#XV#p *ng`T32%0xXk*"ݦDkInVz &<ǧHuDZLIif3)c)\ôΦwifT- IDATI;[/xI$ǘCkNo4yB+BRQD="ϨS :!i u"nyFe%%T$'6+EY&7KM\1 qɔ8CxtbqT&jKh;iU4X.sqΔ!$u31$z!UU-Jn ͬs̎撛╀]ڙnֶ>NQ!nD>N(%t̡IؤG7C$ILF2EHLәLƋ!cA4"HID6@%MUR̤L*w=U)16QD׶1TЅZHJXF(VL3}W_:VO] v<"N:NRb[D"#Tq CH3\2웺‰t38b(Q A6*a {=(`S|4>z͝@zջٹ4pJSOQ*& uqm4F 2$y(a]|7r͍ !2/n,d̩'8(rD`nfm o bsqBFwK( MpB#L0r)g2ܐhD !e(YF2sk'{:D2X k[K=\˗riXuR]6YEtAO(rzɜz㙛Ij=zѣ2ĐKN9O90]~FO/I"˴Ve^yAaeXA -s{feĭUꖫob= ܞSWŠ,%Xyx3{9yb=zѣ۸kywP{饗_YZn0w4p \{u8?D5| w2()1aI]Ou-\-qˈc $ cl_ D\~6Mh/3WzѣG?h'LRJN:$;8+0PCl!12# l&.-W^j p8\_Sfy7J3w3x-[=z,G}߸ g"ːS12 ]yK[~kgjV԰n "pHi5<''jGɩ<3ױ;XϽ׷~@qy_y99," &p˷[AkMSU Amo6HǸڅs H۞;2,?֥­JWqmظ[!sУxۋ^ٯv~ E]Co_7qyj֗ÝU>?hw8h_yCW\qVJq)%W_}5wfÆ vix+W}/8v1΃iٴq3r o"m셸!pdwR!mNN"UWQ"g8\?惟/0r y?GlcO>9=n[t"tE#ιoBdqxyͫc?̟?koBn8yS+ǟʫQQrsyC}g^sٿ~~eow|Wx:|Z;RbsIg}qu#)y ۇZuͧt_/óL'ű'p+Uj׭uH]NocVl""˩X[liԾԶ5cVmׁek7!oj]^//g̣o~z퇸og*np/| Lv^Gҕg _R>ۏK/~eWumfQO~] !p+߽|wSo."m$$7-^>wrv'v8pٗgnۼo{U~ysI\x }@hq3_e_o}uk_y2=tm?n-Az|gw׺1]N:餎~3{]N;w[wr]L `ujpV,'pjM0Tm뽣H낣inޒq0?&3= G{s_S2o=t[)fxGG[ϺQ1#Alcvַpն1#wuw~Ec/|ݗ} O3wV}ֿ77u/{`z+a}\{ޫP:O`4u]L*/ëL{1%7Pݾ>S9;+pz,{6_qYqWPw7133hfedYmdq<^N31a͉ q( yὥ{%PWNa'ܲLy;canF>dlw}sJ5w|+=Rt[U^swuǟs^[q:^?§`)=}1}pj#q~'Ǭ{({e~4* 28ZUkz\r}^eڻP\{;玸Pu{qYgaVx=vܹ F!;U„a^g {!nb_XpQBkL {7+$=Ξɸ|Q2g WVtV:]|wjx>3~dTXA왌,j:3V{/gt?.7Ժt!@N rvԋ۽C. xn{<5Xp0V?:g+?*ӵk=/ëL{wp־G߾~^vezQz~Zβ3<!93?3!8묳W%y&Q3 nu7eQpQlo*~n+ҷxT1#shmi]>Y} \U/3^ֿ[v|틟/}>XB`%]۝_ofϼ5xkeWn']kG?v]L&as9!_o6 v.=]mzx{?州8:s_]NcW:gS*+}]kBݗjڱ\%۟kݻ븻|ӟ??g?s~wڽX1,㩥jvh"ˋCJWU¹)R9BiI~j6>)'xS.&8~^k_{k_| ,>{?<ED_'}xwa)%?zHd2Yq0o=q^o_wy_^W2˴P5 Zx`L6L<эXB`}_L>:V~UZzx9{/ 亲2]/}ؗaX{+Oub\M;o}4Ư@4) `aZ#CV!)qRD&]=z'}9077ǦMZT.q(ܡ7nj,N B@$,S(yH$[FJI(к~j=z#<뮻pޣ9,ApͶܶ8>_}q7@*#uB_ZFG=z,Zff8zFLagFJE9:WJYu:k֣G=z BH$jCnڹ b4͛:FkR!jc0&n+p=zѣGR x.n=f8;ܑ3$KER(`ZK'n=zѣGss4r-RU=EU#`U)B2p֢"/Vw7u]zt__O?+=?OG{~1C^Tu 4uScvvu:Nk!;{v/Վ+mw۾uCz]quֽsfz;'~eχco=dxݺ} [ܼ㦽~>\ŝQ=u: W]y~:z+o猯~ԓ9{||P]2i4 4u0^\`2un80;;<>m`M-s?Dy¯oU?x ϙ?8~_=y?@6ǚB`kJ㞟S/yc=d[:W~[~C]z [9SH.?7e$cO ~\swxϿ8.8}+|i}ء]w5/zٺDC'~jk|;}{JG=Y6L&S<چ@$|U ֚npB@H*Sikueb—}~ l:[T> ny[#Um>߿jO=9?W3z?/n^qoonr޽Xݦ^揯x_w~>г_Iws>2+Kg/fӷrPOg1o׼f4-kՑ?oqm4}k͝U{kk玛sӶG__[j;Cm: < WΌ* 3,|yGw͟y?}1}[%߾}w ۾`Aw>7=~hu۷c%32FCu )\ !)EUM1pxc惎98nھ<|1;=oe?zg??O&'W߹VokH}Xo{pɥ__,4 1LHÙ 7n!Χs10]nΫu@[IC:V_F{ c {dYd2p@ M_ɻOW|[o&~g}|'G u-)u\u-ΨԛNkdNxs.-heV;_eOx7?]?논叼 }xzVYN 7Sw滮'?OdNaSF">%of-_p O{?x^NڧDo9[Olw]o~@6G/Zw9\O^'|o뎿s;8z\ IDATϳ>I&{Ҥ{KZ),.E^eU+}AA7 *(Kini$d&>1KNftyޯWdfys9{zb/ZSV.^| d*RJU#Jm}UU1|0 hx8?|a::އ-ztޕ\ڟ{"OtYwW\ͱ߈ݟ+e/}ۭpOVS.x"'Kgt,M-cy|y5nrGǸrΑ gҵ;į>s3?h 5mpg~ɧks߳~ /~w(wǿϹE_νoW_$^BmV4͠momw=VZwnO~{fZ!>c,՜xUz\]^/݇L^9ȏ7?_|7 }+_7nnO.^| L}ؑgm`Z,GʙTWבMܔ޿Ϻ8klDZm2ml6C.?s5wleO!W;[Z'm=<*É!R4tAຸS|NJ9D4,)q]uM $L&0- GJ|~UZ6 SO`8p6B7MMSt \‡xxxxxxx쓤Id: iDU(A:A6 8( B(@m(epMB %SRĶQͦA)Jr 4 MSQ]QU+A}ߏ % .V.c;6峔ePmG"MY&\]S4\FJ+A}l.F4DMR1NN 2 a&qKk> @XBC8a6OK.ulebr cDJ8m7xJ ?L0N 8Ƕ,+TMEJuo*Tes[md)k `H4B}m@iLJG*dm㾗ϲy'yst_#ߋjkjW_ᨣF|hp̣J(>DOd2I*aTT9B"`p-$x+6XݝBsy_{g浐tlTMM~=@@7ef"Ic[`Sâczj9%a&aXiī+vA׉}ŷ{x[p$,FUU+D"ioW;7m7Vp~{KӘ5流-}5HMUU PH`:PȳNmd3Y.u]x"idYT*lVɑTHdՖAls͛7W\Uͭ]ÊIcz޶x do߷ug̣&{ c/CtT]C5tM! O.(B) ~SX)6䈥dsR`u !3|̝܆8fi2C%p/KU$MO?O{lw~ ef]bu486( R-W,桤 UXJ-oQ47!!`9&T* g$ûs8p EPe(LXl6G*"XF$ jjjPTl:S1g96unZrYC؎]`Hr,eٹ85U&6%ŵC|/3?Z˻ZxGx|s=Acm3GyN^97>#ļ>c/sV t19zz}SF{  I. kFGΉ't:52۶J#)Gy\˶p_ͽFTo ! aΨG}cy{!JA e6假~M]Ri=g0B4UC4444UAU5Pѳ+q]t$VcŊW*;"N~Ldž ,9HNy)Att~ifsϣM7 |>날J Pt}~bqm1ft JJƉFkf&X P΍pdb 3Q9[d/-ťrdiA~V0__|q\8NוҲ1/D$BQTL3[g!2rTC}#?%ۏ[D}!]}I^z1|7w|Ԅ << Pp]Bخ@UuM%FOP SLA.[{N۶KSZF Lo%ЂH)$`piUUVES]AB8.U9~}K__ȀFYW[.d64stuB}Cu%5k6d]47`Db+D/@|u99a?2h$J&7/?ϗ}'7/9]q{hhh TTLGiY&t,S 4ET\Wp6Y˕ 1z=vJ^ M3u!̜7,K$Rܹ;RfKgY?@E TC9ϏQ²-,IJ-yKӪO8-̛7aprUהp1pmbKUU6n i'%}$EAJںb+R|mWxFF5Mck/|fU/ϭvrN=~',ݟXmPXXm ]7hlj&}U|V埯ṕMxx騪F'`MUQu#@k.'N-tN%01_Bu:)EWoB7L1Bof1逓"={Igc;l2@:?M__bBc( QByA\APLEɗӋ`ū+8CY0>eb Vs6˲HӸ~䠃bu,XM̚=!ݱke/m ΛGgg'--*8%)(ϏP;p|>tCGuJVtEV[ߌض" CљA*#5RE ]Ƕ-Qepp #kҶ ؀8H%l嵝TU(s) F.unT݊TVM|kpԊ.}VuI焥DU ߏ(XU|q̋oӏv6Op{BUCiA ʡB4c(0f.JQň8umlgs*2:͞W6$*_,lY :-g&ٶmz{1gy>7M0 *1=pT*j(/̇0㟏>{䷿ u uXM*.߫$?e-|>fϞCOO7sA݊vϟǦM̘1kְys/]s]=E؝㗭"?`pAv5me&4M2shaZ ꅃc[ eV0auB0$5m"ILUUH45V/)`?# /,Twp/MD .Ż߾ںZ %v&B jc5 Xڴ?/Z{?u5 4h7j$6H!qkƋ@Q)TgQjW_K WGF,=ڊx(U,xDEMM-n9.PH3hpu\|rga\%ay\~ee\0ګnE"?o==̛?YfEKK+o7nګ-;qg=B;;۝_ٺA%Sä4uATU4^ntێ(*RL<>kE"aarFZinn&ڊi(B2W  vJԶ6uuBLI9ʨm=AeT284ÞZf̙@8B"B,/7Y7&ǞJs]#kD>|N}]"~ 8hhmhVF-%RƝbjA `]m ?@za2=@a%ebK2^X.{ݻN8˴P bW\ICYU͎8UU#ˁt*S2U kYv2ϧ&w3-tnګk2;KW7K,V( n꣦eB2 "MӪ4]jjZiGD2QMX?py:l}T6ӝNilhK\ % >81v:u]]#v'#Y۶1 u4UE(>AQM BjF6k"\AycϦ:' Q  H:PtjUL. SQJB-L RFjQj^7B{µ$6IH%Dt_*Q8bCsUv UT:E.ö,qЁa;6e8c a|)tU>Pke/hnnEQc=6l=pxZMM GZu\s;c  S 34|F0``CQ*)+(!Ulul8555 +@(j~۸#f1! * 7}_.g+yY~;r=>/|!x(8Iy\%ON0e ܸF>6qF[%W^[>3gٙmىƦNf͞D]WѰ\*bp l& P,l"s2Mǖdқ Noop$B $f8)%LP(=93[3;n6m@_o@ɺa:s&(B">ĦK:VWK̙|~VZU2.( x~hA= >P؏PTG !e-"]TE!@o'qBZQmcddg+A<W&ޟi=T~s?aկ?089N6u=?)J=;EQvÈ$InYUgΤ7ڵkб!c9tvv2)%,뮹➸bWToB_'w: $nL0z) ma9,garf U0M OK-^$SWW[' H&hhl, ʟEsϳ'm-֯k;oSTEcŕhaOFŏ{6*ܹ< +Con ESϯᅕ`*3w/n{ QTZP.vI j`Y&UQqUU4β.TiB%UPƈ|'y)RSbI2]d鑅Ų XյU>&/D"A6ö ÈRMHNe>@KK 3f`XAdž>~!+3{;7֎3{K+L*TUEQu!Ok=qZ~[l["@ pG(F|XaZҽ@ @$a͸/vcc#}XA²,DC 22}n+dlG@?-ҎBLۙ'Hۮ?n 3" ]ӊ#D>ٔaSr%6O5w| <GJ~ yϽ)(D|u8p]t#]l,GUƍT@X;z)Ũ"qJ#+#fϚiZ[HY>A&GjJk6nH0e֬p',ᣫ{n9 IDAT0"`iDZ_J3< '^Z[g\ /|۸9eܹ< '1*i˶p\uȤ3R)B`~xY5r ̋tE((  lmDŋ1|>~ɂHqinnfl&8rH)y[1WN, !aBTжݙ'%o^G^|)jFV*RV# V9S'?p*ݴwӋR'B( PWhj>w!Pu iؖtPT+kn3cPǟtq ALtH-je~3?6/vIƈgߊrG.t"D"A0g3{esc0t>i7/N:8S 0|VYͼXv-;:8?q4a ~{Ӳ?/نD&- d[#T*Ekk+6mbxx0{Kζl;}Gix](z [AT#U4UߌdYlUӰL'c6 #;vޙ#o#6dwHӎg?cϭϮUys5HI{sԳx!F|gbxq'vńwQL\1 1]8{wo;poXI&&҂kb1q0\DQr,H)34p4B׆E$2Mc)O΄D[QUL&C`j#MicG<#Ofd9,3G6˯;>QTUO~ ~5kb478 TUTDpEU|d2UC:DEQʟjY-K|xX:uC9*2oxN*(gqNIlJp]M EQNpiQ8:S:u !ZTWj|DQ"B~?dت4,+Ru~?Ph M,ut/v}}}$R>D-*yVM0چ|~|>|b jTUWHUXt[:05Z[_OoW DT۶'TbX5fʉ12Yh&p NV^ K4YW |lYia[&\l&K.%xߩqH&Hy%̙=,K/œo{肐H7δ 8)pl"T&LǕ34 MBt]K"v4U%VWౝj#_+ ']F>XqYlٲK4^L6 躆趍k#Y0|>?ӹG{L:åW~cO2sHHE@qy2q(##U(T>X,~ 1obFCBkZILvI~HǵAZr̤_QCW aDi|h_{}ĨQ]UU. jkmTUuTu-\X vźr!Fy&/Ռ mtLŽn]Ԃmm~ ,+H 9%'x"/?y)-GV%ˍl!%R!P'+p 0%TT=@ا!vΜ>'4qQAH'cZxxxxxxxxsdmx*ˠd$0lJN_G0MwS7@˶Q4tU T[8Jk嫯x%0N2~0+J TWaX?}n믁 GdY~_d ,y ^zxxSMA^^xx|!-BAr% >!Z6E*ҴuI!-MHEQB(! {ᱏa6"Tm!xb5n³P]7G21iPZ|Pe#.,&^?ǖ8x&o9MEcs/U'%y+ύ/^sh1[ >/$~6ƝA*q}_g{]騊M`8yj"'T X3H)\ש 5'Nۥ'a{3{xHx4xס0 ̃O*s/;aNԣzZ#?w5~y6<8Y} %dףi+鎁u_g{ҕ,B7~"(gfokEFhP K v25^R5_BӒ(Ï ty,G/zS缅ՐM3 lƽHaJo\~*&f^HA깗yXɋXr/$ ?8t:G 9 zH jZYBͼd6#hJ (软#gֻ^1#w$a׏kzԷo~{>A;8Ln׺Wyxdp#s=pBke=em#_C׿6=ؽ-quߠ:@/1TE1jB¡ ݩ񤸩 ~xē/;C 0̃i?uּdͷΥ +EbKMcΟeGc;7{M2>.T'YqKڋw l Q2Yp@PV,ڜ~,})0j]x,vI׽܃h}qX+K B W7Swh~( Qڰ$~|Ӹm5=K 3_^{_m܃h=Pڞ`lYOw9;)3 @DST4]'L2M4N:ݛijGU֬YʒnjKyjT@_:A$"r4jsG21"g OJ+}^&? b۲)GÌfj1z3OW,vl~zom^8/u Z~/GE{,Ъ^Omf4֊ rAֺLmwu,Я"b.G'vaD~ew<3N lYOw94'Iؐ+Gf3 Uql!!c 0|SDsL _@U֮[1G/!mb͚)\ǤX9yWж/fdjޫ*o{ nOS/;#O?V-N>0l;-㏆F;{.?q<2d^]ÆWא4Zgڕ۴=]c|LGHW:tWsLjxl>N1M/Ho?TٳfbZ&km*@Oj,SsbpMze?%RLΏXkfd=uY=-$\$=,66-:'*Wt^@t>8fT7`8H~11ﵜv d9jv)Kφ,`s 0T:5,>=\yr IZCfG[YEъޢnkVYS{:~>}uigMyKhZ*H ϤNh<</>=Uzoj/Yݷ^[KIKh9*B\ڄ=[~0}/R{Q>g r왇c8@860P"RUC&6o (Kzbv``ar7hl ~*_YU;ao@EuI.af}{)g?Ɓ;>r7M1EhA3è:s?/G|)kތT4Pg nCA }룀 d PgdsuR!c)ԜiI$iҿeǞ#-ҩ+ͽMjhT=<<<<<<ry" lATDO938E,4 8Dz0s94T\!X&:EPu+U}7E5rwۏ؟ԞZv մHulPA }]C ">z|yF88[ِsJݷ p'weW}r{N#o\{Ŝwɕܱ|nYя?d.J.2ι3<6 E_X)~ӻw.ry3U]tSۛ;鬋;\˯.;+Y9Gݷq%Wpq5_7Wt>ur26x?U0աM1 j./ŁhLmٲ.4EH!\Ԡ 0t?kWob Q3cNpq2NH}ѩxw;^OcWο|r@jruxq_0Mb6cιNXpM|]Qu=wGv_ ^ܯSΤegsOؾdOaۀ?LK:4l\E ~ '+nvw"{k}J/LU~"j6Z4pxו8 `rM%$2s)֥XyOsg0_y;(w?X_S6WS %ā vS,Yg|'_J\!p]?']zgZ[}$xE_wJ/~׆,'Y<'ύfي-cF|i3\]6o×aڵ#7]S㊛>^.Q.-ܼ "!sT,?wwN x7qIre\KKv9n"J˂wsw}u'̉62]]L>fO>8[_8fҭN![kcg;Sٗ e\^OT;0\S'9RT{/W'&SUF3; 7~a;NfS[85QWEçc* 1jt7v Bd|7ב(J MMSdS3yɘ^M[W:?/\^5,G=~~mڟSڏ\[>_N8]ޝǿOvbq{>.L{ g/QFқʕ?y::LosϯgdF 'LD+Q7yI#=O%7?~ }<r=GO%52u\izO^k[&W?'*۝iʯ\Z~T}7|ou=??0"5NLw#8g:=z2*jϻn9/Y[W>՟dVjTG}D#a H0}tc\bXMX.m*!@ $*K1>uWz։_Y,9/y?_~~yQn|o~vҋT9Q;x!,=(5{·bhgDe;}[i$?>Cخ&zPck[/^\r{+L4SWw־LԎ%';['^WF󗛆-M4y!.YoNgnCa@0t?hP8+誆aLF fqr 4MW8E ʺz*>}OkʭPgefqHJ\4trN8v c G_{P)es*~bNq봫S팇󟽓w}ǼoA I5L>Q Mq oP)fōq+elW:wIҶ}*rk/>˓ y?u-.cTrmQzS/~[cqjn.n1Q2w-a'mٶSlֶҹaD:H7 TMGu4MA:.#:6IQwQTmlK( ""6,/`EEQDzU4 IDAT)AR+bW v}DQ)RC!uw}ldg7[ rf<9y̙mz4 ݎlw"#目b 8z*7V=Y*صn%mv6Kcy&Zf}pȊE/+^\\ʌFO1wlX!)zXѩU us.%L_ 5dYd'>Eh(AJ}Ųx@D5]AtwaBx9"jFc?T;?ٲaY {s.Et|f`%CF֎@%q*bF۳Ƶ?׌cQ-YZr !˲fGEl6. Y#gqN% ;so(b$Ս]oO#"{*Z 6w.o1|6r"]?.&mڇ}Rt( fIZeaVߙn;^%q:ez}ǎ('1OƨHHp2cVi}dT^UJv7AwW8NckȚJ&]^3R9N;tʮzLh_qӿgohߟ܂3r>DIICHMM<3lkh,0%=RO3\WBPlŒmγ#RrKxi$222鲣oNP/O-rbiRb(ÜVdbIHnWZ9ي)Iam$%&OH$..\N;N5kx7 ~/]N߹ :O +1x(|v|IB}ZZ¢{` O & Li9Ŋ +&@(Q׋7j}D$#!:񐥅EY NW.j9d+&]WPuш=Tr,.WJ, Mz?YXX{#`1N7 D@) 3ƆdH@L<߭XA0 8?gq{0@DD*x!oIT@nN p!Envi[bgK>begJm_5.*7VQi_Vmh?>)"vy"Ý32CP0 I𽂓 Q~g!;}MaO-ruTc)yiEl-/CY?y8Aż(WbP3AcVѤ}OPBlqU/Bu ]2?)kӎmĩ?lWe}P,}Y,KzT;I4D(F-TS1j5Nx\xE吗(^SN-,, }؝r5V'c$$fBJ$&$GLL,apΎ=,PuU׉E0 232|w(紨ҽOJlR#H ͎$8NdY*9ިo~ UP2:6I`{m*YXXXXӢ̒ N.s}:RTA%IdN A ^j.ۈlw~.ai:9YXXO^D$ Q%6YDKiNbl1p:(ndzPu$g[b͎(&i"j^l63r%a:= /߃. 6,ۑ.-1s[ >_9n=ҭGw[t$\ ~O?˓cjon.+"]8m;Cg'v}?CgV")Xlmڣi/EZƙ?y0*1n`TUEtD7GrMn:=׻oy%ύ,N O>:Fwx,{d'{t<6O}b),ʈro7exJUfb[>3VѺNes/=kDZh{#-| pl+;T.=Z'"NߴCr󫃬Kspty4 S[[*K %an>1!o4`Cuߔr1#fjԿ@y0|W_B(xML}Lí g4g.7~Ϡpf¥@RǙ Hl!I6ij ܤӨU&YMvzl%fW^gy_Οvh'lֆ@,{ZAl>0L߱h~7N//J`<ؼ5FUegi2ZXc#9t(w*[؛l<A뿾`:0v*].FLgF!XqML/m)Y2G3l: \0B䳒6 ] %%O-:ۜm jAq1{D dD"BevA0#D6lhAK7ßv!% tz@o?fmF+< zspX%LXE'NNⶵXAn[yUcdXiP'~&#dH<BX+WB=9,t>jN W{ DƱ ּj a 2 a "(#FnmDdc^]Q6RzD]1?hS1mJ6#TeTA!tiWx4|^ӛ+7w?xp~79A5~Ƙ6@/2Ct,>oeo\`PƻiEa1<|V9[^B,4V%bovZ DwED Q%"\^bٟbkIJ.HY5t@(8r/EG& LXEŋ3%IYݗS-a.&1}֢ݾ'71hV_4:C/떾%h4ՎcaAlXV˃шG:\‹+~Mv FPyQV|{߼BūuuytNPTY@թT!\הj5>69bXb綘}-}}zC0䴚a6'm @!Տ^XN 6 7ku<{~F|Ǻ'rK@}n8ώHT5M`4Yvg^zQͧ]݂ fGوt[-06RCikqgBiib\r[QTDC@˧i6r2q{88/8EoK\BW21$$r~̃Cѫ-D/$%%D||kj'E>ɦ3o{W8W `Bz_=uY4Zc0& IߏO7z,qPVshC[Iu |1@&5@1t4R@En#'nٟbxƠ~h=vAׅ;ٸRSΚ>߉pyi< nH[Rlq*>PL`rEhQ(f_B@atDr|o>׃XjWWqPBr m1{)Q Fspʼn UL9B'S骎x:fݡ!!jSpڹG'&ޒ}};<ߚ$D'>Hj"5ߞt}SGТ+8&F|L%}:bu楿I2˔D1}C)<7;3%oRT-ޤÐ|IwG⭥a>?ƌANwάY~ }Vri# r 0%Ν}Wƌ~Qv~Z;?AlK\2-ڭݑoLr9L*Iϔyo#a1dޚ8tCd޽xܮtmEm>ЬLsO%ϼY1d4VTt9$ɠ'oO7J*1\2]f2~||˃|Vrd|y'e2ILgM59t\|}5ߊ!a J `:ldenA+f_#VhyሽGKp,i 8*e%g a-׷'7qGMVFo:M}T嚮<V@ڴ1-n&A=AXd.?O;>u)se͛D}Ж@>c$/>uu9nװb6]pywdfYټUu> @ >+O9I><{}Oˮ@&]3 t UEtt]CSU4[;[8Q!l81zTEGuD$Er"j%f }~v1Nz&TH .$}`F:Eܬot?2>Ӛ13b}k$g}g`ow;>\>$,9k\>Ҿw( S:S ()/fbWH']kO"!qk蚆iQ5ܓDE4]Gu4M4D =K?qފw3fīE*؁ʌ8Y tjC(BaPpYTyF\خc$26}̶C=Yj,p#,%ř5]AtwӾ&`wq舿W7i=:ⶬ&A%Z^XӇ~p6dy\ADYEE$Ll_3EY 5<}W". ڔcur x: M-vICHMMC<383X3vcHtݗ'zc}e2[jC>ѻ7;|o o}1}qe/ ˓cjon;yWhC%X,߷EttO}nm}'r5{&rH,prrn$Ix^y,rs.6!74:бcG*T׍`fa7F̐B 3wDKfL+v'> 0C[ѱ7eҜK]fboSgfl ` ׍/ fy,^=3=eﺥ9+W܆%|ס? 7Y>K75;粼UY᳞ b~=_ʒɅ xnTU*1NUjLo!)1z.ѕ8x {˖m#f%fr.Wl ̎ S;շ??;vlV䡊[O 2̖@_/R8k٪ wFj΁GYzljsK)_Xp*?|⽘]ȨAϟ]mU=j?ԡ,=W@՟pri+`((8E!ENPTK14T"qԨY3:oXXb禘}!ޟV[M@;W,y5*nmf?QHS톾;yjq5L />./x7?3[ {gFsLz%xdpbW FNTQ&`b7avbf󙙯:Z PK _g89KnH@pw'p ';tHJJ@dٟb|>RK&mg(*N [HtMZ'bj|#\ϖ@ޡYتE׽oyV(U| \Ƣ-<{E:O > uL?d,17+t83ݎh}򽹈M)>S`IM㖘-f./?C1r3z7PU<DZԼ.x7+fޡƙ(E7tu8e5ϋw0:R)g`^Z;_̃P\^e$nǛ&>&UQl`$ӞQu]Ր͎l1<Ξ#f%f~< #F~Q3%n]j DgyWrD3ԾxU;7ܭKt╂|h20u( h:8V~h39sfuA@Qhv#gqˮ䯿@whaHHz#%fٗDy]о+N=տ?g}!ʉ,DOڟy,x7%3BGDt>tdjwc\zՊ סn4RE#:FN.P^5 @%$IlÁn(ʈj1ek=ۧ, O-,, }qB([(póŖ3DIb5I\N6;q8b\#fNBB<(MY6cիH {[yܙ蟲OV6wtc9Lch;4 CE M.IQt6 Qj: qe[gaaaQΉřǙtKf94 (6 0HLLM'pYq.`5ŋnhVZXX+ۧ@m8]8q\ǡMF;ޥ4-7PVm tMEU9(8aaaaaaaaQz(qηpbs؉ tC&0YEJYm壨~GԘ12r׽.rE>*]`AD>y+VGn+cR˦miWfeaV_!?CY?qCYόc?dWr p{jYzy3vzi2r)o m-[-SXgҾP#dsR_t):>5GYZ\/eaVAʂH̘"vzbt)GAQ^ ͳwIr|}5K:ǨԸF?coLOY2}^Rok#{{gx4&2cdfL @+z|53H}noϴRinYR/(8NbDMm+ѽjqcx ']D@dH>ubt2FoZ/[1JsERyfa|wf4ì>ϧj723cKJ hҔkMfvlՆ*vtK[fvH;9~:oډSN7}rT [2'η{xL>/qpeMYpM,,RG(6ɦI9Li[Ǵ}Y;WW5 3[ QrfU?̟3v1$;S{y5/Gj߇ӁWsv)}QT1KQ( ]Xw1kt>߰brǃꀶqa YQi !X2k3'.2=KJ __)\gs^@~ ^4͕W۞PgbG_ gqmbY}S_6?u,&4`X~I /`[sJ(%Cĺb"U8C DUp瓟^^4fYTOmUд/h~(+|<2wdڽi3gSf47z߱v2wPs5϶3NY'V27o}ʟrg<7InD]c7ύL3Nekbkč_=̛Azg+̢[8vjԽ۾] sp+nK\wc5H`@B~!u#K9cd7xjN>zo~kgpZHQxC&==KcxwHrV$O>;9l͠a=X2koۄqSyTĨgRKW_U#H<}sdn46(Pˑ,l1NCMW"ńY\7 ] XG_lmaGld Qa8lvdI"..>ytN >1A@|7Y@N`BTĒ;SQ+(?f,PU0+3ԸcVze kѯ燠a2īf0yƢc:\~[ W%=EdQ =>k=rL=Oh| }~ꝑ" o‶~F =MmGd*)Q׆P}f&[RܬfqmY <ͨ8{L҇ W@ igq8 膀MAtE z<`Ȳ UW9h Mxa`t22ԭo$]VW_fKχ)}ow=׬`5MˌF4hÈmh!f2P&piu#+~\R/ p?+āpT ~u7{6xaT4ig`gf @\ִמFɫEN@\p!?W9dgwKeQh؟9ϸQ$;>R1 c?%J(yKfHш3s/?MW{F-Tj2 ߲|ZG:k)ilQŵ?[©_Luc%;\V6}Go|+'#E$9E&a&$HHGP ^6ժVEdQ}h}$cf6n:0#T#*V/Fd:l/ߟ<2^viɘW_T\iPgFY%i=qc-o3Ϲϼ9.mY/M"{;(cl%|}(`}ݮmx]e#w6y3g0jyCzH92ߢf/ [y͚=^RI.>܊mܵ+>j_#xkb:_DƟ|h/nFFh IqJ6l1R(%N,k؛'&\ݿ>EcҺ&G݀= ]DA> 6nTt^fsou,ӷqo;}P.0 Oj'da˶ԭw1ţ޸E3𚬅ED8h!Gw˴ZB݋p8%9)x2sQ=]*|2~x>sGdVʴb7H CX`#?Ĉ'˪s[&yeaV@Lόfs33X^Z0S-%Y 3c-ox|̟GS6o5;M^4^ '3\x9b:{#>ux߿Qʤiԭs_}>b_Ђ©Gl(+trdO\5WTfz.FOYtoVgo +8 f\Ql$%o;fKgisquF(lՆqd3"qt <ê/2=0ڪq&=S%0f՗[>+b{glx6qwItXؼãYpdocyY,)8avΏFMX~e`l {f~ ]7>ɢ'}a읔$=27fq)%={-jeOtf̠b̟f}Q's͠<8 e0w8bc}̠x=MU@S V|?y9gofn!'8_edy{`=×Ӎu^Dڔ1`x#VsJ/gGmhݫo*~2U[zcsEy5+wϊX\?ugܞ`J ko妾\ӕq=IuҦ-5iqu,=9!jΨ䐿3Y4_2 w-<9z\U&]+C{'=Jm#gwH {0q_ILęǝxUŝr)v4.KQsm:Aۋ IDAT4|n7Æl+J< ͚sq\+msYej=878>ʬ;z9Bnmn=p!dѴhWqWqpOeo0i*3G"aׂ SQJ%|0Q0;{[~׿KU!EǞ9;&-е,nĠ|)ܖ *bכO(aeTYGV+^CRR$ $THF3"b 8z*7VuvcJ,_v[Ikۅ]͒X2i믾NbYbV/.. eF}_~'ZAN;6W2D;_ Ɩ_v;^!)W¢̟兕8}('gC7"gF,*My/'?;vx矽+\FLSzMjլѣrtv=o;O ~7m\%%IȉtPD'^\vhfCGI`0KҚ, t*/9);vD<ݕ6fRI5'ncT^$$8I1+ -,J\{# cmW ><8i)t[{zh,0%=R}/ϝՋJnS[p׽O?D9Es 3;Lȶ3xW?:zc~/:_̠Lr +2|<;"/SB;\> .!}f4B͙+ ^ǁόm~`ܝ + )W ūaϷr=HDL5B2`{ /Oll ~D vf̴-,,EYDW21$$r~̃Cѫ-ǔJ_$'&.Sb*@|b 4ltB9p M(^\͑G8nEXÓBIhF<FZN)($]Q&˸.rZ/žewPf>ݡSEx86,,,,,,$NW.j9*a*vj^|=NկEE޽WYkq:cz¢a>(*z ɍڦ3\lJvn(۷nex^-,,,,,,;tM(hE(999͍| $@DW",f?z'da8dm.9!y0eDf;iM-vuLәI}l\:_}83#k:M}t݆ \YC3X~;^ڼ4m C+ݲWG#Zfh S"*s*f~ Bie6\\ntóe嬦*^Ń@N3NPugã+%*RDP7. (~`Ac],(ZƂ E$JT: AJsd&3! ̓ɽgy{ν)NZ $,V[ Lz9&x}ރ}sB{z[d4+O_*< z|C/k8\ˎ3Z;c$oi[bh-}>+ J.5_zs|ֵ@pp(j "5,fbc΀ SU[JW~(%%%ȊJ6`;^'Ƒ.}`.æ߾7HWPe_[l sF\sM_:;W 3YAQd@!22}={֩1՘j)/)A-hujjiVVE0s/7Uj:HS`#A}$n*wSr/:4qw- >~2F> wO)T *#5Y8ph_m;NCtZB zb c$d_ ٟTp V+wKT_Z<ziܩ_?_iʘO_)N/ 2EVh!{EEG1!CbŪ1ڷLAdlu`0Y-`%U`EU1G,W/:(Bj5|]2ҧ FـSdZ-P#UZ3РӀ[ KOP]YI^)DFgnQTT`/ `<<]|'/`tWj+`/ҙЋk=: D1"HWU}^1񓒸'Uw*A/)6Q:6>Sv_wz{Ĥ\Ǘ>l O@pzj 8ؽPmVfkNa 5`hFv9.$$&TWgʔ)%Oo<\X(VpPVVƺuX /@QTulJAVc50m UCoСAMyI) ?~<5ܱeD|qcpB= JV%yŠ+`_a*QQ1p2J>$ڠs"buKe"""X⽑>nz{ݵgЇN9/ڑ*6ej֖=aF.AϞQ<(p+:gQ[AN_fгS kVt3LV^+ⱼ{8}u߸Dz< 9.8}&ՇmU^V߸N-eͷ2E/۶:CO[6Rl'8v~!;Vh ^Ű|3v"YÜ?wU|3v"H~3v" o:aȢۙ|V_ "Ud~p+#_W?LLkw忭btL({[?r/4I 7ps yx2L!cc߯.%{A۞7o'[E6ֿg1)0y8:TW,aгSmsx}o p;7~OfwGgkqh3|cJEc2YSddZ,Uw O2VCUe e$f76]ܩ?̀+98m\a J-^R˭\`o m":7_?$`3Zv?>BڻmWsw*oQˌ_;`ӵGݵOj4"y?<:#X5ng 3?0)Ea[w+J6j ~`R.RCVPrZ־v=VKoٛ-=T|Q>~dKX.9bSC}{f{~EcŪ?+\]'{+hwy;uƽg?d岏6.޽y>όGZ5q<8$ՆlƮ8m ];tW0qؠՂJQViI&""АPK%!kD;DuVb IzϏvGH=.ľŷ}GW?#t{9\=ϯ㣝%H!02޾g`B.*`YzuyWW\Uc}Xyh4'sMqhMө.MGLNcomvW؈``5q>u;^A^;CY]tKv ,F7k̝OYmνHh\" :ؙNnyꢠ\ ܆'n8StØl0zdӣ üWy?|oO=|h,K(Q1߭HMCϘ=٩dBʕeeJ_Q*yexz3n& 9,'-.. yGpΟ{]3yndT(JcG?trnc1~ł39Nq6K7,ϼgr&\5T2DNGT"|TP籣F:snOIKePޒlQAw/Lqh dvO}x6BHIvq^4: -&nbX{ <9C4Ѭ:C޽/wugv#4*o( ~)ڋX.Nm,<ùK[(=|<$=,C#֖wp\▔k|xQ0>~Tqsf{*ʰI!v)JZ"nۅ"*B h 3#u<=ueøK6I^|V}/]S_(x䨆bbtyPU#A; Cj+2 w!$tGJ箝,+M& !!DGH8__ m ۞~oʁ?P(@\[OZ ky| :\?,hݟiac,uxҷR??gl<2ՏzC5w;d dl垤ߚ-*6n Ls؍?BF. >Xk 9IziL^&=j=ޗtLCɓw-DbwcQ.=wqƟ ›`Pc;9RfQQ,|YmFRom^.? xsSy {@Sx V+y_+v~g_䐪/Zy RLN1Q|_m%"Yv 7N{6̏ZwknjB k$Ee&䉱WusS/EwW,&B3H%71dT^J+FPjtWn4jn/!ժ-1#/ҋR1V"IJӷ'|~ܲEaat /n#\#0+*Lܙ'r.ߒ|ez|?vVpfajxl?=_?O594r[}O@2ipg: ʳʲ϶׵{7\ǭt1{GtǑ(O{Wpeݱ;Bx@!k 킮Qm3WpzzDسySd7;\av$Md AX0\/۱D3Fto'rxwנr?X-.W, ! ݴ;Qu2/DקިV>hCëBGӛ?aOe5r] t <C_GF{FNI@O;;_<?l7?d58yeӛ= IRaY*+؁ *up=p>{Bey ~ZI~#5l &·;ncIi\xa26 2+\oks;j` 2>SWMMW ϤnMyk[9Gshp0_q  3rͻ’/x=7Yn{"zb N=Wn?!IT.;7-}?6%fŇ7- jTtj Tb\9#er7528{pqV# ;Tjiݾz%):NnI_ϛ\Ƴ)N%>#>ZKO;JoGLpW~ޞ:|k[{k.}&CteAEUq묶c?߿Y?7O;jV>>jn۾f$ \8 Va*4][Vl*v>Y)7L>'T-Wzo{}LK󟵅y[]>2"vGhaAӛ\_ IDAT}ԥ7$$Hvv\Ρrz-.zjՔY˷wzJY;,3fg6+O_z.&sr-oWFhj8CiEF/mSd`1* =zpQPλz3\1AxOoπpCW7k(wIh[SOI_y~rP}w;]ÕcFۄ>jf,FhxN#ƻY6O"CO/*M5'|&^"PvdB\X žjeo3xz{f>`2֭+:'wېTOd"1:ZR,jjH*$FYHXdnӾǺ WEޔ-yI6bcamC'v͵qi\l,j־nOwyk>t8- ΀^CeBؑcXFӧOwѐQfɌq OXx$ 2s?SL_@ r1|b 7x#֭#+K4:2A_ҽ{#/}{nW{Ѡys[C>d6*) WRzS.W8' | ,f-z $@8\.̣.IY1BBC5XS:VBR^yiGvgpXٮMEM˸q(++?K'O&--]c@pRTT͛y'xRz0xЅrjͷNnGϾm3bHDDQA#44C3b_1$$v=1!=Z}nѶ'I'66ɓ'aÆ 6lɓ%66ӧS^^fܹsYϖ^N ۰yy9'OvS Ɨ"[srΝn7W[c@  ^VBBܟٴq#EEGb͗]T Ӏ3M86|EӻOObYͨ56U((INNfܸqLV=tA`qqԕƵ̀o5`sss;w./N'j]FoK7 vk FTk\44]//fÆ nYQyjλ]ކ… ΉA1b]h4zJj8lX-3j*Wְw>@1U#Tށ\QL VPP@llo]ǛcL"Rj+hv\. 8vv&*< VNC#iQg2]]^ɓ'|r֯_(͊sz|gܸq,[km8w7_yXFT]G_ٮ111M!54#[0HMMeٲe^-))-h4(-/c{^11HȑqÍmg۸OmL =͎dQqbΝ;4'-sch7nWʗq5w\L4mppf _˿5f3V]G_xnY|r.\Alr"g~'OvOޟl͸qVi4 hł]vw>t5s7W>}KݧWTrEqqF6F8d[*-JII!77ŋ~z:t()))'sqClhlذ]~Cmt57ϵXZZZF7oH Ԉ::Ҿ?55ս`zr)))xdjl ڒ9pw}7˗/ D_@ "dJJ׮\rpGѡ|ה+ 5tTqWzJ`kqXl6yѣG3IHH*͛7#Dg 4(/)eÆݻ'R3s߳+`L@ PY)*ԥ3Ȁhg=p{sz"I$΀JAA3V@p1d@"Ш$BC lK.hʚ3jUU5knODRQQJŊVjE @ 8郞ȑ?^̯md%:6)ҹ h;NbYؕSU5( ,!jJ@ h*tJEڕ5z4e5hz43: VcE-ZŊnGFjA@ F2|P""Qi4B *""΀Ӫ4dJyI F쐑T*Y-(cۏ?R|F 2:I'*&ZtA-$6TkFı`S@@ w=JBق&cDFE3dzAeuB61#WNCN;_|"T:5YB@pqE#ޚbEFFp( DZmg bp  IQ9vYi!7xۋ#d@ :v=tG6ؾ];wh&<2GQn 6㫵kܡ3!aطmIFYvv0@.@ mJhX$v*+~ţhх8QqlӀ6ġ8^UШ,f݊$I5jт@ ;$ ,SV^,Wa %6<ǂZAPȱ",F5FFVGR'@h5**+[hj6+&c5*duJRB-+2ΰZY}Çh(8b @ AӭSzATd 1q޻D+)>t /߮mNv($''ӷG &uSk4aQY,h"@ 󏰰:wMС#k\ا/h8xӀ+8P3g쨑Ep@n]1/ZP yljX ZACAC=XjupzҊJ6f tz z8^|+졇رc#U1~m)H##QTTs@>v=kّm 8IBoSkr $A9^\f;-..&""cNJ^&ըw (mTL*I ),SBj*P3*O&;i8jTzNym pWѣG&8^B|L< Aà'E֪ 3haw`Z@I-VFD P { :uڈJ){ӧo߶3fm_oބ,/>Fa$df/b6a5镜dbcTj MfΝmg3;wYA/)>tFnf5#6@p>rŨѓJETdEŔW(-5mg}2j Z0ؘH4-Օt^@ ȏ?ȉ"ņag>ķJ/Ӭ5mcylٲG~GǡHIШTTTT@pޱ=7@BDDՓAٰnv[pU4*+l$݂bD9dVGE0<T(C s$2*vT*:&v"V3vc:6 ,#IjI&B:# 8Ӎ80jL0jn6^EM@ gUMlL]tc떟.3cBVLmgUTRmح6!ĀFnjHR5/$22$a쥥 9Q/@ @.tڅǏ](h:E'3'h|Zw:=Х;]hQjbu ?>ő[&%%ŝ&;;}...Χ70yf͂,?ދjIH̞-ᚎ:c8J5[ߺΧ֟,߳r!:Y/pdzհ\]r-]Z/WFSgi̘u"AHj-NH욌;#.@#-rŃZOb6( H AҪ1۬FAVu[ff&Ft233 @dffIzz:\KEow;7U(-U9SaRN2d%6mr0m 鐟rBfe$ef:9KKӝp0Z*wTgFy'yyN,QήnX^N@ h%%EevwlAȌq ]:'sxQp ]U~f^fGQdTjCetZ fWX |6JT 'nU\\III8Maa!˃՘!|tsϯ_i 8|\DR D&_ YqUM1@ 3w\6nuۼy3k}JYEeՔ!ðe|ru6`̀ *>@E_ױtإJfБCl&Z/~J*hʀ*,, %%M1?)))(uqy\EAftcCxu>%Ejw-s5)xkL.L @pmSkӆ؁fj㕫V!k&N઴: f'o/۟Ouu5ƚtg"p\z}ƗQڸFfNu6iZoe ;a,,[Sr5fhr^ `SQQZ"GSd"::24~֬}.SBg(8P@yE9$Pbj9-++f5k/b]#VxoE Ρ%F3FN|^|QrDEcYYqsƒ+j{R\r%@ .l;eDr5ram&R{YH>P ?VNA Bp5~V){޼yDEE tA֜4K.eҥ$%%1o<[VL<]{y(K(̟\E2fKñ̬,}>*i IOYZgXtt um%jn.\Ʀky@ NagTCMM5>9?H=\ohA"H2*NaRYSSOќE ;wJbFݔWKBl635U5m1hQ0UWawؐ$lp;Dk"yyc;;!9 0a^quJ ;@ Ib l kd]ؗÇak6iZAlv́J x  w>Fr7mΖشIa,ݘ'ƓJ\3.?3IO|+23Fe:ՙQؔ|cXCgRΖXęNyz:fe9;SA\u =mꕙr]z%e꿁\'᫟~\5Cݚ 毼@sʰdBvv cyycq_NED UWb#P) &cp H W 7)?QffRc[Nth׎0{=ztET^ƎUfS7OCcOtxV\\III6+i`UT8o K:>):=,CoqqIIΛg> xrEG;kBJKON:1j3MF^M&hYV u'/\Zjpy#1C(:l1#K-7[~4곾%梣7.Ϸd4۱˓SQἩ5/Bc40k 9m۷FZzӈ>y[uj )~Sl0ْOa9i鰰dcY YY K=ITZ@= d;)uh?m ChLO 0v{*8wINBX""#TGxx(U&cPiY6 !!zFhBB:W4pL:~[VVE Ζ%uz=&s-},_@4+yR<9=QqWQ\]6:TWtt.22$i5c:jt)de9zNU=MիlM[@^[KFWz%s0毼n־ :wJ )+Ga57hIT\çj"D%PDFZ:"1̚5hPi<͛Gvvb.|1ossE[VĬYJ3u|P \,Y3fs'UYN!.NbX}ujwL!{JWoy{թrƌqR9z6Mjt%(MMu feéSWzֿ =Mի%l-[@맧"簬kϹʝ65=_yhָ ?;%CDx*DAOee%!O? j>1aNJFeey?{o$ug̱ ER)RduwoϊGz['wt8a+ܭ)@.;ט~8C[P D.DVeMÏ~]F|>#\[ۻ? _';}|q]lx`~ 4p\3?u菠߇1/f=?u{^[07?y̓ϗݽ}q\(ǟ~'KdOwNu4%9*l]RnVܿI'ͿYW^)0dU>ucr~kooqmcd6dc}w~p㵶qw^E)1ۻݗϖZt֚~i07=mlc>wMbIHpK'#5 ,KqU,˘LlΗ6mhRy8Gt#H%FY~Z|VH&[-Zlllc6ϟw O?ſ ^+?|97|7o}V%ggÀlFdc6}3Dܸq0`wgG78=?L?-;G?&t_y_xcB6mlc?N< pNS/ܺydJ&'nc6}?5(E9>=3OW^Ooi1;7mlcΔƽW@*v8<:bgw^ݝ]賭i&syy=5tӃ6msgB/pWu~;|{?)p,G'u xܻukӃ6msg䧌%]~}c>#N8? z&r3X,FMsx8|'wmlc6'L'OpÃcڭOOK3 $yozpc6},7 LFn,Xk %B`AkPc4RIαzwc6}> )yf0pB¹i/ R8 I#Hhs/ߊ{Cgmq_B@PZ`K+H]˨ A!H 3 0).MZQ~RJAV\#D!~ VX2(G"D;S@<"IRZ[('R܂p_'<}Fƭ߼,aj4Rt(ÖƄx)ѨԠ)C !V`p$V ,VTt5,Ɩ/Xx/ EX[qDQM )kR,̝ c5a큩|e{5W]hѺ)QUJ)Q}U_-'\7lu?0<2Ҝ=6lC֢#("#4!vC%(~q˫nroY:(kURXMd:/ڽʖ 8EsZȱHk,L@k{V,N&إaF U B,R}V_\p "s{Ss[VZ Fm^hEkM q <}E(B9Y2"rڹ(xP[̓f] 07,U ~f<-nnkƈ]BJ[{;9=e0CӦIx2 r=ф@;-i"IDD!t:mlES<vÀ pQR.W9r' &{fk]y].E>>svٸXg@f>Əivcysr5瓞G5V=jb<횃uSYa_a={8KLFZ()N F,}N(m=(.D5!%R#DBZT,Yn8UF[SϮDa"ƺ4pA(5-B*VM'd?gls]. c!8x5\)pǎ\I vē)0MFf,P^8N3!y1Ʉ޾{(ұ%1YcM J@I4Y ^F,RŲnΤ' \a^652X0fµl)r6ob]1͍ʉ,sWd1c0ByaEu<Cf$ z^y 3P*e48r+\xU b-|(хߖkƣ+Nֱo7ilmi8C5?}~˝Wn1F"}1O<9Rjuw⻊!5v:\[R:5sRJ(!%RbX NG;wik1lJ$2"]xXcI~L<ѳg8Vr_\G#%Hb%I)4B!8}|Ƚ7S<~LxG#2..<>{~#}={!;oAt.A(MI ƑEUĔ@ȅU6y% R\]lJd9ʝiN{]}vWYjc*sՋ+OyN*]fyNtw9dYx4,M墳۷dYFEE ӈWX]ؒ5N[kرzv/;X}6aYnk̲kcp]<1ZXciwsT'b4E1Z[aJJZnMYJfXcQ" |jh K=3ථU`dKrv+fE)U<f)x`[rhX7FK>ƪe9$cDĕr/Skіo)uإw}7% q.s17¢GcΛT .%ӮUD6pq$IyHa#z΋pQy\VdnmG56fX Rֺ HPP )$Z˵)W!2bdY VP8iI+A[X(ǥB!x)]o;_{$!/7I8A8׀56JxRx$K04a` x8:nTWق.V,C`Z& ua2MJ☋s{dJ4^P."4G 9)0ϣbGn,F܈E,,],iù>i.G!D=//s-cnJX\hhx . lc|5n+ecq&K*Vr]jˀW]r+ͿiQ2TqH帅,qL\iOF rn8飧\_{߿%u_n\y5sMsS $T8(,iPun 鸸y(W{) CXS4yj B)rP"sBh AE/|cw.۽>-LhLxHf-HE P<~L:=@X>"MS.D2vgi ~1w^y^DžNq^[{l>VpEIh{{8KXm1J:^K!:%gڳ~+pz1xx-.;[;`5i4Zd:AYTvs#\ rƸ*3kJ f3RD>FBڜXv)Uke䓩]Q4%MS,"nJfVVcBL[ YԾymz=$Fd:G 9sF@ؚa5QiMnW 2P(Xtp3cgZuhx^6z~nU9XYQ!QN!7VCnƌG\%N r՚#)AJrSXUv/<V+A(@skFa/WE+W3@DQR0 z:ʺ9,dBӹsc斁(0~Ö33ORJ:hS =ڕ@EY4:IB XkYRQs.e mtuEO$oj+lBC㸳G<uηOpvzt]N=%2<}ƒ^xedSUDgp{ѨXE =sT멄J)B  })*aE9)8mpqh+{lvcyDq6N;-Y˧{tN砍dxx>{.G`x~0۝-LwNI3H"ypxL4Mx0p1޹˭wZ- A&r)gO!E !3Ej, |aQD&m2R_{NHz6Ν͍᷾G(ao11yJM 7jc&/1LN&TX@I|tJ0 T6 \ hc` ,+tYhZt:m0((ykxLdYԭ4 ԫ.$jgL )>"zU!h-Z3׈͢Jv-[D̫ō6C z(ˊr ʍdծ@~hٵ$3`UZ()W<.,7[B(_Z1ZmnS^3n4e0`eoo0 th4__/c nvM5SdW ZX޿ cfhNuhǜ!dy\Rhs]T@ou6TSKJIet7ye0\P~8Y q(n&!{7Th2ܾuWnyn:'Kchr.J WEG?LnDI>ZKR</!^Y!K5Qx)X*'9J <&YO?!^ͯ|}:-&p[$XZF sP82}˸\À~K&KR'S19nwHuWy6V1ѥn#"f9#4^+$v36?p[HeqFD\HHN+7orm hwd8FclUsSG[[lzic J[5XӐ;Ö-Kհ$܈EdgQ{BLpxuAy*}Z$\\\$EPѪtI.+(O[Υʱ8H`@ժ E`07>\y lιeU߸{񜔠XB/)؎bpYLmM#LVō_%Qi/c0+]աu]iB,aնYՌe}hXL.:@ZMY2KVӍY尛*,<-rζxX] +.ZWiW/Z7LB9.sC4@sHD<+o)mC_x2[(m>/T GuYmC7$p: cJ E޷:IĎ$aXR6I+E^ĺ^Gg{PA$M~{,1ݝ=voa'&dEDnIӈ.wJ6@}ȎIc=j.,?}L4i|;(d"a\/ɓ8$R1~P0[=ɣ4ŗ%_S?xIӄ~ϞO|2D M3><׮j籙+}d&-=w磕2 C}|7I&l(c.S>ẓB1~KN?Tk&1' IAK1:9S>i`S&єp Ź<|<8?9;wʫ8>xgac d9,ی/ R;5EMd,/e W;섴Z! jIi"rGiDܿ] $xPڑUŐBU˩cr !DaXs(.Xs$\W3m(J{`Ǭ߀m +uj *33֚`ds*sʪ-O 9/%\*eDZ6%˧MBXB)Fm<; IkZ&ɰJD1886Zqg$? 4f`r $BGl[Hs,1i:Dk IDn4is6{.7Z]^mt㺒V]lg$2:'svrB7lsۧs櫜 јqzSNONp\UFR4!p\AOLIc")b>'' C~//&wbӥ JA=MDTLǧ=p~v{W_} .$?5]L:/}]To O̗|d#V(]t)zO(ID4 o:BGŬ޶tTZgGS& ,KRH%j¸^59A28;2twp}o#[mT$i΃Cfv Ʊ9[BJI zE]PvZ$e ./ؾyֶ.F1tܐ$Ky gg;[Hch{.V@ Od2LQ.t~OG no359g!rXɕ?Dy.>ndӄd( ;=" ǷnwtH'3<(l~b%#>yct:%9?뱿~kܾq~o$qDeu-Պ٩`FTL,@,iֺ̹M" Zk,e4p=sjg-EEYU'CUE5lQwHf+fkXI 6`hj]]n!7(Ð ,yfY5|}Djq&{JosxbHI5krB!IO/cP>:  Kwn-.-giO|̓_F ou ;o}KNrk1A;O.6B)y vwɒi<.6fcHvIJWշ-RF#@2U3DOPs[Q6s3:~k Xj²|i^HE[!OR.G'/={r?qx˓|7wy17X!Θ<:Bګ9,"29*)'ӂ[ܹ}[DZYÃ8=?' qP*!G}Ďeh2fb3FHQ* B*^+Kwq'(CvwIˋ ,2QtZ`3?:f40 p]!9 yz6{->vY5O Zx*:^ݛj, ٜuNNU1cJr~N}^ucnKXEkrY6UI(5.JWs=.49œUAU%-HSsrL%<|NMصBkSc.Sk^78:5碭w]<q<8j):7[$x>Ə`͓%HĪIh gkS54#BOSF R nAd<-98bӦstxY~8g[_}$VKU2m1_g?5+0`*X@fM|EFm]cu9 2od !H`oO?\M/yNBpWV^nRdV%0p.Ϥ$oWEYoբPWtY"Uq2uBcHGt4EOLZm:;*b8AwyxJCFdGO;0H.ဳStn;ݔclO }ݣCnQqg
ͫ`k5_M7:v},}`bz[;^|"@2&{H;VuzkjݝE,uɒ0g= |D-XE`Ya{zI"^{D);[}nݼE1VȽwF{G\dcnLb\vi)fQ+:2TB]09hIJr[k-JFsUW i Z%)8!O\مKݦߢ3Nb ScHb8B9J#|$吓GERu&Wy$KI越lAhϞ>5:\<;D:L'/t:8[GltN>)KH9裏<:oinuzGwo3脜 rpK2=N?#drNPe\UidV1tJ$D*UM%L4fFGԑ@?d]p L_gc_[voօfW+qhmDS8.NuyqX PQ㿙6V46 X렀z#6W]W9mk:cL^l/c`׹䮾f9 2hQD)UV )3L#A~\-sW^ԆT^pMƵ*96ҫY%IW6Lu`5cjLg&G1&/E@WC7< ;Hg,<۷l|Gtݗ}OI<"3Z]oȵj97_?[dRΎ Z8Zbw/I'l^zW)X#PetAgE/s. h@}Ge ;lmwf9''g\F GcQx8r0"E\b4ᓧ='Dǧn72&1Sr;Z#qF6NPJ}fbiFCZ#\! w{"]"RM0v8a|9bv/*6pa{E7rw=LcBOV|lm1:?ћ98?{8^߹7-%8$rA A.4u2̴^Yd>MHV RU ]Hь*A&euJ,?qbiKYYbc 8M]۬gEY,KlZ= Ce}?7R[_uDm_Tڨe%W!XmB4ǐ)U0fWf,jͲUM1%yK pm+x@\E\D +Y~k, x!Nj,K!漣lT3Bi{q2!M{+>w1dkRzTcׯ/{#5 $E=n<}&|$i`0(OFgk-DViE (3T :uubY(RUϬUV(YB1iDFy &ei3M.鶻tZ݂9 Z|-RӘFgcɤHDdDj@&%\ۄ6!8vu;0IRs$EM;qۄ.XIog~;$)ѐ]}30Glut=^v;Xdӈ'D|qGo Wv wؾq.c9| !'py뭷w]]r:,bpp<9K2bӘ$D#Dq.K7Hl4׊.*=Hz@Qέ.Uc\ՙ̫xɻA57繝^mZr3[UнYU}MW4"ʅg+wp8$" `kkNd2a4 U|!`>:֊ i&jȟa' }l6ϫc\qm_Ix}+J "y!뀟Y@z\\rV]0Ad[3z3/OxZX`\ ǎd?bEI6{mI{ ~12 `챮tuducC,'ϩj*ΒE#F$ݾ{޸JXs*9,A(҇l"-fs=z/ni࠹'Tn >v==ƇT58l (PLS_Q{ C ]ABnquIa%ktI]z S崓eh?'q(qB [$ ZhI"Nq$4$FL @\[w?]bD HPT[UJD i6AD(yQ#TEqdQ p{ 4%d I.̸ )Dɱ]m_&Hf3̦ q_|׸x޾'18>^!q *.$ŏn}VKHJ"NaJP&ŌFIb,B=0UgV@RS3מ = BK?Q^ X}燎T_GNätLR Z!lڬ=bsMxq(Ij5j&QΈxp}{X·Cl=6>\iY$8mon;ڵFH?s}R"d&}z'@ 3Uy(eܶ7R'0Ձ޳shn,!ڮAS;$(.KgǻwxBԧjh珋KOBA%Zm|ªQP-4% XוH ,q\~x .nn-V8szwwmɊ| $`Yȯo-~hDY/^׈(œSdcd%/"!!%jQaopYe&XB @B\j|19˨FDQ4I]jsiZ̈#MS۸Тik ԜGXpֻxepq/iPuQܹOQ]5uUa;fZ=$LTm0;wӾt/H<rgO=y}#^Vz=%P=ԁ,g2U?s){(i].PfD8wݘd*Ç}E_HAӴ®iM~0 a C )(`0 _oq|4fn#PFQW訏K/~K-fh $fPIVMw(oѬHR0lc(30Ԍ $i` 5*0JqqyK()1dHqh[.I.!Zݖ2u#,ˠT1xL%~1 .zT<}fYôl=gF eWLѾKp~8#a Mں+1(JDZ_sPUo:Ic19Pa1Fl rQC h*Tf(UUrb3N;Inn9=Klbdoo2WWW8G6h m!S>K& UZʄךwAjKvޣ9XT> -x3G򶪄LBͦ]=&ցAAd~E"M&Qp ( 9raAӘTH@(D<DkaQ m )F+f%1U|P##zOouפizYy e]Gz}ޗ憯y<n)k;!?U9 ܃4^yq}-016#"ue5S>مӌ_pVejL)$ځ87JlS㡴?"gd0kNl z:%[Nuwm['V9Y5F[60ARd26:9$Qhu rc7(Bs/0LqۦBqqOPV:/ ,--67X(H'o]L4x6q,%j"njFTSh TUTCqhB GD)RKZڍIM 4D*pB$)KIxf(BǠil[\\\য়~61q BM|2aIzGݕ%{@!(mq66zxz65!Fes?34hӇ#TBiigsy{ ?&8|64ٖ!y@Mme z&c Id?GF?Of,|byC !E'JɊ ϢUa:",b'ci0?ʇ}AzR3J zU0'ft73DG&~^#}t@՛Z6nLZ#=x0ܽVMc|Tu4q8 hp^~j;^0Ha꣌PݢB?ućrhhHh )@%L3pP)<%Iwv Djj (%AAD)рLP%) (gP HIbV(%֑c `cpUMPKhƱ. |oEȐat \|x?PMcS"2D lj- >|}ԀV Ray'`mE(]9#P6@%hڰlvQHlbZS߯iܢX7ƏoOi K;%-5n.A@ fӅ遴Nii)J/0Bf,з.BŵtTk~z1bt<{8 &N[W֭=XBṉeA$#< gK]3rM׬l+M(rm 'M0'·aN9WZ k|°Iz{@N$15a=V dbnp; /uGrDZ mL%RBB[n<CPFpukǬV6S ̾GIɦlS3#4)$lq$МCi "+]hniUw HLI.0$`u_`!aUB i1&(>7?<h;+7lW4=b 58)0#JԠKN:6D];L&45ʬԬE 9],#UQ j#RCPR l]oL'sLsk|ŗXms|<G) ޼|1#`Q $Bau*e,bҜܢ(Bbf`,l f,ֵ#M D |t4KAIA-[oo~r'!<9?w߿[?M2|ꫯ+''u]cY҆L2f2RQvq7Z4@*#UОQ">hu_>ո k9BX?stq7ep8l]` +qz{Eziݢ>F?ψS9s_Z4wpI@PvͲqInZB?B`YG29 E4=I^!e֐U*qwwb} e C&CJI =Bv\ݠܫZ2B&tɁn&j=Jwzm~)hRaY{j <.N (2- &4DTEeMcU6FIV!5d!*Hk(T#"$Z)-mZ2{ (;2&Dpb2d=#l(5ʢBi#77(''6UBQpN!h( D8 M=սwk4NZ?DPkE"pk(%qq{??Ưɶl-rY@E_1މwuu)ON?a 0N&56.+"BZoɐE`IQ3agM3R,´ (Q55꺆6xRAQޡr2瘤i@ !%њ<=n#6 _OeZ)-X, PIJ$I 7x޿{"/(_t2A(~-~ߠk5^>dquyw?vbggPZテR />_}B!pvv%..w(+|qrrdl[KnBJ/?d2AM@7caBGC;6 #AN"mݼeYiLY4 $!@`_)t9|>4dl9xaA࠳2xI*ؘmT+a\WeHB+w qXfmضm5^MvWy紤RnXPR紤]M}V}N~`ahɀ>Ҹ?$ 9&:1$|I(*yimmP%ʪz[;!(8z[`>#LVHiBIn@3Y2'EЫ(|}p&2JѲPFZ~x( PQ9dۂhi0͠?g/sbޱ ̌9B0G0 ͬSc;}[9 P{MP(RHlV% X8C#pfN͚:`({ckZ?I @)"u 941dhVSdlɰv1b$J0"0$S~b^-//p{sZ|WJ)\Oo7=GGGs\Di/`>_*u uSWN_~_W_+TU~77(-~;\_aXb6G1xOoA縸+fɝ>WXÙL'Wv[WXV 7pwUaNE=-#,ô@YDMt(&A u 2p#cfcGm9LSfiHbŒ2Oz}?<$I4 h[u)uV!=3N2jk >CywtGֽmUUn4yP%ƴiӶn|qUUh"$C* QtOg[*YnmƚҺkT(ea[`D 0pnjZјUpT;sז{}ld 1=Go?}NqXw U;&l,̘OhU)~GgF ;)qIރnD=2RRm[l~&1n~R#ռiED&\q#Ĝ#J'A(6^BJ='_v;1 tATFT窘ީP;حlAQ4R-l7c4I$1GG8yf֫;W+E&Y,Mm(g)jalд-&)᧷IfIEnhƄsa6 5f1l-jƌĎ$$Zv'SbΡ@ܰ7ڸ I4I s42Yv΂ aWwΪ.1mК~=Qf>uYqƽ<⠔wYxݫc30vFtd8z] s1gcoO>v817[EY˰Uڦ+d=kF\q{8A|rl6l6t:ApB#Ku`6Retzj=Q+Q?} -@BSmt94{Dg(fh=Hqs}}zhnhy91i!A8&3X$:fOɌ9YV4QG k km̘R#59eJQD"@2<g܏"&2=r <݃b* 6H,0gԠo0H$}sԨ8X!M̏Oq|0e[\]]aYcSY#&EXӉy@總1u[%x PP'&=A+ ([H%슗=D<]NS3>7lB141ye.Ԍs!&F+t}8&Szæ`ʴ&=MXn=P}s}0^> ~8 Csޕw|rOk@U7Mh{5.PӆN; ?6ۇoCR;5&Uq, kB]רF\.q ںAo͢fuÎ5Um) 0Quf]4e4ODRklW(J˞5֫-Z)^x|l8䇢A%F+-MhmBvK4MĴ|W89x}]רJ"%Pcm+u>k%n(GӴhیn(mL+>ӭ9¾RA t08{z1?on0QH  DIpMXE%Di<;ß⋻ ꫱04+kuihf&*2K 41)007RڸK!AbDC5-[ "M'HOcP{'BjنvxDI .=j"R"@Ǩ!M&j\bG94OghC6k\_#sl 045a6"GN"ăJPE1f9FXܴ-*+e`\J8,a%syPٌ[!w Cfo;@M ɤc- kx SYJj}lƄ>^Ac=&'v,FG,B6 #Mv=IľOftl숶:7J-s0b!Cn.swO>A뾜hc>7i8CApp0u+-jbygۼ)JVn a1 U;l[eetmn,(^jx&%8Ϡ,eUTfmhD8ĀPxHd 0AV+\%V+d||;y,0dB#Ŧ5[l7[l6[E;7"&8;;|D# 4BB (0iڦ8C^qusFYx9>}֎ e"D @I#I vUUC*r$ 4haMx3#:=9NNN@GJ(-56B3h RTvG=mZ?-{Upxa?Pp-ڶTg1Di\eS50P 2h6Xx-Vl:ل]@>`#T${ (%&K)QI4EspFMh)Tedk0,C9C84֨[@xQ 3$F0T5hk..ڑ~ߔ3'r:T ww mR8a &uYqnFCzggpl;`,N2Ȍ =Uk{<@Ի-#*GB&=c \ B CfujΑL@ tvG=|4I 6ye?qJQOI,p2mJI&FC5uXLbO>Zke(q~q E#u]m#Opt|(b1Nj/lo(mÍ|5M\#LprzqL iAI4Mk< 77hY-4M|s7ozFYHqs$M<:B'L[CU1ME .!Dն% i[)s7f IDAT"Ϩ0Π5 Y6^gSFO; qȣT%ckgH7?jY$)@4D]W8>:BL"|ݏ6#P!8FӴ՛:%+I&dͷ"C s1)Kű31icFot0}q|ڤjkMaRl!Vx&P/J8”'@YM#b3dz\Jz:ՎVa$HU&7C]\'&6nO}7 /;"j`ٍ}Q.XW641 .D%%d#42hRlaZxغ1`'ҥ촑쌴)gޓ}]kXƩeS2(#;R:{ϕ} a)+)H sf*R{clZhS%eM$va0æQR u@V7vxJ ij* ]سm\L_#ՔgLP6Pxh._+Z\]]sߙ!1 Mt_S0!Y"I8=6jЌSV@[MۛI8P[l6LN )#>Af(M]CٜG8;{֯wX CU8?1ָ!0-E[|f;G(5,BBJG Ƭ0IrK ٸJspJ.`t˯: hd=T~m_WFiOke83ДljȢ#XsڶnPQbَy4c~~:Q\ԇٍ86:cсZPĀX8,0ޯ{#Znt #lRu}'އ]i! uW\aL7\k݃:ٍ2wD˜ڄQ4 BmkԭfMcZiXFH# N9,_~/^`>{81@Yy'Ob2M\,S.'Lb$Կ'QyAQ4׾սnPL28.TV cM#eS4M !Yln,5'O~>AzY2u$dBk5 ]͕XBymI|LQ#NbςhW+? \Nc.s d@kǤͺ(3¶ֳ&g vc DOi!ݩp Ln{luC@gPe?Ɯ# ڪEU5:Cr#dwd4j 3 N3qZN靼(RA_# 3p`a|~6T9byFyO^>K,9|>t:_QAΞCmhw,m*pCP[DC 'oxD-δT<LV/Q[P;m +w^T ʑtER4"̪G\v,ME[]3]S^Q5خVH&I8M$):'$%VS?^|owXvCx<_7Θ6Na `a_C^}}c1^}ݨI/ ѱOfهDpN8ZªڌnMZ ic,0FDi>#Tr'p5dKX&#QE:hHix׻Bfͻ]ǥ|@o`|bw/Ur0o}du|x[3z2NZ%QF4-adbjԐBZZz6*Q765jɪʌ[)s|ɉ͵]ʲynU Qf930tr|ww+l7[5qsȭm%a&h gg'+LS fg_9R M!ZTea*ZCYCX؂AmӋRfƑJA.]~D^k>uӧO `G* s<={eY`]A)3G[t"$\:.(ÑMl6!{{S+@61!Rwݦ@oܬf,AV|l 1؎He! 1fdrr^Iu, udځ=qM[jơ`a4Czh8a4ðw2O;vcA x-ss@el8(%V M$c9i+0m݃C`PF@~V2:A9Fo聄PGG"ݜ5= 0CYظ|\x &{v:tsuOf3f7#!Ip#8ڸNy,-8N'. 0qt|l|RjBUs+C+z fZFVhEEEx)(Fn+0J,"MMKKx5=;zMVv6tk4J،C!\?R= J&&^ ;>FA#Sg]O8E$*" \@Hj[$vdۮK]U>=d]$} A&#Cڴ h;R jԄk=h}F#mG-b n")3>бt=0 A8ɎmjN͎wׂq7V6C@KWCC#! 7~3o#H"y:D{wYi$*h*0{IRaXlcGS'ԼYjhƧ!߻ۻ0z:K)鍥R~4o ދnŃ~Нk ]P rG~%~7Rpi6t{= !=!ё4j)|x4 DiL),B#r>4̍h\QjUUb/_ol6CX,( 0a" n89>l2-D[aEM0IS,IHHX.ggnyF6w?[C:Z7sZ'}4twBٍrlj4pRB2 %4&,hy5)w9oO_}c lcL_Ap۹zY OZ 7SbԐ6AIfN&y*ؗQ 2xN riPVL8::F 8(s)UC6nYÒIY <(Cwu#&0 GKڼl[{# ets6!xC71!C֬ϴOk!B(h-{ Rp Qӊс {hA>ps+}}F{Doi%;DPk h8#@qJ*McM:,wykm( HQZ[R/,{lka<o'WO0^Ջ:,%vS)IQ-wry*h,TXz޼9wN1͜fyJj]e:X Rd7BhDNlFL5/ZbY$@{y[T ㍴Zgy{pLw b0ԿF7=O n!JyM}M|O=RD"W:<.r J)aZg6*PU5ptt1hmHΖF9SpgIaP$zeaj9^hE:nHYϯIIp)_A)hO;kd'P(a a "F 5VKLw8mbY-/PT +}EBQ$TIl:g0TԸIk^iFLTo{!Tn€1ٙiP#dE m̏eYP>to.=1 4s_'RȫO}v"DCpGa=oY{*|PR^n@|͇^hμKE,!rds;M+TDƘUxMrRi-iF~3) 42걅 56P;b^*|HInLdċ"h+ Ok%Ilyu>ذ:li8}=;]яF#mCx|ڢBr~udf >;锭/6*(,n*+%M3df I `R"NoqZdQ9B^Pqk}mVFCip)n =J.<q$(k ϕ#iBX\5QF=ۦ7>reQp63XH10Pp@)lo\\SHMݟ o~(g4G}q!H۹!r^#;iKFS^tA3>LlnӅ LO0\*Btt? :+Wݡ &JRLBHQ')P 5BM^_Wߜ~Z"9{6S}Q&V ']F>=ONXiMᔅQ ^sZ 8֦ gӑgSʶ_kӧ ꋺATEY&F (yL^q RAkIwmזY?.wѶ23Mu:^˾9pNk3PX4.:6g_]t\ag4e»Eҫj5ӫgֶtQ%LcP#'[ ɣ&:K=c0s (eLSc /{ts3Us@3szIQ=tLk cԤePxEX顋\E%>. I9FCtp'! /. i Q!Imn,M*;cRi\HRJyS9 9>#}g[!R 7)T_n &"$TB$n^>UOg 14TGu&1iE](43Mi2cGAY|ͳ<f;Q2,Һ֡b'#$jӾH)c jȴڟ:^MK>khLR"hNe (!N8Zݰ$$ ҆~¬k$ewT 2&Y, ]f̧az n&?8d;!\eD75@7nskTh|+Z]2%n_{ [NEew[)_Ԇgvᦈʆ s\{\Sc/:I/JE!, ~[ YГ(tFosezL,c( pMSr9W:Zpe|+,'b 8n˺ xl⥗i  F\>{< jJ(2˅J-jp\<"e,H .9LyUAjwoZpn6nH$wūxƕ43_nM0eofJӉ]|\y$,vȇ-yy[H) x^{l>BzLݏբ!J9vMh7(M;z]@| IDAT7Gms(cxHƯǗP$3 e _]tLCѧtA$tޭoH,ːQVCR+ԩZ cB@ {c۔"mWm(,Vʲto7Mr>~{w߶͏4wCdgysR[b0!IpBx_J œn4x@(x+ flRRXWxso> }ju.BԠ:Fcm\2+0Xvy.]oO>noW_y.]ºi'mӠ @1*x>NOO/@3i4){qPĝ~jBh-<~Y[7w^9@t(Y,<csYA*͊b F@a;;g&}]_yB ai&0>+T vR5\n j:zh w0~NVv!3E٠xDOMSWzANIϼC~u:w_gD2Y@+KR93$C)uӱRpd(n10J!Ʋ~q瘊gϲ/mF& .&̭E|@i0r|ր@IQ#R& !f^%gN ]XGu#4@?kRu :)%%Э<ϝB)SqB$bR|Km"ݾip|zD/A#7;Ff$ޟ#i/gM{y̨.cз%rJT;sAD@-6R6K>RUc( J)sdY$,~3e4[ RYǼ($b_I&<'o po9px@Yx)%wp.i?(lQ*o:;DwҷY7+<=BLD\%Mia0+hޏJqH 1G x#3&v>wGC 7wG wg cNjh-+:>/pI>mBn:nB_`ǸgjL`d {Uh B9Hд%f-uc+y`Dj@X3bs+L>Xzht:C19V|{m4eҶhV+/a\b2i +i Tm9KT"YPuP 6;cd(8zjr Vؙ$?5;tBJ5`~$jsRA;&6To kz-e&ؤ -CŽF˫90 ;EiS2 LYwzekzB-˨2( &ΊǠoFSLWʼr\u]mG5%NWdQP*h8&ú>ljΓ3Z<+1?qy9@c3~im~_ gI4YƉ%1BHAS7h.ױX,ZPUcTlX8$"f?b˦u;[(PBIٛ khM$)IY^ɏOjnX/O1ͱ5il<ePjE $~m\H/\ɦ4Hm,{(bp'<֔v:pŠzN(@d^,wfLfCXLxVşm) Ms`$RJx)lp}݌iϥ`Cʼ+y:!8qJ9[kH г:9+CΧ:+8Uܹ[4/\b^?d"QP]|Fƚ/Q+y$5<]k#IX2."e:)`RG{M:Qi"F %fhF&Fs,@7?o" I*/w=+*_1 PJ)WMSLSlmiDBXG_<u*tPNl9&1H{mml7_gbu n*\~[;O/sO^^UG;ߺuo `q1NxTg\; KB\|PJqG~V Ǜ߷ , dg+mEJP&Ʒ {gkn'Sɐ ܸ20iyPJa2`4a<;Ĵ6-mJ9o"j@gIuF!M&~]WXOO0M0ޕK1Z̀ [әy=4dQK_iɪr=]y9̜k:9f^ ueǽF6Iq1y\7nĿo%~NjTX,N1ˋ+$-E E˔a<b(nK{  Pa?:9Ft7̀d'>xGPl7F]?'+?Б1L) I's# )g&P6<Z/᠕Tk< C7"TBeH7+Ȟ eX7iX3G\F7١2)zPB&io<3)<&bӤiPL' FZ/ǵT%xS_٫?k̦{o֠N7(,Ke"-J4>SiJ]X.y6a:% ҳH<ɠs3>Oy.!OCL$8mGG8>9|>.]h`0sailI䆔@ 2P~ꦔ+"2a>q9H*ԼiF(zhpk?/?._wA 1 "+t' vn8`yB y׆P8(HbP evb.v;s6)\Zk$`aGƑ&.ss=DKsP2 ?FR]@?_*'|7z{VBZB:$+(4qw k L݁Яd6\BށKTe@ n&{tjF1Vwe>@yhrjAUJIM>Y8τ)zYt,R<ќeQe9BY\dd*<99ӧO^x:Z[άH#)y@QG?~؄V]"e$ATJ?-hpzzh,/^3tBk 4O-=n)M!ȅ 1󳟡x!~m#~tZCk_H㢄qt.o)H!6#Ūt4D?RZ7"ѳ 8Znǖ RɭB@J6 gs )%#=EiBT \H?+5>pDGfI)` A-gņ_-1Գy 8,#.IY& }4F~@A)E{?k"zlY53Z7^[lxU2нƀz'P#mԞifYΘRJ2KfY4sVUUI<P/{f1}xS@*͏f 8PR(]̧31/)FH vՙ OFtl!|IEX8$ `Jrȝـo;P~y>O]Y"ӑPyL!E^AHB}2nH(@Vl*>TLMKQ=2lNMma%,Nh>LQmLc$|v7AF!. @n~4& (g«Gc`&Q@r땎PX(sz}p)tޣԾuR PCeRfRI4mд5DU7V8/"[2P ?Kp#o#F{ơ &TZH ]of:`!$UДr{V; B}A H9G Y i@545rCT5*, 7ܷ ^'^i:̙&=Cpke] (S պ2G=g*7m +]bhO}^!@h_@j$AYQzSK3^pjpKn)_0dyffCv};r$ʡ6 I:\LP#>?ak>O1H 5b\?RaT 'j::$YպH;At6qhېIo|j/d*5Q=^TP hmբ &%(CrzcPB7"\4\Y-gw#hf,3ʳ0APqŋr-E ѶZ ud[ PjRA)]˔tE#s]0+غ'"oRb_F-@/~kyi]cQ3=9^Zm&G6w^n2nv:㘿:Ĺ %D;[۸r 8X׺[D Pc6R P0f n aӒY^l n]\Ǩꁏ""Hrf$aoc.:c L/~@._k2ڍ\\wr|Táp9e p ܶ"Dʠ5%T Y)'Q^!S)8vJ3Ơط)Qimޯ4dDց?pb7av0J{]yA~2Cϡ_L:4JC$BpT )~FᴨRѪߘ(%@;18nJLJPTSaL0Odq\yQ=4F)2 X[u ,@4JHTK\P-2,D Ԭ54FzNd8RmC#A5aQm("W+$1 ~v>E+RIU8]+_GH@#k"\)#0+"7,5\.1L1ϵh{UUaX`^*-͊9#rANؐӠj΄p%ix1o1gSJ)PEsxH{ԝdR4oN" Vh&Gx_|p #W.~񋿻;@0hT =d̚ϑܘZ{`ִYg( ilDtObk6 Ca=ol66^~ X-W8:9jX,PUk4M,懫KФ/ݦBȆ~& Oɠxڪk.F&ҩHk Փ9]:=Bg"aYC? d"cExƫ}[k?|nje ^j -Aܔj/JLGh7' (֭ᩦSp~>% -!R:qT IDAT]Pud:#WMc^E~my!,$Ma5zhxPxa(}+z[ <)(!sіׅ$pYV攷5܀ Aՙ<ucNʥ҅vKĨ,3d2EGƘOqX.ѬWgf^~u\v׮]CWkiKb|"&_D+f,3aozJ@$nԤt`R2=HJ՛7Sn(yNJ(G) 'k6U:¢+!ebmmg0,p( c ؚoa{ \zS, % A'=;z/% J3l5_N^K!c<;h#c77iLwٚd{1 R(Ǻ <>> |4o /|Oh[i (:wz=h@ WJRN*R/`]u>ȚA7*u1" L TBqH@5Cg$@$4@OB D?t:ޣv&6"IZbAᬍZ5B ~^VWy[m-$!U V ًԑJ {?CO7`)-2]6O}EF'7lRb*hfh_l‡V@AiǟRm^+׮a5(aU ;t@ KHAA:pR%f"OONq^RjH&c ,tAIec֔"!G篷6g){$OK[Ooۦ5AU NOF79v1ж-ApUU.KW YRk1Jҋx̌k:wDo( ] #Rf̡8+վiTp A9((sdWmooc^C U[c\7g3l]xO/jtڦ[J%IofsZ!S)漁ccGi~\䢌 - %MaJ@'\{_LyZ\Eu~1Pmݻ0%߬W5#%$&8nb_n@a&mA;WlsXRS̡e,2h-2 ?zǏ 0+/~Kx+|/}{ۻPJnjw>kZx7plo`6L0[Yc^ G 9!D8xvNNNQnMU+,J0Wq嗰siM6R) =T!dJzݥ 4`x}sd54#]j<)z"mpxaJlExJ,q}LTvXC)ͺA46S`a2`6a4a4a2`:B)jbբZ#IvQ{n RJS6duV:QwՏ u-zKB&_P#j쇒y\O/9]Ĩth[^xoǫk.aAH^F|-JfRt6"].Y+!q.b,ܸn%G(,]{Ƣdj$s9щPo Yc=wZ8SJ]F1%ʲ6F+\F:*)ͷ Q2W\+{dd8$Hr62Aś;!^yon(`}zX4 Z豎6k*STܽ\Uy f9C|{.xB`kkKo*Wk(\AnqRB4{VpPJ}w7D]U"qRtfh!뉌 "&kzQ졃١&ߖ6Ҽo0n=)ݾЗYx-s  ~V:ͯޣΦF3@p.#-xG'(;.VZsQTK[y¯MM2jӧ%_|QY=awo;; Eg%(!@ ?y|Q9ƥ ?«7_Ýowՙ`{ ƥKē#?GSG0.\Vע,AChG+M~z^H@I''hyW|yFc`E5D%IXi!M,U%98{h*0D[{j@ceAmCU$۰SA3zX7=db(XjuBȀBB5 ?~gNelo kV+&D&͚AYD7&Өf/>7v+uBJ8Mk (ђÓ(d4CQx|Qb+/o%^}u|/}<TDjA? 6cƉV#Fq C} 3Aڤ^ m)f'$ P:8m-<N{*Z1("OB/v$.6B$P껢f [9)'ɩԁ飒Q j(Vcsr!\Tyf9@)E]뜽owҵX.U鱞>>8x2Ob\b%p)X'?Ƈnz^qyckg^z Qrw?O1ÄMjӃCPW5x"/h_ @(CAQFpŨ@gM rܽso}@m;Y(N*=}ڥ̢>‘b),~sOSg۶$|H5Vl_mH6BTe-{h 2&\8"tX,8=zy= Ҡg+CHRj1T֐WӝM-bh~O'5m==S 蝿/(ȍxu Ti;/F1m_ >O.LFX7&n| ;'ɓ'/(#NtӢ:{ŒH5%ƎP]WIgEy.|_pHCj=Cy5]_ŨlqּسwS\'z*Gw8xv7A5mh;(__KӾsZ/kJCU58<"rANfr%\Y> Jp=_%>s(˓S+L,xG9@agg۳^k?3o=l0+z$Lv̧.ŞRPP'Bk4uӓcNNfM#m rX5:o N_)z0>):x8 !yc!fP چP4D'G֭Pc{w gxִt d#g[riUݨ;qA"V4V^ڂٸ4`"AD f(3Ѿ)\̏BtM%Ff*a.\ciIb RN#T(wDj}8o{O߻@qM9!Ԋey,AY{SsN[:B_ R|~P KYPvTPRoYA<˴"Q@jQQEC'xt ޶(I7n>NOO\.#p)@@P5?z]xȲ 'LJKWկ~_o8=]ÇCf9rVJ,Kl899!<a E^W} [[x϶p8<:slͷqWoswuU+U2UNB6s㥨)()Js?G9b,wQf>LkiLȮc! "RAiRY DE֗CRMh@ A(ή/Ѷm0Og0vk_:ˠxϑJեrGr6JoK38 6eWb]bB4MWSz1 ^vڃ*-&n.(Թt_.7ka0QY}< oFN ~ٸ:bv,!@a@IE 8!rHEDFr={vqܯ>$#p8^q Eഢ ;mViV hp h B#IwP+s]&S@ND " 3x'|fgDa`Z)JL%QVH#f=4^NYA輩@ۑv'lߚ)H3}Jdj3OY)r/ͳ!+awG9(-aۮ_'f2RK_׿~i%>x}{ZCZaogE)[[o({"=^=LLkjY݃R.)NN̄Tᅬ_xQ54XVԘNgN' RmjlM|y^1M042!H !@D1o} eQրRycEx x E9@0Pp.!xz {;؞0Q^߭;ȩIfZ{2 |zJ 0agwGL_s.$Vk1PX, 4C0rPBQ9# RVQ%%}]lyE@)`ٓ^tUJj{T6v yk=PM.gS 1/̰nS4A:IɺYX[TWw{;~?~Q7G(nN ɑRx,KYڂM`]O@BM(۸z (ehZrܾٟ ea]Uy-r:E4}9r4թ֧ `l(,!GI rP"S<@!#uCww!jxu,s,˝nBr<nmI3NZ 'uɵߧsg  _2z ob\RQQcIقR>ܦL)LW,?;4807AOb<&2_+_Q̤h4BaujuUC1]Wm .8Zh[0٘sQu]c\#-x/\r 9)FUF6 B+^iJv:!܁O ׭[+.H-iHXCZRz uZe@ ]QynRB)mlK= ZF E2g,͘KԝUy 'q|R&ߴMXZ#_ ߱4t w<dnׯY >)TQ7dQc Cwv âq1J a,3=dj mbw{cYp"Ը[!Bڔ`4sَ"f89>OGxgUF#4ƒ, 4ڴrC Bn@ m^PN IHl@O=%@eDSU9㉾~ zs*j#b Qi/DJ(!{q\Cd^Z_WBĬ&O% Fנ1/&`n=ڕt@~g}X@S =U=O33=4jp{#( E,]f&ujU7X,hFS!hdcgf2$jZkR '9t=S(MSfEe >dW"-j8(&9;GS4.Rq$h1'| N[L1 vLd=mxt& JěΒB5P_S;,&}= |nUІygk"T[ax#brhѹ  )H&Õ19GD3D%BpsX!暀!c9(MOM 0]߻mA2)$L kmz֑ mZ.B1@>a9TcԠT"|#*1T^SPd 9 YQ, lͷpio2-&_KrcG{+dPCŪT5h-@@"כ2)nfe*G∛tw8* } ƊP|H<6fiw|+,!l@7cgЯX['&@nǴ1w=8ʲDe(Rke(Qz @Kh[ZNM1N''4- ]SyA1LA zb%ZZS?ǖ$;Ή.Umݯ3CKl0`' Hecg{׷v%"~%#fVUKW.M{Uu8;߲Zk[40ZF >W]a-V $Sn-:Xۯ`.*R[A#c]=sOqM wׯ G*I !a00 B3>ǐIjPB R(=OwHra=}=*֍睸@`i LDk{ J$橱!1uF+jbJdkVC Fԕ (Аi Jy|Ŝ@ 틏5I(UK(=4^XEZAkKw_|oz]qqy C;ݲB QlZ bJ(ʪ8%-={3+eB Qoszrpw[0\ cڠw` 8E҈ZafnKb@g0+nn;FڂpyqR&*7Fl$s+g\DaH T>]'_ u^qK4z{^y-yՑQYMegzt} }z}1e# EQ  sj΂ob^}8CV'[M\R%ڦšm.rysynGLףd%^(>pVyߣ_ zc~=!qЊ HJ.y {q2͜QbɈ9Xu`_8+@,q hOL6 G;oWYjߣ\W>[L NG 5nd]vS8S0$!LCnC]k]'@`BaO1n'\<aKDLAmI9*J;3>w aXf UVb:.hZNN5օ/yO(( k1Aѭ֓/z8|7xPWp"0Ά" y1j?@1e6vK J^VsZ5ZJȇ l+ugiz/>suc? TG{SjO\ʙkiN? WW?#>Sg0pp`I'>`x{Bp'I{syKEΒM4 =:f=s ItvkD$(777x$r^)>cSF2j.}܇;ڣԧGm_R1sΩU#ZǝZT侘x` #oslj6s('ހo:@Pnw8 oZysUzsCBB!UW5NNN뻾aіMہ5w"h8gQV ʲ5MG`fl[Xk=rʲ6 1^U`+@@^p RiBkqq}ףT;=hwD"1u,g֐GYy2%NO"n@QO(t T# xxzx%j+Haey|s| <{z7SP I!y;9fv\U7|M;?_G ɩN8Fˈ=gɐ_sjhFnqEQ( h*o1gھOymC/*ٛCѲ(F<|_[Ƥd3!ǙfeL4V=_S>Č}ߣz袂RH/ ZUAhc٬IJRqBQxj>S+(R(u.붪*)t0{;M{@5n6qbY k$(rTGæC !kPj?j>(2HC^XpuqW/_ #{pPFNȉiCܩcՁ%3aa`2dHpp4]6fX A,0^1i_Р, A6X&-y@Sڧ VFwz4N̡9JaFJJ *RPEFz>]E۶x5~ǫWp}}3<}O< szOC0ܲ#~ʋ\5!s46n`"lj ."xM&ʾ=DAқ7opuuBi`:x2nϠ /t:8 VkuB*?{g ݡᰇj`ceUV*Ң9aB`AsأH-Ά/fK5{bFP__p+05 8,:jd!NfC@ Y*5V#'+*̌J0djkJfF*6lgzZ^W؉ q] K Ga~"=__~O?U%Zӡ*k`KM}؟EF0Ȗ㈤x}}kj ٩*yE=s:wv7Ak LoRVj\E,0@0Ƣz(ָ%~ .?~ӔbD/%SămBYxaP,7-_ è5D#NƸ<7g_Wج6+=6e0vEt^ǿEk=?b.6G.+:V/`d;],W;CC"=>@_f%yBvpPa 理X; 0R 4T ;%,HV#kǦ EP4F>['Y݈Fc5!5ޔk[rkO&F$C ;*=RCG#'+ "8a(Pq6$?h>E8(~u ]{+&>g&9VCb(FWc9eg !Y\__eH8 Hؽz]5uCպ£G[[*+AU gP(GǷ@w-,?0S%HPV 4'!h2qY!xB9p7~NY}DbDV` g7-!yYtz:g"F8o~Cu &<,.4 @x "٣G flj@WAQ(F1Aq\D댜E+6n;G3a `H hhi댼C'G`#sD.`&7ug1 Ezv"9LBFYjܠiV+PL>49";6(%s?B+^Hі k /3Pe-|15Lߣ"M=ny4M8*FM~@G`ZqոLC1 yN2!:n.4n2jwlyk n^Q!Z9XG% D\@V <ƙtLǜ^ҷQˢ$LJ>Fp51l68==E]Ah 2CBfpd~iL7di肅?Lm{ p"ЅF _cqsC?GP$mV%ȬZJt4)yC]qf 8 b;X) B,NP1`Z+p̐@8I+BJ^%Ӓզb7!s# :6 m:YDp J2"vuQmC~mւ@ h52Ƨf""z3i[֠Tp~m P8%"!#GѢZ3VƱՎPIc7MyHDGԴ-Dz})X:XHU%?<0c/⹹A{YMfM.D$ ^k2Br8;]TSpx{(^WP1wƫ*ku !}.C`Fލq}Us42>imhs𦏑}#'}N1'8j2cTлxd%,z[2Ea;(1ڣ1#n?*s} sfw/ԍnXquF:ggM)N?;-˔wMVݶf3mX &:??O׋ PK+ˣK]>?Ki*ء!ʷ}2^~=v7Ў`\'a W7WxYB@s}S:5؅ vG H3jJR*8hT 7\]\0 pm wd1u`AMMt#,w̟ cN9Rmv]|لYYI}(?Mۢ( 'EXkx(¨7fHX igh-]󅥄F^}R@Z|d,FZRO E<4b|lؔT;;淆KkvxvMӤ=&>Wgs,ɭ)3 UW+\BtK@շ<`Buov0pw 5M<|nHL`^:q, 4с?G>^ )A f\@mdojAo HliHP$3撂94v|E.LƽR4(mC:0G\Mw`ԅn֡PH=)W+|d~],;h]o+.s`᣼Ʈpu}8-*o|HL S٨/~~72CA PPiXW]@`W((AGkpB >&6mR/_{(QV5={my+gsIg=Rsq:c`q *0EqQ1 Q6qC+QՅFw#L0]?A, @)8kpZƣx4ԣ>uslȴТ?ѵ 6 <+囵pss7Q%qdSh1+L8: s4A>2ŝrE~]6PRM<56Ϟş1.//b6DeVG:/@Dtb<%ӈȴxvCF~LxY*PDp}Ʀ&(mB0rxlRb,qQ7:-G9_d1HZ:O7-͚X#(EQ1W7{`Z<Va|;FYee5̉ZXIØ_ (գ:P4?J\{^V~:D{$;7 %f(wP\UYⓏ>'O!]iak`(Dy 1\bn{s 17lOyU@}6k?./d/%~w nѓ^Ci H\>Tc>}l,td(#[7kg&)y&1C=NT\7cX8PkCQ R)sѶmr^,d{#$Ezbm@,VUB(N9٦F-!jJe>)G#tҀшf8O#cEB*RKF{4R{V:o\PZ3{KӐµ]Kȉ7c(]:+w+.E?L;soѵ> ah9I"(jTG 6!*2%id!AF]-x6ƪ :-x] $*≡/M, Wu1cԾt# tDpqq}^lfNrD6ա91=٠x`Ў|F̑8TRyw&ٝ1*MV!zmcNi!vk|g+[\=`{FӵxE- y,:5koqs}n'g3|OQoZ]׿'o7? ~asv S8/q#B(_2$:N/B7v?wLܞ.Fw9#G2D FK\H(~̱ygi5*#LZ{AcJ`(|#,"QIZh^ٶmڃ6$}";Z8}DbBώ#&b!oJIs#9p $xQրS{:ǬӈPt\Hk Xz.v Y{\g,Eu eYqw_Rt6Z@VMR!DF!$ܼ:dTKZ3-@FsT- x P8rr4,pp-H ۃ 8U~`CW[N-vorrAgI5t+' :fu]cKFё;]V 7I>ƃa0-F (JABc(Fj/qgnGn(B\[@0jQҚJg yA0x4x|1k\_\BzP{4!Z9=ųgS\]_9`/ y==}, ~ZoØ>N??^ ,Ț?&Hw_LH2=rS,4^K^5ѽ Y,\]Tex^u^Mƾ݉b3ñIJHs?8жޙ٤r(Z:WUF9,QusOkʞ2i$)Ju'ܨ2=/p[;W; 'kel"M]иŏ6kϦ;:!ƪ顋84-WWWB! fנ9u,@ޛ@_<%!Q,4 "ƂHa;ƜFo£r~3!79] #S=Ekp-\Aa4uɊr2nU2a/pNi3 pP94jǔ;s'rq"B3VY)P).P4h3)+K<)"(YT r;X58tlA~a\57~p`{QJ͌m],tfq]oQ5=&]`Y=}|Xxw*{UQ2߇߿hgOx  zXOLFMS̞r\u~ʍ^a.4-F%qXX\4-zS(UbTo_^UWV=ʺ kq LhLA|'n` "$=(qL)xƋϱB|/P0BV!+~8{`%lrT-#р.t:#3zƱcGEʓ_˳9sǏdZ5)WXUNi:u+жC&Bs>tu‡c ӧOan1/_WB|P7{O(J| ~hĂi,3t{6D8d gggws *9}c)R,ɤh0AjשUȴMIu3P38kTcހ#Xy0IIȫʍFk77-ՒH7#a}.6df89c)6* !L>HK~ԩR9eH'#B@fT1|`V䟗(cP,^V+b >zG.հ:X@ǟI)zau72nhאQJ=u3]%RduaZksox kumh]̘ +sۼn<>49O;(q#_SByΒ(:9"N%~/ִX+l Mt ֛-=yzt]C h0,/_w 'gec=tgggxjkxrb#H8.*Z3!J+Gئ'-_YƝ/*֙*\V|E h-t;ȳN<!^kB)Aк~ ^~*9Zʲ6])o= kN8Zv![i)?__ݤDZ Rʯu8 \AaB1g7nws.~9j0(#ʏ}GTtp.XL$`^]#:^)+\zbV`\Y$B(}/$A9pq pO$ӺP3`)&wn,Mb?0)Q`t */n@ SHW7I]Ә&=AC5*bQ=. X:ӣztma]7;<8zmE&:*y-Fz^1!O\CAi9Cn$y+!p%#}b}ʭa$ 9ZX봑Zy~8{;:Z.=1pIeh6 皱G딂fusIuKHn~ &|VL2ʢkQZ1KoA O{®9PDoz(]$.V+nOJihf;QEo`@Y>(lp -'76 KZ9>F6i~%+"90i$xۀb|NvXf)kM6°+QEaΡ ƫ𺾇117-wX ,hV ZCi~)6:s/)b{r1*΋Czf\`szY‹g?)zx]U{ӵ`Q6JA pyy'gUwl}C[6Uf5= %*.QzQ.b-eDxpM'#ϭ7fbb$/5y:Ib 0֍Zُ̕4m.#?\׷/T`l%-R4ֿ艖]8o=hPPEp~sь̪+H}%/;H%qL ϤߋהO;/HWS~]~nsS9Nu>?8TGX{UJ,&+c ]v_MZ0X~n3M$oXoR' JțN&|_8,5=BX| -"l%s7`.!\:@lAeA TG;_E& eBJ2 XI蒂,Z4mIQ}cGk,lD*%؟@!?-1_8 fX(X\x ?o5 \G74 =NDBc[a5}m}QTmwuЅG]ĎTlKLa!Ӯ4Ah"Im/, .zp}r=e֪I'?_7_(=1:U')}pvN{z)BOkkQ6#cu2Jhڒa3e aZ89MJωs0598_sE`H[Pred|xߣѼ8}`d ^UvmÉE.|Od(Jwp=&./Fnc @＀!B#op2w6z<`2(:q%g֚cQ\>4K3 EQBHXpBͫ;YбZ- roq؄?Gx$/^g? >#_{\xo%kz&7-4BQV*}NPǶ*a #Yrѩe%ЮC ؘ e3^jhFG(yٸ̡1QC (' S'ˆ< Vd%ZC\|#2ћT%pI<] ƶ<tGN]5sEҚIth~&2[-h=捼 ޖS~hac̥ai|A~g <ݥ, OP7mD> 8:k5./.w^BB0`%h,ƿt\4ԁ:C=B  rn$~|̓B hضifta+JAQT E0u> -3hzSF-YKczKAQ8FDKyS̬ NX2z c1xYDA|ɋ(cUQkT>\W+v "N`9 _/:A_0 $@?_J$8vBNgC3|<ћ_ Ɉ9o2q X7`xSXJ<=xr,3jQqۣz]lV6SO6Q3Gy11XJ!3vmӠm:}OW7;8!((uժ*4-j^o p,HyNR"7wyMQ# 6ɺdq\M$\+IkT^__c^'g>lOxrGѐq-i0DEUdGnFޢ~ BC1y1j˫KtÚ+ zT ]P"^9 VH\N}+=:2D84-`Dy1[QH[f4zߡH h}Y̛`S Iqv*qTz.1Riœ{<쳋 A^H^$ 44B6~V.\(]hz8<8zۍ uǦ[,l3r.@;kj^LQ%ۊbYgDҢt[̭c0-ɓ$fgmEC۴x 8Йv'OͫWhCn% &(cdmi$ݹ\Sx ciUꎞr*)VA֭b8xřŅ+=BL$R5FkuYF$ieQ5NN=98Gew^ZCq=7Q3D 0ĞQPG2mH8zMr@XtM=CrёXlH<ŌBkl2'Cg|".ߔ,b ]EOkV%Ek:غ\]^( )렫 "}p?$b]`H|Pj/rvB4GO k;eѫqBtGq!|(zs=7-FcPF3'-'J$ubfd:CCsg).2*8rib$֍#!ف2ZȮ%4L# 7Z%b:NXPKjhb!-#~sf;?2;8nU6*K[zČq<=|<20O9/IۡtQETR}{^<)ຮ1=uDŽ\[%ַq$9#"ǰBd0B ]G1<Ʊ⍠A=G)(rN 6JP`NۂXN\lZ1)BAqV\#G'O;/D& N좇1,q c;d-6?‡Ϟ(kx4Zed6m cZob-ޏ,[Dh>G,S6/'$NCQ5߼6]{η"ӆ#WJJBb(@oF'k<:? V:`Čln V΀o_-tYBmPmkkm|Xv G lhހ:-VC`gz?šuBiLVGSf2d?}ܒU kv<5qLi#|$ *qJ$:In2%[mqTFزzLf.4EN};wO2.p>{ m)8 ʐf,iJ0UՈtp@.qrO/~'(7KMFZ){tXp 06-ѱZvF#L ̃ɩϰT|S~ 7৳w QOC1D6\H%pl5ؐcRp5]gz @k>EYh0J&(;ӡ:(iJ >0i 5X` RPZ*{"8cPWktno$51C>FsSHf' XͫՒAhmS gᠸO6cȘ痁ҷtM {E!o%> /Wp'OamMmǀc /IH)c ,O ]Ǡm*Gu(Xɏ78e BV5H1ڶ2qkӡ=+v6[OLb"!@猏{q#Z)o zG o+0zpٖܐ\ А@\u)W}ak-ʲS@G 70A).7sQ9nU:zɅ 8H$FLƗՈ L=x K0ǯWA4`ƀLzAkQ ugpZ{q .PV, jEV GA 1Vt@M܋1O 0-<WϛGA5^)mz΃T OP3ENYmVzJBev_p>4nyqfX?}I]S861FDf48qwxs[sE]vA FVQ}̛};FfŷL*pP_5}ϟtw?ţ9???.E/6B ͺB 'nlD aHiH) K"v<)/ c'rf"lp9HRtPIG~|@-%顭C+ ]pjz9:^_RV jU0ng#ʰN$}&Nw 'Sy@ZSpAWqh0LV*ѳ E]lLK~wƺTDRjΝ6"[hTU-h?yi[3ƳӍ+9E2E"Cjs1[I9KDA5X,P__+c\~->x<}'7xqXJ+o"GWAA ghfniɫgp0}YL̈́w{\\^zwqzvu(LZ768uHkt$^q2ʠ Ak A%B40~ klf}Rd| ."F5&b-P za{@- dEzzjx/laboTڱ@36&$[D@7St'+!НY^,Dd&4UO{(1=*eZ-Q3H?]F6\dfɦhػ/Ǩg'MG'kԚ)PW5)ěF9fkC$E.>2 ?B2#Ĵ)&*+r䋂Ï86$A?Ck2#:|=&ęɰѦ=[of|i'CfuC|a[\~z]P eYVU@(1d!d9EErsNp b K;R4*͏hZ(t$l#lqغcHл6Gmgx zxZ#N`;o܀np%/L%R5ddX8D9X?K<ݜ?~ AguA@L rM EgPY@9/[\^CB]x{ [2Ԧj s`9,P*Z,+=: " c-ڐ۶oU)c}| p:"ٟN2ȰC4܄&%/ʘ8 cR:2Qu:>.GeW"rA lɰ~GQ4B-2M(cG3#,ˑHyw>|O?d蓣oy.рG ]^8ɑR\n-4ydCx} lWkltgHV 8;:@s7> ɜh x/!|@r 7Lj M—w"y7$;G2Nd4 qCI6ւI(Jcqz9x;ಱh@Bc/ŶԨlJo9?ڪuAIP"]CNA?yI =oDl" vqqk=)E"(DMaZu^Pup.䩆qP2V(:QQj8qfA|a?S;u ?Ȳ–; ^( izh-E#iFG~7?1&1$gg/ŒUYXMNw40F 4#HP6颃E'64ugbCb/\TYd/w7_ܟGd]1QHDfed[>oϮ#pϣ E3+Ev4hܿvؿy n=iթL-)O^ų/<Oytqzn>ѬL$)v\i:%ǃͰ v52 /h3wdÐ= 1qq܀aIu3fC h -gF9,m w+4Cz@C 1sg{<^-FL񼷓=tad0N`#! ^dTfHP^ E 'QI|#cvm'YwދE57r=-<睃L=' ;i6}^4ԕ9b,!S/j蜌Q{e\U?ѷ]hɢi  zB۴X-WA.kBeLUag"PK PT% cRwI $umXkqtt(ma\f"B]م\-,`{.p?N9Ʌ=>i8Ԉa1wEue㖢)W>jàkrb>O=+WKnV83н~.zt\^F9H"%Ǡ~CLN>TE[gŃNX dyAoiI@Pr.8o Fwr,%ԷupaEEW]|X-WpR=)iTPAiL{<}6ʫnly6Yf۲ۈג]@x1 0N\A2RاNNijgCmI\D8;n?dM< 3M/uitulץ8R:^k())xmA>.7fhErc7T좤ݎ~bX*(|p<Ȋyx@JXVXEvx/=}=޻]_[Xyӣ{qCӴpqtticf8q\=pD2A:44O>Q uTHL|k zXgZ^:{1{*(N*;bE`z6$MCݕlK:a#*ʊz]&>{݆R;`/Lm;(R2iF%=DUUCkv #fFn`bBӴhu]PU%z91j(Ds{p޽{8ZlS(KL] qQG(A{?j4iLQ }xwX}F]2NNd複_**_ĥKpE̷n64YB$e8=E @8![DBqb>bd6pTBBvQ :^sz3DT ydB aH]B0 p] ͵+p`a* e#3,VkA(`J%#tk.-ѕOל[a֞MH$m'Z0cOҴSx*t(J!Hѧg~x? :gj%j9 y}mQVҸs>ڶ mެkܻ7l 5waأiDPGqzxCĘ9nFP:cpjq5G&&~PI9U6 hEG)cٵAPwϞGQo)30f6DƔz*aH\ޙ3|ͱ{ IDAT4CcPNoȈI Ψ)Z&LKiA8.t좕3sH 0t,C^d򪟜b '  Q7`Ð:$p 謈;*iAYt{[!vd7XUKDTm-<3(&;ۻhVK̫`h`ktsfϝÉѸT!,PfVKX'Fi@`IL|2LkPWPj]9b1:0@jT8E9p#hn=끂 c0\bt<Ȇ^u8r!vúWagg+u,MrpEk-]p9ڶAUt!Ι "0gƱ5X j(90ݱSfS]Z;yVzuXS}7=ǝU|JU@=k貆.4?s誄 , a pd1̟ɭ##A:1qZ*s< S (VRY"T$41KVہ5E2P=NN#0t#@--f(Ŋ`HB@77d)B˃" g E;j}GaRAaZe=0skTe(Dtl\oÁFrHHb-'5A^)u/CR!t0U5GӶmg z)BY4~-KhkN#?`㏡.+0D* 'gA)$yA340YV\b|(x(B(E9Z p%pq7kT$zj6y$#ab`VU(ay˄r:va$/llRO(E3-hhM/{a4?3cwLaIHv!wC#bHO3y`4C^D;m̝sͩAL לD|(ΦAY#Z2׫5] W 4kq^|Qtw!Ӕ4GG "cLrzAzHA(D9PsilL8a*>.``E:jZ?qǔ>/>)@)u-}؁d:9?kC=`VEr]Vf59E{=`}V@׵pzqƋgI[G9 (x@os۸q' ELS,yT1ք,Pk= '%2@banO#n(mӆ>|Rz=S^F/-slNr~T!Y=xլ-) tڄ('ɪ]Q1AO(ʢg 8S Ci1q>);WMaD/S-c٩̦M7.. UU'Dy4Imwnc6Z+RB@pPQ.eOJT/krtc7I(=X5-pM?oگñD[e/B߾KMI}YF@=I@vX5 sZbbV($LDP0b鷴|cq@S)tMyeQTupa.Ԝg9HL׸^t!n.r2w5HRG9uNM) 4:ڝsppM91Ntq#qXSO6;Kcc40{뺦341ALAnc*uw0ȜQKS-"JKUU*EO3`Eԡ>~?KqE\| W^ŬPؚتJ9E NjgfB?%j#>6PE5LƓY]%Fl(F'2Ip*$"Ih(eX خ;C,'qyHX03{.X.ͣurvz ȃb @ZAR Yke1L`[bT1&j|/e@'P VF(+lo͓89z H2u =P@ImR%kucaFgu+:EьН]rdzptp$RDCJ`emwH1HiuS \ZÏғUb_d0.nޔ/Aշn7񯱧5^}X-cvwQk>aXaE]b BX-N\Gk-زƙ vPŴQ@B[8a MklzAۤsף$4AAV bIooIӛ8?KyN8L~QP Jhe;'F~/WۦA4vn-mnE['Ooaɘt}OB#?|\SlvRYFh3vJ,ps~o_#Ѝ}aSϣo%1罖S Ili7s6ÇkPt"Ν˗/ʕ3ϝ{(MOZ7<{2dvxw'v{gtpA`}?cF VAy@Az]|Zd8ƍwsu,)$Td-g/ NEO*6݆)9<ZU5Ha$kX-u F^0^U*+ᕇW2 SsЬ`b%:hFQ ER k`ҦǩPYD~F߫15O:^ lùu \qQp։~];,nª֔gVmhgr9}џƃ?#ݟמ}g^@Y[<4S;{,.K<8\p@- תO LVX{ɍ,`9 }u( d夌wY~^oR'h憥qoqz㜠~WC*@F{auY1>M1|>9"z"3}QYQ׮qp&]OxZN*>Epe#QnM hl3Ǩ=d]C#߆'\@[73<R# UiPDQsPwl- 3hdLzP,JMݝ\8gwϠ0/J0 ءujL+ősG%$%7j뵄fIZˆGg-:0?z |?.*hG*uY*+10fV23Sp,IWÓg^=>-l\X~ͦnc! 7`}Ƣ#Uk0!blNw~ c-ivqe'م4d+9{l5qZz@r%:{goC޽Cܹ{^9+報Uak Ξ;sGG8Z!BΗmvwwcxϠyB+(B"{,CYaW֭ ѵoҢUa-ߔ`#R ^@4c9ͮDmma6ƆܦiZ$k.S2(4jR m2;їvАc1)λ<eY[0;XׯqLQ \Z^QOH2@$ܶ]HCgb!NJSPPh1h7Yd=ghRv8xHwyj{q.ǰiiA"?DKrP]Xt fEh_uUbgkT;KytEg;֋F҅k{/ʻE^R5Mx ]kܾq ?;ݻ>wG`((m =_pXYQҝ)I!2Vޣ_ʗfxBE]^7XUpS6U@j}}NV^['vJF8h,Qxls r7k*[[A#(:i${ |fث\`?{,H䐡Z£Gw؞حn88<(4U+mpTBOL,*F.<6XIA)߃>-&R?p:6!ԽFYhTA](KhbXou!S-bhQ9NWZ{{cs h#LJ{NFhJX]יXK s ,0h\P ku^:F Nl`^mmka*aT#NV0E=^[HUԖŀjeOGp.ܹsK 1L;Œ1I0`҄lB4*Tñ-<Ͷg9+gq1hܳթ4-bYUzfJ<8\a teb(pCFI>$O]xS'5EghIOE&T9mBIj;xkȩ_dutБMp~ppwljW`STo`$BBǯdߧ JNu= 5A(5$֥b/HÔ [ovp: {`r>mbS#"ἃvvR&:9.93;v^uE[8db 21@ʐޫ0Τʊ)1{`bDfldk* @2tTv^"bf>{+CPV1|Q.>4n]εPJ@uXy+s 1 X<>yW]6ڃ5տC٧&8wMM,І*(?q`sOH;?1ߌƆЇ C kx2`AƠdFA?j{4%;haj0 ey1cߟ7-(LΡ ªi3ÕF]W24,emZƙ@Gq͚ICVUΟ?/n 9vz5im@)06U@Oam`,UU 8\yRJ_DA/18}%s={\,\py"MY^oZ.鹿&@?3`߈5zMțҤS$Ὦҙ#lUQh13(j. qs6T7I⾒XDPJIDZ2E1sQ¦1 %r_U, H8ޛڔ9i%:H{.uAwvJؗ=f88C`/<dy| ot^q XF!CK1lv*cUzt_A>+@)B' gt?ߪC)y h7Ļo~7!ɯs򌟼6?{G?͝0F5mQ֣(*]N.> ,r0Sx/ǟ 2QN3Ea.1: Ff?$jh3nk) k5PEQzVP(K= kb:b/ltLY"+I gw]T9em!- cօioW>"fƘtI^ڶbX?N˓:̣=WsnA:yB6k_@5j3ACL[3٫ܹs8{,r6 @NA+#T9Ug\*QX@|@'Ç,M M z R67lrC1#*g7hzF'8[!\]xTމ.wyˆ>O) jrЖe|i%$Aqu]fv$.nU[H#V<(;bakl \q$:zgstIZx42y˧L@P-eꡈ^<  Ks<07L}X@<^qq LP+(c<h.TʼlL!x7O\֑]ݻ_ÛocX& (H.K&@wƷǞzwG{> ɁA1JBUn­[w 3Xu xaНk`yw;cs'?^|<P@")|bbGuH0bM/9`LT8fO8Ƅi= w= k]ѪAJ JA*6ru|@\;d Tb 3s:qUT/[nLgϦ4)Ddd:  D䨇9#pv`|ӚE"\Mg qlʬplX?9m ~O)M#\~7,aL3;;"'.EV ֥.+pM'/p*VmjR2-P><&Ul6d *sx s >fI)(ܔy!PdJNĸY-дڣóFVhs C (D=̪(9hlU%ڔ(PR ^h *N;tд2 hVb{شIi:XAgǭMuCB{v ׬Ziwy!Tѹ'aYTrCNOD! 4|o{fB,$ QJ#mF1NJ%U^s0!" |:DM߲u ޒ}S 0y0)TNxR-/-1!+Xp盟#+(ƶM IDATJC嫯/u&lA3b`)TZ9muo7~O=$fWvqTބ 9-0P (õ7?!f}LVnݿ];8"b3'\n'7qx땷+җ^|`fXd-.5+Xo ;YS Fd+B>i:'y6ySjH]^h"g.I5d φ?qnq&Ǩd@1E sO.hOm(pkiqqnL@!Ud8er3>GQXgȁe㝽!c  k昞Qvbd^#SPzˆ3AC0v>L4T8%[yZID^=e%%8)0|:&guܹs[n^1X5 Ҷ-FlooiZ0.|D0[e:4jyL*JLWY.Q8=壿uK!]u_aÉ !0( _(ǶpEYE6`vк1"DGlko޹=< x/¥ nA,N$89g.Dt{U #8ǪƁy a.\>~)7X}{JQgp8tm#faW i4s,@k-k\XdYvB~MyIl֤4On:(u}$d {p.ʴc)A7^6^~ep`7]<{Ny/HBz|%tȬ9T1ԑ4=`^4Be REFk-N}U(L"t+Nk;tֶ;.ř [4S?f[[x2ww ]Т8Ҙ9t.Zkn}g`x2 )[vI\(ZB,qTMFkMJ) @,ٔ6Xdʲ{XTrI89nTᑘX&¾K2 ¥pݑ9]cUe4T8"a*jk2*FQ( K i{`-ls6Z7>?>O98H>'ᚊî~?~|+_ūqݷq6TAA}-\b>Tݾs gvNh3sKy(t >'?KʓWc|k"[ }mrF箻iSR\ӛ`Sﳏdž]<7yRTM8Y^O_ BYva4F*szKJ{W5M9V'J#=\wof%5Sf'aoS<ګyOy_Qc&1@To>q{mlmoܹ3xW_w_tcVJxܥD'p NYj$-yE;Zn ohxh# ºҶhӅ$ګIIfq r6%kڬ{ER 9yR)|(zNCW%i \cufh]BSJ qC"VbZ2c|ӊQQ $N8)BSyB%ň? IWHކ(8S(^$QMCA@)zxwkhZV(Q0lxX)9Xrk>č7`w|頭t}s9PfxIhwo⥗^l6C3~d >.mih}kPx "jqtZFKF} ^ke!(ѳT6:r7H*ńŞ=FRq#UTSکxAAkykW\nF&|luǜhΧëLQYz$iYuo'Uf0akyrqvͅPU>5g4$N}?Y"7b-4EYg@׊~c\vzr< <#GY}U>/G !jq0V+P P>N4&6I+$g 0g6 vBvqz6ZE蓌T!\F֝hm*Mfr?Ή<8EIVH*FV*JppK8'W^?]V3ڠ;" 8Gd%u[C$P٤Cg"n&[u8u$Ox(2҆P c<밁F#hH*Tbpl ŀ]xg7ĵ?zB׶!Y~nIrV1m'@/#hA+W_}_W w ?Ew՗'9Vz9ZE(Ds<9ѥ*65 C{G}{?+GK_ij=3;"=?*'Y3 9׿ ;QNm"%f,j *Z@(el O-"/ڲ4*؉,ܧtc ƦK0⒞{Pཟ G%ͯ} wnիO ƐRڶMyὅ'u*nʯՇ_TnEynzP8x~N\l55ZUd%gm6 `[sඁVUȠk)9(qb@+[̪:="hْ^5҆k7PT(ww :'@gԗŏogTPp(IJT®a]ׅh i1`L>ekYU>Z }|w ^s"(4\+PyHE1[/hyɂt{֌3F-:9nF`0wǓaP&+\FJof!҃y0!ڜto>1Li;m\K֖pGVht,cF#EzZ=T Nt hV:.P/2@Ƥ$3f!]*ӇFX?F1%bTŸ9Ee_Ffyq1[ujt56dQsخprr6bAdܭb7!XtC.n!w0L!:lPL",AI.) ]}şK-K}& S5ApAFͧ ᨏBtŇ18UDxǯ}q+pmxW_G緿S8TK#]*]cf,,\Z:+N@kpw?os[gvpsχD<}H]iiMAj  {.㑸<1܏h咜  Hh8R)OApd |. 5m۠(> DcI:جYK?Φy+loG|"!5)Z+v^.ZSxX8~al\56T< &$6EN(p , ss\Sq<6GVZ..PP!cátay FUG!i$G 6EYQ5PaW(gYk჆9*8#쵏:?b1sHg7pVq1 "pc՛R86sd1H0y:MB NC ?4Y+U#ysEpTb^L3 HZ !f[[w6j#LZmY1EČӿxQ6ZJ!CA~ZowE .L2Ѧc1U5e@Oq y1KΙ> gIܮC'lh)lPT ˷m_OtoDŽ,_tzaDpYi #VHxEF1h7R4Hl 7fP= oIo h( UHK1X1%6ܥA\tda8,vF#k2,|Q*HR@iJ;4i4O??׋Kf`qԽG9P F> 1!̯VB{qCOnjBHϴzxYe1 85t*wpQٵCaN_e GҾ͛@+n)C*rY\ 5?2S݇IM)AN8J]Vx5<~I.xc{75} 9;7oO[+V+0f/qqn'Jb]֩D̤{٬D۵X ܼW~׿7`YZWZI!̦`^"6pv=ֆS{Np"PH رp3&G-*V σk'jxv/(UD[Kz0+L'P>-xN7wdAQX-Wh5LA. J uykQXW;'=k">6>E#n\hn]!~wfgQU%gzzN)v-LkTӊ?r+oVH ]  96Ɖ7|JW1fU$x>77[/cVZFnjst+.F]k7Q bZЬz0zs)QXtq9@BgidUS.䁅G蝺yDE>\i"70q,&#J+cibTc]r}\nhW h@]pՍA |r/YLPLz)PlEY, xc39˔+lJF%2Q>gzX;|=xװ3tQbVmùn'8//888um+JzYE#W h.("ɫ#R  Ľwq9V^˭؃TˁlۂFI@#(vuF30D=h5{H Q8='4$szb'XF= ^\mJR>W2m"+&3)q'ގuH7dk#ƃ ZZ>ۭܺ1qED0HCBK4MC Vk ȇlk |&-R$5&9xpw'z?O/|m|[|tܪP:`j4Y} O ij,)F%H2ZwesS 'PYtCuX-mX[UqY &Q$c xYl1( gmzUr؈3rT;DuN""LꃖGIFZe4Fڦ?|2(s6BY ʼnu[c5xs(;O9e{9>P3j8}s/s8njպF[.Z,ЬhWk,Ь}{gs [sg1S|x~KW\o~ }!֍ťt3 Ix߶]9 Ch*=hL`.fPK#&ydLO) =`G4sjãp]EᰳXz [d*b$WFȴdĴBSep>JXH*UU TdMbf^ZP$fz?{{ScӆNZ,_˛漋yݞL5|x8/qT6$O}xn]럓 GFZ 0zRՏ9exwtt"| +BQrMՌ%M$L :dcg6I]UD_ᅬ'y ܁Mk=5mT4fm>he_2wyU]o٘RBC"?~.?7DXbocx /.0^\?_8D~e <,>8~c-ro](_/{pw~70np7[臟l s9NPӹ?&?PJ5+4_>5">;drM WϩnsFZlp~ Uq1 D NI\o RB=q8sb26: $d 6 V5u9ewcح7Xٛ(DSaSNs09a}=>{0pcc\"9w[`{>5 D(쐌YƊH֔8Rdž}@2.Qs x??80/̓I S28!&R7a*JC{댹 PpI5CPҤ@s`uɶd5D$v& ''~Vb5%Emag/kU9 >JcMl`-0.˫|XDRL&Hv1\W0]>?yslU;;=#y{{plZ : }?8'aDئ8P>{c|[tx8>;g]GF/%{x=8`bRI$gHBfD#JUD Og*F? =0:2HY2ʏ@У_pxrx~8X >up\ "v]-"6煩-:1Y l{vbСIS4+֚MՎQJКKZ啴1FG},LYR/!TL MNTPꝤi4K v/bbÒ +/G|%_:f" }_ 7)NCO&CƈuD긨B%t!Z745#m["ThlBE !U,lQY,H%.D(_V_w >?<ƃw u@X/Z@ |ƽbu@0F ƔkK¦eZD"jZ"&8#@>ul'ucH"' 0" cяaEbT׌w&^ *S,UcpݨR{I #.gTfG{k-cб&h5Rg"VJ)^dA "$7`ףp_ Xկ[^{}"FeUAl_T {"MZR šqfb}6o;?>)qOOOp GJpiY+66n=e= Jb. gn|!<;Ycmc_ a1W2~Q᥄Fp٨KTlV(_RF#i6{:b^W7uS14aG=Տ>`3zVx~r'x~c>+1.NӸd'K>y;;5%\fLOR1ՅI8Tq|e#TJf[h):%VM]f휴_|Y8&8{?Q\Reuvf?-<5o0$lmL3=.n=D "<L5?6IuhugvM"yc6Ơil64MsqW8q9Q;y(|svV23AXCS,A֊)$Xw۷q1n5>2Q6J 5gsSify[.YqzzZ,='FհZ# Q ', 8kE90i5G]x;`X3vsʶH)l i;IAڊ<*1'w,bQ=z }o.^~ }.m^HS"4KGrv4EX.)??fI8X]b Au+u:chk etmw#Ą[GGrqqd>ZAz;KB#w38cAAXDaM*)] {"b~U3Bz/͖S:Q6G.FM&A; RMZ$ @pq"DϽ|)5u<)Y^+a64{mP6o$a)[nG lo9a~)JUE TIwj4[”"$6ݓd" \Br]w[q:4UM(LppH#:cjm2 y,N '4Z5A"Ǐв'BgyV1F0m/Q#!Gu!f0-={{GGqqwX,Rja\` K]1Tܥ )Y.=,Bؤ),%ÀrĔ3^*꼊cUqOOHOON(_ݾip.5)ק3kiq2t_Sd:t )1!fǨ|}Ϲl]Wy5pl\iר#i3{юRX_o??'>ϞC} X5WFYc?Y+Q:ÌrrYtԷob(RٲW/s9$}9bxO/bʩX ¿.2u#z c8D[t2b%7*C)3Kd&%L>d'Foqҋ$%?2$*1cJMh\0`RQʝ,,>.D D,KΕWJ4eMz^!Y4sͅTO 8&:ڒ2VľPQY@bXGV5_P4c9XS1rLJl>% ګiIr Qw,!",'Xd#~# rq4â `g/q3OuDl5_\AQ3c񕯼oO6;_.ه FOXƛߖ c`Hܭ{:wئ Lqizd o,8Mji@/W=Q[Ĕ4ݢCuh%"hxqGu UWULVa  ֚|%&0?HkN[-)Y#1طj~ 1fappvF ,;q:d.α:;8;;)={gϞcHܤM[c0#kdRu6+ ^, k,X,=ڶC{ /b◾ k NOᣏ~Sשm f x:8TeVMAz|$SrHq:g !u1qM<œ텱e}BH u0 FUTnXc>x2Xd[hF!*@I11='Ov{xrjDF֌Z31Ij3᭍hz3*USޓiBUX!V 12-Q:ۣM^X +c06!p*@3HʅI|<"Y#Z̠:M 8SEr]I'obZi~mS&nrubjv8GhҚty`~7-|8_ ٹLfXuso>Q•9*bQU?﫦h58q,rPhAֈEN4,6+ ]uءK2m3!stt"(2e-g]13V k>+$Vąi'{>{ }obZcuqgO>9a@?὇6A c6Qbff#d۶@B)Ѳn:koC^grfש Be!{q±mōk\VA[`%8;Qye>fQK[Z*N4@U&=FβM+f>f\nKT%Жut4eQmh}3ʐurfyZq^ǭTn~*10F# Kj"ltSS/RQigtqU)rYڞ4eAKChl.G SRa31JWeAܙƠѾ[F7.fܤL~GdJMX]QqRBM#"yq:ܽ7_Q2bEH#=~7~ۿ4@'4(q ۸yN%R^XFzȞǞz4F(5>Uys5J\rSIL47 MX ru1i஫/3}ˁ43o!`-kD0=( '''8==f)K76Hag!٦qZ1=H.m!X1bijoWo!]ج/X /,_Yk1A+uݩ\3f)i4Mj7gJ9C.X鿙4N1QKOfiM)j֕6&펦gTfR||"1"Դ3zPUGDM;JsF=!`h-hl H7Dux  Z4# xbJy3FC̜`"@ak 0z$254ň!̏aʮ>3{hy BH` QzYXWq˘i8"{pfKq`urWǗ7$j.B8׀Q`״R7Bj}` !2""TܳWfa2}u#;ܾs~ z̨)%Sm]f@Nߛf#@XGڇ}hHS0Ν7-X_ĝnMb!JDp`ȀbYpp]M-$A38EwHvmXJ9>n߹ ;a10ȱ*\@4] vsSWp6'XZ$ʣaIpS1M+!^2yuc U2Hve1U,:t>2%*_i#xqqq3bZc{=ANĶ+ܹš3)G4XhX::§>ģ>Fv8<޲CJ;o[{tHZ\RB7%nםT/>a6S=V1_r9\rEOc" "deoV}UWsJx8eV04MI F909V(3N4ŭ؜NSV^ؙuR5. `щk#F8+qCl6E>3C? 齟7qzvCX{gϟ#ŀr.? ^ U=S,gl~&"Y喥{t.f=s[8MMf%S3cba߈![k,f$cTd/qUKF4>Q@ F.@Jg E"zU5T|aҢXx)aG`T->pZKNYbH*<^$G&crnksfT0/ଅӘ,JԂi0y2cFw$1Bx)I:}\ȑ qbxJ13a (:QtǤʘ03ÇaF [O!OTh\5r/g<CkϷvf@d-' Fxyn* })yO\&y@(kjV/Q k%}<+>ZG|O}/|0"v{| 2@͜';Հœ%Irs_(ڟ`Fii'[)ko񸤒o^U\W_5*w~Së̝i>pz b)6 65|?o 1l臡LS\c~z]1Fl6=_Cóܷ])%l Rhۡ>~)Yk?_bӯ??x&(XN\Tn^7i4+P-jԋP5/m`fDY,ĈAF h]e:B$A%^;v/!IEuYfkV 8Ko|L{%stCo,+CчaXLv8%{ڧӕ&it=7(])u%qPҝ>SuI&vbcFCu}~a]S޿m6C[LeX,2&:YjSt~vxogpl IDAT`[_w)k~[p\FA7ZkLqxvSփF(hT-p'9l)p ]%Ǜ0LzhObgO`0NƧd$hUt,Oi  $iL`آ:TT)gX(aAd*miX.ʖ>Z^XMjLN#\( 3 ̰I@:>$`8L' WAn? @R5bd(^P"`Y-K߸kOXRR5 =3 Z됫Ruqkl٧" 8}S3+b,[w}>fbl2nT_5Y#9x熀=|{?rѡERoE(0O Z W1bfA60Wb:[O{4m! DB@)yD:s_=Me,4 ĽjNN]J}g$װesxy:A泳U_cꛌQ.NO`lZab=bc_4b2 zqka OvcFwND7MTSB9pFobc˿;hklbpbGܸgWrU'lWp˛9韕 K"Oԃʝ>w{]r`;\6!EDb\ m)c?#]y: R~vLiBn`@԰!R!9#Y<5H,7g+fqf=}~>RϡyՌI &$l)DR06= 6p)bBE֓MfL+.E0[N[o,r!?1lx{)&+Aφg@ eL9h}ғҎ%ׇh L1A@>q G?ɳc40xa?*"$zG֯\'ީ$ yl: )qÈ6)a=hkAG?E؊S Ne1 gVXF֚Yiz '''w^Ÿ"/#/`T E@SBm9QwvBJ7`38tsvr@1N8_Wx]LNh '=J>}g!Nclo67$8M6 VQ$  Im\f'Wv,W&nk5ܺs~~_yq̂[E=8oݪJcf$eYte!8N6 eXiCSâ!.1LbpʑeUOшBbp'+z).p n<(F$GuFf8K]c4pE'.Ԯwc8GH[]l(5̲+XrĔ$:a5T,˽+fc Ug¶ .|!5~Sr A 7kM9SCPQ:1%0BSQFP$DV'(*CqVU(]*,>TG?).:8dXv,A6KoiA @"DLKDɚ0!&4C6ܱ) ld&G$o* O ]%Ԫ==R/*] 3ߢ^22%#  [fk'NիLsU̻.ܢ MA@})l4kRi؈[@Dչq&U%BOb!of I@o1qD P,)iNk* t37:-gv9Q\ L xa ^^(C6}brJE;K jn+Bh&ά\1@>ٳg (#D6j g$ q8Lݖ1f(DKR^<G}I׍3+;KS]Xtt*ӒcvƐ6KOp0u&.,fUq|IVeXƩ/z$;YMVʡ3d;pm;o6B@JǟmA~`4,MJҫZƳa⋱U2ְjϪ)&fIͺǟ~Oï*޼&p]|O?_5?"߸ɏo DP葢呂~Xy@@)JHn4Fk@$"ckh4*#N%$ӅX*8恏t`'阸l($!>E;`BQx(#0~`:MSLՉ(1qf 32jȣ`N2 #Mmp Xki Nf9]aC=ʘ㪅7az_Z`& Q%<%X-Řk )%.ߓy4 @uWhp5;ve5o/]E)ɾ whе-hc£ODF$ Hkqu`(ѬޏttVmFƤG)N:*6e fi]Lf?_z}-|8fƢk18@> HMP=,,>$,);R@{P$UM7#FY\R ȕ)Sp6cbL P#>/,ڶmlj [zSKE4]hl`@`JyK4K,(hŖʉ4$ڝ]zfSJFnk:٪@%0`o8:)< G6-֫!HOv2o,)]64M%G=@7\fv+uo,>`GX[dbT qjVlt0Y-"Tf@ yּ]MENZpcTcM$YkO:!S ZT'ti`W}9u |11FD C { ܿ}<6cp^`=l`JiGe~1 :2l{$ GE??G~W]MYG7|1L`׊[ K7 TVBs /]2u1cB@8J%Đ֠$9?H<5@@DZQ\|fܰYt AG[_ҫ9:[rmjQѺr!ml<^&T7UpNP MP,}Ȏ֩'VS״E"FЯ\.Ѵlp|qkqkdCDFpl9CD¥ eXevef'J3y:_c S#el*7q ;O??GCHKRcA@iR**Wc刍]Ub]k˾BЎE1!M]#c~]g@qw psicvk] .B7pzz'8{69,Nk ܛZ$#5DT6dxeǬ/-B4/=˥~/BS fԒ)O-2%ѹiT-*tC =^Dz̜IzD;V$)E`o;g{wqk0xks,[qCgMVc0K되a Lc bHT)&f4hbG "4Rr{h_{} a0Ȃτ1hLgTylSFb(ڪr v(3494"a_#l.5 B g BK9͆&Avu曐TF iXḦ,:7*FM/aΓγQ@hnݺ#k[fQFiV+'0e'$Sz1ws +&76l1/eL5iL ˒H~UF3T|ҾYҠ]bԽuW2BJ`IW sƮLS>$e6̷a$u? ţ!!]H I+=Z=R'K2wU˂]9`Wyt: ,T\ ~by8ӧL$ [Ѫ4hW1ZkPmCmX3|ǽ78JjLOmF Nb=_T:"&e~TC1=~7̕Aj>JY| #oLB7_c ײtnO/@,$/0" {q?»~EazRc-bsL]WҮCQ.hA>W>ݿ+ M/&#׀S+GdzIc#$:#Ӌ$WFWI9;@ ga)a#|=X>bI;8lZY3j|.jz>2vQͶݐQObJ0uN#ZރX,:8M888#ňautʤk~Hzߋ* `Ť55~Y{FbnVEoGq--/qxxojJhޫ:>, 䢔Lj81ؿuqi1C4-geQF0c8l6 i8O)fwT*Vwyا:=!d2C22(cjs28W1jrcw;#;XtcMP!L1m[F.e&rb5%F398+z:9.&Ǥhu˦:ݨ+iad^8^}ץ*# O=. c4~qbM6u$/, 56ʉմT czˁuh}Ȇ2\Uulhĝ`n$g0xXCpŇ}8*I"ZGQXhe<2 NHggַ ?%!z4 8XX,,aLz#6 82ۜG4jHU;cr4O̻e9Y"X2YB:,ubJ($M`GRО\dZ +DwCk_t:[Mֻ)t؀oyۇ{@M[wB% Ib@pɁDa}2"qiD &LA"EJIÚF]Fx/;gWw^k?ůo4BՐIFBp?sb{LfaǠ0uM4X'kuɑ$"1yp@@ hG{q9 c71"Q夭}e企QQ5ص $L56SPHnp;plt7LvYP*]s4֊Xq0Ů"t$f%j7P@TTꌩ6 Pr>F#_M:]~T!Siz.l8'l%H[0MdO>8l:0^ k`HLIO׎3;& .ǿຍ,Ħi}ܙTY"DӧX.J(fjT2p*cҫAHM,6j@ @Ԧ8boKaqB¸IHqP qX  *۲Ab(ŀf>)[8::b@״97ߤE?w ѫhĊb>IH,R4_u[O=9 ['^8 'xǭ_91(MSף9Nzm$abb !_<K]8 @cq\k !sM?5%0J#(%8HCuS),FV+0o<۶wIi°Ǐw~~3К~CywTmsYN^f;N10<{_"޾?nzhh:PSՎHZ+Œ3SXU&L[1.z_ Q\蹼 !tl%=Տ?]]yWTKaG[CE48Jr,Cz7. Xt ޻_G#X~8q /%4541 cqM`h9s¢cl``,Y+#l 1 f@<ᶈa-JhpAl,za}>Cܜ# +(u_2O!y\qmL#"!F1nx.N]vL0I 5wKPdtSbW&uFR@Zr.xIT!*mF:q{T}ł}i!5T5$AG3+e)s19`G]2HÛocٵhXܓߣ5r[aiժ WUL{QTŮj?KoւfCf@gl6iL,Kf%M2!9`itr5Ǩs##$20IU(#R۲* m9ʹzvϳSׄsl5=هC&~o!M,tZc '1A^ljAFF+Z8cu0pl(aq8roj'@H| 0Y @\x4BgF`FY'F%qD(p3s~4wpt6EmѤcngq4yZ􊋇rjeܲ0Q b:瓲)"C,͠]t/vHW1!7HtLZGiZ"L$+Zt'ՖzO>fÂpqq9W#ZϘɌ[1Tz:}H)9WrGba[ܙYjpvvC)R,XwO> }i6 F`,03~DՅmfbJnY.E >Q cg1N1L #:m.cК.}4 p7A{Ϟ?{:t4FSM6Dx0Ƅ5b01 # F!s XqD$]pm&&4N+f̄8L՝!wV %nކ TSJ茳sVOv[)_$̚эNU#[F` ]hRhGS\U\L@{q[`[ϥpDh2#46:s9~!EAB$# e.V+a]zSD gp"N'bqҾpcoH^\Z]`:Fa)P4^)%aFq9{ Dxٙ xjn8G٪ 2Y`t acacy gpry&8~R b6~(کF@:ϩ4 ";ObH @ <1l$ՆƊk~Y>&99§a4`4d`^&uK "AHt9K,8e@F۴5/8i&%aS1BFH"%X,/_`#Ew1,- "B9/_;cL"r.e2K(2$P?励Yvl$$ &:PH!H5&={/L3{y?06qoIvΖRULw @@0 H-پlB7C-bP@H.C,3Y%3_UyÞo<=bp[:Ũc9藞_f*AXᬦr=}L QU2KoB/8(Mt>"Ákahs$M'eGN,Ǵmzk[8zʰ$5x (MeߏC*]^_@L|+o_Wٞ}~)ܿxd}PJZ-Mc U\;bJ54:}amZ 4)BB"cc2[n1 Ya(q9#\ ! ᢁ +ȁ$w xoh#\Yw-o<~̣X; 7:Ҳi5SlY5"qz/EF/ bر<9jBc˛Ѿ kQaRɅS)@9 Bϖ¨= eK9"͗mw֢BzFu%7h˪mI1⚆ubAY]T46a3F#93~_ :dtk'[)q8 -ŚV?ip՗V?k]qYY/B9xgqL1TWt/p)黜JiQBW]\.txϞ>Ev`Q\:iC_Bf>_A\!M]㤦-JeaR95JG{J(1~.XsAq,N0b8\]hHJ0zO8b 0սk$q M^N3:Q,^N5J'цy씰gŚcG$a c`9ivc_8oUIeh,{I^x`8;;yuwǞns!tv݂ecm۰陵Mۊ0,~ȇ}W~Ь|޷9w?N/>}}L(ΝܘA^O@<K]0 iz?2Su ivļ 8D$]D{TNLcPdJHA cu+T?rHI["/Čut6dQ"f.[?نƹﳍ,5dd5ߋ.etbcT{Lc^֊E2(/[ 1u1~ȣsG§ֶXeh 溶JvQ-5NOʧEEKgq>$?uw#s1% HIM8lyĞj"82&4:%RhhO;ٯ:\vn8zm.%TӀRzL1CT\0 R@rIH=l6 Y4777jn(27wcݻuyv`E)|ỿYL\jM.XoeW/il,άoYH2ͲH(\%"@:],M8$<@Q}ĵa8q0RDb4=y=R'Ad֗8}\URgIJt]Wkn郚k$"B.,7鳏Yo78 X:KrZ?yLK+1/OݰsUq/0v89{R\\K$MH##%6{zv7lZzzZcyx_?|__x+xCZ˫ڎaNkOYm#TIYHS{aTfvhrж+c56m282JbMG4njH?$?;d{$Yb$'儤IMi'7sb#<MB%WJc mcc2 jEF~hAI(-9j4-|\Ys]H4e]Vv;9#FMF|)C.kļ'ENq΃O]+YZkf|_a{o;o`mWfGΫGe(9Pwy,sSʎBI.rLN 5noSm$Yk1+RP䠐<׵q| ^8 ,~Uv3.ݳt:(kE(ʔ9E2nU*uXYxϹB[\_[sEvlܶ3tUʽX^kU+׺̈́d5kW>~—q;ps}$nNm8 g<t1vr+%aDEWEy۱A8X(|wM}KQIgQ<ǑEWcgaEN$`=Q\$E' 0&V)k$gWO)N Vig4:]FV3y:݌kwͻ#iV4m[;pá&bͫs,8C]J|2*onU>{<\S w0t] I3avXבȺĔҒTѱfM,ŃBK='!/Mr[]ep];&&I!F)\>@#bGETm5!B:av ͙uL@ocmVQ^bu63-i'u+1&k#SU3 TuTctNȱǑpI)ZYq3 J̜isML]JaՓ>Ge>MH9v4Z"#@$W+X5-֎\mǫmWpmےRK 9u_i=ҵ+[Y~?+ڮͺHmYGSVO,2W(_k-Qn)"h9 8b 8eP6J)Qa?5Ɣ-)JJ6mypuǪqgM+=Y, M`s@C"d` y 2vC_}Zky풘6*g87G Ay&[lyB\&$ gH/8Y~;ѧ~02b;$7Lcj kAPqbѽeRȈ*cYU~yGLK]NUUn\i8>۶l-ݦ=ݎ뛛KBskCk]1"&ZiY ljDVLm (va(ʟOss]]ywac(|X) 3+|'|gZQM@БhKZ2TUݦSpMik2KX1e9,(Q\ \heɡ݆#p|X^lC6lWkҵmiZlҬִMzq-m۰l9{ptMl[\1oTV7@+AY ֕vׁ*T(c}d~kW] 1jT*`TRaG;οb8?ƈm1x"rL@pv~>|_%|ݪEȽUӰ \>݁O?'$'&EdIx|l+^\=x<co/Kxo[Owyy 1YoqBŽ3kW^ra u$]e6 MYC.q͑-+Eyif+-BKJgfIFF3"m۱6w<6ͭ"|N1kIr|FLޗ G:yr|yٖh(c1 > U_^]'lƩ8&CƼ*)wtc(DL8[ݍZ JLFEP9u+ \{# [9)j" ;|fq9ʿH.նZ-ϥ}Q\C4'0BĚi%IWXQM)ъ~$iqm/.P~;k IDATÏږpdTSxEiZ+&qSƑ]f}tjq9,tfw>kz8.CJ4[3 C-%5Ĕ"]{ [.򱊻Q8ek"BOSom}ZYmB6/nXѓ ı !)q,.FɚlJ~RUxvL d+k6Nbܑݬh\mVr)f6%!P(c#JgEu "-3HTҁk>}js+ ⬞$=3Cc5&gIL+Jrp&gΚPaLviכ\=KnlAǟ|Ľo}.W/am7x|>yZO>߇Þ4z^n0q9;8zI#+juo?t]+Ȣ7ɷ?| q5tbȝKWF{s \-#Ia0ֈ{9fzYhOtuT񄤞GGֺnUU;yS Dh4UoicZ4AD=: ^ױQpE`i # L˩ǼuS4ܬ@NºF&j&u#AkRkc`J [EpUV1ĐOVi S &tgE7Dfas4?u<8# .5 DiT-BLYGuVDtT*qRXj!O~G}D:n{}Ja^c]ױlp.t,އ\,u9>3i)ᮢeZr.^#ֶ]vՕrZ}%MRâ`dtXvN]OיR]qzD Ey_'PYmʱ>o7z1?" 2R;V=# LEV4)2K8?*Gf:ŀrmMqZcai=e# *`VJ*knUe')'HA] c:5c[Duk}6C䑫1ڭ$I֍8FN%$͔+1 (a;ajk=.dXzTH$#ll7͕hVF3r Cf$$|fxpx؏w?_)%~z|.5HͯyUGtw%ZX:&UUX4Yg;]̟sY梌Ry{M~`Lr*ۘ(]*ZV9Qsy/q`zN%N1Mb?fsMbZ ^hc Δ>*AhGV~~ϋj3bG^\]Ҷ-_<{v0z?G7ބw~Hyx׾Wx%vՁ52d?⬡m\XWZ8%9QGn/>G v{hkl ~own!-6"CO8j)75JEbƈ^ B M(Q1r !V5)@cjpd9zϚV-*eP1T 4λα:73b9D)J ʣ* A6[;{~FkB&πfɵU5VXxqu~U=+뿘9Kj54+"Fz-o+˷\2]SFE^y91z15nFj]o8fԼ(N7S')f+i̼JvF?Ĉq~4XCh)cZc}S|H-_ɂ9C' `GTN`,f}]sq۸UsG0RNO]]%юJ(=9?FTJőm,ʤz,KRYU IjkLz=.&lϻL)1san"8p<_"oƹ` c?T I]$JSD #6<؜f*8&/0摸66#`$'% i×z-ǹ9>l74MK4?|psӝSb<)8rq~Nv\_PL އ|-i0~iÏϯn7ޥ8|6qc?,n79NF FDOqq|~&Z3vVЩyR,e5&z`ެU>|F֔5a;?q8QDM@eWS$l˶5lW-ae :1?S8 h͢ݔzeCoUN8DO udP*bSbY-hֱRrV:n^9~rodsk1h0jq*,w\gR")h%niRJg ZA ljYRU籏1[g-g1b8i OakdɧEk٤_ Vޏ7 v [N qS,nKN$j(ew3k>NCNȬh&~cjby(nq==\ TOo>WӨe<ִݢJ/O]WG%<3dҨQ.)`ۜ!:eN0V~XFpˮ@}$Sى*L,d)qJt`Zs\lt ,8SE/X0q6kY ѓ'dqUmuMjMYttbYMi#qD6="fT;萻 qcCZs2\ ȭdƈR1Gqu,055DFegT)E-xHa)mWC[vOyrJhv+lV$w5IiR̄enS֕9 ֮hh]ԂwpRL~nYELAn]T#u RB~}RX ݾ*1;C-j"Ib?qА191mw Ԅ`RYݴ莛n5kL\_K_~Ki&)Ï~cQ5fۭxvء8UXmI$zͯj N'"ƙ> _wqqpOߏB֚a_}w9:cS✡k[$$\}Ϳv´)eff5z@(b >[u]'< Xw.[102tV,Y#Jz HbǀwB$6"Wi}JGq1ĠЮ1DI&j)(lNQc%*MW)3 "3:F sEliVdo$2FϘ<@P^q_ƙ.)-s@ OuIX:.*YT~֑M3Z|އ=hъF|Lh#imlNH ͺAx{bPwoo_1=ま bN;1D쀞B8ߗ&r#NtVMfu-%w1g,Hcv%R*ȓ2ZU󲆨q cdm6b"x"`L^_b(1M$\P%㜠5&&Z%JD'|bRF'IG\kMF,1)| `b]xy`1ZRWW>*R)$D=.S˼K623|S-iLƮ\%4JGW8LB>'LObpGi:1It6qH <:[5blSRpcaa.=fuq]N'==9:M-7IQѰnר?f5ֽ; kIJru#+#N[l*Qn mS?uh#8]JM"XU9 5? ǾA;kibOWu*6!u š3Uqrqt*ظJiڶ뺜+V6cIYV]C: 'c!|eUeѯZhU-G1)rbMAoFSvTYC4J"cn&7ZD\tT>o>&uѼH!tg:ҝE󖓨BI-$&_PI8AS})2R`c%XvF=j$vCň5xe^uT'L ΒR:} !j)ګhS3"GϺosWI:8 z<}GE~]Y^BF6-Cwc9Ԣ+czQHkkl'2a>*bԠuB[FF1%E8(j: Y0|f ,3}^;(jD(,b(vRhe$jEC6׵.xYd^Od &kQ\7S`m칻924|!2-(Ғ"m+g2!fa=^{Glͮ@QH1Fnnv솑y뭷wuN۬ØhOnfE_Џ#] gϷS˺T>ds4xvaOciL O)PO2T/o󬸚0mrn_J&98zbHQeN))z*/gFRhOE5_dqmfmW/ x5׊B*e:FF)z?be;Aϩ#kb'!u@wj1ߐ!&bH褫 ZU S Ic^b,dP@k-782dAcR*Q2ÿWέq@K)Ug ~L{0`Ysq OY;"7LmPp"߲Qwvjtۈ6kٳg#k)w3F2Sס'EV6#ΛƑh6o-RoG<)|c;S0"Ym]xٌC.Dle,].\4Ǟ^s˂K9Rؕ]L+ZH?=M:Z i |]0SF;Mr,oB}Y4T-ؒۙbp8bĊ!d[dEV{9X3cN&Sd=ˉ;ZimXoU @+BQ>&;ιV&$O) OGiP =0qK/]qGJqBJ5-Fi8bQ#HhAyM)拋 =yL?h?y>ݝL_zoϐ'xx9H$\t-ݎaH@M9WZx衘RCĿ3 j֚jKu|)7ׄ[os}pօ]MFWN}tJOZsT>͒G#) rʋCrv2d89B⒡Om+bP$t<*t 6H'8aHė ͢|Xjj=GtBa8ed,BF}:2c)$ *$E %>FtJ@8۟C2YU(HH bٿ*<ҔcJr\i~=IE7pfF%HU n׳)0Jiz7Aj暏? NkYmVf]pբ c{vJo?]x~uh˿޿m.]̳_%n]'Q^!@"^ >NL8=~ 0܏r0(zodM!Lř`l58gYoGV:}FNgc3!AMimƚE>>+Rռqyc}%E5g~ DӘHD೺SZ;*zP8+gqЬ[aP'Iuo}mnaݒB:8~?SoͰp+>}F҆1y?9'0aD^i=ë3ZB9/9}~QH r{I>C\/ ̝l,n-7Ba;`D[ VWBiieO!dV>%+^Xn --…S9HWĐ )рAX茦րVQBc;x"(iFJOsմ)GƬNN,#%)MGj} (_L'9UƤc,I1]T5*2:%lQf{~LFwv6R/61-\oc6"c IDATT=L O?wC']RbG p•4lI_ko!l|||{ߓiw1N7ӏrrYC)k%<ߔ T'|';(,4#وkr$EWop8ԯ+|˺gZE LH1TjҲSC-ڨ5Ag=TsH4n1r|ANɎ܁[^3״Yӛ$.ֺ xyؑz `ZnTDq8:]ϊܙ<֖5:SE 2sQ.XSǗjZ֚{Co=y7 s"c KJ)a.Vx>Gx:{TJ )%vOl-ݞx4A*)J| 84,İ^O3#ϟO_yz1 =t2vgMI @r%k8#g圄? Jjm~i`^MxQt)cuB@'SNkVR")z!Gt;9"AL,(؏VF^eG1( %RKaC†?C%ӥ#Q Vwkc B>V A &O5J>R4FUX !K6ﶈ3yM+sqt2e&:$Y3ϟRn,p8N)6 t獕zc5__h>aMCcZ3b{܏=]OkaqV7)'ن?NY663OqlIH)ݷȴ0FZ;E)kH~O3gJ ֢YKZ4l=ջRLmil4W77x<)1#Mlߒ1/^CƊ~>];ZL :[ e'\V"R. .X]sS\3;f;~kZ?uBg}-S\E.` 8'Қ^{ eʮfw~77 γ$>hȃ5(=%\gd8݋_90tMՇӧa5jkӬVai6 VT.^57v3e^c^>xeq0 mkw#WWaݼŋE}i 8?WƤZk2B:;iLL/&;8GF,%dfM4s/3RV֔7&ФDȓ4:Ow<h@7<k[d>iMQHj :;t(0&jCF~9FO<>$FphTk)bBDŽE> 9 8irBNO?\'9uQs}ÍM471E^\];ƃyq5^:C[eLɫ)a[R4m6:.#rP׵DZ(:y ]qHtU6ߨJ\m!x,Vg|_2 Mlop.YW1 5{øDѝ(V j橉"@um)]taޕ)xZve.:2%VP}H?hGӬi\d2\]^%JJ:J6Mfݬ1Eg| xsc?rqYp"GY'ljtP #c!K.ּϨɸ6w% װYG4fI<sdZ-^%Nh=.ⵚx+c4Ok8AO+MΙ9BPygNcZ /d^6qG׶\/:W#F2o1ݺ^y'Ӹaxukx6G U. _pEjcѶL&wQZZ(f:L:~T-8VۮSA0|b#mT1DVNb)j<2^_qƟ_ɵchJ1a]+s\WYU1-Yan$n'FQJ[|z FmHӢoSR`11rqOLAEq a6.E)YlR5Y(]A'MZc &y99kQYoǧ&;ku_#hM; :G-I 3033(5F @Z/N\3ݧ>pɓZǘ 2F!|P%hfqf wU_uH:ÖZ0Qi)NǺw~ciTq4s@a^HGBT˞kPc&hџjK7OAr͌NjR$̛UL p q:B9rtŦjk~pi[]y~5j;VniPŽvs<в)~.][hcW"lcƇ{91M1puyI0 sssJ둘ba'9دJ5ݞfv~~RFژm3F:b(km4jM[ j;tMc+][j=’yjAv_:"'Ԇ HRE"64ZP(Ӑ0=Ʊc9H9e v"RYL-9/:7\K'+8>7t֒zsLa)OޏH)CF?A0:b4<ʢnܤJ*vn;Wr9W]5]l:vB1Ϝe 3YK\ySަif181lRm޼Sn$) ǾNhk )xn H):REg3>|$(@Y\m^_p^b晎\v;ҽ8Y)/3+77u4ҭ<K~ٗ.\8< sݥk1w$fisl1眢1u`tzǏ~.`ơY˺E蔡 !eu}6.,1"`+_'q\]]UK)"J4ĕlͩQ&vSgZ1JᔤqW7N2XNp1wVNsp*,:*&ÂttO#[T0:Dfq%F. - 8R)c2y 1^Taj߈3N5~?x(|gx>f}?^_GSp;Z \(Pƾngp>9F>#!N'x~ypPYxrJ.뤇3ڤA)%nEcd6fZy@X5\MT޼?x7_{̣<Þ?dFšݮ"̓/D9rlaTB /fq٩@*|Q՜a0u;!Szb?QeP.NMiCj`U0 ).Su]Sd@ SgF|lC.mq|7gͯղ 0faq!U$\pM# "ajQ'ZbA2 P".|d$@v@v 䯉A/PYȁ-,K)")Q͞kukʇ>{F>u=gz>aZ A_yιGdQ~Д]>7?Ajgbȸ0.<Э:E!fghgê3}V\_]ۡ#ԁa/O-m|ψ `Ԑ."Ä!bDŽQJ}6c 5^JBSE!d"ٔ-Bc׿5Oc5TTzB,˂IM]׬/tMz;.s Q.zJ apuخ~蜣PL uQ [B΁عz=T@j[fPYUTUM^TEY&:zn~$= _SRn?B:caᡑaP)%F>mH9{Kw9C GDk95)aEjda~5}1e˩euSYI]q77OH,h}I 2p2q՜)G 1q2)gNV%̘Ab-2P LR`f8tl=ژ wgP",>>f z.Mt$nR]~X,/EU>Ua2H6a6b]q8iL_M$Wܧ 7LCmxw}bEs7\b("`L+|@4j"ĆxwOK&|4 ?s>1E!㨙G[[.Wl6a*1u]_a 1wER @T%0!w_Hw# G|@g|*\YR,~{1fF.Ahltm_o 1k>-< 0*Ř2zAB41v!9e|rF:B"eDdB^SR]ox/)?-5(bWĶ-ł $`H{uI#` k yܻuC,x!=xSn>eܰ٦\C]<z!%TkCYTud2IiUE,V, ei2w8AS+t9E$Qq⛢O?9A#Q'om1"$e0 dqtQ/ㅬe MJ1-'>L/AWs6 "AI*&Dj*. P#%FsTǎ#*bkA$SCf )OoU K"/CSWH7%DB8R3r@AͽKµNn+l:@reYF%!d9 1f=e]"ad3c0V> 'ա < 7 KZ4lvO4'rx&*dlVx6/69Q5GGG#z 9QO#}P@F? o<: JIbe7Wg4;3&i:+I IC%m2N0k,Gz}G~㘳k3{2HBЃ ,>ާ4ޔJ?F"RHpmL}fn\rzz6<8Mc:1kϟ>Fv'-&"rOG5^4-]ΛAK4TUU1NNG̏L)u=Je+GQzfuE]U)8[Sd"%-w*,ZktQP5Цdb6(ul9j̃i q֣UEl[1}qGGtԤPGRX!BLwHBuQ>%r.؝E΅L_5FB2ۮLKE""H"lMx%)3U)J0 imA\JĶG>9+JR u* 4jHȈñvW"Fn!>jS`]~d.$ (d)AABdsDЊ%iE!z3s)36$AzQ)1{r; 0F!zɤĔ:'yBY@Rn%d76c'@1i*瑑!ذ3@^k(tTXin(2g#7;ĀP 1YӪNefffg9LsKZJI H\jdh[it f'TXkhG!3 E<8q\+ZŌq(?I;Ƹ@zpl 20Gǔ F4g!HIM$p8=nw{o닽[݆]h"TP"bjz8mX\\0JB`#$ۈA@9%B.ҽҔH0g -f>ƍ3''LONfLgSʢ`SPB+\s>R?CL Ofy.yX^P^>E=}]M9pN`Ǻ]cW+ʶIBwc0As|URh5@q98'V\Jw!¤S;pm1f*cRʩ ԓ R T@ *Sv2B p *(CxAsS=']RDe__tɬIA3{`N&}=D4Dwn;0"&ˬ$"JIwCU*Ft5eoZ׏1ř)%ږ9,eFS?dѦDI ,>/bQGƚ"x.6zK)۷Y.&]wTa(څ+c>$9%0bID*꺦uzz#AjIQ ;lȌ%R=D-P̏:`z#9"E) YҮ,oc=ȑAp'Cw~N;<ʜ:vg^Mّøz 5vo܅.bm{~-'d ='su!E T٬lVOWbDz] ; eȋ,KJM !>ΣD$#|n >V "HɭwT{j|6+$Sͧ?)fG3~}.VJmG". EY&ކqrd6I) >$l]i#Oϟbp(e0-n顊.s08n->g!}8\֚ܥ>{O/.xw)ʒg8֭[pt4g6uVUI]הe9cQ&Mi!צN yUQ8>ɏ$y\/ؾo}K'K+IYo-A].(7 (#e*I m IDAT1a]e;iiYsw¡MM2h( l(.e, ΢eqH)TK%;1ʁ.ޏ$ag50!B$vqp܂Da@fSF9h7f=ﱏCIoi.u >;Z;x{B;wլ? mcb%s,:y!H\.֜ޠIkm*+`&Qw=.,vmwB<0Ιzfz&]Y5 M5ǣcN>Eb/$l>}??LAy-FxTf1,o]uMy8@D%GSЀC2ý=d! a.W'-bc˸{58w@̱ɦxH)St3RzA ky#`_prrP‡k6Fq)INv:HTa|BNtQfJz4Xj'{he#Yo IC4g_ .%":GۍHUprg/I 5YunݸRmtڳ9 iֲ]\<|>oϦܸq[nqM8:SmkLG2i򔃹;EAULW^{[?k?e׿Γ~ET:u2] ̧hkbb'w]&v#).Q[Aܴ~ Y%9v] /h|pG)/| <w^ tmQ ȬH!vuL&(dXʯ4U1ġ#wQ$9xO;7Q\>-E3[ŐFIߘ8z^0YO{_X>yGT%tGS2w&$Pu.xŗ)uAKi@@}i[\6sm$9)"D. e5H"AUm`fn-ìQ(xXԽ'a&"9{W 9!9b%@[<,{???fW &Da%!9P@٧H%)ot֣ @fr7w7?_2QgͰߴG\S]v,Lvx=b6 #SYoC;l61P uԮPGCvp 4*sl6[d ,c@}V0y5-]2ӽ(tqƣʱl|}NjcCԳ^smwCXqwX+4{ĹdXʯk~rw k}1uNwx?qq;$G|Dž.mڶPl`F"E@n7q KJrZrnl]OJfTZ`T6~68 |D' EOJ EhSeuV(Tz'|!OWKQu"erj`B*V*W_9p*?yo&RkZNfW=GU$@}s}N\IDb#%8n*l6, %zNjj<|۷o sqlk Jtp6Z1Nx̧_LY•Nn½Wyk_G"d7$jH=2\ptFu~#.I/A|Ͼ//o.mM`Czx+x)S(BZ-w.;UTīYVEezJʎ,kz(#Pvmw4b= 2OÜ%FM{j΃ Hُ|H 3V"Rqp$I(lcb D'?3{| \[-~7> ~_ ~|t#N[w!7!ٓWFaG%Iob:~Sc}֨8ucفhy&''TE1\),Pro[|cRJe(p$Xr(1&bF4+*n1du\UU3)kF6uK1La~cq/]`t$؎k6c_?n~=t܏&ӱm$1.`wF)?cٰm̮*K %PQ*%D 1/O[.ySܹ˷q<;ƭrJ] %YL^Mcwo-yz[ 1\Gg;B,zʔf'{DHͭ%v `ѣUaTW5f:" v~*>я??ǺݰX___W0~zR'zMPl)ߛ!(E-)+uD=:pqݲlhV+ma,OY7\^.X,yWY2&TF }:(GcД(BIj;/PhV"T "6Dg7)PyPKtݍx3#z{|Uɷ.2ۿ` Bg@y >~!#ɵhȯ+2LyEs'1qq!LZI^Ղ"v 諤K!>D08 !fgmL.ނHNZ/N> ,wCsRN%2-8B q'聫Ø);1;!ȷ]/_]fUxzR\Ր:09Ҹ^MyY㤔f+g}si|du'''L&ٸt5rĘY5-tB埡` h]❣m[gsl*-ur%maZ 5c09ʽx]0+i  fFl\@?djb`5Y^4~v.hYq]?1n@b!y@IJ~':?~|oރ{fƤAxp3‡1``,?DUO5o ͤPW5aQ*ڤLEs5Sz:3:(2KIfE0#'ܽss.i~].1uiU}z6Pvy]Ǵ/]q8Q]YkbGV]WpGcC@Ngj+qk˶+dc0?c ^o؏4!6aDBR)HnNknkD }vjsIY_y/fV6O/:P/H9y}m>OP"Z$ q>OteS<"#!)B@ 2D2!QR/Ls4)ꊧ:*݁/L 8,ZK>|ṽ1#'18?F0z,;# R!F$8@~ZF! Nsy1:SJdeY=G1'78].Y.ל_jw˧+"mf^ |?piM&LKczb9{ wx;XCpNtޣGnk(QQH*g^m®va38iV%+9 O#5m#\DbRꔹ85^xd(a%1NtZ1!{o5}?sR1 870EaYo'w']9/Ĉ8*roW"rB|F*HQ:KA$nY.餛D&LNNXO H`6;_?!Ť7z>S)]pԣX\|8hc뢌:vƅ.5 >f,4Re~ ̛qvf\:0.qg_ z5יst {J;W5paHٸ37dMC@! fSnMjik UKt>- oaӧ,ct3t*.?x1ܤH`m{mnJr4?b6QrI6Ay9ϟ`Znݾ. 'O8Z'͢i4 <||bqvӲZ^"b O0Gw?}ňVBkf`sI!1uE;|l)'7(46 BW(xfqv9!`"7Z䀞YLvRZkL& ֢T*% SrMt-A$Ն(".Nݟ+|_6\; #FL{eu'#9Ϊz7ܝy|Rvc'|윦UKY dWlE2Sh].£Jg'*|8;;5ҁ6ɨp||*u~|G0'&ܶG[Hҩ?Đ UUq||;O?/|(蔰؜?E%|/w6%#pIK5k\8evwG+#Cj30|4 f̮oc(y?@hJEZ۶?z?;1Y$6bGi֪GF#|Oȿ⟰i6)O!>pzrB eQg:eG&{>&qm<6=^Q#kRqd\?<߾0P2^9ݮioEuy^,>x{W },IL~4Ѵ5=:z N+-~-/<=g}Zǯu h-.l@wXi`}nHn[.pn[l~-.866aE 䘛7oSVsMfʀGƴ 4R´`-fVO(./sZ6 z2G21HpYaX+3y Y9j8ww>;,*2Qt&/޽niW [(LcHa!f(N D1blCi*sg%z^iDs0|(eLP3ddH"mѴ Y jp%@UlDڔD.B̩#7X׼'.:QI˗ic,TeMYGH|FFǔI®ys)qj]PJJYl6Ph;DU!6 9t&iHÑ߳rSAXHuM̀vOTcXvyRkB2;Iɶmn޸~mײnӆ e<RFHA{Ѵ+ſ?dZS?Sܿm[V;8YЙJք1a>a7.]{^W}_1XS.]5YkRꊻz z5.306+Bɘ5 F Kn|#fYXBǬ!*$(Cfv%O."$մMS& JkD̈/0 OWrRhY2L;\x LQ`)a?nqqI=z[od2?h/P=[Ԅ5\𹰐YWby1"d˴e: Kqbʖ'N`^(I'تMj&UŴO'</W\.%&E6H*.־͆)./`DMKݚ-.(\@ud*DSXDd3sX_\]8T>iktșAsr<|"ݳS>s%Nfܽsz_7 \H5)dʄlwxݧ*J6D%Ps8,+T$;~Q~6 mDLh "5$Tb7$vD6MCg;\ɵ$UU2@ɈZ)BZ:$ATDk⥛9PRQg7oΛo1Ru-ֻt-|@)ر̰b۷ʟgs_OΈ6nԩ"B?9:&x8wB٨yxֿ!Yl N<7Zqf?;)s]7ZۦƔf IDAT9Ҷ0hZ.$ڶ% +C.ޡcJ+}}|TGsl6kpn,W+6:pDzةM߅MdW xvڽ@8.;lc3{yaR3cCax<>#`=tM 8OU(nqv4lĉrn-uL '#6b.+$UYAn NlR`}fMg->$HU"BĘ PH~SBQ1<"}*CwU##QBN'{C(Lĸ.Jp%Np'MX{Y{tfjyIU|&.H41y#6"E87~Ownw;F6%[칁yz5 PlJULƚO$j[mSN(V`m`4,WKCTs14Gu[N6rCc6m\?=dž|2CIIe* (󺩐 &!G8sԲ$~ԡ<ٕ`ZVs/z^sL84{>BR5ux@Y ]/?H6-LSYu+-2s]{"!Rk=o8R)1P؊`"^I'9- #S N) SO8y֫5ityMfuYTSdEmbdYimê)-:٦R|@x0B$˧nCtsBQ) eC=%H0"g|jmK_2^d>5G;jגE)Rp5J&4R{wز=ki+#$P(> PT/ޮf2/:"ʔs/DN|((AyQٽNXYOC "*ujG5)QRV%*%H 0tF.SZZEמDzS_>L9/޻K%ڧX3Q$]f_(a}nfv]pD.Bw˾ːaKA: !$Ӆ-", 8oZDrb#1(<^w)oQH#Cȴ0REQpd&ֲj[8f3tm}@B Σsk]$Y"ޕJSAm5BHJI$m9@|IYL^u1-]y;o1Ns&uYٺD. hx*s¡QgB0;y5Yg\iثp<7]0^bDu׍aC"qgq<^ \rĘRctBu^ׂ;B!uMW`$!W-!MŅ#R/4t'7<}-2Rӓ#væ8+X,ٜtaE$h(ct%m"vTc$Qi8BB8ߡE .$lRP~ƅk]:Ƙ'+VݸA\6<]kd߶70BCJ3 ~:~)8p|ODEVD11>n\$TOHtX .&ssRc "',LDGU))99}l`a:C_ע,+oѕ(J& E)J=kɯORT\CfWN!Fnŗ^eq}vR+P)ViZzs~[ ;DrWdB럣CM^amns<2lzsٿPCn@6Pt>ɝBv9#eYPV%EQ`D*M]O ZlpC"4-Ͷcv8!E. :!Q`~|ʻOA,[j!P!0#䖶-M &G9kW(0# =""HG(FdPxmp>F x.٧*T\PxBEBpv!N^5f ~uwE*f|Hݤ:^8ҁb/,5ɄRrqqIY=zDY(V~+4!,ta'5cX=Dbz-"XmD.1(Rt=,J" F<=g.H1tMBT6po][USfc:yh6f}Ο.yj~J19R}}?vtȅ!i&҈X;1RL4߲bʔa"#2 _Ɣ޽#@ݜcߑ|ۻ4ZTs1 ;:uLBZ)شHr2ڶЈg˅|OK%dT)efs?טg6-)KN;, cԸK*k\(3-u`dSQҨ4}tX$Z $nd=tx18ipDZۡE6;(FFN4l֡o6(hGGPL3$>X DM8Q106!<1: 44-d6 @9a=40Q7 Z_+zjw"C:rx<>.<wz:N_`T\5m4\G-+ʺNk)J脓82MGt4.biC0tzDĮ#v-v톰قT( -ѵlK s2A(6BDE '!$1Q=(qQaC#t{=nqr#!*{l <)tC,&3Cc\b~<S?<\;"&NC3h3C+um0an9rO׶h~ RoNw}NFS&bs?M3&u!(tـwTEli&,/W,GFO1eI09G,i.y`K. 鼇&D .jZ:HECnpm^JIEQ1[QHXy%'+>ӗbvzKjֆӛwy{-43G 5R @T8vx/WPBs\X".)"qocIvw˵WM6JŲH c X!<Yc#&%f7[Uז{5"'f[Y'(3捈s{nDF "mMXuEڔLHhS ĪA(v"MԐB6 Yz@E^7¥.LsIu:&D&2vbDt:;WAAB+4Q"佻ך|^6ngqa艥i! Bۉk~^y:uEZd}qt$#ш(Rǵ)U5'j2`QWT.PV.=z>!Ȇ,\i# J I4Hj J%ydYl>l<^by&eY3O5pD-{9_|I,FK1][mb=|jTJ@dJ)E((5)ĄĈ;cGQRʖ]\++ J B8N%UĔc)'؊;sxi8Oy䌷KjAl3ֲ،OXWS4jA@?ݍ-bd&>ʠ7"+Klxd{>Jj5 h\t[R"ei6D> cr( C,MǤsA<ˎ[6'Dⲅ6Ü[TO7P|S!J![):$f\P̴ E iw>]ڦ,:h% $D Q*$ROSbM^ڥsES |fU9Y=>,\_73V#Bu#^1 P#$Nb+-Y:FƈC 3˗O78:=DP"**z H#ٴN)+$y!U]c`6_0pk/q6K~#o(0* /VJQ͘%֒"4{#gg"˳yZ\5%sxz!9.z~w+kmp +Y[í8/󧃭|huIvuV;#-VTUMYWI(aF>: .M+Cz DVXp'6f 4sotZKIAe7)AeQl7<)N09:l씭mQl:ER_8( #A[IT>.K;66X>zJH*V=Qrnr0[ 'ǧdy?Y,R?\HqCpL%jcpdA> -,!kV)R%ojصHk(Bk11D,'ra21͙LkCAr*2WdPfV-J]ʤk5^!ȥ6@ ##2gs#&8|CQ4wp68)~fgsM~@O}5\sٽ٬ { A]VۚH610kV̪g1UL,rXI) ^, Mh7%G^N/bhl oڷ6ӔF+Zڈ4:VB' FBcJ&fMQlMfe-yʺsP<9a~z1ML(`bsF-=I )> PۚLxb h!B%GH|Tq|va"Y-7nf}l^,};&Mmܵa9F6gzaL rl>{MT.Sמ\{Yn 7ǿوVxJ2cF@JTBPUղ+? Py$6əhm$ 4q(~Yi 6kS)T*\3$*s !.>(#|mGqwLw>@gvGJͬDȥ?64*grvѿa2_޷qw>ËϿHTIcBC"8Q6#IŚ'̼h"$e|b6ky הrxt>u]Cq Cz~@plx:lMٶ{Ƴ)EGgP$f*ʣcƧJsc 7o>)©RM.WB &QK$4&i(7vC0y=ܹ;oacF'ɇGu]sE)#EHXulF Br _2/G x㭷99G^_߾/җ_O?OTeDZ m!Й!ޥW,ϓ:2cȲlB 6ܸ7o[prtjzJ4eٹEܤ\ؐc: fl:yUU2͈"L/ ea߫\nj@Yn2,t!*S+pu`Xju1(] -ȴ<)A5<*/-#E@=0zrh?UhXت$9A|2_5rB:" ĀT&1#DƔ\M( _gc3;pvzqJ#d@ cFLNp!Sհhk7$J/QQŋ?-|*R&OyJ |ooؿdM߸ ú ڪ =KVS4VO#֋~NKE0c1%౶a՝5!V^X$AhFdDeU#uI":8/3*rrZKM2-nɿѶȦF.H&A ?qy5'Sј?fR#D J!2}DϠUN!w_{w?,3\ IDATpzz+F" (rW<-q[>ŷaGsMg%y*R\rBH6 !Rnd4R'툐Jƀ4sdžD%Bąd Gg[>}7zxl6Ed2(d>UY2_,ZzJFzWs޿!GgS ^+}d1uJ}[JbxFA-nhl4: B L 8'*"mZ e p1p2>'g'9vid;gv'wG"]k:1sAV_~WI=ů}Tu5bK~_V?O_#4<F"u @L\,X4h1 !r>YΫ[pxW Gx7q|XXM aU+Tj8t?z'Pg ky[o|4^yNl D \Tvb!@giYc$Q@)~d6Z맸N19!D 1SFdJ!y.jw\=<'Xzc70VyM BdVXR򺠗 w?; S!5L#[V [-fos`-ͭ>ÞA ;2M1e݂Fzlzy^ث@D#lѶKI#"Ko( ĄK`rElK8sxxȃ}nxo6 !uP\*a1[ezDnvу|^A7{= 0Z?'2i߈R<7"-!%!у1s69C)E!pbOjlѪ|JC*I/Dg9[=lQW[pH'80BOzE V\(Oo)8 `Q(-0E6O|yAkxGU n.o7&62m>%3q>֋}_—=nݻK(99s2CRLFGDoTZ9}9>>/M9}"i Y*Uق;w1/ܻ{>)>k|ͷze>"/<9n: Z{SGKĢd? K,H"'ՂiYv~p?eF#\/B$~G,n-r=t}$6U'ݞvvׯۿ| cSUt:!љAk`\ ^]cNn})JgJIb^\03SU !Y#cDuUiQb9G,V2P1{HAٸ^I|e" gSلX1r,sYrHoP \*JSFI3Fۛ66yN4Yӆ ֲ7c]"c͢z KuZ&Hڠ=~C]Wq{"u6oG;,?pQՂ<3KvH#Tev|QdD!(z9:̝Q?dQ{\ikVo3 oFB$dfn9)kg<ϘQxwk+{\v{SO2"$>Dtn| [W9=>dqrgGTcFFP舵gx/ D@!@ &"+.цl8{_hLeNV3Yư_9~竿&z*98>;x1[̉RL? jd3[%-89w1>S``0_W>^{ODJɇ|ロ *Cy5.lh"e4Q*NsAh!!T ,!,ڢ%LkJo%>bqʢMdMfp uD9;90r4n۝^c?bii b ێɨӲļspE O{U>їQ.JO9%:ŘCVI)#O<%ӊ[y3'^?_`!'0 ˜Sy;gk-]gf`)]&겤*|mNq:0MCrHzh"zL/cR|Zl|$"Ƥ5kYK[ش8s{T1 #ЕY?fwn0EGSu&a]ciK8Ǒ5ZSz'VzNO9݆tkK ,C{ (mH8š !Rm$uI^!q"*T8$Q%slGc`CNĒA-"Y h੤ bxy_4D:vUԚ'/4F>.O.j]IbrvFќJGwLJz8g}Tu{bN v9kvOƢ3NpY>%-6p &LUʮl&hh|Mw )}@Hc4Ey580;ׯ^W~ܫ>:Kil0'pk }\xITyG8;9%$l!ֆ}#4czYsxrDY-Re3z!YiG!YǕx[*S()ɔ~C+-6ʫlml1vq&B0Jw$P˚)(ɡZՈU8NI 7Ž==7/$9\2Ap:/9xm>8:09=nDMH5U*ݾC5'HW\;tgc\}nwn-4YK=#)ӟ?FXƌpG<+l6A!F(ĥ\a7$޿!o {)AN5̵u@ʺdh{RH\/|뜜LR1.v(kƝV=F5e^`*PͭM"O s<'lӱVJ}{VZ'mt]XEP괭x΋TnGS]t0O.U:,-Z}s쾯ep*,{eF]opt)Uc(,4"jɴKs/MLyb {':5khI/sLP4ad2d9|2?xn!Oժ3i&WNia zk{u뎏 Љm6060;}Kc<ԑk6H*W 8S s3_\ F ҾQp+xD>hTHy䊍1ߩ̧cS{p]?()M]gkl+?>Ν;llo{_g1pK4W_|? x!@XXXglsl]O"?xo!xVsr\gdDgrP9A~u{l2"ٖ%2([h07łYMk̥KOǩ+z2!$y@n@}8'.ŏ/ 7o%prQa#}Wy֒>ȥ@Ksv9*O$ׯ%:f3rV y>>Nx)DMXL+޾56:OƎy'8tĐ~ts[JŢ#%`mĠ@2>)uEl5ҝ@*J(S@߼7=sh[9."ЙGSKVcjRw`w䨪ٌj1JQϰ;( \͛F#Rrt|u{{{ yCxǨ+`ٽt- OJNsF)>+QR[&R'k:10EJ#KO$&)>; 5Qb_f)#U6B:D4y \YGUqX&)T)DijA >ǵkW3EUN8:0_TU4S?{e=Bc>"&HRNl%4^SIK7?)> Y[$,+ {#. ^k*|,"Cx)lFrC\ M8\ kX[ٛ:q$7|7\2D9F1!w=`h{N9yF3}$ꈈXz@|W9!_gλ2i6/MZ"~ʫg,# ɀ-x211A(2Bzw|N9\3p/Z yJ QaSeEWHq.֮a(Euba#9[ە{f4{g댶zgHHVROf-;!D EǬ*\ ,!sThQ'e0U,6YԎ1B% a,>H- tv&s\_}|b&A{## \J`D26A.ii .MwekFm@| %?JPc̠Ja\tṙw?$el? ~ojnOiXj;\Mgw=]vO҆>]z;=.y{]~Mtƭh+E(uO 4jiN((#FĺW"l¥Sd&PT$m *ԖhHPuEǵko'$bx8;=c(pttĽ{N@lޏP` _,Y^}5\]B:ÃD)cw9ق(ll|`K/<{6J$$m [lnEY.fe`:R9ySa&(23noW 2y'qN9)q,(gd$[&/p, b!86 f.R{bHԆ,w[U(*y?Cooܜt91S"rMr/xfkB1 Vc /xɧ9.g u,=xyYz_JA-K-9}p·uFVg0]KC}&p'#JiDtHh2-e:hyK 5W`*7^to~W^c>ÖO8ƍ,yr<7xw8bL&bHE*$T$Nl& IcU}ulnmyB<Ԗãcn!(9erv:i0}U>zYo"c/cx)NTU{rg~> Go0Xqwo$|] [Ж-/X=*( ,+ڒ:(;:(D2$+zx[ޖDk &N)\VU Eđt&!ן7ī arƧgeICL()'Lg$n\kB(QJMkrIؘ, К,/!eՉG$ezJ&uf4Ox+RCG#>.%d&'/(8oyERB($Q(lBIX7q.eIn,!(Xԋ`"k6̐BhRk<DhBcb# K^ {"D`8T=幝 W>^rBVit1)Ֆy"WVN#=N IDAT%Ao>G'3T̃)y^P>}p[a>]Bҭ 6Uo(%zZ&ݪ T%DAR$sJ /7&._a>;Up|.ȋ2o=O /eYɭ<lsgLhh~ѣ׌L\&Fz=LZQdRiNk阷~?{ K-M6Nh *1P!ΫʰZjY~]7t~`=A4+fnV;]! y]w]aGe!D7u[~.D'40&oњi,ʒt(U5'ǜMڰ1;ȬxsT f5ZxHLɗX[&HwYN~_nɧ>2? I>ȕFWnou 鮝s"/? ڹ}NpޝK~'c[,%;;=~!sRJ :LuH)6ZJR Xi}LO]rS 1ka:_ސ6'+2H#Q$^2HmG)o.mQ)3%D20H8FDR$Rud}97$,! u}vvEY%WnqlkJ+|WBxgQZ@!RԑR(EXi$$gdA*&g=G0j/E&}GUm)jdhN0q:^uRW"`-Ä^1@e3ѩ|FtCD+ gOi6"R6\ N"]@bR|dF5BR##B(K^x _cO=p3 e1]bi9)Σ&cjCzـ=hƀNGh@ zt]iD q#Dy6 E.r \RƋ/ł.>ǧԋiU2DmܽIEN  ti(QPyɔ^x/},<Ѣ(zzEQ`a8=FaBeBj1(e!Y+X20Ztf !{ܼyMѺ/%E'XI֬m Nؼr'غE*E3]\.cObxya]23)Бeʖ3H~@|D OWovMj+_x%\--@#>`Ӟ8QPŒI\h:M$"ybU I_G $kR E ˦K'UcO n`aQ#G5!LS,4F شX5;t =ƤnQt" %6L'dpBkC7aM#-*!ׯmgs6mC z 4RiPᥤg2bgǕkOGI씳iɥ]vvc)aXdKYk}LJXWBHLi" / e;TZ!Txᨣ E93 :ي gɢɜX2W qaQ^W0_e a5bkhGs<<~C[eJ+Do\뒑.kn2Ru`UVwvY$1z1>"]Axu=[<x(䐶o]> !@x4Kw뭓"94Ǫ BPT c:ҤgQ,ZS5e]S9GikAw-St%pqr|QBmԁ.rf} RIQן{.^X/S@42&, ֧^H.gVΎ 3?q٢d(QZc_:w5'XerpƂ߶ɋ8/ڗ. xn+/f=D$9&w#^u]Q%TA:f1'chRb2<f$iF^i^($"$:?'/0L(/ȵ!%#go4&q6Rn]R@h&`Bb,k P"ʨ=^FX˴IChCRo+DH]خM5C8gSvu,'F F&A&xd2*?e>(rv(2(R˖mWUp։rE#ɖusj5]fq_m(RܝYLEuN@%($6$sC rN?~Kt 7%F͍ܛ5IaǗDDfeUW@c00PLi(#iZƗyQf;҃~dHHjhb8@c@wuUe^wFd#d`eʌ^;@ CpoZ '9w/ˆ͚;4A[om|# !򩿘%7P&jm@|?} 6*O~wիWӀNN{>ØGi>}'Q"*Sˣ ;yRdQV:TΑRUqrh5<,nf7B!y(}N F'.ħ LXY-o Jd~ &h?b8 pkѷ :`iF "(NWM9ͦ%CoˀIr~1~`L7;huZk.˧GbQډn__n=/  Ϛ*]C-^-fYC U5:&Ō+rdj4TP9#- kXGbf˳`ZHpplZ-ڛ4u6D4f`bCJi쀁&gYͥеM"ڕm\8o)5u!3^wXiC)o->(k7*8Ō\grA:Pf͹E2=\)]W nAkJ)ش?BbvP|{y(4@`Z t1'< >g?GZL 'y 0*IM?G0vɳ0y|‡g]჻ ~ZL.@g ^(NyO*iE>J 9$"x'U S|?[j#PE.C)`*nbٸXMӽOT/20 (uk\8U^H߱Jo D7j˗?7 av"XN,P:̶5<c4I{(EǎRA1 vAg?Ȋ.K^T@/A^}(:K#+h)0oW*@{&͙ nqfb&3Y&P&GiʑX{+J.=2^vU+d$GJPrmb\iZ"^q6!2JX&f}!u'Jm9@u˜z,usS>*lnG_o//?G4}-lPs89>%#q4PdC ĹfW79dN>䚭I'w*h8?b4 3X ~>?J7!Tk8kXÑa3g}FL7%Y\\Kss pż[asm663NNLc 'CQj޽;mV kyn1#Gm0!![|̨h74}PwY_{/?F(5jqP0=P^yD>bD\Dfn#LyZpi&^!DMaNic>H!~*jA  "C]õ`A @/Ca< Gl~G|o}O~At-0Jaxc4Vc7a1>EgCZҗʻ69>"QӘ:1eD;Ӧ0 #`bRj?#ݿϼ̰ Bq@ DBZqZx5DرX׬ 9)F6xf/-Px9&7op8UZk.*BjVNj@ӿ%pF[ֲIn6iZ̤ _NP:48dM0d=t ?}qOY:Mi닇CcYx>*}<0]"x|H}'lgX~АvGm]]9|p` Pa5Ϳx6M̠9@eOf {/ᰅәu o`&N^4hïk[-60B ƅIj6J>Җc$C{ܒ&'v|cu9ӷV u2+t25Z)q Zq"wЬa3QHcqi ֶ< C*fup9 qmj[Mq*V,x: ~O`R!)I12sS!d;z aI0!87:;T˥ޡ D4=19~E+t#A[ M _ |VPZ(_'| t}~7^>@M\E=7XAq8tY>ׯ_7ߠ?~[P3b9%z5K!RUY LXc ̨x@ 4 >'`BnKT2ѭF"k"EI`)"Us^&);mʡͣcCIad}149'MGrΙhpH5aYx,c ~(fP]ccqq֫x؈&7s҈3 ʱ# ~9LRlUYP,I@*m Ud]TAy M0&`t#ixDY>E.><0<㈔!hQchg?7^w~?a|я/{mlXna/l0c2?y>x|G/^gNnuS09GK~mAUb^ xx@ ia caW⟓~r (Xvd#NlbФIiV:hbp.G0G0vA1Fi>9'o%w2Bd(mYNBݘ>(LtQ'rXo@ IDATu҆+~}QO prA!G\}# %WǍHg|&v/}nّ-XAk 8a0`: 6Aŵ65TZ2ı<Jz{?۟`+lo-ذ*Mk`w[|~?:|| ^}@p@* ( @5~W/_}fÏzJ'b((4Mub8qm|ǟ9?_@[  G'RPg:$?uZD! FfiDK3$N7EQ׊<[pAϚAdA8nFY>䳃jXa"Fa$i8hM7wziEX4F|2JBYD!2U}KyUfdVkQ}xAJO$E` %9j1T; =V3Pj5> o,K! pB LNjl ;;)3-17*1FxKG4xc>.Vh|~>~~bۮZp<7/ۗ U縉O~wD9ON7p7섎t*$!r8v);kR5q$2M}Ƨ68v`"Ccd$ n6 !DPfB̀.ԲÖΛ+aj$: ygJq)ahXÇvamҚ@`ҳ.X%*KUCl!v8sHڵr\Zr3.oZ˖#w\&$QZOq*IggJaހ>LpANS؟ p4pr8U]y֔ˑz}Yc_O 1Jx8i Kڽ_|~FBUzW~;;@)7Z5yXÆqе-G;=B]dd4V/9栋{>;)5[yĩ:6%F_ꢾfߖƭ*mVY)=/isrlonAF@r{^;gb)H99jsЕ `9*gK 슷j"88̶'g5=NM)OdJ{J#5H]];U;\3NŴr^v01Ub՚K)`FkZRiִQQ0L>6cŽh>h\"^z_-B8FɗէlQ#tV8M F`9o \cʨhSD6y &&WHsi~c&vb ine%EYrv-BN)~uL"Xr\Cy1|XXkap<1#Ơ' [ؾGE60FMc`ռ|K> (:Eh*܍nkvŗ4i%KpA\߮5?'3.'GjVH#8?W Ak<PF"8guqTeX+6ahuBFmd*H^BP*g3 3|sJ𘤖BH]Wwg_:U eZ5]mYj H~M@~z=2B4AQ"J;c4>oQ\Ch=>Զ2@X՟q &dLH1bׯ%|UlYrdz& y@ɛg ] ^Г{廴',u+6Γ@X'~K)<{of(tcGV*:3&+Ĭ->3ƃ8 :m=*x90N0> Z [IKBq1M yM;V)z%qC$><^lm`酓 ̃L"N*k-try纜{Nc'wS aF\S~/ 1|Kl/sz_ZZhZrͥRsOۻ'[Glwoq8Y5P58{do.&8/ӋySŠʮbibi|YYMGt]ܴ0czR((B3Z>#d,3J"5 ØhXeYXSgTvJnTC"{MӨ$ATŨk_lD}9wp{TUC4gqq!DȉxapԔ /d>%( "X B͡фq,G3MZS+]INi3h%YKڻAfa^[u))an&{l&>u0QK3L &gښd†_@,{^WzT5Ǵ>Ev2! ت8:VHQ3+uM^w֟-5;}-ah=34f_űj_'pmy~v0yͺu<0*Nf9&a.T,Y_#`Q gy_/-4FOQɰ~v| y4 H$,ttBHܦm"Y|OQ:5m ceڶ-ڮi"*Q0Fj,KM+BAc [N3.W_3"y,YRlZ/_ҢW".Ft6ӱr'x{e B\SqW >@cfXV `ҔuW5"(֚"% KzKcz4/膮:C!.]K}Q1CJ 88Ny3]߳HBӺ@M VY+6cU eA]~fT\#L i^J?SQ*0 VVmbif3IK0MөX)|Oh܇5@t}Dܒfp$y<PԔI&Ms)O!7ew}snZKW<.TJ"3i&k`)ݨ!kF<Zw~g7uALk#wGF9W Mj6]:+$W06Vw&,<65iRis |p6yn'jP,nᦷ/%5 n=:Ͽ-=YpO+;Dyz6ʇtsyEH(MZk]Ը xNٕwaC 9*]\t샵Mϥ ;8uAu16d.3[1Fc\f?8X;t] [kx2 T~~&ݐhbo|EUӂ`xu)Bzeqi|,竃&=0rr\i~16gE,Z0wln%K`Q VM)4$5lK Rhk:11 k=9rG,yZ:4=$XrpMOjR3$εqF7ir,xY4MӔ1*km2G%~ε$e6L4s VZ!~øK%S7tf=8F{Nl\*omUʱd{ׯHS''Tg x)N=o^fp^g?1 G~߼~_t_/n{~ժzf,.^L< PP`rk@Qaq@U|:9Y82Q8rjݴƓi#b)bZBnr0•lM0Dc-]+H.Ω7랙Gs 8B6P95 .3"2]Vya2bFvV9Wi }orĠ DWrl.|m*%%Jzam'AU뗀,Pgs ȟ_ O w>5w\% 5 'w]z ݔFyԽ$>:N'0No e>AN''Rk46PٶwVi@P0" Nަ 3кG7R [oK:&M hRB ޣsZգ:9>۶\l7Ǯ*-9j'j`.'La< xz((h*]gaV3#(·꬀8-/z .Yrt3.B/ŕBЫsA!"b4K4}PQA(d^ *Lf 4jy1 e+GXKgyB;X7fk|GoћL5Qf1Y%@Q;Bï=”\K~x,9_7" TyuB{* [RGɳkNoדt`O!2)?m@`#D0˪|M^&b}/]3CH`.eRa",>:۬hMۢ:LSRa^?O r;hm1 S{v]bW[@uB?b1&/ M4/ֲ'9k0Ap[Tl0NcQ=Ud7Yj{1~%._( Xk1#1Zr` 4bXMy1N(w)h& Foxa6b}ZlfF2=~`trLmZej] u:G&1r )ZkV+(Ww9*Lc=T0wr6[OW%Ĺg53Ze1?3 PZi@g#3U3$Yy\>;#r|(+'jv#@ s9n9]?J/kK늿Kd֋K-R֥Y2!JB:&I/VicXtztaum&R6> .ͲyW4Nmwb{ɍpt]K\\87$-<׻`/xf $@V9rac 2v: kxoARk0umtҶ-pmJ 2]h6|]ahdZ𹠙3G~qIKhafa~\Rz~eq:n.Q !mssc񊚜GЁGIģI|2JQMUpUgyhUK>P:awq8Mhw'tm10{rMdbƢkkq{CaLqrpeڷ <8.LfuY--YK7_4.5~Q҆"EPHt#TynA3}Wzl&ƼcU,`g&͗JZ38>-1FBZ gbܭrWnKI<&7ẋēG7k@tYvGz}+ LGe6z㆗%XL V \4[NAǣI|Q(v*>{,J0⓪W|+ IDAT3P@ VZ9cP猜y\ͤC))8LSa~x0 1;CӐ˕10P c ڮ=6& Xoh.c٬ѯV}SCXV>Lޡm58Ygf-4AMP}ߣi,kB̭#Ņ ":i( xBP/%6O33(' ޥS0"DF|U5*q8:ÄmZu6Nj(maL c-Hhݠm9p=E]bmt; Wk 5r*r,Es2Q\OE1H< EpN2.my~եec2>K t5tQ1"h\;Rг|fPnؔ{o#n+k8Yh\LjR&ׂ96Hܥ5re Q:%]f-)GPD 5\4[(y)8l0#BR$‚ *R*NJx"z.<-t&611q XG9OtB0p8O|jqt8Nptv?Ưa銬 VzfA <)3] m#AfKTISTul6 ֜>j&lL7MgSPNa-6-)ӸQ4 `ؿ@[O2hn=PِDѱj1EyBD 1Sm[$wvrC<8qqc&Xqi0 #~]k ENZMd( 呆>,KQcK!K IԘr!S+[T%`"NVX'(e֞;9M>fI`&ci8X p%*v\k_㵀 hb @k5T4e 0nET iZv.o1 ] M ^سB394 _l2o/u ^{* $IV2'+0Vty:(ZNӇT"`.Xy@}HowipI7x\ʄPί9IPqBx a0Eqpxv>&}Y.1XOeG[&v'7*((c ࡴBrEf#_a<@^ȬA&8xrba^mLti[]c|Zml>h͍p(m8Zf)kYcҦ0E'|QxCk[)= |Ӧ]cw˵ftH+iBVZ98&?i FЌ*YkжfvQܑi!ytOQ2"ydBw^b]೤ï ؽ&(Nfuhq.3]cpeA{{/A[T/Arh's_fЕ0]:{xKsz͏-p(2!}^1+Kbuhn֚uW xZP&cLjh,Ϛ1*,}ou Dm}-:\Xs^% r/ Ӏ-*zw[W ;|!Yp (G]׳Z3sʬ4tQ۪u^w0b[NGM׎.Ǻpv ȅRQd KL-rkM m"6zmmסmuCuԛfN^sxnjv`CFH]k |2tgm5tf ZҮ+)pRB@ %אR`S{(_Rb>LٙwQ㠮4]ơw_7/sX|u+{!H]́{_lןQ^pa}aV7fr`kqm R̵.~4q`vs[tB  +9kq7]05ujlMu!v"tiZWK1%pi.]щ(ar1~5Հ<;\!d~PC=ǃ_5C3S Ӝ.p?mwDv84pG~Z,wpR4`7EK'5Nj**iQml,Xë#u+4'8^L55Tc4ZϭiZ^l{Zt]imxyd&mf0"(2 `upl8! 2|-tL0'0)/ii7PX+(890AiZzQAQdBA8yLn1v"XFZtm1hx5[?<677tFDh{6]b5x+7r$GRK<-V-\Sj `nA3SgWl "fWtžaΌRz_Eրj -֬ӻƆ_F?XŵKXȮT8D~2&BL4NȻ qO|s(%adC@~aQUgst|BA^z#üK Y iLs[B!&Xk#R>s=UG6]5Č;kt {.q:kXS,̎*^\YOڔ 0dFKHpFut 䈹L]KgZ_t0q o?⚞unx7V\:}}^xLh*a4E 0`?mqE۾fxn{x7,T]~غ* W9",V>ǢS#S*Hy Oyb֊[mcaӈ2p4l}m,gƇЬ6X7XWM]̊k6ikq N#6랳gZ_pQklcKQH+IšBOyN"k?ʋN7"ϲvnf4#43*טʟחY?VI#fHLIMGO9BUPFWPbt*FmX3MIT;ws_cst-ml]$K.2ap9,'fiJBׇ".UK%s)=ׂPDVH&4IMb *8C05K;f-.Kc-.ZkRGl4Oqր2)O65 05BI2!iuY3796}T9- Cime=#| ƀ vFDJ S60u FVp&ن  opm1yh@?>1q*׋9 >œ\peK0m $(,!0^Ma'@4mi=3&TrɁV -f9Q8ϱT҅fPK=]Y$f_S]=>|7,r]$w8岣Sdj`2ȑ&IFffj *:Z/'M L5"b\$T|b4[^X{,H~6ow<#g7b2K ns7Qϖ"Zq" `kߓeDoldry!>% i:cVK!zһ?yZ2;PkF>,XhMaFK1ӚicY'7I9NeIZG!QcODO33Wj.~5f(Xp8q80 "Ms E| L.2a% !fQVVMe*!qHfuO i4xz|7 |?RYL8RqUP:v .۞yEB#~Ϣ܌5|&"ӍAn1B=v΍c> Mf_V5N]]סX7X7 vט-V56 77{-Vl6Vkn`Frni Ҙ"[r *9#+v-n,51^[ll{hʹ #3eSv1@4d3RFktEx CRizXr{(ȗ%9ltH."[s)@"]>cbPyKcքy)bO" \nל;=b)'Yx6M]0Yޱ*U`J}!އה(k(g32 B ґԭ2ڿ5Rv"~y\u&ȮK4T)I:OL'l-S`ϗM"Zk xn8x8CLJPp@pE-Ọ Y+}ˆ_\A{_,x *!*:rSP8st:͛78C/4O0BCUQ%SM!Ń)z~.xxv=޼yzgX7Xoۢ_p{s͆G[AwqA PL[k "yoZz3Zi9&l~s)tT4M.Β[)Sۋ # \V8Jn)2 `> :JL\&FuVgNRhd[JvJ"Exd1=ìI*F F"y OS }q$ ze\&K-L/!8ZNۢV#F6[c/,ѥ@0|@nqЮ{v:{&>LWyhkϺtO.i$kW;WKɰԹleRx.MCKMQt$Ȕm(k\p܀35x p@c\| "t^[2#Y+[SSɧ5)0@c80G q%sId pG"NeVN ߽g)2ۢA1inj3k+;qQ2<.cYkw!%xUƆ"EfȆKAepg";*jC{IZ\3%̬^gcO#gD _+@ @>$oY8Þ̌3p="rvWNũʌȈp7;vYdܝ?>A3mDA IDAT8", QęJ E>,tgcˆZK,}7TW&@cOO.lE8f hB"x0D'@z {0nM<\WL}y -Ʈе]Nlol!{ jTe \9YXhu:LŘ;d4'L41 Y˃eaQ_qht>VЊćee3XަyqOVO]ʏgw~'5ՋJDénhl \Bny6Xo#0bcT}ۉ5O[ۏj5-v5Vvh=-%(d/ )X%i1di-n,ӟD] 6M?F0 yK;8҇ZCK!Fk-Z.6 uPр ̺m*4PvG7S2M%X92Jʒc/r.M8cDGkkpMI8o>{vꞏY2$ sh6rnAQJ_s|Of^ ?#3=ƍ ֢-\OLg(O5XMzT#ۦoڛẉG)mu}0t1|)r˯;_E8҈߶Mcn# w4}gB6P&\KwXx`bIvPóG۵>լ<07ɷ)vm4{+8 >5ry8{uLb4Sϔb u<*a*U mۀGU2&bVh:()U)TOw`6uN-X,*އNAr<5|{9,vGc Agޡu7h=v-.}Dw=l>b:O@x !F,Fd<FzBd{-$nEQؓq+g&\חWJNՊ 1@ܨ( Gd*kg|4V$ &(&v0F<<#Q!뎳S8dlbJ"s|uCX[d=w;^dc&_U=}z f˗/Ѷ]b,^cuΆC4Z07R7_,nPf}# &gl^.+c`(6L?ǩA5s;5 .Οe|ㆹL]jB~RJU$&[j<@KnǨ#h)%Q8@J]:Ɍ LܽjϏj =݊9( [he{ 3`C/<~Osq_3| *ɹMh-NNƛGabyvK3 v]נo;G]Pdf@Y8ZZNN@VڄwRJ{c0 q\AseFQ3mg8v gg)ČQefzi$ӎ:D>c,ɢU"=섔0m`(Ȟ~vgϞP"x7FPa jxOTqBtK`2)T=~◿gx vp^N><@6 D H t_5s0Ѱ# 80.9G1w[-'63|pMn4b(׻F! aI+r3}@('߇kbHBpZbtɏs w pl1AQ!D0Ik'aBA-SL pz1MďȠ V@l}h@ மW_kvIӷΥG;2r͚S[x! *S[~ alƬ1#m,lcb .>b}נ*fz6E)xLbo(t00hZ?Y|g0wp(z6?)`<P&P8$>,CG+JGfXUt9 P_'*h{;2`?#O"UJ(-mL/0X.gV kM͛7X(eSݏ9 4~d$:,TO1ާ2F0Zm믾[ by3“F-\ AV!uK^S\;)Z\of #8ѝvh7؈12”6Z* lnvlv0?s6;?MUߓ䘖-p;H9+8ؙAթ4,~,?EujO xT}Nr gE>| [Sֹs_91c2Ap)cʡSsDN`E ຦[zR,Y\V鋲@;ci np;t_xߑ}/S,S!\,Q>'AP)G١kZggX ]|lWSs]ᙟ LY nCو|/j~H1dEqQPipjܰ`Ѷ-=5v+\;(-8zh( Eq3X&{G5U]~b R +|JxoQ5ʍqs}/^1裏ԉ9l$O yIRDėQ;vo(dsUGv/n0eQbV` >mr@O/t↟WiCEQ`B%cn]pEa` Zs)~\@(!؇sFC|Ԣiἇ6t0nlhAkS',#\w3Yj9ƑS[OM) FNc0gFtQGV9;'fN&,NY'PbV0g*yx'f~ [번 (_!m 9}}vqڣǑF}Pw/?5>=JABAj>SJ /I?1m(RxG)q:*jha(` &h /=Yn O.:?G5_jt]i0_,X,QU\|Kb3~Ԟxz6aF2/I6A 챂{R<̞8Ua0+k46)0nQEZYdSU\4s(l ,m$ .6&>fu*SǦs{|Hm;l6tPʳ"Rk#4ʵ5'yEڵx|oƩrWk:ƹU)X+Z*h`P!# n;ğ0MCƈH]x|6J Og;gop77W>fb,M~lDzo2ȟ Mm[X( :qD5>S'?۷ops}-rlh ,BMz|)V6-^=Kzh|B۶ };|ՕG dqt(޼y7o^c6%`*9|+VkpFfJhF&,h 4V̻,w`RXCiJmYuY'uJ0ʲHCBiXgu̢$Sf87ƌ#@HQ/D) 4~BZ^`rICPeNc3YfJE~T&1kjfb_tSQ99k L*L`g$F6<q4 8/5  \۷BvEYDv =Xdž>{nORe.R";)=g6 Nӱ'! I㧂'`E M2ݺK`4\bp.\"~"K9m`8Ԛ1Kmeb=F %3w|kB*(gR ed74ΣLu4"$Ɣd=ZR/@^JIз 8w6zG*5b>6Xo)Z3q n?QƉMFVvLp'У=L?8q0Sz(aXשO}ŲEU]`[o0_ W%LXtIrr M`V!2d(icRy\~Ir(ܑ)kVz4^2F%i;x8g=,(TXDػBf3A%fEWI(Jysl[x+xKZo_?@6p}x#cSM?$|0f4F+Y*{5c:rSz&a(owMpIWINZ!ix/vknw8ӧsemRzJ(Uk_aZ(!7P[RZCtaonP BU,9TʀM ng 6VT>90Mz)?L0q{iȅ\񔔜9K)r0KI1fGS(1zQpXnpyy٢FY΢jeӬK)+:)[v^c:?Xܜ@m:;:Iжslqv.ճںBєv( Cw$o($9Мgxv0tS+XiF㻨i:l H,bYkAN+⠓u=)sAwt0(^I$V$T+1 cJT!7Ig4zeaI8:0{:=ΣK~@ڔbX+ߜ]MyX Y~iLTHv 5\^kw5c؆~uBf-}6h,J1/u0[E1M{F]c^}:t]''at STк }oaw;'^j B03|f M"kRQa,Aq 1Lso+m{Z@V莏db=Dj9h{57py`.n,zd 6[<{8]FFڣKa,NP ^#Gc~@8") OfL5! W IDATH蒔;5>hQ0G(L#Ԧv{]ymE3dñ.1YP"8 F'kЭ&hqY ܼ4=Ke .UWYkcE^jҨW6*3AUt_ee%ɜ61. ggg:Ztó V 2Zy NXeS(/?σhzvvQQaV>6bR Z4tx}6؎rd8J[KtbCpo/Ng@X%ʲ&|vNc0J) @>h,$vM#XGlL04 s5*Y7YLS(ϟ!sRo0)C18?w2J$>h#abЈ-}DC9l n7ux,S%{,ODr'1@ϴ0JT`Vuʯh8{Xp.<3;iؾf-\ %3ez6fGC=_b1_/PTePUU&z/׮ueQQ]eSfHV̂2Hאe48&0g }L|Wa#P2ʜh8\3: cT RfTvb0Y>(((@x+y u]jtooտUAx;|_\RDiċ>%nC2<;(RXx,u|xL>16 $26?p0&Ȋ{ׇTE]{ÈHr f5ZkQB8gu=*[Ӱ>Ym$Dz1>(tMbaoѴ-Vjbs> (#`Dz82WÆ90 mSX&نƁ)a,2۫H'X7_Za<\6.R^ ((=N':l{&O){?bF䉡`d\d#+M-p\&o{@ !9#(E.dJa̲䡕ZiVB;#8%(ל~G:R{AvnӧDo-n1Ğִ@궨N@jNG3zn9ty7lO3'E|w. ^wd3_o4a3]dd%2E oFL E>" \Tg k5F@7r4m2\Q5֩m[t]^sx}]`5: ZVKa؜Z+Fv}n$ObZ',)`Ӛ1_sð1/jtR"Px42ˊBC3 t_o=D{t8}Ӡ=@@UV`"aX<ƯCFV-ۿqn9Cm:¼O@I"W? =K KCɩέA!xp l{ / EY]g| y A&m1!nBҀ6U5BX\);+pPv&n@G-NA nojbe5)\z>[f)/ BᡌX<_ kxh/5v'XRԣ{E٢i\}Kn~zh!L¨d4{B ]&!>EU3 a?` #o }Nxtࣤy̡!/: gx)@ SH$7'`8f((LW2Rc)|rs?,8?E}Ѷ=޽{o^`f]I {J.<$ckJ dȆ2CZcCk~8WJ("~s|uzN&TU !gsTXScˣضiP4&H;3?Č3Zo!WeY1L *8"<1(SDYJaN0rt$cZ᪮0F ^t4I+[8gOx';R2 V;dct+aɐ]!+zfk̾k۱BoW Mz,Rk"LY'N;bi4NͣTlr_-x`w-B59v}9tQHr/R˜u= (&]҆Q}!jdur(2*C+*ZB޸(GsG ޅ`B,Z4X_]h*9&טN&@@d[`Z:7+T3V >-BT3Vba1[TU SzMs6n@j򗇮k>8p9gubZA E}?zH:WX#p 4J#F820Cm`zFko@#n{ 9z{9j> d.Pӹ EثY&dD&I_lC˲1#uϲU>HtDMTj|$)`yz\w2Ϭ.8䇷lmMӦ5 3O.ϛSQ;=ȅ/{HCCF֗i*m 5 ) hmAYTy0RaJ 7 d쥝Be8GȦM9(cܫ;p34cVpf-7%m}۝6|TL >-Wqć,aĐdάQrI!v~~$+0 :"'භg (7flg?ڢ}Ɍ󂄍s0%ށokEj2s)1$ky :-F!ÉAC5F_?33xl^D]3H0ԃ4I~ԩO c5N4pK8.8I>޻Qu#CC.8 K6}'?CJk(;" a @A3yXαXCoLSRٺqPґOD=fst4^¡`mCgf&e0œDE0G1 (jh܍|ל##RxCvl),y}* `RfnjLƨSʅQpޤ*AkҚ2EQBiI$u[ F,8@`Ǿiwh>[Fӧq<MaQRtE@`kW9pZ4ؚ-7 1޷q_.mO=t<{-9`,Hꠝ1GMS:fNsjz]A)+t-`QVUrm ";G߷&-n7S":KU2f4pqӆmi|)  x`Ok-W Yyz1D0N %btșa HJl)+f m'a=je@i KC@R!40yzK$0#ʢ}dD'VT%},M@%(RPֵ> <"8oYҔžqN0hJaƏj\*x'duPaJ|} fZYx4qb㉼s|  b!K1zA<Җr`kdYϟ>#VKֳ&φ&p/N~ۣI>a#mq7ѝ]X9KgE :ؾ{b?z )t4]aƝ1*<%7z/ox zqLD=HȅϜyU!Ϊnr|׸YtMo]oX,P**aߴ6E '0T <9(@Ye6܋^)GܶF0q 5Xb3G\(m2V[*s(Mq]AsXaJ`t}| KZ$( )el6pt]*A8[ᣏ> ^i4+/Ӭv Cج9nDFF1NOJʾ}K)>ټ:cmBڶ|*60ekGY(JSZ\Ubh5bfa!B+?rQȣb>d6u1 wEu>9hQ K®?oG?ӧr=^BOΞc_a_^D@UYu-f҇>4Q? }d>8H: @ CXI*rZ]'uc 0kqۉ'O.pvvv[e@*g|RuL+ EX8j-m,;RƳ `!yoڔ'&Z6`q sG_5:+F>;vkV^b^J+zC PdP3[o5>}O05 1 zqPCGT:OMAך(}ưz f:wg{e!7^@P$ܩS#gWc>!VE voMmMA|I- Ep#osض ~Aa@qKUU YJw7W`L3${] ' %)BEe9柧@lp3.A<^Oo9=zSwh_x?C*w/6S7o7Ox1U)b{&3I1Fr06M>Rv;oa꧛2|Lg6<.VfaCqԐƯ=,;onam( D!\]д{g%.˜;=Kf)[)qyy޽}ހCQx~!ʲ uQBlI~>bBMWY#Wk۶M5(|r]4h;hv@߀p IDAT){%r0eYaVP/WX,X,Xc>C/_Axj3fU%u(솆Y0>p<>*>xi|)1Ϡw9;6MQ4[!”LQ:oE y7p&: ׷)")G31lO qÃڕ51W {>x'=-KoU Z>;d3a,LhuFIedz}ȚOT,}txo{5y?џ᧟euk((}O?D 7(9dQ/@èФdC@vEQy `?HmK.Nɤ1xQ7ۆ1(&'OxMm$@aۨ[c/`ଡ଼FJaM~jOS'vۂr H=dz[Qh @O8TMʆk qvE da`7U,ʲbH'k[TPມ {VqCaG4"t zS]ONGk\\~#8`pX_~U l|hM,wKiQfOLRh2Yߴ<~&Q=egkL Mc[kœ ( \^^R} ;r*dr }@SP Z!B,n[t/]H1}UCeUK39QΕW| ݤGam*yꢆpm"|>G]u}&FXTubtC(|oblVa6aJNDgZŐkLHX.Ϡ JG.&Vd{(`PbהG{:34tpKr}o.h>zAEG>3V窺wXfuxm`?)WNOSR{L1<,[#a'Y4 æ$}!FӍ 1ŘaԅLp'0Z)J# R7qqv./U ȅ7Fug]RM7rPT£j~IOBM~>5M[ v-t 7O?%[nly~Q?0CYx}R2W=ޅei.1g,+Qw99zݳ @cZu"gzYKj)NKva,~]~M;(UUG8;[|N.C}t C}6zZw0D]5h[wW777ouEv;777CYp-DdE#>N)wM@p|`u$3PP%>c, m} a4.'OO?MkM?b@V=G$w2-copޢ,FYa3d#rnN6Z( i85r#3$Jŏ#y$ǜ~}m>s`b?{# 7`;q>}w=Cl2N ŧvWX]Ӻ3ɩ٧4t7hN9m1T~ٙ^Cqߏi BvˠIX!vq+7' ,x%=(~ xk#sv;(Qj %.DJR4.B:Ƣ$RY4Ky|Asc:3*ꨊAΖWo~Pfw/ͯi˷hgsha`iWS5fe9J;Q c)z v!S%];+^Ӭ9yae!UڈSy{m[ɻhY@]#P7})I;٬|^ 8k:.V4Ch~7`HThSRK>20FK#!3*j- tÛ}s #ac6t 9g(婝b}Hc bOaX`>,7]Ù۔T+C#" \# ֑ӎ .^q}z1_BDװ HQ+;ɱ cnSJ9uBp@\h/vZus罇 m'ϱ![uA~oxD Y dBXG`w*n44H6pTd)h8Ɣ=4Ho]f<5C=-S]ߩh৬ߤ=gQ\dg62ؙN<F ׷`f6kv[e|xe3~TBĕ6'FHlbcڷGHM OwFJ1<>j{I6&XkX 6H!CO؞ҍe%s:ߏ{]`_)׻ֶ 6p1[n22Hd;u?WY YQYyVZ}"1JtgL=wݖŻ58u㷩G vܟ1֋e'КKA\d&4B\.X. -Y29(B&W!a۱$[O[G #HR '[259U<}|R-gXpq#DiQ,VsqVaWs~<ުzԇX;Ms.|ӛOʇT%o_]\| MfS:ަ.P T'KVXA2m9sd1ZuA`}̨r%ݗ^-wۈ"*4lF{ȝ25!^vvߤ[#6GrQb[Н'JrpGKj*d#CniKV8Uac{?{ Y8椅s~s EGGQY2MKq!*ʲ",%odnS>8V%9NtZV<k-YNҴcS#6AA#xc.PȂ$(--Əhc&0nP}}O͗KڅD"|,%2U͙ưV,UޣȨ ķK"$x'"#ẗ_4m< EbɾgSߕ#Mk*RS_+=#] m9E<,R(5 v>0\a0_"bp78V1SdD`~8QML@Yʣ=h/A8Y:<9Ĩd<~eq- -!OcZe 8G!VOiϿ!+T;c LpAV,1`CLǶm%ՊC AƵwEUةhAƴ<+w7&2&$EY2(Fcr2d:CM1A@Qdy7"Bh2'$^߀!6{A+͋Ӌ3gl8aYhVul1nߛq0eY,W %U\[Cj?(S\HsJ~]"N7bMX7nXbsgGw%΂I;(z dH(\CQ;6{^ glAi#hrw)uݧWiv37C1HuKd`D~gjjqܮvKڀAA{WV+m7٥\S:+ Ɍi1TiMk\ʱG჻<24ꜻ|/w$R"'u]CL#S7+QZ.yԠB=q4ȾEA,+i, bt!bQ=6OoB喀?4IzuԮ p)0q=9FaPM]w'/!l3W vi"֎my}b>WKߐIHg:غ]`w$N@h>37_e59MY{t]y޽8s_P7&mn,s Βg!}Uf< 9E &)X8Xv1?O~GycĴ=:?o6֩= 2,Reyt6`oɤ$4*SLSGɡǼihڟ#{uW|}|NլfuCQZR75eES4R7 ''Nְk=`2p5Q$|+PI7(G0ubŒӋ V.LSG/xݨ\]%AyN&8f̯L)$Y tNYyM7:3|*%1wیd^ynaM,ɕFçߦ"Tnzy9.Ǐ1ҺZRU f{SR(Pe(^tCpI+ctB׊Gp"k}_ M 4e럏cZBw:8&{'O⬡^M,MF39OjY,[m, 2>v*ꚦiK;5f%RR\3rUl"<v"K O|(YA[.Wn6ۛ] &pZBƸ%76Wo9L %d2瑍c9U"/rLc:>G!@y Z|}=^y$w88yt.6-. ml?(,5x}ۤrh%98(y|=mfdO͎2д+ZkaX.,|E$G*fhd:e2p>RfӝsJ'C5m|Ͽ3,Itd*ڋd.âZ"`2@8kj] sZqvb24mX>P੗59}ʲA,7Y %2o]xmg,pNBp殷S(`Fn,)f8>wnL -MF$pw' kѭ B"V/^*T}.6WB{o'| 1('2<||'GY%ш54ԫk[1{{cbiW4M5TKliU3o mcp֠f;www"&E@3̈4z4Wg/Nx:XOcV}lL+3Ҷs[lV,fɢ^"u9ES L2P0Ŷ.38.2}8<(M:5gXjUWs|rB]U WHtMk$wTr}z),wrxUU?)NݦXڴcq?`1uc{բoߖٻ3Ċ0Uƫj˗1.Vgm{⪴z)h!SYB\@@Bfo;ah4ڶUwd/Z{d9csŘU'kD9;=c6R1KONǂ~|)G~uqk'{7ХLO6o~udžfxh慛m0'wR ,UѕYںxCmW7⡋,L+sU`=}5EmL؀ӴbC  rԞoX.*ɘ B ZtAuHQ݈9g`>#ȢVт>#|KAŊ/O, ֦%S+"^s#SY\GfZ.M1/_`ot2a:3D7kyAHՇHǧrZLql0)\gHjUaRuuz$\2gΦc<( xm mUqn%Gp(y'B]x_ή ;Qx=~'W1w] Gyspi+SS:bq%5Q2%b[Y7&[]y h3#[3E}uVSMc (ȋ_ 531bI5tNY8XZ@1Ϣ[V%+FH]R&5M9=ӬMwA=q%Dnt[+t)bՙP):-C}Xgau.0{Lz]`coD o 0<'SY6)z8^v26uY5YQy zݠl85Ї[T&X Y@ ^|%tZq L⃧u-V91$7&uxQYzDz-S.loY8k1")oT.-֮M !D]'ʆ2cA2xO>瓏ٳW支P#Ɠ$/U$ke:-xh!!tb}tv!RdF" #8>[ ' d^jZcZI,ViMjmVH#AX%2EVd<~z (>D?qo^[1C_ k'<TCS Ad2OD#YՋIܶm$ڋ[Ƅyds R9"Ð6x"vjDI ^tRnYo?/AUfl4q!dޔO19:nU}q,˫9ɘƽßl,szTĶ=n]!wBY$dYeq,6nD$>hH+^Th _| /_ g>^V f,I >Yҥ'䄴 >״+˳'|srR0(GEIqck]islӦsӚmh[n/ZD@kT!3Б%ohi]tx݆@4E6J@\dۺ-m¬Q 6j.F\(SQhP(V4 Y-Ogl2L0Ƽ9}9kq>>ǧߔnZxo 6BuK6Ϋumd|N+Ε v^- u]1{RdYѧuCŠOc1w v .2*t ]8F4|_=cֶ51Nkc1B( :V !DdDyͲrd{dlSFa!(!hZx΢#UJWu?qnAWi pju>7)&8;1ժIxR6i9EJz\(ƵݜK.16Q:ݙz&32tMI1>{)E6jHO8.,5+z~[gKr AYZGcE1-0ְX/PKoNwfF,Y;[kLݠ[C Șܓg?cdb<>փkZ5єyŪMeQ*R ̇{C0.>~cdp."%jB5Y JJV,#-Y~T<2L(G%Y7ݻl4f2 M g]̲dѤ&-zgu֕c 6㽫nGrYsJ{77_@t+i"f-iZԂŲMPUU1T.{Ośzg:Nɔn+Z:lǤu%GWtZƝ"t629(!A0?{1-!Y8jƕ4&19 Bh!EH!R!+B'hiqvR!~D4Zd&23 c^<*NN8=;iV|scfG1Ӈn2\iC WMv8D_C =MS鈐"&%iUi4#=R*:Cd${V5~9raC!Hޘfzw2E1w`^]pz~b1t̃Gox뭷߿"##@F̰'\ HV]p\,}wxj{$Z 8g(ScxxĢ,jѲ\-X4݀K vBM7f[٢_)Rkf1e)PeDwuyIWo vtmu^J絝wuW@|]51̯A~ul A6}NT r|Pń(:|NfY 2ns 6M::}@푩ֺgzb_%U@bq%`!hy UUԈ8.BDP/>jt&L8:ϣpt1OP:f%xd\̲x1Mi36u9&^꫌$ s~w_|ْ_pzv̨}UGu*E&`P뾗;Ω0dݮwBIVx%sBY0eVuFM8=6q:[cppw,Ccb61l֧fc桐!Fd G{ 6z!P  6FtݞNP@fQ*"h\r_| ԩuG#rD1*)˜PJp.o=~>7 ]Q-vR n>ڮU^:Jo ƆV5@t\=_또T -tkQwHD`eqq$7)3wZ;RQkIB !vuquw!'?,^kN&XYª6dR2M3\tZcO'λcf{w),DeJ%"HI:$IS %(* ,\Rs(2E)4/>^ZZyyuq"'gj'UNj~42< ,pr/m!(]XP-W,K& ZoG}"m[W_''OSɘQVDsMHJg~vy󆶎zxi9x4Os]}+^|o{?{j^cksPr>'|Ϲw,Cg`+ 1$…N'7k/;?ZKcwY2D'IJd(c!G¡@coĝ;wcAjx41'ǹNΧs198m4ֳ\YϗH1N(LbZ>pxlddz:T|/ CPu2.p &j8-6Y]x^[r_Pd_ee4n !eo ?6!nڦf21M.!(?8O'X]o Y!;p6 f;`\7吽{ ׌RWj:Mnbj~nI_6Uf߷Ⱦg|?svzJg]u@~e qF';M0ڶi vTE8lރN/x(g)ceU)> pq~CSsd4=]AS9}-Ǚvp$}>? uBrEI bkE%%J,98?!DƌE܇b1W/WFSVIN۴T*B!Ma6\:LƨdV [N`ϞQU+Drp<{_e.ճgdJpttX^g_W^:#~OpP=W~Shu+* 娙e;h=``! L;b~"IuwX8Onл7ؑ9i\+7)NDxPa£ӸS8"7M8::,Il!|I:"nT܀D-in@khSpvvă4_}wB)MjXԪcl̃G<*%dR 4p!ig!|W/:?OX-,]?$ zOc,'c/Vx1!r}=}eBEQHGmFudcm6<_Txݖ9Bnhk1mkhF^FVr*2_(mp!2YTEQ~tֹQY +B=k]Z,/9홣t7YlvEa:a>,/ӘJȋNqch mS!䙈Vsl?b]f+m>`h$,9=!4^1oLK?`Q1FJEky4{)>C@K1DM\ :+}lݐѼ|L2n.3>Wp<{!g]S9]a ߟ]sѥ¿W,|OiX G,V+H(GSΝ|m?;3^NTN` (Cc2JP r@N Dͺ8HcXq>G5m[VFPw}~w׸:)2o8Ce6Z6pnIw\ qh +꺢yeY$WD=1XĞ,PF JFٙfnϴ9шxDI/_q1_E2Đuk~9{BR%UjB HKH4R "eeѥ?q kU%s8}=G?_ :u7>;k#tUL,}!ׁo{cu]mw|ظ!~W@zsq-g՝#ؘ45(&F#bx5ؕb ʓCչX)zw1)\.Rdbw5=Y KcF0͸4%G&xkǿ/?/;Oc]85M̤]lXcDBA Y!Ait1 ¼u,G3L&{;u_spp??_\~ϣwx⦅e``(HL7ǯ8??g\΍4m/K>S?e4.*FO&嫩U/>}[jeo=Ïxc#MXv1Y>E%dsXs<'p||bl<|CF JӽY)J3 Ɖ4%֯!p!V, $B(4껍Xxnjݶ~{Nkڇ^*\~YERb%6.lN6k҈{lJNZ"d16p()w,bAcGI8clkG V,SyF^d,C+ `:3 Uzy%Ӑ2#A}qC xs:}]`$KQ"RʈMt BXɴySy  E ާ\sF4p׍J_|ChF^^5I~ |{i7Bxl[Ӷ5U`f{e5Y( ԏjL&g&qR~C5V%w7Ni=~)- HJQbO4 hD ZۂG(\Ɲ]ju/O/8{Lh?6lLhoVu=cM,Dѳp'R7>k%2Z!ƥqx 8`v]cVՒ0bw@Y_tUU9S?ѣGIטᒃ+yd6^|'zH<2 m9w%Oy>?#eyߌe}*jhb?[t7T1u~w{}̽ANƐ 29~y&c^oAvBQ?#E(*($+Β[P ú@ KKApST6A%"Q/ ?{o$Iv=g%"rhb!Hh6LW3Cԥ̆ΈH,$@4FWw-F/gw#2r*RR%P]~=.k;#+6綄_Ǭa`Hi0^'qzԔg$,*0C& $?@r\Mt-mCG Y]JϪ32ҵ߶o;syٲ^Ai-jI E*”j^R%QPYi@8<8!D8=ɓSXC5+] *bb9-EiHtZ Iя#*&| !Wxgf3\ayWmPAF/ל$rMk.(xݴn#cx({.#m}G8vʺjzFӬ1Q;RkZIY &(< j$&DN"E'6ʆtB+H\Og?)??дk~?ERB'DK;{b>:FL-x;3/$`y<肌l NQS>ڌywk9/Eɕ^/S>xcdߒ,ʔhUJgM ^2NzT \--U!AFy)oGxjO|vV]6<5X0:10 Sک&mwReu@筮 +]u/H@ iNaH90aUiPi|a EjבӮu] }GHSΫX';Q)tr%XY ƊcޕB5 ]0SbG@6hh̖Gha[1]<%Ft4rlFU5oy[[Z]j׏bAc&z z]FgEsssS:}M&Ê{[E}YG}"kf׵mj 5F[ #"u%,oc1WW|C)){.wVGS;H)Qbn11#UYRrް9S4/ȨnzNo_bNpieJUt /.{ubO f ֤ FG=kbZ8YHnłVZ3 1"JtˋK~?ɏ~fEv *L{6b(qHNٽsQ6fcEJsy';v=η(4$C"݆ԉu2Px>-}ԯ~S=_1*h$WCYԘy[KnDp ӟ[7HlOn@߷ݨR滛CӬgaw-m]v)K*FB>% cR58U"ZR'tI:R5]S2;sGQ9UXka -U}=Y42:Ni-_q7HFih$Ҍ\{'Ե^!.e9W=gνl#JyVnfս};LŦgF./k7 pQ:,//xb,9:G,8+((rKC_ѝZB u6RMߓu=!Fw뜦1>9s^N {li"*ݳVJ%⋧<{ǧ}kV̕%foʑĘ!t(UKK=yL=a8~j'"&PM,Yp Xf{r#҄sX`t w,[BXš)*pj+ $ XBK4;ESpP\r֘0%jb.cNO>>5r }1Hy)%:޶un #0qC®qi>(o{nT6u: IҰq콧]K2tB!V0w&0*Tm UDӣb'gjQAǀMA1M&<ِqϨ \aD7?_e}O73.o/V;+Ϩ)zVghRH(B=hjF:ReҎ0qRʁ{]w[su`,_(0rդ(LKBR->F(lIxs>d'k>1Ѷ&A8rAPsGkq"}sV%wez_÷NL*7'<1l17-4Lb"2#ThLpLA =)z5h=8KB!$MAJFQ+Y%㷼@ "dJ7m teuǟ?!gO~Aۜ!C|{J~|/!ȎEj̋-~![MzJi_Vd!(zоued3[v&<+B3SjPP^6XMz'Xu%ܹR=%?pt F߳&k[LgMff7{NSFs5=5KK^ƾMa"a\N6m1;LS\DޕSFkw9C b tv|ᚱ!IĘѸٲlQR 3Q-;%WT \@ǭMk͛i%-T*H25e=8%^}AmC֡}"Q* ^9&)C{=yFUT9GԳ~ٚoG$ŕT6uzܤ7*{ٯL8nSybDnӬ۞k3MN445 fՒ0ڑy[* lfݼ4M c rE]E!A^[\ތDn]37)h1iJ,sR\.Y7kL]rbj}dlX(gZ^SQn֊F/OѠaZ_2$`Mp'r- w-ĝ1ЩGk:kmJ& )YAwqFzqꂫS&sjF'N ' IDATl6sUuK#1 ]ۓ""WJ MFzֆ"=zt>Z˸kh\ɏɍT[KRzVQCsq_% J"=Mr5], N IHz֚X.;o/7TRլ f qcz&gϞ|=N=[[1Qzb'B?G&:Is)KHŽȘy5 U!&YrVyxߑ8΂uC*Btk4xDQbRO$x)~~[\9),'|s~XM4%4ZRTρ  U]QNK5mَ>UmK_QaвQfNߺ۔n;įI V[ECNQ↔D )E+ߣbf1:isMh0DtDDP!wDZNKm IYg)u*(8IVX4@jJ[R5] j 9WJʢ@zuɃ#޷hp1ĭn> 6osvQqm PIW3Gf|#; 6VuDz dASV'ƱC.]6m{+1,#o mF fh5/gC9kk>}ѽ/'srslAV0n|.K<}GcfKki9t6< ?֔(o_` ,0:%4=4h`d R1ajm bM -DZy-)EyHUb~Ao# B`<+Mnz d @"e˗QMeAM6P7&7cʱ Kny{;Z../(,N(jkE۶m '}ROSFmێUUa;}?8+ODX<  !@_zPʰ\:E]n:\)դ\inR 2ݯ}.9%bZ 6; EߝX=S' .4X.IuRE*'as&'/DabBŀN BnVP4!70S&HڠD)mpfҖ7t>W#rx0gxs>oX,|7kFwX5n y0ӻ&xh/{ln]fzS 'nRE]@n7Sg7Y f씺60lCaplb5۔kpl,{/Jm'lԘ9ƭMcpj!#˫+y96+.ZRSU;4,o}8nM\k3fcX i<\_o}o|Rq 7=s징slq`e¡5A,jO:hV (@rjBBLQ dJg_L9KN'G/Q,0*58vA+11L%7MצJ},|~hnd|F%6^c /筌-or;75nh~0?2Y M@ UgQvD4*ʃ%Z1{SgS: 1BFo f:+QDY+!I2(1ݨ`5nŇ pތjHrzvpt|1D]q~hvD$ȽyGo ݷ(}n4}^ k؆f6}mcb~k;EbRKEƱIVӷ-rpEQ!pj2Z'sYV3 ̍1舐b.U.z޵:Qe4x/V,۞'e:}rD)zFk`lVu}u#x +chڎz6PJsuyɏ~w׿/ߴe s]=:QX94@Wk<˛fv#ovpw[sK|ÔRۮY2C&9bJu1ppD!8vLZ-Zː+Yo, V> w-E+ ge0 gmʰ6o?gQFqԵECzjXCtEQCiUV(cZY7 ''\]]Ͽ'O>m!놛! [N.w5R3l% _oȰ~3-Aj$X g'sJr;dsBDɃ}c Ew*%(e>UhY(olB?D;NMz9'0iwu{LX0H-p ⬕+Δ8Ue!etĨȩa1J#:G)sq8f%gV3 \Y!iM@KIx:QRh;)N\ozȃ'c68:8%c_Q mK8:˼ݹ[C:}궬}7ou^_uP^=nc} y E>L7?N}W{{LG1]Fo߱{ܰ1J,"_-VKcBt]Ta7*}OӶ(t[(P6Bgw7L_0͇ۤ$3̨,Y,9==i:fӳ ~-y,Xsq\>/#Ӯ𽈁=}_~o~[LꊓO>_'@f y bqv pֳ[6`y*ǐ N wQ5gÔCmByAΣFki8bĿ~Ѥmw%:pvKCZL,:;H/SIק۸jsLoLy5I5ըC ׹s쬽:ӤUFSN$fm:TcIxڄs:_A8I4)zR *آX̘-7g3]1!DY]+#zH9m1*7Lj$*sʪÃ/>LLjV>7KjqJ{9 ⽱}F`_tDy?ޗml2jn?%ԶD]OHm>}cNd 1j쌃=!\.S5eYnNR8躖kI{%`onQ8WhmTn$9;1@Mnۀ\|5вBz,//y o=|>RJqyqÇoS~?nŒ 6yH|e<MWWțUJImOI8?;0ϩzdkdK|HǤeYҶCYVm[F9-#Պ(u)y^kVcrt,:d$Ft$zVKR+Rs,+Њ!;Uk=E-!Fc,Z>o`0+?iH!?ѬV4͊![oE$m&И{1voz#Rq3ưX1FoV׀q\9*?u$Z)Tn{a DGF+R%#"vmPl2բc^FplRZz).A)#[,q_1`\9~ϴFJk= yscM P{7;^j7w)bM7M0CH,W ggW_kʙEzsy$2+) J^<.,l ,ռZ̙1;3%=qw6 "9t_ZKjxQ`ox#1`X,f39Z둍j&1#UYQhڞ3kg zt0vZ E91#Mit^"jfJ*o$jLiAq~[? ԽeSzY5-眞^Z 17uג Pb*R=)$ZRʣuhg RLTG]Ub[hkJ1k+blH@tԡb{^n=''G|}ϣ>ju8١U}u6;/Qg|&֧{S5M/z=C{Z׵8)#: mcć̀a{44t΁mGnFgݼpcfKܠׯ:P4-k[ڙa j%ggV݇3Ӥ(!xJ:k($B9,~UhJ'!;+e"PWe+9Q"Ą1o#)/q9&1S3HCE)(:!)ݫe?lNUf3x >xįaGv7 h(vl}9^u[Jsһ7D{à}oqY{O׋{ 5Mz9'w:N=}߳Ze1S#MR2b5ƌ- 0]>aq yGNm6,2‧3k|Y.-5hJKQ1 f198XPef9GUEIUiIY_E;-V T#Ea,*HRL}pvKV ͨf1B?#)q 5󲐬emҸ5P`3^ϊO&F9$PgCH~v`vkʨWcr 0ƌn\7Ą0ah(HP173,ۏ+en^<{ٓKU*Ҥԥfq0:֊<)ټfMhQI(kiue" L頙,RM6#\fFdgl1pFk./8?}JPo.|G^IRlFÎ_ { _iir_ ܫt+^'1rna%ww¨> s',Vr|߳Z8=;f9}/S裪(^8&r0Q+gYi[aJܬ!xؒ%d~$VDpLc%}>+P9ZCYX>5ei *ҎQZ!@jo|/f'|{ېt!N+ǴCc\n@)%B{ QAMvhgR]]הuC?& X6kg]\bDUU%uUQU2j<:Ci[bLJ~@quۈ˸Ro)׌pk5TL #-];f<672gzоM/s#zBm[.//Sʺf6=ȺYP0^ziɍ CV˱K\1 1JRcWa'MX6j^n:&By*b18=%6!lbn7*-oyRm'irb$8?_BplIڅXhzlfsS &4ZI,C_-m9uZ*-ݢ,X\./hږՊi%|xдݨݼ|ߋC{|kRU1Z|0DYs',cteХ-1aі3.2S+! VfJa&NRgM"8P5IST1I[&k\/cڻ-Hl0J%5NQ8Ķ Q61DLsg( bd=:sO_qs~t!Ѵgg<~IAYXgi<)EߓEP lF>ӆ h3ebSJxC<)$HsdJ[ًJ2=ћ)7y|[&WU?8W|q [T(o>}^ڥ4^F*?z}j^.ߺygF`wfy+Gw8r︺<9QUs 4m32>We xUUm;ne)ݪMж-1 ct :17 nKMcp#c(⌑X托wtQ 4|aWK~Tez,+L)_>~_|1UQ7-w*2rslvjQ$F#B[# $Vah ;OHfCElК!o-]o. ͂6iX6`")rVxH6aúY,,J+}z%_<Ӌ3Љlo 'D>h ZTɢ<`FoZK7eYGmjƚ̠ 曔 /Hg+JerR4FifGܯѬלw?C~YUG'?pZЙ V 'ɮ+3M<wwoX/j:ھ|_tkٺg}7I]ntpmm#X4ܤHWus;DT5 }quuEYAE7YGjUk5t iZuNH!Glbi! |XR#0VQ*' DC}cFHU:?9YGo@RXAe Ǐ{#Fa+w(TQB WWYvEH9N)H^庫TEqC} EI>qXcIuT4VJ"b(48"F{P o;b_I2R"n!N:ĨX-x莘>O}."yx]m*_kjbB ( ckn+N11Iw.^b;IP{n_ XE;Dlt%nRsc_j۶(2shvZq$a48zǘmMh.k6Zmk!w9NB'lE}Ή^nIČc%}3?cPd{䔪x睇TpG)ju~|駤3OH_֭ |)j xtvٳ ;>@خY͢{ 5ej[skom+Ys^zG7ξT%/t=< )3~ ^T5$qmjղ\XE9\awX™UUY-;.9`qbqၰ|NYN6։AE=in{S7n&0}ݧ\$J3aT`8 sibl R3}$FE))JG]Ybh\7bF lNq0/JU%\"nZ-cfB Hӯi\q6 W82-EpE+đ]hCl âs1oD6d7B(reąg kշ*^7kvћz}n@613$u񕃸޷}.Ovz[x})Q0|)Žӊ~o8,T)]w*5܅:u{.&@jut7#O61JgݬXr| $nP^nlshJJ\W 0P*G9EԠ[49ƑMj̛!9_kMRZA,{9i-%?ь*0JCO⊵%_#ejTld6?Yp]~[k 87&wm+R׺T?UyK*d8],.yRU*g,YiK#[kM}zn$3a9 p$qrU(8gOk$f}uS礡md<OHdM]yF E{mG,-y+)ʮO <]p"C|_ '|dn@'R{љ!$w|C12MR{;>Hc>]00"\Qgr ǏLMO1sd &&&ȝIȑu'G;>  F2yMV^<\7rDCp#.%<o{=eS5usw+El`OX`0{f=By${dV@0kJ U%wNvbˌ\2\ ӷ DkL)2E('.]0q!`ruC&\ z~4YbTnd0 xgpYNz'Ycff;Cd.ti. O(stˈxGOg"y%w% g +I|;]~icHa ߰#İЭͻu+Nty;'޾IAGD V=,P7sM1a-FGȥ>~f \yiw:9<2m6%D3[:\G1LJ%(DBX'S! Yu7( X1ml`LBV$ƦN|UZ&&lN$\!OI2"QӪ7? hQEh-iB$+2aJ`%3, $"T>vOq·z{Bmآ<5։XY[ݿ _H0<> }^n0 yRX0RUlYns+[qiCٝݖW^x7I7)8A6DVkÀ~_Cj q@GKB?dj8:C?J$RpsmtM'R"KEؼip./nz[]&54eFcڥH-ӹQ8LYS2Ѩ7x,SMΞ=ӧ>}VWbjATpoڡ0}/ct?Dq݈ܳ % ؗ0vDвO<8Gw4=lF%4X_^oEutTw9R7BD tO)IQ#SXOZ;]:dă.~4G7NZDͥqZkWW|nh5;ϼFӏ>ӇG8ڢHMY83D]zMƥI׾4:M2;Ya ӏm̑8&]6aNsbb$6Ҝ'eP-D{ny$<-]eJ |ZD{%1,<&hY3bťY7킔u1~.#кf(V t:ZWtFvp1ܶ*Gl륭9 B)I':/PJpM"A¯1O/fN&i2 imAg'<^`m(°ݦE Ry'Os2r hCI.'gxs-$ـN<@4s?a4$iԠ d"#$koɇ+L5~KFJF064bL2"IT RiM7Io 䖒7@h*|VruY~Q0sgW\}waҜhl4]e<jLNL8R=ahGfDg@kC?`muFL)J@3l $tq?&34qMN) ^Np?W %~!pg7B:[1%H5D#Vt^BJE"Z<_zo?L]&N2N|o}?wXO~N;x^6Iد~j<9V]d0On\u-.p77͛fP!GKX+Qҍ`07bZu+aZȲnKV+툊Q4SF\sDF[RWk "W|\:sX$:RlMsQ EmuiG$J$IґDpvJR½7k>G[M=ddDA:`g\ޠtAuh*m72//$lX+vs2*6صn(6Mhۡhb $$XHS$FfZIJUIqQX/Si)]ӥ>DŠF4/D`ipn,G\abWWB[k_Zru- f5 7 wU ҍfhKYǏ>#z̽yO D&mnU  ZB?$6ZH,qMTH2C-HR}a}xciN9Y`-} }uA#H?[T ;f*_~VS<~vM&:|T'KڇG<+,٧Er݆;E[I~۹-GrOaqquxַ0TjHa!ؒQR#j{k܆ݬ,HDY3pmuYb(E46[h \4HsM+e,qCگ#MWaĠT)!=xG,o86 5ՅHd`&*lX+?Vs\͚(J/[ -,lֺnѪ*<FbZz^<1-^^K6 <Əvʓ';mW'i A 0U212'o]LUN5\ȊJ6O9Ak!%AlikOlx#G$rIXɯYJ ˰}÷L]#nPV"h3 b;NsbFsNͱƱh"˒ҼZPp6__N+vNt=@=FJEJ mޝkVW?c͟-w.uyhwDIj)H=|:u91|\̙ghkIyc#(B9ƩN𥯼΃'o% `13⭷X^8MI)q;)҇EJz>]B)jfew'ܹ/ѬI. N^KX (0 RWӕ):"FӘT ="BgTҜl8kXZQX$ 2gD i4JBIr׀g5&x'TQojt&8^8Q],k |KVXVx*ؒ {ԢϽ @1kkk}<' Q2&q/$RYK?6A%Mף'%y9\䵦rŅe/43O c>|H oDP3OXE("ZT 6V>gceAfs?0 %zUI,Xkɴ,MdnmlqÕ )8sE5Vz|sr5dĉ(&NfzL)p'OyJQ CtZKD( DX^j kxCEX:V :3,C)TA4Q'sV4u6wKn*E  Lϒֳ.elndu mabjZ$bd(!7%d9v Vr)6)@I~:J_V34P \1yȡ xyֹ͛$ [ȉ,3|.vy!^fI 4|4>X =jsG]}ÂEBC%m;2 ١R!"Kzs7g{۫Nh{9)C%m+FUw);=mrty1r7-3Q(qq zK- ò;ջ][Gyu坯Y`)Eu^Wgq9j V'܉FsiFRS۝KN u)=13o̒$}.kOxwC~_>?oМDJuFHTW6tѶO&FзD1i6`B9'0WX=,MdԣzlN0sǎ`95^uA6ֻ4c4)m،hYP kLOM5Z_]40 )\<\hWH'!FZE`%:&Nv:z=+7Hbؽ(z=ҸI2c Y$)IJjcs@6Y0"YqϰAF$a-D+,:%S"xiLFh'K@MLJRmеz-tJ 64FCT_^!(vo֥1 E]ve3{1 V'Ry֜:~ t9rlf=d' {侏N 1?^)=ED)K}:ul 5hnCR[n'AAJmI2'{ 4e{w5hR7$u=`S4#q)W ,ShSm r7,  rF*ߓ$&I,ZҧE4b0uRI5Tw۰<W8ouw~ܣԉ6G GTk"jxϣG't>|(cN <&R|cfz)"zᢇsW* &L>iB<r,015> 4u& N8B?A)HFN0)]jTPB *jBihN:<|1ǎX_8~MޮPґ8!]*՘ܽ j *TPU:::X[['{"Nrx0y J!(7++TPB ^-t;AFzFEt{}tZ2H^3X!Ψ:˜࣠TPB *Txv:ڽ>EWqB5YN j-q*TPB ^5:L!c&uN;>f0rtux.cR7MnL]B *TYyz5||yPJvƸJ9C)%^SUB *T*ۿg&C4ap)Z\ޤՊZ8! x *TP«n˙NjAcFSQhMĮNYF3W` *TPC/h'c'lR7/hNO1q8 ICkC!Od!UZBuξ*ku=ܹsk׮\O0~=jaVcrəGqm| Xk>ql|HW52TPBƍܼyD ƷBHj f ;Tk⇇ 0Nך9)Awc&7Z \~VBB@W\[\\۫@~mCӧs5[!d BH`}IY'"0lҨ5ﳺʵkתAPB vp'XG>Z'";uGcrj !$3)Jxw !q.\@ X\\`uuK.!`ff7n<+WA+WX]]-{5Ɗ///ŋ:gnnn{ny Xr͑w!cxϋ˾47|\w>ݖsNsssܻwΣqǼuN82v(}fff~4˽`Ay{~xm mg{zmmxsss,//s֭Yw{{ݎwyxSefff}/0DC62o.߿w>X52DӓS4uLLN2weee2t Z+++ܿ\񽥥%VrC*&p N:n}_^^1ܻwg`-Ƥe7npR2b2ϣc,`eϻ>x;<{V9GNGlD˞8}ž<}Ҟ8}Ҟ8sžx=u 駟?/Wmvv֮_ZZWVVl*1~Pc/Ǵ}]XXZ{u;;;ko޼ivaa޾}{Ocb/_\k/c߱7%;;;}5n~n˲7o޴KKKv}<8Xnkvn=syw޲1w<{w^{׌^,τvl̸;qۘ/ KKKj=sU0Ooo N7m6:{x7\ C  mg95`ha ~&D^;=3w~u~oٹfgOړǎܘCv~~~uf dΟ?o޽kϟ?_][5???nc?nY1nc)rʁ:[cǍ|Y|l7_{qUY^aw[ҥK,//o;v9@o7'rݎu?njq۽`?Ay?n~x6γkmymyr v;ݶQ]z*`{$N#o]~?^~j޾M3ie_ms?M~[o۟ګWϗaVe^緦2Q,W^Vvvv'͛7lן97n߿?RP%߿@ߊ۸m>ҽv<|\ۥø}m-ulkǍvqsnzqXw;FG2mr/syw޲1wK;[<~ܱ#Gb9 I@oݠmZK" Aˆ*${,/mu+>-ytnr&C)++X*^+=XYfa!ҥ6nݺ˗Skm|>PB iR!JӏӏyaljA7pBZ^^֭[.\b};w,3ٰ*.]*#&׮] nݷm;G[ҥK033S*]r#N!Z-v͉#p_~b:1>myywﲲBI [l+\~k-oIl7nܠjvu?˴ZݻYYYի#*.\j ML^>??_F._7΂2{Yfwmt/sr^ \j _mk}1;+GRn\G[17n "n[]cS˦ݽ׮˚VKpf$333,--1;;Tܿ޽{Z-Zֶ+wݺu7nTĭB ~6^#N:EQKۥ׏}œFΜ~ZQ BN8g9r#ǎrY?w) .D1}561>>; jVWW˿ݹs)Ց62avV\qrXHa޻>Eƶо%\n7[6MIvZ߸4h7oֹw/1\Yfee˗CIΖo/8d>]D [^^-*TPCt6?w珞' 5;[巾BF^++$)ibB |sW\)#>ێѳ z"3L B*5^^ ΟX\%)Kcz{"THtnm[\uoU;1LGn߸1E~@Unݺ5BvJ󹼼\Fw:NJ,,,p%Ο??Bċ+TP(PQ@knl(!O{*Mfϝ^B KFe>Jkˮ]/Rvk WΖE>nˌk`$p{RnN}xޠ&zTei:QaPB *r{QZ PR66$%c}8ѣGt;j+TPB 0 8)%diF8<|ϧ뱶N#N>: +TPB Μ9K`m}S#|+_Eg)?яX][!^X֚ja ^O>x0!=)|"p*TPBWR C$1Zg1H 51E"ש7,JZ51%I#pnNCq~BC" *TP+k #}jQ8Q)tPLysIX,> *TP+x>AR" 3Әƒ A Gf26hcà *TP+k5B`4g|ZNĩf|Ç3 )%BK 7 VPB ^9ΞH uT/Aթ(/ !NbܹBew?߭Pe|n~EhS'3Z3xǛo3{ A)bmnnNG}ͥjk\ǿj^GJ6G0ԕ3vrs먗+(汲 p\PU q*Ο 8x Z6mf!Jiqd2x<IĿS2:AKJz! rID">:.gz}.OOzcӱj}Њ55YR#yو}rImG|ls@ )vYZZ"'O$w9~mOf/~|}|xw|o*/g<7Xdb}*~7B>ϒO''?N>?%=0y鏐37l@GIvJ:9I& xz&v#]ˆqxdff 8řxH =nbSYJ7r?&;-HWT^c6&z[ZA}rI?yBB)hG~CO>EO>EاW)rϓO?pP9{+2\e=ȷZ`=~CߠJ^i}KgFdi$9vԷJ۵.Ԟ~{<i> ! v^9nz={9:VbMMVH~k\b6z'I,,, a~~'c`򫯡\TӸ|ʥ -f8P)UZ~ۯ+@` ͆:!l;F!H$IVVt333-n5~l6~{@ĭԞ~7 v#Th~7χ`0(X57߿"WiݴۯԊ^f?Əݍk{l0Ru3ٝN,_+diR#Te>JD6JDO++  Yχ@ Fsvd2VZV\"jf k<ōϴ-`?ԓ .Vc9jZZ|qIA>$Ixޮ/;m~gSzBP0;rţR. K._r,L+Y0<2݊Z"x%PPPPvY~r\]04dCv-e\|_:6DBBtcG2Mx|ϔ#P6* jBj(Jk}m_ǏX,`yuoǜ0s&8eYmA8ێyG<@ܰJqc&,ˀ5q BrˁLor ׂ_,_Z&&'w>0AXDPJ&_%T BP(7,rP²Ee33Y9ž]BUAu+eTjKU0&Xf-Vv<߱b[MNļC#eP am}Zz`YlvX IDAT,KhՋp aAyłF]mN=HC3BsĮӉiíw:aQ*A!ı`9np\a0ۭX[s96 7ݝNخ:S(4GNhڞ6J8NL5#c `q&p8>0-מuRZ`=z~,C1P( B}8NMfkUUds[0oVVW} _Ի8>zOF\}1eY遉,,n'=  AsQpBJt\\\l$/Nlz6-4܆w;zcHDovvdRW?gͺDm{mbtJԊnl7~vbZM=w^WzzH:V^t:[c[҉]Q57Q->FjAgJ7^TsHӘZm EksZm E/jVfpdV3ȭd߃ۏDZ-yTo77?埑?;G_}_uWɗ~ɓdiiI \.d2T\M7Jdd2yBEU6$J2ti'Gs[J2W꯸]=iв|#\l'v,S_=0Fnn'<F<ˎE9]zi[5'F7Izc55Tc[8ZJn?4JsDN8 'a5X1 ?|~|so"D~%EߧRi9dFI hOG͹xx<W޶: آܤ62uN2ur{ɑǞ ~'OZw;8>zr*66JVkXZv6\{<@|>D"d2 ߯ڞG:nG^:nGKڽ'ry^NڒCj݉nj qթ o/}XF. umƊQC#>RFr83NbMmF͸RϨ˶zcZ^"2à^RbeeR-bf 0CՁ,yiT6ݔNj߯rh+FEQi%%S%ko,VSI[j6맚|#mB!il#׏t:-jZlujNWsl6UG7br4O4x:kVk1GjyܤgFj4x:߈:WZաzVsVWQ2צ8!+MMw;O}B-B?_!4A!+Wcii Lb_ }*e`td2 +l z T$e' ._7`Z8CCcdz/? y~/XͬcV J%ZQ<|:|iѶ}HP(bB ?es85ھjΩ,Xb{a%Za[}' BP([bF\!zuFBmP?S ZrJB2k9miԭ(hHP(P(8 4n(, Np**2ƆP7@?TjUp,ͺ--( BٽX&8Ff@-5YM׫:O4o^9 ʵ*9Zb@=HP( e9p,āaE4Ѐ@(6ھj}tNup*.\\jfJ=E$J t:ғE.,,`vvVz@⢪=LVnZ"`Pz@m0m BPrٵ,\ +kBC u%lCvmZc܋)8Nbddt FD"yLDBh p8 Bb B ǯIRb N BP(; Á=fd3`fY@me@TA@>\< p}on7xpkn[/M\`0 (<Ϧzb!N6OP( e &?~VjZ À3`Z-nM 3p/$x"zkWj  fSi εx<.]✝me+C 6v˧北P( s)nl EQlfn19 9l}m_79F\˸pr`.N݌A<G&A(B04,CDjvCP( eBqp 9`X`10p4n8V;.]JfS{q[gb/HeYv~Ka\y<^49"y2-P( )ѨVQP*P*Q@r`M zz39 C`Lf ;1Z,C6V~*]xdkȨ zxP,p;pm{t:vp$BP(Mz"4]$eY]oU+a!~J\6rbiFP( 0Z`X, BBŌ=\wq?x6EdVV@Uk  vxGP( @`X,ǂ3Ծ}(.TСQ*kd2( BP(]ǡ`&T-0ܵFFF, |^!KX0͆ PzBP(ʮb50 j*j*,V+8b:X\z2LLQ/&plZRB  G) B*.]Z(&NfʕU9Xpo&Cj(T60okk98 BP(]DZ #Àl8ep܉g`,j66 XZZ\<VzBP(ʮ>1>>Q~ lkBFcp( y;R7j>D=HP( eQ(bϞ= 8'z\ ,(C6,& .^ҥK٭2t*BP(]XK8{,.]rQ" `6p8C0|*zM3s( ԃ BPvv3fV Vz 0ԛopĉ{zɗb~FzBP(ʮVf3j`1قbZ@瞻~.j=h W.X*3N( BP#kh _]GZրf q䎣 8{ ^{އD\F^P( p8"˰٬0LpR…+_~<^K0s,\o¡C4dD"F ~\(y`kH?Lc~e e1<21n2 N'A0D6>ow~~`Pj}at:Ҏf9icN'"7#aTm\^wM4NgZPVUGJh>]N =Rl<(}-߫&y ݆nt󪾓K}].^=  IیNIiJcK-DNtNsOCm-5h]O;εƅujʥWpU76Ш jj*<.\0{'k(mP*]YEvuo^~%ꏶeNǑdx077';P0!bXXXP'Jd$hR7ߣA4SfH$Z>C&A&ڎD"yLT drSh|L"ctp?]'_ >! wllj!<ϓUۑi& $w#!$ xdG+ۨ^}Ł6EJ6ӯn땯:Mн'Z1M'>x<pjԎx7{Fch>Oq?ѩ}ncWmGg~< O3~̯ɓ?4GZ^B}' ypf y7sSx<Ҵl(B0T?JId bG:H''VvL?R8r^l/c2Q_mn >H&A,Ӽu+w=Ft=52V$6~٧i^p !_cmm |r U 9` 1"9[QEHՕ+ ,w|[QK~>HD'LKAb1|>ayzk:̌wk#IN^juR\_j%v+~4*݈ O&-z6~Wm7uw=jw#G+'D"E={'՞ZLI[<VR6o٧?w2f̕[;o\=0V;X]YEv5K.}oQ*#pvc#X^^Zv{0<<?. ٿ?([Qߨ( eӄV[1"##8x!]n\]w;Î=_WPPPPvY~כ?T> Վ:n:\6sϣQAP(RRdG2Ȍ BBimfl7k/axdfŁiLk\,+WP)WP*o`dd%T9A&ExP(7"j!+CC #Bp%Τھjg^r5WAЀeȂQ eA&(4)ʍNTDz pjŌzE ŌI'}C`Ft .-QP6ZF^T4mHD#x lj Lkf%t;ֶ_ۍ 0bf\.&n$cvvdRUW5ݚt:굍.mM&pݲ5oti]irPӧOkB#%wmն %_ѥ6^:Z9MnJ1NSrvu#Gwj 6 8?kVeTkeX-&0e 4E"O-GLO z7*[J۵쥔{@>rqF5sѼJv7lm$j?i8UO7inN}]X]͠hd2XX6cOCL?~۽g!`4/ZL˖rĒRkZPO\Fr,|7/zx7Ƨ-#q1H뜥%rIOG~ ?Eukgǿ|W|<1'0 z&3FWO>A>~{ȣJ~wG׭nrۑȉ'ȝw9bgoF8cDI^lxk]fNݴ+7]. BiZQkΨ ;N@вY{)X,&nb"ǰYNfY\7Jq زW9E+w)pjmwhJEo^4:>ۿu[uz 8=3lzbp{#$}!G!~1'#<kz\._(*h4vVU\Y^ukJH{<~i&H$I~~?t˽zif}dvQr7*W ^P(tVذS3j}Uӭ)92DYn>`PQm^Gq ڲ>t}iNcGJv҂%FbԞ9ёQ SSal 8^i i{&wq;;lj&z+9|09x ٷo@f.5 NɆB!<@\.tҷh4J\.צ6d}1r:oJ5Wۚo]ZoZJ~ B4;V m]{_td2 X"hFMh-j63޵.#Q \z}ew#Kzr\z^Oղxћ%rVOfgs9R+. ܽK3y||g3$'ɧ}c 8㏕"U& FC7KB l0 }I&z*2 BnҥK-?12:={p>Ӈ0daԱ_J r֮?+\߯|@zb~ umm b|j2a`Y؇0[ԫG2٥$Jo -hAPxٍ(KXxNl1o>ȍtP("ˡZ0@]"B1 0yxc`eǃp8LBBN\ƥ!+p~N*}jwc(X^^F\dBAPPUhԩ)ar(|azPx(>|3 1La LLLb0JZ={FFrV b&AQAբP( g}0FGM jzC4,jbz;-T( BB Vv J {N<4:F7 XkJa;{r0=}#cށj/2)r/^i/&P( E/UkX+q{/ھ ܭn7**,Cv YE9 A 4^/B(,$UJP(Nel؁ёQu. ^!}4s(lŋ}m_u3x"]?#ƛoe7 104{<  [,ns:ScH`H&6w^wStZZ‚Ԇ4{kmپO6>S}Bv$A8<<bL~t:DZt7jBP(uB,bl͇n=xI<\P(P-UP-Q-af9XV W`F6E"hY0 $JIP_oҼ-HjljW<G&iy(" y~S 0!bXXX~"rD"fpd2=NnUigۛz|HӘE*ՁaDQx<\d}/R)]ˡ%SrǻnB:5{mP(CM /".\<5b2cĺaFFF0<<=I|j6d25`X ,l`Z`6fL"HD)^k\Ne:g+ǥKw-3LOv[\BK2M<[mBP(pYLNMĽwmG:r p,˂aYU4Hf l~ρKRKjq"?77EB!}ˇPHz-K?*'9\f4"Să|AP(JH>FCO!_(_?WuԪUpP, 8Cڬʘ>HDeI&IY'*Ͷ_vfY8 R梤Yf?GVj+ছce}peu pjU*Ff3L&3 FrzBP(ʮÇ`,ce*~˰X aQh@\FX^zBP(ʮ#J s٫80> >eXp08:aPUa2s8A BP(WVPp{tb~X5èU+L76@VBA^d|JzBP(ʮcnDZ[]!ޏQlJ8u#ھj7NڬZj Ōz^BP( t U#S0u`<kL|_W-a@acRJzJjs( BPvc<*J N.RS8v(Ha&epT*q CgP( 6>L6 .l6㍷ıOP! j |fˡT.QR( Bu,& %ZqTk\>{BsVX__z> Vk/V" BP(!xW(5q(p};o\wqE\FVC4 F.bP( 9s4nu݌ۏA~dXQG~=w +*z p&ˁQ( Bٕ ;Xa8ڋ; ,̹38fpdl?@QGZ?ЗBP( e9q!(7@H f3z Vj5l0`0,BP( r*F¨sgϟ}xCYbT*( (K(W*+TTk; z.,TvZݮRAq;w:ŝg;m<8ߚvwST ~ŝgrcݚiqK}s#u;farb=88v1X8Gp͇aZW;0ێ[>4Jj hHPH-#?egN ]"!-ml|#>h۝C:-`~h35{ɵĜ_bW6or1$C.Vv;*Ŧ^^gb/OO#_;SH?0C#(N@KKp4\JaX'ktZ@<>Lfss?ſS1d2RpѼo"14H< G*5dIj(%H)Zb >Eq\NMۙ]gF&3Lf.TЁ UòHbqޱ~Z+FM`pBxbXXWNXD֘3ү^ߨw~wYDbT'Z}k#1oUH><ʑz؇Tj 鴠}>tbNFrm71W =\#j2:EzeK`y 1{x?D^fMw߾euscXg{}1rםǎ[L?@&Ls#'O$KKKvZ# 糲UۖJ5˵[jjU$@!$.kDeB!>_bͶY JǩjKN(@'?3Z|I*hW+On1m:Q! 8tF⾢tknXi+R'[+#j2bE bs뜥%rIGf'>q_i_Y?&u'w? aYm[ sp`^kuiz{q?c! l6+}Ao+IiUUnMr6E:-E_%x Btuq$ x&ZS<͒-N?Z:%6S0;u>/9#e?]+Z}PjbrqR>6+''GVګ_cWc6˙zm\$[vcC ~tO;ؘjƐ1KMGqp4Fðlv`dԁa;n$Rj)ԒNN - 1IłmqSt,V*8k\lN?Z:tRd  o44^w#y>( YK M/M^\71 wc##IԫJLM#<Ο9/՗_ERnŁÇWe39$;Zn +W_CGAq^fYM&7x}=%--j:h 4DŽtgA0XcmO33&o߮%7+-߆#2"B!ajv3_';-o$>NuSsZR~ndCA njt!33M=zѱ,b[wc#XyFInA[n(JȮ}-xćihp"h ԯ-oI?7ul667ul>l\\pAmm}ym\ڒ|Kd4]<96cK>hkncX|9+V445X.xy;/R"^>r)*^twwS #Q$q-ATȨQ 7!IְCe4& ~㠡 IDATs0Aք_KR256>l2]|:?|'ߧ.ώ;MeXS+:;!W>ϪkD@(;o Е[¤ }v$A8q J+(F3\&MƏ~O 㭆-װ={ [W^ARW_ѣC:ЮR8Jue;;)hmZf&|Vȼ58zjPD 5W_&) k1q oivRDOO/=IHv7!1񪡆japmmX7F$Jb8s<>|i,ZD&|ob BS@66HH)7oz=?6 -7-[~Cfߵ k3s-3T}͈VƎϔ;2iTFty;/̞)5]kt豣}]4iÛdxjQjFl#d1@i9bheFo۷E6T*S+ׇJD)nk\կfW 5PC 5EW^(dDk+A%#Y6AiƌCXbժ.$AJ0BC 5PC 5P[?6!d%N'ѐov0 knΚݬZb_ c HRu~?gk#ZC 5PC 5)1}^m#<֤}6@HhJXҶ H\'pp<0$I0Ff 5PC 5𖃟 0$\J".۶[r  u( I1!}w?i8|{g3[[űN'o)I{bq\yʡ|| }]`^gf8`]ogk>l;9C w5KfU%G ZK`^xK?,^HAknh-C o?O<;Ȍ=nux rçƸ`]}.8+9 LUVISkQjyű97.:dzw܊;![ f~A7'~n37.?sWR,9>̀{f>'%ϑ۞|M͝הּ/5qq+yG&[íG>Ul(XQZlK Nt!{tRz>w6GnsEpng_|mqS粬rD9&OBٸ\lcKۄl09/җnwLV|L/ N [/EsR}(.xM]<]8J<%|֬"e zXkJQLHDrGˏf;8R2I˳Ͱe٫g~M{X߾ >N}'yΡ쇟e߿7L4yFsp݃ '|6?x ~SYn^v#o+H#DA?RN~S~a>:}c2n=)n;Z=d+ߪ].~U΁됷=B.. .;䭂N𞗸߮~) ~nQͣy{lE ;ӧOgڸ,^5Ȟӧ3}6m4wj6OaL2w~F7ܕ$ol:atcjO.z{ L&ȶ6ÐPwec$)cLkΣ9hoqkGkgV#uLGN|P:Vo{7 -p;'-/usa7P;U_5eyUy"oߏߜ2W wq-σV^YN]7i=4f=%~g)ҷvo8:h72f݆W q,y;x$ѣ6@Fy M&BNXϻV! 7\ 1+")W\D1\||fo"A^ý?sXe2b4'n4? e{xǏ#Wwq+ zt!cgѳօ Y9~qrsߝ˥Ȼ{N*&N_ֵjO(:hR`yߐuh4Q]`gj2PgkՎޛ{8BKsYaVY" fr`v͚5kØ0Rb̛c'ގ9;ϱv~˟:7~8C3_)1awp5O΄ew?\T$z. g hF߻D'>>;a]ss/mhnݵyS2p# ce`kD7FݾY)8jp+]ٰhA7'C3ϥxE?뺄`xdyF#8uCu/u^}'fd.zV(z/ 3> _9[zGsѧȿ43)u6QQcϿ^=S?qv>8&ܴ=P WHo ;0c`%TNv-5=6S^Z*Z^3bJz=^eֳvvc&O/̥LZ6')whVƚDn|>t)ӱ瞛Ŭee)1$J:)aDk+mGa! L[6[t9FT(ֶ86.QqƇحcSOr֯~ϧfN,9cED۔C8}8^uڔz|>1?`e,wnKiGęo87mSV/nR/bu@NF L?KM ZSoFp0f2~1_=D|LQ~G>(=C,=.ǎ`k(>p ?Z߷2˙.k:cY =8ns%OYak'?|q!=Gz_\T_z: ;c? ~wG㎛F0̻Aǝ`b1aɼ<y ?0w̛`䘶4A2įt5&׳Sޠ Лcݯًh1.jr9~K`hԘQ7 3J$yR e\nA߁a?8~u7mJ_# w¸(.O~?K>f ܁9Z'S.}FLxi \a;ջg1kՐmMy=*(ߟ lpl˫kVn'R":y,.D)s.z =?SRr$*86w>wѻ8#ZNpBSە8bネ˨ۄ_q?h~n.F'Ģ޿s^pxw[>{7uMwNf/Sa}4:DӴ ٛ_+޿K_TyO>=OUpҴ|vփ;{΢ p8~~ T6qu&2p?ӏcWXҗ"hBN_бUV;"wQQ3 >0d ɶhbygr>3?' ]H^Nڹ??(͝\}v:'ałFn6PUiuoaRF7,,O~G9k/7\#gt 4,།S?ʍe8o~* xd]y'O+}Ёܻc~}ߢnO{_BS?s.6KJ%&dS|ץ|fͭ!6%}[,x]l-rS/+` ~AӁn8yn:hBr -> rVD}nH-ȵ~lz#˨Gz]\w#x(Cpg?Z%t_vʙOڇ>{ ~ ω`V AL圧康^^|?Ji탔-|%y;:;5c\ͅcw\>tВkDfG{,}g H'G'~pO8%|cq~ХP{+`~a XXua< nn w0(Vv-[o \+X0ane&rc3cu̙3ߢ| ӧɬ&ja룣}9cƍ q.  J-G5գTLqLNRYr5ӷ6kjhH!%=2,|7Cn׈A# ֈ\T.Fjjxˡ {!CzCH9 +vLsR_Q m"\C 5PC 5yfq\#ˑz.‘dYF4#twoýPo`'M"?vŦo*쩧swo]Wd%|N[Ԇ9Fo Ff]se\{6덍6߷1Pbr鵼 Oלo'kбr%sJv$;бϐ@kPQYt9|hz]q#z{!Mo6gX9o[j8ٱs]Lj}>/;?}>w ?>Ⱦʫ'^ 43>x3>{ žO>t}덍6x+xE)YiW^#ն2c w2;yxZp)q^Cכ3Ftws@2ۘ2?zj%=c5WТ|ScFfTH̊5\v.7j4R8E73qRWBͺw}bo87q{ogHrF[ZMi׸`J/|~t>nv27\u('{߼t*2}o*=˸ Gj^tMC_G׷__r ^L¼Yʟ^UF|vnط.9wǫ?q՜ѓo_ c;_r/= -g\ͽdH7t@xM;o7X~o? LĊݨ<w·~ɸ{Y:mؖlcXحsw^ƙ'}{ozo K3\ے]\W&WnhY^<NqHzkڐ͙lcvp(e`}]*ps_>8.jrt{ # \5khٶI :I-X 8&ɵ IDATmM?7FIJU^ٶgX{_fWkxY|̿sɷK+-?^\;ގ#:\ms RC\f/_VBiR0ںo;jLx9vFn~tN+=zlq|ky x{#cqOږg3N͏Xmˋ>:27ΝG~ԇ}u}:F$3adQ6鼁f#ɻ9Ws߆0< $RӞϞHykmwp᝗;۲$˽<~b7=nRv09nY!A[zݹ\vEžx/J׉\5c"= ?{8 ϕ|9_K^ : E|6ږ#."N^=#@kkoS'}Lc+k/[8.z0.n{sА[n: ɬ ؗ O? >\v-y@adbiو! $ρaC3(yY ⾐rZy R~~-,W︃FǦ:vީܺ̾$OOs:KԾt/A@_Ob_FQqeqA$;1 6(,y7_mҖwO0swpޔ{+cs>[[fovlA +ַ׿MzI!Іƺ<'@ۘ6(Λe5@WoBrz^ h@U im8%ʥZ[[f}obѾqO"=e9Kj<jc[CMf5&tqEL2#G`1@'֚r1dadY0QNo]"&j^CM'joFOORo  kjdZøێ9C)Y5k!I ?^jj-vߝzZ[1ąV6>/@ Q}^R"탔 R"DaۿJdYǵ7ڶCcnz=)%a"a82BJBk#%>BJ\>R$J!fJY0M9DaJ[knzA JFZ3GZh{L{~U戴Tme$4^W~Q']VL*+ \M.s~N~Rwo#B:8Vjm6V+1FSBX T^H}u=6HDZ}5 I|$\֚$IR <1WXt R#*(nEmoRhpu:i'U;i;Ϟa(@y?9h Jktґ~߰*I !0JA:2ڱzZ6VUʑ0 щBJF\Ekkʥq!DjG2TAzU=U\i:c(jU$qapS1JkTVֲgV_,ղs6`SMJV"Ž?AA%*`&s,K'k9;6(eP SQ*%4Z5Z*qp]:)<%a!!'}K>C pQFYǀrX&IbTM iSy hTwZ##7FS,QZ# Rm'{Khj,yd:Bmtj4x1q@}!oel!k TIۍc{u()PDrO NeF\Xi;!{i?Ae+;ξ qP*#JYNzN sʸUi$8%P*$G`jV/L: l6`32o{JN@.hcpu,ɑ23) \G:.㠔" C4VVI:^Bdd?I$@BhSWZRW <>h\Ϛ7mL_]WR ~yL:fQRg.qv40AX )}:m׵?o*ר!KIlb8pG.GVH(R(mYyU%YK;`MX,e8 !,i \TJ8$2DN^v\$I,9ѩv |ᢒIKS=t]sJ[9[]IbtmY(Wᤄ8N*q@VJ0%8cަsʤIW5^<РľBlR[9~v +Om( $%q$d2+h_%lc#%ݮ%&f$DcF8㸖ULPE'w-"$\8cI6ք\H;k$vnf?+q!o* uud2$P\ IHRBʥ2^/AIɞxdYۉ՝Ϫl;;`͚hhjvKRjpAb BwBL6GCC-͔!}"Ib5KH4( I$%6j*Ɛf!MCVdb#Hpce2]݄Q L]>O&$ƶXu$"U}y6O3t-Dx$Jq?UO mte⭚u6*bSq1ຶ29~\.f),ZS*߳',Id('d<H!dxK&!0n8^}u1qẒl&Aaa'gMuQ"cpp\&m@l63CҎy8f5RZZ2JjH I#esyI0 H+Y4G:WYC1m_qf}d5RAN$],i$AkǒJO+lmts#RJQI"+pQM:jμʡ6uf˗TEEVi4 2!1Hu{;~"vMIPJ6>u`iЖ2hܔdDE66k,S.\LM  ra3pdj-auIC]HJI #<ϣ!0*KIYHD+L7pJ%B,h 6xPu*2eJp\t<:}%TJzL R&ZG#֬^x%w]$!n.O9,S.QZlJ]izYiEWwW5>!etѮdJfM:,ٷ%RD%.4=|/@HA\D I(K( td5-Du]0d2\%^jW4oWk2׵cl&qKtqtP(jem~_ r<( Z*lFJ|u,YRڒC |'%Vo+%Hz8j'aq钼] Bpl0:x0 R(46뻖*钿PDQcJ)iB>R"X'&PC98]aÆUkW6{ѵj5p3.#G ::VUXC9> 6 mzqK=k\Qh5qM:%&AJ\IKmu-]k- ,$*MQ+YT[gBf:OL5kǡFʵ]82]F2HBdLufD[B6x9B eqp}/%!Zh'vqqs ?iٹ8.\|e&MX` o%zģIkVzj}eဆ(0Y/ YT| PFc uBY%hI6yiMyqBP3}2BJt.LiG]=%y2A{SRJ:BH%,Yy ~M_.^~mkkm! cK^B߶ olD Ioo/NgmNtvv{FkC)EoOr_%IDwO}!(6g3 5BqlYye]+{F0ed751Q*=g6~pKDv}TbPY U"-ˎޙFgfnN2Սh0Ku?RA':頳4se2d-gd1+kZPid2HA_;\| 0LʲI+l(9"12 YO{qI \J=?d$-0_,͛$}Kk1)9G&P pV7aJ VY %m4tAl/WW/߰gPFa嫯bH_b>i机=(f͜ʲfwZ4ص.k#BU)H'{ Q@HA{I1g3)%;9ee&k;&6,X)'%DNM%QwBΣ:31S' m78fRFW=!tʓ vHQ~5UYM k8j9K`!SIDAT'JkQdTUEU(#%_|Yж-!:2=u'b;nn^b#1),YlŜjVQTp:l.7x( Y BJ:^e\z9q4]ByOw, ,I!5s $YK-o_f\rqyI25etLz]Ga N eb1(Jf|>LN6-2dTU)ȤrQ4hNC vt]+`Flo܆0ãnR e],{Pzrd9)4uw{6=B<Q2h5P%(x 9e{tZ}хJkX4B%kAà%ְZ{6s[]W4UXi=jDP%U%w> [eCR UcQ iPvo -9=Ml5_~3nokK"aLwSkMb- 9#Fl17ئO{NmhxjM4̪_/9/V v;l^!)Go9QQ7WlnCzC{`V7tmGSYm 35[uc+rpK |P:O};n Jak cYVs~/X_m8 o)a8m j@]he%\wrOI#&",C2ݔLWըO_Vv0{Gp1WZ7泙,n{P9)˒H:ÍmK%Ӛ)?Bf$5c0jZU%֖hٌj%lSn9Gu8caw|>g\N(Dd mCTbZ2BQJg [NẁXwOQqfbrofڏҲ- {! ic:Ҷ-ESSڒJ_ZVShɢf6sxp1еNA|)(uN緦ihf[z ;\xQvlObĔ+% GikfMiV==~jЎMTQZ%E)arŒu.x"Ȣa@J$Z{8:<75}P~,dQO9ҪJZkk\ƒ_A<(4h}jXK*r"CT+d?.r RTO(~{#j̃k_k A.xsbwwwSְiʦS_]-NӁj-dlJJ^q˂9(,S\EϠE!YG+ۇ{bpkHR뗬V@wۧ >b*h*+ c,Ŭ~x<ċKBL옲O}K]WBfsLc8w^?飨Y]gjt. w:齣:!r=m׵ܾ*)p8e{8pquAS7xljdS;( #u}9$6"}6w1 )±4uoXZ^,tHGXnI2HUU AI|6nP5mc_c-.tGpwNmYN$hjbRV>66cD#CPo̻gJSXOY维(,5YeMrG-EQ1#?, tRռ8ffqSWA.4:|JڨB4ώ![3i@F=h1;/TEk D sq-[հ^mxHasƚ9p¹.-`/': j%y?eT064Fclf !ip( )4k%nGU(Y*-łnh=3E٥`h˩q1P5*Ϩ˚[e.4MZꦚ.q*Ș(,V+֫ 1v%ND;,bL!#Sl6@zRpW**( C2QZr!St)v-UirEe)b)#_b ƈtUD{cT<@|W|!D{x): VcS EaZ, 6kUH˼D>b oUa)˂rI,X4d9wf.`PUDR=&Εf\R TJ[Ҍ:Q֫4=qHKEe#9Y`VVS$GGB\n6rR-ew[O7ꜰ նxq&pg{.y4m>{L2222222~č3N4wP|}J-` yab6u|w|H.0ddddddd|ND9;w=Ljܧ2p ޑ=p1G28sF{ wo<|x>y[FFFFFFBι\wzzg!q\u{Ny.######s$pݎܷO?P<_Z-######s#qSIS978!me򖑑I܇@3dK~({皥7#######s"pOQX\GOe򖑑Iw>I~(R?c#^;#######s"q'$yCIVV22222222ߑO SI&n,ҕI[FFFFFFFedddddddddddddddddd2UK??IENDB`mu-0.9.12/www/mug-full.png000066400000000000000000021474561250406103100153210ustar00rootroot00000000000000PNG  IHDR5؂ZozTXtRaw profile type exifxڥkr于k! 2bv0˟>'cv*DfB~}j9=MPM|F\RN=ϿP̑|/|> *G&"R~լi7QRXkgU"{1~j%hOm9w{F?q7T8k9_T, _*nGNJ3{{l #g!N\\^_*/[vZ6/$=||_vބ))Tx+buTZEK~;ܹgN- zhL-vF=~rETy7FE;i}X?U <շXqo!夹JVڡ˙Dv 4sꥶ@)NժԦGSχk,)pZѨͨb&ZCS4Q12dji۴j 6l[G a1䞓>'GVYu%KƚeӋWo.n}<ӎIɹ~-yJ^n)RhZ8#"/E@Wc :LsCk|Ey@diٺp>=[PJEsV>=K>UGvg!$E뉶8:襼 ® WU6GZ7Y]>sR3͕MK7aqr9Xm%Ot&Am(~\h:R$e>:C|-u@ZZQPZ}zjs*Ec):p)ao:Vm4ӴHk>zwPo(ػK )gBDR ͞*#;p 94&ni.O4P%J`ۡ*Ұfp* *>aS:JC yKUۥ9NJy_نd5f6Ag )}ݐUP-n^-e[h$Oiz!/. 4LԨg9:yiDs xJIS/(~-4iWqgrel{cf zc[+ S/icq=\p"Q3 fi) Żw&W^FM 8>k9BO^nOxc]dkI=`7} ޣa+SCpǞ TQ*ܑ@T zP@v/׷ePpr98OjJ7X11^5q܃%urJ%:f#OT lltZJԔNUm5HѤo tG[\ $%;W^v-0pbl`!wk'da(Am^ZD;p Zq#;YY(r}`1,NMY`! 1 4ZZ5qc$o[6-"F-IOV)0=G8az H~q`iAgv7 {C (GVmQT h*\5bkYEFOAe+RlO"|p!ɏ!!"XU3U4 |DEw{{: ު/Vsi3zP0Zƽd֛2= BXژCQٰЌgM{U[&.AhۇQ~ XL`O\3`*Ŗ(c$i@3?]AwPBf1mÝϰʔ ZAőOD * #fZj1A̷QNB ؙ= Ero{{=mR+KkݙWlO@zJ@su-`&1J0 cDb{p:Lg9cbg"Qa{3?PM,Vkwߘkfj #QaҴǐ7:dF#jQ1@z3ZB`cǓAu$wnJy]ڝai`.Uň25Pzh1Xc !r.͌by:0Üvzd{Gb,;+4~{B`fH+]FP-fr## xh2D#] N ,JO87}.3YǾXY3FU1}Fo$,@76ZKT| ςobkz(FF!nu;Jz|%Ik!ZKcY<4_&[TaiNF"ʣIS FI!reuI>|BY!Wss bO0g0JA HYO ePPB )).,#ϡBA jd!kMq81G1 2*JF)\"Sq+=c#C7NƒT Jw˄vW  Ҋ`ި!O,Tz9ĺrpՕ3)ю/'p'Ă t""%H@F!lcW6a7H9Znbކ# ]v4pPDJ,ꊾBIi`eո's٠W1|\.&|BQ18ubDo"G5X L>b aٰ~+>H~t G!"ldF A?'BUĄ?4:`EvH== ,eep.rG=}4}{Fmpމz 8,3;^Dq#!@TCM_t=eS"?¨B'4&+9f@.{BiBI3 1$Wބ\l HF0H1~`Dq2s9C9}-iHá.v,bH~Blډbj/bvu ĀpRsBITO IDATx}yTձ9v̰3 ,APDܷ(OM@%yQ&nAY_ܒ5扊Q هa5=B|Os,ujV:"M3;=Uh YZXX:֑(4`vuUM]]={zgûm-mmmX,D"Lē^[UZ59Qr(G9\&qfaADk$Uv}F@X Zk0@ ,k-h`*_Bmlvw ЏhmGs(G9Qro@O6//u Z`XkEc-Zhc"(DK;h5`'otDk|_| r(G9:ԥKQ=Gwy?05-kD򢈈R4&HR0G9 F>QmlnDVY4"Z0h!H[kl` fGk}d2'셈 P_P]So}?:](G9QrtWϞ߿;zB'˯{iSrX\ܥK=YXXx-޻-knniii=鿢y*ֺOުxoAk03yq>yݺuY6l"nܸP]5GһkR^-{-荚3b>slٲI&puQk׮0;vڴipOΘ1FaÆ7AiK~|ÍS44tXk`2Y{ }w+u_h9}r..\yͷmo1(G>?Iuwڵv'xbѢENW7߬J&#Gܵk}_QQѫW޽{Ieљg9suW_}Ν;:p֭#Fd/=z|1nj3&//ٳgg1d7tߣGq/⁹]XXjժ!CҥKO?tSiiO~?o߾{]x;vtr9#g6U}<촤gF8&OЭ4[ 5+kKo޺ v ʉ8熾Gp̰CʺF]%G{{)U?~w?:suxfEC4__pӨF|]o<G9t?ٹ{/CIJރ K9(T*mٺs{^*/ޣa Pztي?=+jjnV=ߌ} G֖;cEjces.].2hI >D"'|AF+@L?i蓎g``mҋ'Rx2OX3{xގ_?%@3 O>P9s׋/[o?اu]O=޽S[oK zg u,hmKv-)>Z@cP)0* Z!fp)j[O?㬒]R^9h֬Yo%K aN>Ϙ3+{6oO ~{lڵÇ={˯[o>H$_zW]uRj޼y數?1q}{y2lذ|0??˖-W]uՊ+"G>0ƍ}/fG?ёG뮯}kw}w~㎋.(Ώ?G9|TQQ'_\{> ?u]v쬘\ښHvkEKK'w}Ǐ7_ݕ|~>+U4y?mb壝 RSs⇭=80Gy2Ɣ-ӻwֶ17zKJwaVAfg^KK[k߮@s=< "gsQ_|1"sZk 4e5Gݺuk,khhX|ԩSx㍇~{ɤygq̚5kׯ]wE4i7oR.]ȇ;&=0f̘9sܹ3UTT<Ça>ڽ{w"hjjZvO?]^^99 d[ ] TjgUm7ƀ5k 1cc|c~`}c}c|ci7 q٥ߨݵK/CD'J$ ,hmkK$ݻw//^J<+--oo]uժU{wz`'9:t(444y+VX|ٳ/o_ז-[{x<ֶe˖GydXPH?2:&L_ZQQǫ͛7qDzSg֮.W_|ݰaUVʕ+O=TDL&>`y ۷o5k_}>~e]tx[XXHi;n̘1\J)̛u;gΜoQk}X r:sϵȇ;&q+,,4K.WzI'YFkhѢA)*++R#G9r]wE**kkzR8x#m5t|`B4cZ0qOя~x”)h}F?"nٔU1"x饗>#رc͛g*%߁*++njӭ[E=s3'N<ڪu6xAtIcǎmii9u:=䓗^z̙3uesϝ;wn$vss93fx?xvnϞ䟵>o߾ӦM2d">UUUH@9Syڴi?pii֭[z'|@D*V뮛0a… ΝFө۷ZnݠA\;v쫯Ɵ文/]mΔ+{N:iРJk'6-{}QS[?=ҷjjk>w3pϳ>ꫯ666׾5:ڥ~衇Rկz]RRr]Nꪫ뮻./gf*>}: /z|ld,,,: 0~֮]u]JA 0@)u74%\R]]y|VnܲWX )?؀2 ` ~`ese|] …/To ,rW/k׮fA_D"'x~e˖Ι3GGuT݇ڳgϻ ryAS [o*!;F13f(**>}hD"xv\{J&޻;w-O0`"tM#G۷ɓgϞ}-P 8nb]wgݛ|gSS):9k%jnn裵osN~[3ܼ}~M0aܑn<R)7W|'pQ%ŅM^kWl+]qúo{'<1ֵd5X,֯_X,vԘ#9G'|1TO%T`ua$mXk5oj7bx* /R)KD6w?9YK/W^Xݻw9{7`ܸq]tQg&ug̘AWUo~]vW:o޼cǒ)_r/~ `…`M |'Ξ={eee'x%K-[dt1cߟ-r]|Ot]vHG:/2{g͚}}}ѿ%"w/-^P V~'0-wIy( %R1.֌_罳sښkmҳݺH*@<@KS[<޽{cc3AX^@CHƴbm Q!6)/HTO$DW 2"FէOXbŜ9sxꩧ™3gٳgԨQc}nã*++#C\-..5ko+\UU5mڴ>uZ~3`@y#~<|(8RyyV-~c2_huEE<ׯL2֣Gbή]Ƙݻw}"8YkW^}e)Fqꩧz]v2dȔ)S^z%D|g;n@AAW^y7NzK+k鯟xN}k1VhWz}mCC6?pCCCCCkFGqҥK15IwYgcǎ˗qlዊƌ{Zrqq1oD=Qt}[[+7mٚJzJkJcʕ|fݢ5v/&)/@J{=5)3kl" K/\['?3`Kڶ:@|/iP2mjM60&R~2)Cٳ'dǎ vqﳯN;wҗ|n:pĉWZHߜyݺu'D{]`E]t477+ x}ߒۙ6yTmظEӧ\//}Su@%RA~k&b`P`s>s㍳qΜǻ_`9{P@,X(9ѿ 2d۶m_vuN7ng3vy9̛7On[dɹ+x]]}JJV3O;ҥKEEŴiV^gƌiӦ7n8D㏷l9 VJnYkr{H|}HG 裏~?uuueee *,,lnn |_zgrO-`G%mw>=+,(8qN߿_Yqյuo·}֊ᄋjoc+׺ՂK5]ܪ8XV|?ZѪ%xP Xl[6mݻw h;&H%lYOҢx!06}{hwfT1"tm}W3ӱ1#^N(ȿFӧ?#w8pĉo{7HwqAk|aȑ@}3f^s5J^ziw!uիWWVVڴiSN}W={>}!C~h ,[_ٳ^إK?t~iƌ0_YG 8:0@Jw,"yF(71xW 2ݔ@8N;4:t8h ~fڵ׿E]zۜQ%顇*--?￿w#Fwyشitޝ4>{?[,z7_y38CYc}=餓Op3<ODĪ*7oF7t] ---UW]w_V'8{l7L :Rk׮t?}STTԣGk-9s}Z.hٲezy566~0nܸ˗kgϞ|ɹov]wq^xᅆN-Cr~}$)x۶mOSZ%Ktf.?nj?~^^ҙgI3gApWb1D[*++.\8j(xH쮹暶6҉,Zd ]^I)ٳg|3mzw5$ Vt<|w߽ym۶m۶[hQg8󹩡qweMN ̓{v-zc&]&}Ϸ)ߦMԂ5P[g;~ѧWpϟO|G?KhyAkOٳwǠ߁RƍhѢwy /fXr޽{/VXqgY[,~YFZ맞zgD"{K70}tztΜ9o˗/_~3yyy  IDATW߿g?yUUUĐs.XZ{WnٲO>YfQE;/^,}РA-]u]^{Ν;uMM5G9:e+J)kKX9Qر⎻.+ϕU5Ǝvȧ79ڳaE064?Ew.ZѪ4 |cKVܻ靃oUUGk:WUUId{I;8m߬]mv6nmmoIqU"u^^E ,Ӗ " VZf͚7xYW3 0`ݺu6m{?ç~3s%\BOR)fZQQ1`I& 2_>nS%O>?[~߽xҶ5hϞ=?pg'|?niʔ)z}ɓ^ZZL&7ocqwuwo޼gRSSEgR{G^2Xjתyˆ}EL9?;[23"kp]믟6mՔ p5̙3s9qE]wu|meKW_h$8iX[ߚL~lп)'=d`?䳾L` 7._Vz=zEHmmmkmHjbpֵ%%7񤩓܈yˋުփQ;S/.=Wzmn{>G݁=Tֵj^/6Ⱦ`Q=(4l0Dԗ14B5 +&ﯿ]Xw${7vXcLn:].}T"߰9$≢BHyݻw7[s3{գomniU]'bIw߹sL}]}sK+Xn]}SA(+O$D"](-}؝[x[nZ떖n!{(??ҤIk׮C9C޽f]噧Ux})/ýlڼNy}s#Ӈ~v}c>~O8DȦ:^-Xcu -=tӣGx޽~ Q^umoᆛo Nf/6l܊-\G'8k%M~+q.#9gQQ={QCW\I"VΞ梢ւkc@QUUU-\p„  0l߾}ɒ%w}Mrn>G9Q<9Q:pL7s??+b.u⥰l׭~q].5XG0Hc5ZvZckmKK _i(i͇}ޚwG =U$Z?>}ӧyѢE_?"WQA~ގoG"n[[,廥%QGg,ƘXƂ(Gx"QSg;vU7x1뺮Rx<uS)5<}.Qr(G OJkȻxL}wN9aK}w GJ Lzo_a{Z~2i+o|ncScsSSsUUhW;mk]Jwr(G9gҁ>Ԉ{Em{6I/֭<|T.ڔTHR};wU]YY\T_a@ԎӳgD.QrI_.ӰwOCrIkmzԮ]kll.,,I&Sx~+ycxssWݻo߲hD)TJ)ݫguMm(G9Qr/6o*JKV4544o][fm}{& cU}z}ݺ Qr(G9ѿ3a=u\HNнKƘ Q)8y_nCOj '"c(60u]?jFn*r]{O7b,}86h@} 8c~ Zkqk-NJ&7^@&z4qDLRuTM1z1KZְua$K\/`z&+5clg#%/7XuٴsgD2FaQ'Q?KȪD,r$6$^l^b; lBvS&#+D؅gI8[* )y cXIa)1Xka͂ݑ liziT$'nYrZ$[&ńP< 9fH8Ai3$PFZƠ (Omr{ $4eXHM!D<#17؍CL7e! B\6y$J] D!Yv?C92Z'9KDn2SRX@"b 7rEx,9,R9<)il(NYȥ0rDۮlD0EʘY;`˜&I4"0!"*)! H1mH8/q{&.\&5B`? Cς.yVk]{qda%bbs +J_:Z̄bl4fx$@ve0EMDbj%^,0B.v1" nZ ܡ sT<3_ @sKH?ᥲ[LpŚ-Nox8.}Obs\cJkeLc2ZTZЂ5, "Qh * ǡ*D``dDG)' 4 ڣb-̊4 tdaƸ!@gYI,'F.DC)x@#yYOXY`gvJ@Zkʬp@b,U ;T+iI8\$=GJW#2[HB&SPQ^dɤJVZ+ X&d B"2ۆuEQZa,0af"aF] ^#^Vj R/6"=,~+K{5܅ S,]RQKVqSZl)d}ϐш-8Mle#<_.x) q@śbTsp ;=+H>E,rrHb3 Zgj5ϼlH%a!Ńgȥg!SI;څbQQRn-yU`5q y8$a%iZ CG "auX2fI0"-oI!y?hVKYF-LjYPJY6U,Q:1h J}A**#i 9ވh:"hGeJ!@{LœbV31C09yFԓʖ;vC!!LX'=4BZkD\cM*@qT0`hO L wP82~`Ei`,~Bc_hp4$;2ذ('HH߳OilAf-,*kZd@`N JɃfď$fgEe{<,$eQaQ!`:>:쇤xҗKsdCK@D[] 2DQJVdٞDF-len/_-F7q< Bko3 $1#H!T1V+FJwvnJ"DL{@?H!B:9-O XQ"]vIue`RQ"䑤-D06 kO:bj RR`, +'̚%M/7Į+]e*bP,]f+2V b\FsbW#v3X\q҆Hk"la-4 -><0iHfs)a4 x @7!NIG Tz,Vqap"RQvg$yE{î҃h@54XZ)u@!tZ'Xu%A."VJC:mj)Q$ .E`IXƌERXX!:3bUCo"dT1x!f3>XY6lQwOg&lfHlxKB2bK]YZ@mR@`gbi4D#EziMGTLaE҈ȫ1^8VOUҀp&ͣ쮸k e) m&掔@3, "$ %7J2RaeG Axs.A@99w"dB`E5RA@L$/(ms} < 6;zf>ߘJDrҢe^ Gg 5Ch!؎a‖V9fH8d3~YgU'`3r+V,4 V2O0)o)Pa)頍b0e6k-[H!Ϫ*Ր#b/ ;YșV$mpS|8\.?KGaHiW}}ysĈFvTޣ"51iiV&y؞1<*$HͰ4uIRZZ¶Ŏm4,yEΐYiq(EC(g%TJe 5*0u;.^6RrKA H&kт8Vk dU9J555Uָs]YIĒi"∪J {%Y^ 3A4ERRT"=3DJx.KGXSRRbC( ,R K;bӘ֦%A`|LK =cVh}vHxFa$7Ҧ)E 3SY:,̤6hg1˴FQ2OY(Q&>.0$$aPaL.= E?q2jB|iADJJ$,8GMrI}g1ɨ@Ҧ3?q:הq+1'%͂7@ \psVCr!^yY_v .0ev"J)KeʆKBqU`|=3dŌך'0J IDAT6f(@lN=20( aR.->1jiX%󲼸L(_2Ae~p*~[МW B,8ڥ1jv47jr Z@Tg(kAĶĝ4 'VZ^(pBF㫓XAX؈12@>,rׂ,,i,bǫz툣(* 0^3 <~>[T/rv K,F`4YK5s;٢>#PM&71.@&f:V"v6m~R8XkM` Zk1ȼZ: *+A`28l|GQQWͳIRB213Y &-` "<,f!]2U$v䙴}DP<d#Daxfڲ% ^+,,8^O w伨+PQ K>1/5£i@H[iO*HpȃbMMM%]K=/hmk۷܍DV9'd 263sԗ4UJё_B(`AU:XtKmZb&^`k1"Ǔl٥13; ‹".B|)#vl;`GZSZz.1[(.o6aT,N6nKHbCq7a vv?s"BLs \bˈMg^X)3Yɓp'&LH#|y}:sES)8#ላU;0MW2hiu? A _'-sۄw0dAQlEKL0Й"^3@e ioG &(K{n<_~ڤt#jNء7VSSt3D}kQ|}?G7 ǡZgϲ)~d% „^2o&H̦L{G"%9y$cXpRerYEDi%S!)py$7g{Fi*o/ %c9~gjĝxpϬ2- ch kxlwqs \^<٠yU],$ $`ψ$Ew»uVd蘙lRmc҃4G{DžY$tQQ{ D|3Qr˾\8 pw4}afg`3C=~,Ek (UyQJP)X qV)(jGmmmA`}?hhhL$Rhݹk{%ZE4A^Q@kl{Ź%iŤ"=Y8f`c 萗Ru؋56v*'0L=;v쨩&LѼ9r7f ,NF2_J)1/t jV UdI^\kL2ȞWGA֠ 'ϔ(7gcRZlض萷Gy*=b@̧ؗX؞`׈aJ4 pY Pَۖ)1-8ff=!Hw(Ϧ 5~4wbfꄿg1X75y.SY'@5j6>V@G`JOaZ#(c@)RckyXWaU=1D\q]G+n_ I;gk8Z uXRiYāM{(nfNذs q# X90#nE&JQ7q5M"+ZP̈Bf f#;^4bJ/ҼR 3)R RUl7E) *k81`IWgk(KDG̭𺰄 [0H a}fHh5e*2l)ڰ퐡#NyJC;[$ݝ\,G*v=t]u;HB aҍ$H  g[:fqD"A`HR ٢rJi陬ZX`Y$6JkMA#Ekz@^^/b;TaaQކƂH$#kiiiiyU{J#ŲMОAD|?Esf7"DAk%Ȼy",](Va@YHd6$`'`s(\4; 2N āz)ctW* ]nL?Ow!EKAv|"&uPvĊe0⑨\Ul..c `nA bQ&2h; 48lCdkA /0$-$U!X*,DD tf8K 0j~~Ƥtx 9Õ"mmgZK@,}1䊷ay1CRnAl0d);}?ftC\XwO YEJ>9ZpS )Ŷ%({1|ʊ>MmtFdeq+bfv)/iT! UQyQ$yy51Yg,,'6w'%Jy$Tc \"gdz$͸֡`k<a\ j<*>x]p&Yn*+%bA:cC@% L\u>Zoo ]UJuzA@nsGh;J|O)H:Zk|29R0Z@D[֤oBvM-H$sE !X*@{Ju!( @XSLֺ߰ni\9@*kAfjUNX ,.8|,f&{G>=yYp8K+ӕ<h~oss3g2^"0MǷp'2(b^91hT xNTV?! qw&T`*թ|gyb.3wzPBoh}-Lb j X[ @-IlfMeZvi+s[z{ښ(?3˰ RHPz4\,ZC%SوmVhYKiͺڨ o߾}i̲<~ӧ5-%2oIQ Lx;vbVSNg-jM]z'On~/yst|w/45fs3px,Rj"z',]$vE$k u Z%wYH#4  7id=F3YDwu.D@7V; $$9zSLl[9S?/W( AF48zTd!m:rD#؟AN%RxŬ0KaXG,ȶ - +;ɞ V* Y #RvvAL! jXWL*M(\DZ݉ܝɜ91jN-TEͪDHFCJ )޽k?GKYXcfuL<;;ى 2bX#J]^_^_x2SPfٳ.>x{/og 1nNo;q[J\|TGl$*Y~b13MqH۴ZWH-D6)jc1dLbԃeT"oT{ i2B~,@l`dpYZbd<萐Nй9 !F{Tk޸ח7ʢ`]0 v d] )ԕR+0Y0NpDީ?j]ЦC6ۿOBv4HcKؗw*jϪ>p2m6h!܏m 7Ҿ2䊟N\WWC7"flNاm~knwo;AA:pPB<`aMSm̜D U0%Ybm7W9[bjZ5C"(S}{VF(%ӗkU~.&``hN\(ډa(hXzmD9gw+唑b'sз٘ 61'Hļ YF<*gT޶:yẬ*Y@ HR(P;C` 0AطsQro\iV >xX 9Ȇ& F抈$yS9f6VuqM6q#hH;H'/ssY,Rf>*Tء|c[,7">wxx<~s]]#"#U>MgϞ> k6U u7j4 e晆X8X\,&ᨪp>0B2P'VE$STZy$XXż3ãY3XXmM%j N5 \A -lTYqӹL;ŋ c`[)Iy1s3H!bqP!zz ɰb`]mi}Ն'A*8vT ` gwbﲷb ˦^W OWot }eEP,hi:Qz@ʠ{S McLuR`BtEdI꒜;qRf){-2ٯ.ڦ=#"+7EcdÀD.!hۆq7f5)8u5Km4":2wo]{`V;r¥|r Ʃe1xm~:Q2%нLmv*Vfj"!*"ɓBS->r^/?ϳ<5\aSvm m!iե8'MiSvO 1cxZ?xHU˫wÇ]\)[oX8(gV@ܼ+6ϡ/`'A'w@3pV7o>꥽`ԛ+S%p ETY7':*1fjG kNB:+A6*@U?狍*UHV|Lh{X(!Y1ZYk8HP"%3 )&AqyO`D:AoLJ0pPFhpŀ ^2A!]J ! C;)n$v !1WMr<o6̾= MnF8j+hfb^]PiutǟX6!ѶŬFخa^Z"(۬ԶȣZdA[ߞ  qN幸hVb,q9d$D9ؙC ȣh_*b3z \;CӫjЊIl{ ɻco|,UւFVFf%3\)VT#%^1dBAI=L dH|J0ZgX 3<%K)Њ9)b:#H^Eϙy R(DͰxlplUh W#ɑ5\닉4 `xd)ApJށZ]5uV}eI=KV=[Y&-`̨okyPc*9Ngggyѩ֐Emc!af3UG(Fqiˣ o@#3YUd8E8MyyI;&aܖQl*AHz;'O,BW0/@sY`Xr_?t/( yD,TF;Y=P?乧~ C))Ƙja. 4^{rA(:*skc[MT/TcnD1w`.P08t@[e;Ě\XV%L}P xKȀY 𦓏dek+n&5y!al;D8.ժWkD-WlS_x_|W_~Ջ/noo#UÝO<...H-nLnb% Bp̦FC¨𓍇fJ,7{^ y-"XVi8Ӵ,4Tۨda,1}_  l3yN16 4JP!_iI:й`r&0֔5V畅^b-*/[)YeycR쌞Jn VQQǠrZg+9BE/̱l0Y$H%[%z?eS1K+Kw5˟}10pT1vI8K'"]^W@Kbtu#TW9Q7 (]$TbfM%*y(,rzy،ި=.UW$T`nBnpvpl0XIQ%QUvIjlz:{%l 6 Q&/1 Jq3 o6\a26T BBec.۲1!{ꥑC ^B ʟ*~pu ,ζmj7 9MhBi'lƔ̃ 0} qoV-vjшDj(<;r.=P[5UUdl٨`AJy଀֛ݗ_뛛y< MKBD4"ޟ={v~qzg]թ%Dݱy&yζ\qFt Bo^zͫ_\<|tñH:q}ݿyr'~c,D~V\97sEdl̉EGqfnw&Ys^ ګp>0j*%r0j-XZ@XCN`Fg21$Ёgg6U> 1V<_<6 HWh Y𐠪ULV0̴F2T>|]92+ RAQ-1;![%lmȖDzmru?3#.jֳAKD3sMI# k(SnTi-* &YI<:{`a0E$#CcX`@mI6nVQaUd_c 8.p ( w7[:"D]tB*lmwG.ٶ8Gita⯍?'}hal6Ai#.x.֗ ^E[CJ!dcd̚F1̰=%|կUS":M?'ONePFQ,Ŝʋ:[cXZ5*2xp9޻N뛛?/ѣfvuwisZQ y>Sr⮨nQ͔Kj7F!ܲEʊ1`QPL &#ʴU‚ ˘!^o*aAc^e+ȁ,NkvO9`T)18)91^jsatA+$Ndv[omA]‹NN` NH% 1ب-b;僸ieٔ!`:עF//RZ] MA`f$ZP_6"23twQHW#DsC""^,D0TH:gfsÚYB i{rRND%^оQ ~,|`KS)5%$JRk93FLT54 HDB_#Pmq9Z:,AXega3hnTgի ۣGq\JQ#P>FWC9U/ӣ bGXaWLEx:{o \pX9 :@EHrFS/ce ǀdsɖV*lwH x<cI{{ia}1[6mȺUQ]4hFkJw,d)کUlKw!jkfm-FY_Nk7WgOif=zW//޾o^#TDB|Uׯ_ӧϤLik6c رr"LZD/(, !l ;́FM$ڗ_~oo~ko~7)+isI+d7O>:gCv.FG`flI?jQd5,\90Zv`XPk*:0JRlf0nԻCsliϚP)'!B5ȌܢւTpTVB~Si%>bcKww]i>i3 hm9 v,s50XЫX-3f 3gc'UQ:fQ9GU{k{GB(F!-ӧQRmdQUy A|1+Sp+TjUҝXVw%LkF)3\n8KT#EdSɀgF]B Е|ªs moDNȗf5 xg)ٹ vslӓ'%fiv`t UtM[?z_;NBpٹ{GB, mBJs U5m~e:%Iul-ȴV7 EetpRMXpd-]O3[w`)3}q:ǫRm4q9yBR,kE.iߪPaC;8eWT;G0GШ(+hE`bz6'Qu |=[.d):_Y@p; Yhb9!;`䲵!ZmZui"cI *fX _m+iϟ?'d)+Rfy&™{5IHAJl3(t %Zh3 wM~5B)q]S<ZWզ0>'0K}_ӛ7w977wK,zaя߼i}z锰ƶhh ԫ%W2e8 ,3c^q6iS3;N_77;_xqugD_Zѣ?<ϭuU3]uk&1^mQ`*c̦Fz\*.ARfV4*R0uNq0֟;ˀۡGZ=j6E5{V1aJG~.()EEc/yG#[( =PI+!D$dI6l]bf֖dSabQi|滬iUƎ1I6=hԴWj)xBC!3u@ KfcxE0OT,2sLU {qNj֛(uvZ4ՈJ,/aV[D$bw] UZbU2VB 4ʱJЬCSDDMDK%Om Yڍ]QYN2^#<>7 @$e0*lBNM;EOer,_Nv>\F ,dS7|>:dLjqb< | +Y o7U&XBC"WC'K*(3vJ1[!ufFmqcG05~5JV&( s tn:s@|$hW`16un\YIH z2+(TXeuexrwA,8+9 h8e\uս{m}1y^k&Pfvk 9ɛ-F7ՂQmӷ@ s-Q@%ȑMͧ߼ysww6Bytr/1۫CdapvEu-L#hU4,"٧qv:޾}ů/|Wo^xNjO?J۬EH}H y(9< ChBj0@ucA[-:mxQDz_zB0k!$dXDCS㋰my9E[[ R)A w8w$%2"NgM}ӂgͨ?$52*(0]`,%z2Iه>AcQp J֬ 2NNyVUR'@@P换j"kc' D߶. r 67J00 :; R QEe⁠?nfc'v "8Em ]~e̞h)"%V]d9#.ad,۝mLO=#jU5k3"ިB ٩2+ǜN:`ǻjKM)8q6Yc׫(j֬kf2qqvvaThoH  !vΫƪFR$8jh]!H8P13Byk`"|O1vQP!HGd#( 2թ9{3@^zTH' DYV!0a`NH;Z2 4nK &\ȑgϞb9@tk'CFrwSDSMUD=Te Zx "#|lP5fSk3o 1Fg;[2f_z}yyp<8Ƭ*xO؊|ic^%]"gض֔˕,O>3G^ǏDΦiZvj `*Ap.}\6,Q"~)>p2W:A:=A;vM1@h%> *)jmA{9Ӷ2 =9g` VE?ttmdV mb-,if>P7" cnRNuii0Ȥ*\ .W#"/Z˽ 7hH%"/ oU+Y7<7g0\nvlo(Hn2*= LjNqyht G՗P0.X='Xf^̷QXUƌWpV+TO B۞. MuȰAL`96=AAv*Y;F^k'xJ|cTA7v˶cewWk + D5t2mx2 bUb%k2ژTD`P;`?XGρSD?-w˼pEluPUɯTKP%\hf*:tB$JG!rf,j!يgsT6GD!i4$oחo޽yp8ٳ?|ٳ>BBcr<ϗW4Ϸw_񛫷>'ynTvR{o+!y :w)GZ+:]"޿yW_W_ջUG^m IDAT\\^___]=Nvz4+zP5(`cۦڊXPeBQ&UPo3zZ ea WH&F(hSWxi~bƬdv$aAygڡFP, DJ vۋl,"[CKZ2Zȶ[4WܺH,YP&Y޾4{;f3LU"tvQq2ٹo"P!=aA# y.R|}H#ml23rBy=`>`W2 7c*DMB*@T5/oTes`~QpJU 밚^qIg!U4u@եFb} hJ#:`H+;^/=$yi UsXOA`YQSH ӳ2*{"Ja־݁]6Nٕarte,:&K};xxS$nRd*`#zDBQ5fΠ@(G܅:;@#T?ԁ4ˡ,ll)2'  .afȠo*c-Xnu]gPݕrwwcUyɓ'1<___3yӧOw=z{W/WQ0x""3jGԾ?q>xD֐}Q:j9d`3#NOeK;<O~sssvvvvv*|7<"r}}ԑɗ?zUko'}|Cie*R-: ԨC2Lę^f5j n[ `{h^IQF%ƈZ|qlЊ{aUcz{%繧iTMmϟQʥuع";uo9B 2ljfR`E5 l{ f-fU03 6XrUP*xQ繷3U&>狈 B>$(5oHM/1[3k]*[쒃a$ j(!#@;K:ms=1)") Jƽ؞fļx<6-aAwxP%"TŧT %Nof+̬rNT`n %٩U`&\zC]s yt=tI,?90AyTH{ix<" ߍ3ȯ@¸N6ZX2,fq P0!hŜ) ?M v+Bc@^MdVweE*+^ǼAw1m[cػ:(DczNǬbYy)3)V({JҫhvPh/\kz?Y-'y(5 ϣ 1f[GQ 0SZgS}o.߼|w}3_| gӇϟ臟|gO=<֚o޼|+&>zhqTD%Dno?ڧ?ї_})D޽w8on~_|aOۻ4ϯ^ݗ__xi<7W_~tv&jST/.~}7^iݗo]?Oruyuws+1^.h6ra:o:E津r,? IB"tD "`ô4yssp=0p2B(@)b6b Z8OFx[ :Ks6ƫ 0揿`9U c5ȵJ310![SxAa$XU[-&TؿoAS6df2FnRU-_GC)vQ*.&)2MCC[ 딶A X,m[Y#QHMPpea[kgKN{xZBmwbNLx<h?xpӏ?O̦ #^^^b.t/~___&Lp8C˗"*M{3kww7s8?Nݗ_{zxŋ,ե/me'"tߐ6L؀i{ W>|dA 7I.M'lj)jdͨ͋hS|Hn 4llܯwX{1 xb [1U:`|EFY㸒!v0 ce pbYv]T=[6xj.B$ cqBBN?|=`ʔ BU3 HǤ_Сni)cꩱL3@Ոvꚏ@ oiTJ3g`j g̱C"E "w?QgRZxЧQ( PU/~ͥ/1ggoϟOr{{Igu z=X`G0U?}C_t_Ӳ5$eg??~do/K$Mns6Beţ^@p''ְyOF{q)dy̨ :`Q,w!R -gLUihX򫵖^ C0vKB4V?Jd\q|j C͹CN^Y򳈈ug} &bc.5{Wԗ /2ZI8}gO<%ZU|Cջ__J__10i}{_0X,9X@EXSfE_0Z/-@G,s:άX9MnPEu6_ܹeʼA!XY6R - rC0$XF5{9ędVJz!dmQCrpid[ *mf gK㱰0B`) ^Ajc:-[{[KFx '"2dohV0:kHes\S6f@]M"Z_;BDZ֦͂7ft{ktUz{駟|'?ӧ=jjݚԧҗ3 ɋz˿B{yP_TRooi77<ã~5ⷫj}?”GFڦfA "+l W^ZXD:NmTYVo*:M-QYu肶fiRqY2$liu)BXb-`-UucH0mq&Z!!*bciÉQKmmKEÇ6c'm9G:m¬E w^$uIpN^DT-D"[d-ˤ/wc[ ÿ(eNai JhYS{`@O1c%ob "\j9{l*Qo:^A*Є'06P Vn-c b*iym> & !r_Z7Ə!a?N ""yt0F(vdL5K63r箪&%"8sc2G[9H^J 3Q 9nu N9d'>ۍ59;:7*-xxmgxx&P0S"g: )ʁ8wS$jh3zt:y`,s9jD$˃\a-GJA@Tm y "?[bQ+*m/`!S:68[؅27}7263PlT8ξPS|,G{>"d1q5c+*t ֒I"SCfv:!2hWԂ;# O̬%;O|S(La`&IКjv_+gFѫq@|?dQ`/޶+=Ԟ9bc%}[M`EAD9DBg9l@1;&W H"؝pJa 5؀9^+X dBxRg2,ŚöQ%aݗ(<hwx˶ z/KZ{Qp&-U%ī*ZbSyWiۦj.2|޾z۷wwp81'?gYon5ί>(O*"27/_&_?~JFL_\gg\<;T'L-\rj<\U84 /2r5.> jٶj*hP!lck A J$A:wfX ;u72(r\8͎'hyCUpXkFI `NR_i*UED4In[᦭_]ICU[U|֥.p$B"2uUͶ$*a[Q;@n3A,Q\iM5 ;*\[ǃ=[$v#Pؾ Le( d`|5WSc0YX{F}cȅEg?{0Ekv:, YXrE,R?puj9ǥP=ً"R{Ә&`b$za oP+GR)6x A rN,/J.p=YW_,h;$?Ix9ec&g52JΙR-FU`RV[+fP向VGQ Z!nTa϶` Écsd9eaӏan?±.(V6 #-"֛JkJؾs<ܨ@US~d\,S8/(45 Z>˺[FyofQ;v}}7߽<N7w7w%uËgΦ?'ZkgLU[Ѯoo˿n?@Iirc?_'.^Uyٔ5eجU"BH,D{{5m:S3C뿙FPeS%B`Ӥ@h Tm'È4s@t&mEt_Nݫ Y81 dT*/'4(:3vQs8%)b&JuVwo1'XT$1t&պB,YG# XdVX&M#hu1^lyVlm`fA I -ə(T浰ńMy)E\Xn<{= &ph.`'Ȩ,'Uc V`GFJ1q4_19]`lm+ͰL;?/"ȃ}jko%f:%B̡l<@](mAW/\^^nϟ=?ٳ{FNdj> f fkMM$]z7U[Q8 |qqyiv'L_LEij17.|磏Φy ixFM)M֧)$ܽfȞVJ⾦nzhza#= )@12Bj%mrJ" 齹D~~ѓǏ{vyyyu^12٧'!!xed6GLWfZe3gT']Ji`PQgT ݺ vW#"Z#$v-Ya|f^cXӳ3VՖ# bY:/Yc{T- O%-e C_c3 ry6  =RDj(>`?VY>mPp7UZu 7p`Bj[^o<0r3<6rߑkBj@^?51f.jS-j0Q(Re6Dd.ofWhD*tԠe^$.aJ}R't pW5Uܬ܆D](<)_0g>`&`"Xl/UjҴLZ|QH@ Ai o6;c`*Ħ 1ޅ+fg*i}< |D!iӴVuG XJGc"ۍ1hc##"gHSkjjv~~*Yt<߾~x;gO>я~|كT<Ƌ߼}ËgϞM>YHL){_=!nH ԈܻF{hQ yZtz%ìӘdli)ӬJUf}q}ǐ ؋'PV1p1diCDu5Dz)kMLW]f'& J՜3@zysؠW~CB6ZE,$j`̇,o/f\<>>yVcRw&3 QYI]{Yi;M^rϭhi(qͧ0d*/gyȵRP{lb>B;WigwMi Q-)¯'Aw*U OrbĻ94v >"{2TU%M ZkMrlFm c j2otyeYU&x/ƌ "'HLDFA~j٭Z*TYNЭs@i%R"U%MX($vdBfV~I2ޭ{a}ZKP4N_{?TRsaC\Br/Zwٻg[a`ߤ0C8&/<BC}V}|SQQ` ؗQa39an}gb'@D}߯={vmmm2Ngtxkv} i@5ݻvՂ KcցU mGTUE$bp' ilB&q4emŅ-7ߴ{G۷'ɴ#FZsk#ˆ(%7+jw6Ԩ_xml{sI\p`F.sHUrѐ3dqX/*Czefc,vU5oN=3NDFXPUm&9qhAu|[ubם|K<[*#!`xlΆ?_ߤF=L >/,c۟T(%զiEO K|@m#U4C)$^`!*K 'x)DbA 3h)%%,ѩRJ_4bPk)d6Sk D ^z , HOFD4Dd,\dHE ![[J%D9dAP3f?eW>+S~w}`!dEJ +8 ;"m&DRd'%}Bq|Ъ%aGQۦSTw|R\anXq'b,<X0`JnCWrXK1CppTR9.9e&9 oMD@]ߡv"[)LIM{Q{qB$"Qa`gsʱU$ fU\*p]m -&[OtߘųbηKrn?h~xޔcGbr뻳{w49% >8h8ML؛M/T㐑v =HXU]kUs ?H#ԮVKP1jTU= JjK ߂?r}{WB"Mln) {*M ,HYiBIruŋϞ==uY=loOnK?vcZ"^J n2,//ݻEGb)%Q1fm428qrkk> c"J)Aԣ cBmG. slBߣzO-Ti-Œ٬<)\G\ź պ` ! WyhߕJ"si1q\C,MUڶ!+Ð-If6TIԸ\+K,tnwwA [ UD/^⼱Ijs`f6,4n)b;B?7)>e\(hCr K%@767ϝ;wܹK̼=>!P6+G:|/,,,FpWKr;˱cIlKtǑbBV9yVx>c}/C}=FG޲9r;+g][[۷ouuee<G M *$w**eG7~ fJK6+=i.izYRR:'"B_3! @L405Œ7)0y{DԂř|#"!FhqDĉ6Yҗ͏hxhVEU k!"#UiVb. Keڽ#.ؚ={σd8܀ݶC8=" 'dπ+ܔbT'`L 40=C[F gQ !fXU"Qeqp:1rpdO*s(o*3mB"^Q.y%1DU˔J؛T?K .^<+&2hV aʅ8r&% Ye"RΒ-g!SBVD9>R7 aQ\uU_O9RSEBF34K&XBYKI f gbN9'KkVi i=68 Nf=ѢDrfKw@`F+ %P(Ys9P̩ We1W ۜr5I*uP>UFH)I>u}B%t]Wn5Hn4nn|#vޥСCkkΟL/ؿoiqmt:Z}<bҮL0NAL҈!bƮB!(,*8Y fFF9NNI >fP 77v&{FP`+, GL%u0+Yu*IO 6H NHΈgd 9 AUAqz0fiJ,9儇"P, aȐ0+l he6rhyp(!Ɯ%6MNw=3S=Pb~Yd`5pYlqpn쌗@ı;cĔY5Uga/Eڣi(k+*@8҂CpYb=)BR- f.X aNIqыezpT-B5XٶEя5TRA괪)v*@qe cf;0rbbqxX+ULXSIayq~3o CD 0 "o8|?D18^`ZkN;!Ȧ9}ש` ^zqmL)BU꯰u|Q"A Y|X)fL47d7QS!l!'mD\e^Cm&$YBSDPQSU JLC+ەP4hf6U[rIMRJ}Nĩ/UV)ěZ?`dBbfSW<;~ D^i w4֚ GUC *IH) \HS4ıʺe6 FUXy\"-wcȈ83,?;p!}T՘@? p70c,]S̱ d"ĪX_Pc]9e3ؠdxRNC xHvv,\GlTX!bD"8i$ۍ=H9ṗkgCm YK/O3Ku4:Wbb>dJ<8G],XB>1 H%UK&m^]2LLcd>yzӔn! P /xzR»VnQD &j]J\yG(ק7U|x/j!GzRniqԏF e^{8F欬HHJϲza,$XEAYUdap]ׇ>! >TG%SȲ$UE, ]TId1י YesM~ZT3Y e2s >n!f E-P_P~Ÿ ;I9rƎST G{:T_'[j6J3TIe #ا(>4>oaZ(ogȺcuw+ؼ/\ȇ7E?H:WSC)ʓ$~ c+DKClܩOT*K@ )>c4iYT7+"U՜ʰN9#gŖc)>%|pu. (L7'J-y l`!tԿKG-/fy<uyQVҔ)1\b)cbb q(ݑI3u)DCq{ m)seDn+H5bZ%/+o*lD;90^{ЪZWiVv7/}=/v؂e4\G+zjC/{WRebie H%>B) oBQe1Dճ0Lt˖bVuؗjԢZ^ѷqrhOXj8, 1Q8&4 E69cHܶ#f]VAEYrM8M`5$^RxPJHyƮ.pǍ'd"Y( LJvrefT4KQvI2xHZ6QpZbGKG%_G!0WMB{=H혪n߫*Zҙ`g: QP4cXI߭{O#x I <-Uhˠ=E%/ex%vV_+\[ߜLgN>d2t}u31!"ݳ{ׁ{_}U,-,4)^p4MӏQΉR22t:]ZZ\^^F!D)'^cؙyڞLʸ񭝫>?ς7߾믻U䬠T|gw}vǛѨi"b]JR!P(hbJ%Ĉ3`) -m-rHc(#NDJhTk 3'(U" ( r6ȱO}s wT%p>a*ukzEX(}aǤBR&B_=LQsL /V6䄨dq-,MO %e1͘Q99û-sbfeaaJ!zSmLT,,gu3 q:r)~ ,E/F\o)B8,# cߧPz\1=GSJ>$"v-̇m%ʛh Rڊ^>^%3*_\h)4cr#U)%<0JGRi, B`ïl|CD| `UBƳdC%qJT麮R¯z!D~- ;EUI)c2%V"L*숺tQ(i4-V ˜UdGxUh"Чi-(Vʈ`Jٸa@ }J!p*8h.ْ *'ՔIX\^&V>%:NH quE!S.%aـ06BT"u1#P-}-`g" ? Ds8KuNµ9 bAJ4 ef/o9snccJO縎x;KKڇ:tpuuuia«U="7m}:olnnoOr?iG qvst}tvHݟa^XX'F>9{>UHSѣW?nmm}{{yUWAh뺶i<Op3*Uֳl=Ɫ4E>.NTRʶتF -41\}By=8[xU#{hy".^c&"aOC0"J+UE$_2swȩ aMx@)A\eAZ<  H7%EKIK {ͮhlkw/м0t).kXSJpQnGs.f$px7U,&vy]N}_IE}B,5nn^&ҾOta˷ j"UVU 6~grNˌh!r)IUYױpm@L0`gc;K-UED5Љ R`W(*$}F gAB ?`}z46ixUM],S*4r‚t >tIv 5Ei"w ͕SzĔRKZ?4)r!YyXi.T/{Dpd;,J߼Vbsv113d5v ( ^V ),Ĩ2b ,U|! 1٬/cW@pV[7uz8"wڔy,t,L#dCDD~ >sK N*@Ąo-GǏ?rȮ]Kmp|/eơ98 H۶RJ @*66Qp.S .}z|ݓ 0IrgQï1,>%˻v'N̺t]v-/y\-w]X)I3Z2 Q̫ZQ7}J;4pDTYRlC SN\d mR:Lmp74L읗HHSrU'y[zg.h;:tTЂCTLudTJ}!6G` @ 6fvIL;' Uepo8feOiAh˼4 萃uR^62`LPU.[Hj5HZMQ儘-QræØsN}2|Pq:UΡ!V \yj]GjJj'xJ VA^+:WְsIqX(IZS 50(;:O[_?e:X jO*XR,2UaM(.|-d>!"&ao"_, l݄v\sޗs,׌]qXQ"RO9J؆R6Y=pjw6^k9g3>^B %|1! 3⇾1%FVO]`.,^;jݬ3hP\*Y}-u]:STO{&۷Q?584gH2 (g+X-DH˾Y 7T(%R$\m2M+KQ̺~2loO^^uX.(߷w+ dvfU7 [U8Q`mP١σ^pVNp=Ě%Q.~+!_pK<481Ѷ.}?/gЪVK~rA`.w++{BȖɅT@[,K9?zKXɌv/3+1šeCɃ%"8L/ Q]X+ԌQSk.0Q3}C֕=dY8y3ATu]o eOisUdTfz2̒@ ѻi#sCDHw G=vض78^**óBOX620/f)fmUȲuCl!DBjE#iq!K'̹il͆|VJR"+(q#lhy۹?\'jEFSX3bZ)SG8J8U[yh~d-~U%ա+Eq]DsVLWk4̾fZ,ʕ*fJXu|6&2Cï֖Y䔘)Ę+_ɺz\Ƹ?49;PU`B߬ψ %-R250b,6)) 6BB\$҇q;6Tzj8T%[>t01{`zCĤXlf ZZq:)޾Two :76deK͠ bm!ScPE{>lSJL+'%Ę$QEcQUsh-/V}^6wJ3Fx=?/x'?meeo K”ֿCLˏOP%L"&7 4_=@J=c>c>>'FAiSSx/bi-%K/qD4wj{6C|G~WSYof k|J2kaG;;íՀ2lG?rOJ <)]ux{ J)ip*|͐Lkoo5́CQ.KQX_)UꜞR@INͺ%f{{BD[Ο?}F%U4>C$ϕB|`XAbl(t@B [v攕T%0jEa/u 8D,f\:n8 'hT5;0 G5~R5 *b8Tݹl!OxiiiuuOӶㅒ.QR+-S#ʢLJDm~gpI2*!HѺx0"w"3(9_P!֮Of "M)\\ޞL&vs ] ;[)j~||||N~s^ O^sпAb|֗߼SOR?Z#=8D4TB F B BCXUmZ>)t^\ּ@`+muO?!~it:'􇪫@(&tkq:;n>Cm5Mt<uRxcԸh'6W\SK\dO/R7Ma7/֐J]Bl| 1s곈ѻO<-C}6Hc MN|B۰|<,01|ǿzg}ѧ%>W2!Dyݰ)%Y?|ŃHD4>|_>}OS.;gLhɔ8 MRN1FZʏ{h:9v  YdzXu^=3/ Tjw:~vT%v*g<׃ᒜP&fik׮ݻv-,ѱ̬KFbPdcY&b>wtKKew2R,$)I!WO ó0D4B5snΝu5$WfoJL#9rȑY=~lTW|>CZr"2 6jҺ,W*>3sL+z^}^=\L7v^1>43n~uJ97{_޳޻/~o;9Q]{㓾ǾYG:hr%1>˿??YK "<cO}#o'%Skd@͡Th1rh`zSldg+x2?ك ;% (><q1j\u=:DgYXXTQ0mx)Ol:4Mx!|&ɋJxBM0R*.TZy)?BJ.)^dm_LVY'(V"2&i?|ǩgq֥cǎ9rWӧDSQ{| _3Y򃭿OPD҉?{]|\JDa߱Gv0g}=/^4#&Ň1yKտI}ܰ<;7?׷Ӄtͽ6^z+뮭+dss<yc{k/^~J$ChUBT61D5Fz -Pٚa:ƪI/ḣy 8uV* Q ώE2(=<5!Am@q \'*8Ý-U37F둆b%Fe=bsIe4itڌGu&+yo8Qir Q=<>jm %u)Ϝ9!)JU@| 7\{+++MCBjNUUy 6DDJ&f^(`fn\!61! Ӆ?>_ꯦ%3oo|?/齿m/_ܴt]_z;Nȁ?٫o??~1;?sS"wwس_pk~^qL<{_/=œ+olyz[~%Ȧ6ow}͍b5?s_pkwk?7~xm}Zv_;_w⫞/~':[2i?9+ dϞ=kko˿/ x___۳g ZTg7_d+UgYa48OFcrD!+e_Ն_+;]|`Z?Na0Ou €= `Hd1t?; T@DC4%(Lw]%/HU1ئmۊe@=+4LAltG D=$e}D덌LӾp™3.\u5ԗ]Y|—3.y+>W|_pqǑOzng~Ɂ>|_-?s9_ﻸpl6&"ooyN|߸_̅.238ZW6 ?(qoOb?iO'ͧN¥f-D!0TXBZ (@go;uA9p9 FB=H5r c \B%l"\[y׬lNξPh~AUIﬨ(Lb"Eɧ`GLgɓ'[_P;C1ݻ?^}diiHbl|Q7|_o?6!F& }-;~LUf.Cለ86ϟgcGSWWwO8>LO^׾^4zoK>6n뿼}Kd^=?yk}#_yC_<#<~7_vfb>kM<{~헾t>gn}˿O?K}/z W1:s^_Ɨ&3H,@(޻N=NoB*Po2P_;!K~y08CDCٯY7cY닦R͍C2OHQb1Ɣ{fF4|U*dM&>bJ*Bսh6>rDDFW;CUuuuc}ˡCPxP+{X%0ȩr&@$k$*LD$MR󮏞={]z>Ys |g }}]7od0{[󙔮~5wt}p,2~bV%,JӍ\j }g|o~[ϼ>'to}?l3E/]%O:g>tR^>yG*Mlǟ^)Q?bk<(2݃c ez.#+~\A,z)~P"o߾m 3vŃ4Mԧ~q9yɓ'P<񵀿@s ͎<%KhJ"% P%Ӊ0|M9qTM'Tdb]e=Pzuo ܃5!P933fүhpR@.KL JmjcNB1p2U}ܱ{4ͬ+NW2F$!Mzm1%OK~Y!Qq^Y/~I7]1LJ;;t8PGDߖo~ v̟>4;eUo_-Hoɗz8n> :#}ӮEHhmO*Ĭs8XiK~$4QR3!Y9OK<Ȥ- G~ݏ'.NFCz^r%U=O~i߇y?ï^5O >{w ϸv<=sk~w^tJJa }ۅ=~֣V}޿p}1M.K˗ 7\']kpoǹADe/M"ڳgϾ}y.\=aYrG>cUA鬊pӨQ9sq;T(a1`㟵N:*hhp{$ƈ&MN?A\*.A1j$ "2pPQlh$t  4><7R"!DK ua;ISmوVB'$514)%bm㈈":w¹s666TUDz-,,3x{6?c$=~15y&OŽ@|Y妛[ۮɁ*Զm־!C;K])0!+dܫdݽ{<mD$9Kb>c>>*M76K3w~dӅUit?Ewo}_>igky{o}[?Wp9K8-y瓞%wWp_g.ͻn{"]w)h҇ W2p bEw?Ü0~\gdd'čJ퓃CC tM8rdfr9xpb=J.!o7ُt, Z ԉyR"(*3ya(^V= dc`RM7Ӆ)(B+b+~iƕ Cj2W9OH݂]VSx/K_'nWp ǎZӿ[~蝿TOHzā#°u\?rf;o^|v+K{:(aq칋;mcZ IDAT,u;W/=vz#rK,",fn>@FJERJ1Ɲiu… .}SMY4]ؿɓ1 D1Q_JJٜ4?)s!3uڀ>9}QVtq☒XxJȜ,Be"hk<1mczo"~ˉ'pyRWq/?|R)'ܧI|g$,ܹ]^{ɬqg$#pN zb|IRm30H)_^`6?F`| 꽷?+ 6jZr-l%#̬MR WoJ@b"gy\~lvKԑ@*+RDDBt[u1m&(1UEri!ŴO777O>Sʫ++;|= h4OG)>hC"=Uٙ\,TӶ3| UE{zSYm"9}ME9bamel@ K7_[_n 9\&i€/`L&;ػwڙS샙G{v$e,@۴1E@1vm-J(s~h&Bpi}wſ߾U5}vyiAi~<$e*Qo\ڨ*+KutTP9(;z%,Tf!ȬC@ID8BGD#m zcueg<oll^|y<ǘ2@hʍ7ܰZiÔzGڽ قAƸw^쬯3C$̼y+/^޸z5. K:۷rѽಧ4 4m3LYBUv "FMnkz$@r9WJ۶XQM0{F$CH@ 0/#&;3;}~bZB aa@&tQi`fr)F@!Jb^Jۮ@Ռz -m_sf_SֿjBu1Jvɪrݶ5W%F*%߶(&[gCVGi::1'#s"t I;_;qtҥWNb3+ kǏۻg6K$Zg>V$Dt.~&i4VsDޢy*NcmH۔9ZDӶtI`qˍf#%yV 0`5gRuCD$D ~̄5jMӨ>b^i l,`4*tXXekYfU^ejyMb]6(ӶʒGQd?!훺5IfJCb w1Gavcd{3!d_re{|MshrDQMmTJDm4f_m '""+)X5kƩEτH vYh9oƇYbB)G 0`<0k ua9%DDγuNsm:tC뫫Mv!<;ٓ!QX@J&)F!QETTbfBg+Vgr;4MM@Ow C1FDB8Hbf~@)%` 1l9!?` x#qSbLfo/Ej,LEd-OefXmZR؄:&y'D3r-o#xkjmUOEUgn xh=Kn6@C)کoCZF#'r!z.ucSTYv]0N苽P:m񥚑_8AB!&s @t]~~\0` xP$: E8SaJɳ^5VzUkJ+5Hi-d E]%1 %nX֒+)X-%̕ǮEQB t:,1f(zeQ8 ,ގ1y]@Ķ7?:VXM*5vkd󕉄y Dg) YJ 0`H| DH  E.$cmgPdޚnR㟖kP+ä2ΒR9Jiv:UClQt\iJ8G1+6!"NZBDrNٿl79r^j(, tP\9`  AUC#*L|㛶*WJYضNJ8M,dZ vu]Wʬ[oe:sr$yjs*kÀSObl-/ݺL2L搵(hǸ^lޕAz^jy:nlm^xecks2foQ?45Npi7Nn2pbh4Ҵm"gipg-b QU6: 7Mow䄙rvmw>AʻDLvi罰8rLl 0`Gje4idh0@D4C@DWrPjrkbn xV}EYX38r0ź< Q߼rT=V[pMAfvLqj-sJ0silWFx=yҥD0NQ5Bǎݳ4tSfƫjyYgTkwM։`_鄲#Γ !@@@"c{'ܜ%@!TKls wBR".N?1@TŔEf: 0`k̆Ú,T١yRѶ1oVyEйH0Aq|ZG9 $`I,x0]\|E@6Z).z -&h `}rU3N5&9bv_Z``$Ky5 zqpҺ3SzYavRLxggz;YTE=u]#ą///3sT+tVSy&R呤1b`НeW/5^s]7:罟o@.S]l5RJu!0iwLDDx_s%NWS_/~:Ipܣ7vKOkI|;] /)G~]i>k{׹ 033s)sPy9{S2VY2L<0kF U;ڄ+͕,A],kJi1X}K>T&h),fVwٴ@P \ADE|)Xڛ!Ee7J6[[[W\oOXh׌=<mmm2> >3rGOA_F zlRtbkl_ nI~fL(IGbE' HTc1YHRVlJs9N%T["x:$ ?{gw>7s Wmz/E|%NzMK"8=px7WV=s+_ZiC4ƓĢ*"&N;ɇ{?3 0`L3,clSӈd?&Yg0QΩ̆v! }N,52 $]j/ Uhi]5(}5 /Zふ4I?Ԕ?5Rm+6VwMB>piիW7nbnjŅn}}}}}uޮ !rƶL>eez$Eh(CԛRB v@Vb ,ֲkm7?/,,pLEcc D"̂h2z諜[~s"e5䗞= qJmCGG%Fc"sH)$ ,W BDc5<X#04q&̤RD ~Z_NL|­xTFJ#n(̠ *`TM׃ T >ݸis.pR;&&L\DVWX߷웜wG$爙~ڶY\DjՍN)YG}hv_==w`D{4x\4a"{䘿:Y%(o<_kfq@I')9%\A ^D̬9z*.@<}?wh_~,7YFsfqs|67YZ{'߾~KnYXz?xS54jmH(#.ǫ̒!m^EiS0`>SBq_ jgaZA4dWMo dNX2׏K@cȯ\9ZsS*(aj[~w\vi\@"d2x<1IՁaKYX8p`޽ RfR4n/w[Z@bHQbqt4g}[0l@o}ݟVo|==c6nu]K/CPR O3`< 0͍D BΑsPP"RB-cUI`,ng3薃Gjds]+nb(F6\7-0Zbni"P56i!}zu+W}HIqD`5Goۻoe8ci*2)Ld׍eVgѳiDϣ M-DĈ9xh&ɴR`$v ,""snR ѹ $W%U ]J5 9DG;Dč,92=u}zj#KG ^<7s$2}mی`zRh^}_;p?EY=:rky{S"7/ Б۳G{s[%C0EHઆ70(?SL_X6^S vsƷ>nQRu~v2o^fBՇ IDATdgZ}dZߌDN,9.E3VOӰ}xXtE9\\sս{lEulmVaiZ]D@m[bmdXFD:XruZ۴}L-&1cfgRrmYr )J.u4HbLOLkYq/yj?6-xf'K-#?w~s{|n? 1 ^ҳfO|hܻ>+~+p酛nw?<ѾwK'ۉ%XRkZ>3$Y?vw~ DB BEӒY:GRH0M}ޝvQz-Šӄ*Fǔ,\ 멦iQ"Iz|VP^^(S'\,DN?M)mnnml\1Q3 Biiië+ mR6f$ycI3MsDd)4 ڬn e@m6$SJ"!sxNB!$Qks'*sN(J68q۴M*JJ?˵[6+/.O7?^%!SxϱY.]{box !0(,U.o/==7]yyC7}gp}>ƛ'7OǶ>ڎM~# pﬥ/;z q?YڷMh.oFXCxNS 0`3D\V xĔ {gSDX&nKI*s(ҍϨ|@i{U"_oqͭ)n쾤'%Iօ 93bn M,ٞ#\.8}昅VV#KK{,>xdiim[Lo[V3ZЏb*2Qg'dF%"Itl[4fcgќ#Ķ<Z`P=IQ͓> +XGoU*Ew5?{avD \0-R<9;}xvn8s?Eܑ$Fay?gzʼnnr\X^xd>71 ϖG--_FM8DC -.-(rч~eoDi{ݯ6[mHG|=kwk{EB C`@my8ODh)vĎ}b4:nkkue.rk?sOc;ĿM'#Mct!ݯf<Μk___^~W-w ҉T4ĬJPXiu‚Dm:NnVC,Y{b" if-KcuSb@m.RmzW+#HS?ؼt'o +{m>r;?  1B >xngGzrF @?|䝓g0nL"  lC܂0q9=xS ~ dv|1 ]>~9c8ԇ?yۀ_'i: G&Dr2Xq&ER8+T*g_ Tkmm[!ŚX~##eϮsSi-Jj^Ws^ (ij*(tεmMٳpȑE/{ѝǏݳg:$YvRSLQ+앞P]TJtk4 mk~h`:jT2ff}][T4wzVwyZŅpCH@y91ךC-t$뿯wGOчxw%10 Nְv9WWp_5M;%Ů3Q؇A"*DL2 qTgﱿ# e!za#Afiyo<Ż>˾Y? {D ]tkW'8ܧ _~Onyq2pI 1h"޿x/f@QmA&@=+,3Ĝ/\ZܓsLi:40`2x]ɦH,B躎mON _<H]d(KO%gv0̴33i~ FtDd>=* W:0ok8+WPXSgՃa#^yGXZXlTLaS-2ouPF&` %t^.MՄg,~bCm}<14)rY@RJ1&N1&!$GU4,WoU d!9#BĐ$!!?>~oڻp[oB˜0$LO}]rKV>~ <' wkNOs#oB [''&n<{o|?w?Ƹ3!=tX8 qv1Nc?| >!rHi BhYXAf)UL{cʺhrw6hzrsN+ď.XBYץT޾vFw-Eb5VaƁkZ' h S;,;P8^,ۖr4Xt XFzQb9!)+%ޏvkkcRne[5@+,s5z)b E4MADPn-ƈ'e^!)},[jKx޻heD$@G\fz3T1 {Wߔ^\<~Ǎ_rCoCP+ z}uqdon Owz߼9/{aN#?xk߉  q<Ӏ[}ow}_?pROa$k_>d- 0F79'f"eV($yO4s2o]ն@J)1bH ̳^VCP+P2&J[|ȈC{XO1|sCjkd,yתdF5t?ztaCD]Mu`ۚ[ܒ`A'1|"Ґ0/WRyxtw⠹sB8pn'njFVsӵYQ8%Dl|3!'"rv{7!pQ=UHb!b3OwÝ'/7{'7}kC.5Goۻ ol6nsj1B؆8i'WcbZ}4g{+_8MqR.8VJJqy\ 0` Xz2CCP !?FC!YB˻[^ئ1@ЁA>F/|Z2*m Z^&ҁGQSk7J}0eiZo*nOm8GHд>5HBX=U663ypi@8Oڇs a6JiUɖ8\.]DؔXv ]o"I.;k,Gm FW)~E`:j`LWٺG-_EGM:H]xhEo}|f/yK^, S0Bd{s詸>8Q'ρwdp~%/~:O>x;NԺ 0`Id;zHBq$Qfb6cMDNYsy$$~|CMTC @ Y6+4oAN]X`=Yjଃy-}Mb9iʩ;I)(ޮgnYs|KCVJ6 lUj eM rEJ+ދyz)s|s`Wo46 ` 8()  1?XKF E"z m8y<&VGwkNiC9xH}wtp Az`CHX"yGmiqxp׬}Wg͙ŜL??y{8 4* 0 !ܹDә4%"s:sgE&jRH1P8JonJI@;K+ @?hJR= BrFLkŠ* u\@cBΓm yVY*]*RJB(4BYeDlae-`˹\0̕~Df5`] $9>-ߴ״09{_ p'AyR 0`s*9K&ٷ˄TRJk ص-V1{@"% " ;3f@Y+c'h_ɪѡ[/!HGp}CjRcdǺv ĸgj<>LM GuogajHi c+{JSe"עif ~[4c53;QvPD2ڽ:KBQ}Ϊ ,sȵrlsSJ1YyG*vBI So_8s?=|M/ ,M{gYukpA arucQV nhzW_qX`O ӫ} 55cNP>x~F,X"sdA  s 0` ,ܐWCk(eV;WNI?L1 Q`aɴ!Hs*wYQ3Gx+T5 o&:O46bZ0,Hu|foK)ɬe/9ʱClũ\VAAo^=@KJT|NR0eAt΅#:D"qbüek,Bh0{fYk$✿~bDD0KPN@׉œXA񞙧14HwE Ir$@4ZމshDO|O>=?5 tKşw➟t sXGFvg]B ;xW+x_ ?2_2%n-x!Eb D/D&kg ""(ݨjs$-MX%x;g9T Ya)|x:$ neV(\QY f+!inĻ*rNToeAĻg]џ +)™"2̬1MٻH5Z߃L1r(r_KShb /Ri3-ґIḎiYU,RzD4Pv^| R; \ @!EZ}fK/#B)fүYڗW9҂c'k_޲щ{|[Ǐ Sn/}7ߏWG|Y++˟+7-D־_;{ޮkz?{CF! 6䏿30`>0Kl?k}mZcq1&ߨ*ݬNA&*QgT JϨZ$` LE%Umڴ3PfinczbV-a"⑀%KQA(joʙk#P]NOu=[4f 3GU ȨD$zR5ڈHK`BVR/#Νj^l601 c(s"Rp]cH"þ]Ogק:wޅCOޯ ׽gu]4~3"GN9PŽHs{v`ssx݅W.a韊ih7_=^^^ 0`YM"'9flA"$uwF|Eh>27ne(*K3QR.Engv^;Ǧ1*JE9Ҟ=͵ 0``(wia>OWcJȨrxiξk6c,L3Rr\ ݖFbE9є5ā"&KE$!YGo.M.⮎ݙ:ou)cRJJ߀b74e {#.J[*US*\@Ȫ mۺbOdaՠ4iK< DTJoJ 4c9Q6@ ѣ(G%-խ r/<7B6u0`<0!RRMSRQ5}cRͭr)reeL:_ CSX5/`P]jl ,HÓXt IDATRVl旦j eD`=5"h # .Q"VX 2Y@FmPzdb],~֚sLcU7i6֤ 2=Y 2T&U߉HҶ,!=Ե3=4MC0aSQ$k :{Fs}Kef[,1pNQ; ݄) 0`>P\5ED3#Cb *Yf_k"LϢtvx$f$jZ^Ww("XZKY+>`˫ǏF,lA]P(STP&;^$f ГkW-4AV"l:<$9%e -wNCd5GtX{[hV;.3:GuH8Ҡڮs8\1՚ؓsΙr0` xޢ(/rgcFyDQS̓=jI)ϱ@HvŘ-e5f0eX #Cpm3:TI8I )@56j1'{UөhTA2քEdԹ.}!(֭ڶq>["h"q)*X] el2"3iW j{giyF)%|G EV5GHFDP-PyV!a+$UΐMZW 0`V VBܽNS1k$)%jGi׹cO\wΚt+w ,CT7YGs֌sJ)X[DH3EU0/ckf)w*hc7[ob +[(QYۀ=9H) $aطm;4 u PSIryZ{]>cZi|4D DcֲMD"XYY'@NG%r꫙-q[O)#9IY=[bQ qbRf#!zff֦?A 0`+t,4evmI6M)+7{drozպ[QP<@͵jyLb)oP20+,>:-  IC;Tc('i|tpeiuuL`c$SC5@//"]U>Ȗ;ѬH`_~hkJ`gOF"Ft nZ;D8%v¿*WC2k)0}ɦjD1Ĭ kĥgH rkv[0`g0>F39Q%ttONW$w0%`J-jr:fП뤹)\`?Q-2@X^ߤ V$>U /v#imO(ͺpqǜ毫n/譺bDD6oXD",t:!ك.dj(R"#M}oyKK\iYc# HUSesm$fIszO~xQ2B I:}0Kz}rpf6,f]C0` x>N9 @D,-ҜDfݽvy̔<}7LR9JMBc=ƇM"B ~aRJ]שB)v @ϡJmN& $ o Qoloa'acAF ;kQݛ Y#A Vul ֳY!FPVyB^GT[s.%mZNN)s̕y 0"c""I8;9yݐ !J:!鈀"(HU`AbTHGi"UUZ  aSw}sǜ3fCȒgS|'Ɖx1&5Ve^HX $16S*/!& Wl` њҰL=eC$^Ap`sĂ^]%(!2  auؗcFD"H'`Yޗ_"!@,MV{5uez$:{0Q! T灻BӎjeK#e ]Sbْ<3 [)y(Kk uĐ8ˆ'+)G@OrB&r**l `DܢD.@оƿ+IGe5حEa =j`]bl0űU\$QJ11N8Cb@ygF{kO9TS=!^1F>'ȧJuj-E[CTxo2y(I3c,'yAi!NAt<heKӺ}CA /CK6dYh4t $عR7k͊ƚҕ;qDb,['i!\N"cZߞVYeUVYe-sF( D%7pK-E^eVpPMb)O#PV@|7G VGн@2W9Lc4K'V+P@Zu)0uZl*Ji1$ڥzZH.V̭̌mɺHe0"E#$VɘESYZ-!d,-).c"E3s=,^ M*%~c,$}7'5kgDx}H00,e9 { 9YÂʚ{"<1`'{g'v5}e\gVDN+*yJ;~\51V fI$s (a 7*V@ R"<li8Tμ[$ZcPXDՠ4.U ")FVqՠzl6(u@{:7 G14A-DgH3>+JrYftⱉzZ2Ќ1FBCSլmRk\3ߘ5mAhc̀+>|a+q}ӧ7%8[DDyˎs2l6c1ֺe.K!9!<%0eԣ(O>9v̀>传f͙s-=p6;Z-RYeUVYe}zP1  DR a!"$ޢE@`.2ˬ Af6Y&SUG#ʁK(T} f U%@`; XM׻Kx]nr4^R,H9 0)7zgggZ ;!X}eplA@e,y΍=2̪)Ǟ93{,YMo5(eD chuN(*ZD+?|&fΜyͷ̝;oϮJ{WW׌3rzޯ_}ԩ&8oHd+}fAk.GQYeUVYe}A__^99 <~YF9sdc'YB`c 8g؅DMV)9G&"RLj~fJIPoRG"&ں |NĥQxNjVsdkRBڪC&$)TUfIBR}(š)+527HK.Z >x'1]U{j9ykubCyfF42W^)f( y9/E-gCteY$f"fY9=< ̍΁/WQYeUVYe}pC:k0t|'H;0$ 9;眳Yy 8)D>ii+kC FuF ݅(@RFLk&A.m$WmHA IC?V]TcL]FŻ>6;)9gL2!0 .1GmE DibMhrNxWRML&@@= / iAX hQN3O<߿a_8( &:HJ qj5ՏҙOQoM7 ]GF GH +F]U{)@=eY9p\$}˛ J#m`e!:C-YYieLhdEQuhaIP3OE]4lذunM:{~yo,TЯ"9yH{ޣKi9Yj)/vCEU**lQD('Z A B` s+hR0jBD{G{q,'t|}"#ޅ\Jh2 ^i8 %/anMGhS,H$Z2 W0G$R2="1h-ʒxe(KURzѹI_d$ؒ5B=ŕ%@CVjDSpn N0mmm{,5'8=@ˇq'Mr>ONj48dȐ&<O>W^{_gtzY4kzg3lnXSMc !&s$BNu[wksoo9 M5Oy|" =:'^|^Z"kOmU?kf)>9bu>Z0/]~G])ϲϛ܌WoGvЖRx?|+jۜnκo#Wh—껱t>̼l?{kTc+7k_؁yӤC0IB#.%kMQ?B}@j"G#uHO Ibc"%e4-\5{P2+uMIfFC{`@VsE#u'ҝ "Hd8e$"zY =K67@K2 rZ(.˒S<ϭ5X;@ϟ?.{W`͛<̙3dfvE +Kf\Yv5_}t嗧<ϥsNS=d8G.^kw4X[tΉ:ooY!?reqvcz@=~t3v<[?x>hף`_$H9K-+cqW>][0m]h<şw?ب߂@-~ҽ{[(ޕ*{_qnCιLB"!7̨9jZcyGD9K$Жɍw G!I)ț=݆4> Q,HPD IҊ4+I &1 cqEiB# :N^(S3ܣ`ȡhu"k5"bAUHBV}uUb( .ΖkPʀaxazϥlϓ~bR 2yo\spe_w~{xl, ;31fmz⬅oͩnZkmsP~?f#>Qv(^^a2rFk :|jCro潧kqFTVYe˓k9C @JRDBHDb,"]D=cqa"jGUc^+HVo6IUA.h/cP,X9@oɀd(˲b[eۄA"JQ -2c {`t#+cñǁ4ӑ J"-Ge}eYƱP#Bڊu bSJ@YzCh%ɺ1ȗs1{/Skͧl}-=8W~' y꫟Mn>_/a͑3?qq'O96hi1w~# @o{]}x)] <7~ɉ>wOw__*x.?:yUw=o6~qN|oEGx wY׼bl9-6?fؕkS&#{>5>R}#'|F͡wf CӰ~yw_n񌝇g.xh>{Y|뮧|/C;tzKO. IDATqǵnQC?=ܳ>3ϹIKE`ULa ? 웯?v I[i e6b-|9>L|򩧟; 6Q OeUV&"Ck^mB|]$NKxGLAD0%cҥJyJI.vURI71K}Z^KzcD_uHRvui."Z?J3BRb=&PRFS5)z$D1(x@`f9#"bK:P*êtg"CvZ˖+&͜% ^1&˺}ɍwO"{0LJb.-5NEj}= άƃgF#hЖ1_fV5ɛm}W-t}j~okO`n?zvERZoxy{/3+p'׼xċN:-_{nz9K#s"$c7'\~ߤ]е&C۰~}喣.܅_<~O!tV#Rwlk^?wqěggcX{X*$DƤ[.ziN}x[fm}ּC<&+z׮[tN wئs`WZ  拻ꗙ|W3 ˯w0А~Y{ WqOo0(lzۉ}smj{KUVYeK)@P*9#f"_ fX B$@=2!N}A+Q8 9%Z( C0wԎc*o6vWڼF:;'?s"[2(5YR[y:7!@EQjfv.\9'JIWG~ͫ% !Ɖn_4 5Ben=n+,K"oӂX./ J(=!c *XƆ Cz)v>-azIǸh P3ScGy]\~ּm/ݮ0ח>ۗբB 5auDcŇA?{Z#ȇ܌&[bs35G_u;.z}luɡXnziP-v|^5u]z;@fFch|_^bmFs쾵N{s뭷\z??۾a˕UVYe}Z9gŨDDD7դA]_ޒx"CjhX1 JIqhMir.ؽ,ᕖ Po4 )uJ@ҁ3ӢE%FT! #1d*eY k2d:<0Z#&ξ/$Yn* uKtV)š96(0Qu'Uۈ(*D!C9@]la"K WU!??4 .-=CҳEɀ<<3t>^l23rʍ=9_Ưd~c:f3_||< &~]Ӛ K;jw~#8j?GZ>?,dvd}1?3yʔ)O^K{ۘ%nC3Jvo{K77d73ؒaTs%x1bw1{HMi,8Sι~EBGW+ FLBQ W/Z) 0=urª@R uiN%(΋Ȃ"[k#c:Hos;ǑoHh\~K#;"juY_R,xK 2}!hoG8~Mħ[+v-6#cw'WN:?y{z.w~䛧Kq @ 棏1{3? `?Z}v|Wɮ+{qG6ct#{<3z[veb(B>WhcWf\=_?3{.tpQ9skh1c7/voQ]axKu ݷsCoܘ1;vdzV:;W*l92V\QrBG"k3jSd&d슒S΋<I*E!Cr$$j8cmz_uhi)rLW@A}YNk^`PN3,cq sgꫯ>bp@dx7NJW]u׀r|)zcQlkk̈V):Ubmf ,dg KdҀ2^Jd:Pe6n49(3Sk0b! @*\(_e@ "yIj)(KA:H+Vn\#_ SHm0H9-]NWeYTR2;-n49aDeYQ+]2Mvh!1h4TS+Աk6r5VRB({F1Rh4^^ZCDE̷5i}%sd~"ze.qT`E,8Yo:q;;td v4qm)o=A.tŭseUVهB_U-j4c3 (J"B֍'2{1q@1VJWd(زlfY&gW frqA-"}L H>!Wd~@ bjV4 XGe!\/eLzTGDebD$n\Cʲѐ^]w 렬Ta(B+[ |=**^YeUI=3Cy!2ƨY-V,c&5D] &^b{`z].rn ㆉPXl+I뀅 81k+6iC$E:II7~Xxmh4R:PmZ|)ˈDz1՗7?𿲦G({佧iuł-{ j>tk*ޢ fHD(Z-(͸ҙ23fY'+dHJ{4+ZkK0!+"Zr!"0 Fk"(l)UįztV"N!aBN^C̀H@;'T3H$SMrBGĢ(Q(oaUfd9DK`"G=N\ES{AB~Q4f |l1i4_i VIOQHLRĜNFHQ10Z$h]B(t&. tZI:P#N&tdl"2(1HSw9'qt"qyM_͘lk>|sgoDeUVYeU6oN=]3rtAUJ!YFHP E4l1`j5Rt{E)"ԏ !] e=6Jҋ@QiqƩ[)ApP X ++۠sޗƐ1YYR܋H8<֚,}Y@1wx8 a,쑖*k[4۔Vʹ&Z˰x̊ u2*6Sd}s䟸oCY1Z>c֠C?衼`^G޶fFmpdlUVYeUV٢,G5v-+]e H '""CƐqgƂ$<{Z+ *=ђw'h!){SlP+|_4"ޣUl6M9KQeH+}tZ@TrQ2Bu]]C"$x6jG骬*1xW y' @ ޳Bl%B Yea,<0SOq VL k@Z I[=+,E&PET_?c7|}A{@gYf˲4HhaόH1[Bctq&@hhxYFhp Y|윬R␬ Cc: S4#Յm֎ ,*~r5a! ݷ]ltA;V[e5dfo1~ٵUeUVYeU܊Zkw8Wf6#$̪3#_D@s΋p>>S%=gyl65zl-‹TX RLH5=X&$KfZޠ~Ǖe)U 1C֣( $ԡsa\yf휅R>z]kdyDн'J}1+fW'5**,5-@Yw[k!A{YD EQ0!>\ñWZ{`콕h@*Ѳ :b̢:ӃpkUTYK}dbvY"1xj%f \4c س@X,ffѱCScT! ːk +_1@ɠ%kYIzc NHYy^7&C,3c8pZ֡Ժlzheb*JΙqa 8?S7gv<=kE*>lP?K/`#hjuD(u4"et7D eh88 fv /•9@v༵v!"UH ie uƫ?V5!΀*^ ͇Uov]PJAi.|& HʲRݘI  QTrdyK @)CB95KDe|#L2g)Bke0ȀG{eΕ/NzC?5oy4x~˵=**{C!V3YE4CxLO#нl-?$\/T $`U4peC]R gY( E 1R4.wOʩhlb@/j@(4V 4rp1*+-cq.r'I(]" r /(0EPG'dnLadv( 0,.TK,_[cmC]]] ntgfX>ىس_x**ly ܄S7]+D?G~mmḏV)v``Qerc<>3oHG`fd|iD+vkd- .Boe(wY kT]61!͜T!k^/hP 593[niv(;B񁵶V jmm.\7/O EH,.xE c4"I9,k y>Hu< eiLB^ɉ!DjqX̡o7m#UkC\_>I4r| &(""dfZ֦Ó{eq(J}@1QA3y{}9hVYeUVYe˨=胟>s ?#%" ԾU @?\9&( xF#;%jd43H{CqZ KWyAt.D+Yc/=XT%Vu#2x!(rL1Ru,AĆd+ߺWO7e# IDAT/"" l(݌\:gYxpRUcB䗔5ȞP#Vp @xfF1TPW`2+"*çͿLaD4}_ YL!u$8N*AP7t )Hgj\烡;纺VbݴBm˦(>Ir:mR$ U2~-Nэ$`b.@@tx * ik2šiRPZvٿ^4#3 ²Ecea[Vײ`.,7<㵗:@e>{ i*D*93E@ -H GJ^yIBZ-4R~!i+Awu@}}SD,K dkABu=v_ߔ=!Y)33\|cQK1i6|)˒(hw"˨|l MpT<ĠUR2ZUl4q=gIwtQܫ{C= Vv-kYj>³OLuys!&:b!hYJy=01[2` U؄tؕ}G(JeR ˱V>Ԧ C5X BĐ70'F4GeR #"B"B*: bS,|!=JZ@w<01Dl6%ѱ:ッڲ,LkdιJWԮ 9^\=LZ֠&YKƘp*.\4]bhe>ED, `-%;ۗ[YkƓ}r5n+/Nzɵna^ !f& V\]bRz=^ {*!/qS֌u /i*i }rI%&uDLGOE-2)@ ZƋ2e./#/ 1eqe,K#iL@LE!=̄0.RyS(F@݀n; ,9C"j5""=\esnp-wnY*+9ʻw]9;ݎ;o[!p1ݠ:zSu_'ly.QUo-~guGa{W 7ʶ!5^w=p^X/GˏU#@Z{id'X¦rwV-|V{|cS5_^N4zB{csulNin[tr޻՗?èU,qB~-ܓ o>EsG<&=ncc_]T{Dc8UPEB1vUD@yt񮮮DX@"@!O5-YHAibJՄdb+[%w#UQ,&+C^}D Du;]Pv$Q}Ť=pO!&IUB0=j5h]ܲ<*INLy'N ^DpWefi I& IڡY-"~[ӎS{O^=a وuv7@09npf7<7 II4-ZY??~}ӫ?qNٝ]}˯C_a߄W'VZus{Kig˪1҅9o ]Vpa^Ww;y~jۯ:sF%\ >d_<#<gvu~tÝ'NϷ:CgK`/^p׷l_m=+ Xki<47ٵ/7PO~a}w |W}u4"o~5?OfE=ywi{`?yOy˚uék]ڿ_1oYZ;!+ȵcn4:m{0#$@4dz Q  |Mcꐨd*lP,HR'p K^#= Y9: }l7x#WSƊ4o+M9K"0tY%dN8\z:QJB` BױDed͢jeeHWԭ\lˬz\'O➵8^T C=*ik1ޢDT[XtׁjIQp}ݯx۱۝u7OW t$C066C"mH Pr<~kC7ў't!k|MWsy; 2~%>KoyX6sﷻ =F/=:_.9v1ve}vd8;OxoWݾ Ξ74CD_7.ff; X:awo}#>п/À776tү?a|K;_r+/s_^TػNoލ؅6~nmbͷafT?B|IULN_S=L9}!#rclf,~/+7 ~ޟ۶jD״ kjzkayi26!\ܑh&{_1$XZ- [:+!x/ovFf͏7?ޠ<_a͇?y6*VzU(G @Xo;*/li[]N=~L/]w7GMKH4pٖ~Kvҝӊ_n}G9e#> d+ ~/{_}!|BD  C&#Ż !bK -KN 9S*P3!a(V'DOXx眨뼥{ψiBLWNKt0aȍ?C+Q|WWJK~Y^gO@&af}" 1ƱG紃kZYJct1e kYrey_a#0&>xgb[ !oƘJEwn~9蛷ƻ~|;|׎~Fn=O.d5=g㖧﷗w|W @4r#Y3~v#ο`qn}"!;W /*Ic|2k3a3CdINAF sKVH깥E4`zoZm-,rfmV !Q7X̥3`4BuB&?bdžo6?aBdY.\"HzÙaOZ_ξKcO2䜯k`XV7' Tr"'$j\00S46p_FC@5NIpB—[@Q@hnڢWcdMQsN|(Wr/@")z0mUg1lT4! (=轗f[  !BBeI u@HUX ޞzLIRV ℃B@E=̾J}hsRɁy-I"""DE?ʳ 4-"xfRYi3n5>ڈOA7W:#c _;20l܍=΋\*!"Bc~Wv՞':ʡ;T[~v#[|b[?wY+7~q[Ko#'fzǎn#ꔤ oU~ycsw~psm"6-n[֐zߗ"Mu{~^^ڂm=v򝷾Nf\ D\Isf˩wrsϼ+Y絼^eYVղ,d q$3އW:D!zheIb4jG3"QFuf9zSո,:]o;Ί"Wu]s00 P1gg3#gB̨0 Hΰ}oft~aw Jؒ{={UVպ+Vp۲e H$x"'$K5FpZR٠.nՔe|Yaּwn&icxUCaXN,F| d,sR'bMiPFlC]u- C4ޛH{x{)qA]8DJGVJPkR:PB#> ÀrZi-4[3ְ>ߑ;z^oǶE5P-BN zoVB tQ- (B*%0! Cy@84n /abVeH=+$Iim ^TJsm3K` ۻ W!K qExl2 HYerA&?OS#!a1ܒ%7@#*a2]ץ!e%Tڤ@ EnqC!8?(l2)vXyց!m۴p srb~ Z+JЧz]7BR:dэ Ad}PLo[BcۢwųmR$D @k@@GJA)Dw$_TpW)8v[ש9=Y&ٕ}R´U呉Rl#;}y<-!4֨A ׁ8D-["5#-&Ie8pMI,e ЇaapqG_Iv\<0`Yhhp]rP۱U+&5"'`5-ZDfV<]w$c@~d@dy!_3;<}n(.ޥ 6oڔ2:Hsh[aXC>*J֯)\t;¿•KK֯Uxv>^_y{eZVΫknyM? &H #Yj'V* Bq@[%(ZO&o0NCz0 Z{7c[B" އMvt eZUs5^rYM,asP/MH4D̫BիW/ZTkݥS͛K-Yj՚wҥc9SY&3)amѴ v]7$e0T o.\d.]vY)%BFban(.7w;S& 0 ]O*s;**.E"tvԱE@GZ2 R*B?RJ]:N6mxD"}K׮g:}/;(77K^{ _HXlCoW؎_i[L?W\nfkXok~m=t Bt-\Ϣ•i3Lyեrʕb^,5󫿎cK* $FuM)5FfVJ9TJyzBa%~eUb6K)m,S:`!mܞs1"R"̘JQd0-gцL, EYi. K@'{BT֦0|?0(\JZ(&y^"Bpz"8Zx 4 '):VDYA诐 0 L!TAJSA2hc%M"~B):R:JB[#BHGr¦U, 9aB~LXG 6pȇ^&{CݗyCnt'#cUvl[T_[vO3!sˊj EMQh P$|Tɴ$GI50ze0/,~!5 ZesoB)8=&Ҕ잕)E3& p=B$x-gжs)/yBh, T1SK ,.,2D$\G)Ei7#X}.QJN07cM%b.MC\Bx׹%/t)[a%{GZTX9h8B(V39BJʊZ{c@AABH >*++ +˴;HiFI?y0gz S*ߎmkk]i튅Y!@vad CyICj Ḯ@u&= ? 0DNhDd0r٨͸weErvnzKeqږ7Zm(-414`dQV' ҬvbЅDz/v o-;amcYO<P)b9q?A)pĈmeajB`"@+Rdwrh 1/'4u\P:28V3ĺ0ҝ P! f* qdB*H(  AZgK Qdxp9>|w9cRbyyJt:Swi U3^xYEMw;h+o& Ctsul V.'byzs*ã~;XvG\v<ח'Og>unNi3նo5mDa4:oo?kC8d:&DX8{⚟[F50r9j:+iNîknwn5M]|E7{We R1 m9ABǑQ!!B8JZJ-T DFBd&J$nT"],.'!HtlkhաHpQr/L40~(ePB 0Y NVJ~)▅~ӷa+ўQ 5ij$Ѡ6Vͼet]SEҦAA?aoOZ8{~I:Q:2rѴuEhY?97?{qkv_~KTʯ_aC/)vSO&cjq·_q ξa>kmV5M~ǝbJMz: !jb0I ^T={'_(3Wu=l6KFuw//S"À=4P-QUk, "`[nhM@U v8AYT D:>#ګ^(T"m>gǟj]PID~+pZ 9E/>xW*E:{w`g0_=8eڈn!{s ٳG[ЀT?tDϯ *>6&ܹXƛj~Y\HVFYZ]+u<2*Sm39=_}Ր>lfI{K7l1B,S Sk`#![1VlvdpdnP`qm1y`GMƜ1}> ï/&Ң󱗟7qQ{9X1çhAy]pm/-3ns著J6Ÿ6:u3 ,^4Ő?HgG[,Gu]Lf~#֤U//^,?ܽ?Yf.?p 4k47甦-E+0?WnoE/y-ovȸ}dG8*(ꆃοvزN;oF;[sw{'o~68Mv9ģLWsk5߹@ Ō&Rg7}]kkVV7reFcm?^sbbIUVw {ITK7/OtmNK΄M?üjey 3o/{o~Ƶ7.ɍ5j~+QP1kܤUMFǦN,3~,g@V =:ҡ `=M Ion2WG#jT""wg؆ד@ 8w%Ì'! h'lVRM4cg Gp;rah+W[ `I SA@r$qKUJiKиZqp~J90w\\`A)%tG,݀]*duϙn[É 9?Z 4b^I)]#KF 9:{B&?NB!"`n}Z0EqMť*YU,O`EpDG*n5-k9𢋇w+}~̴d44VVz왧8iub~=k߾9yG3 !P0Z4F}lo{%ʛ9O;p=4}m4hY| .?B; t1Ͽ[`๗}ƿ?Y_T m^ǽw g{]t=so}mfIƫp]bKX&6Ѹ:h*,)X?Dļ&So.uyP =-$Dt򚨙=kyNS/=jw|RȆ[D:y͝#}~'uշc^}nlH9 YҎlGeNMqť{iy^~HO* u:}S\ݹrZ?&y͌ղQ?u=8: flM5{d^}5Cbvb;w":-zyn4Y%x+bp:K/>X,i]@DipKwP|/s)s&"dz uX-[$g38u׾묑}f<ƪQ޹Mg4~3 nsTs?v\xQsoQ L5k 6vncyFC"G)@JPD@M#uѳrSc!!CK7Y "YcrXҠa2kA-ݙ2 $lL.UjVPhGً g 9P`0y7zm 3ȍ@QMVXGp _q H(&4ٌJ2VP+ RJe  jNdܨ3uk2*FD)$/dU2I~B`[ѩ_%6O+즫Zۢ\^j#?Οc8.6*WĽXY{%]cN~;)Jj)\Ð$`xfȀ=7ڛT3,1:rYp>D"APџBX,&@q:B @ym8:7BFR&UZ`h8I{6#)Ux~(4t*^O! bXPAg,yajWYz?tݧ\6IwI!*W/mSO7&94 +3&cݬ=B(˯HkfyȤvZB"j9Gga~Mq WEZ{cJeY ڧPAgOC6dOs@-nQn2.*O`ʕ JnbU?9ɻš6`kҡߕ7 27_ysȿ ^~ăH&s% Ӽms<)5"0O^b ,DUu/pM{0&SD@kJ;bdTV4y,0'e0sCddIY" ?Bd|*g)֊NQ'NC؉hv,6!$^K$0HQ:|6`1/ Oa!2f؀T^=W^I<›&Lܫ(N 3 |5;AG?-ajO:n\O?R45?t'\}*?8xKT}}NJ[ZNǃN?oV }f&CM՟=tui7uPz/ ld&{ # yyYR3q#U >0sϿ?~7ZZrꢄ_tJۯ0QLTC}w:3nǾo.W?O#Q™iy.U5,}Uq^pQYQe"QRV><{?'N!+.(jo? _d6;z|mrcNO0eAt*NwM2>}>8o [*s@4yXU>ާ@]O6w4B.ʪ>O;y=?WO5gM|.;l^'_2"+k8f/Ӎ^bk`}D?J)Ra`PZH5 rZk8\Fy6YqڪTseɂThPH]Y9˧y,s'J2ņ[$Lq6eDT SKʷ04!8$L%DYKJJz޶m[WYbŊңsR& acJčYbTի͟߸q^=zb1DK:|5{ E;wҩKI)Kq-<783zv־}[@@`Z򿄧 0Bϛ7]vx"ѵ[qcGK'}yg^ߑ_)ÛȲ=sj?NÇ=nc-O7}v]Snw)-)~O<"e+ '?`g &KMIEQ]Tl`8V:ّCv<ѫu\G!2SkQKt MQ+s@HkNPȼF/})W)2Mf鷭Z&c>J%ix̥ݣBv/:+?kErM.(~e9³153nUA1bVTYL,嶲·12r~QBӸˮ;|vϔuJ: yD}&Nlʭ/'3`K7>;$^@cg`ƌQ߹ Eu=λri7MSSVX1/{kovs]\r? Wژ4蝫Kի=\777uݜzj߸0#{dy:%&DH54z h]?!{JU4e320?}O {ģ$FLcn15^X?'ƕFI킲Wg) KsU5lM~*k ;G %aSOvUL9`ñ1'l8Xbɜ̫ !MʾKol%ՀD"ᚬGhR+ P Zu ɟ١M"znFpgm&b񞰩 BrB_I))=,`i,&)f%heu*;8RQrq[|: QOƫmt9~K J,m;l\=ggOKkmJi|7`<'[v$Gx>e#I|o[XDB0i“Zk"T#!A hM`N:KR(mHR!":5]lCX#bѽ|mJVIט{v'Q?la`?d[Ѡl݈ƶӗҟVzg8D2s2j+Ab!XXy9r(<ϳG=D&L6Y əBfQ:B6RNH)P)Z|"L !5J W(_4yoxopÎss5qz2PTx<;¿bYϫm};@+Mo ן5J/]S؅hčgD ?io(OL6aΛ/׈BJB J* YJkĨ+ЩP6 FgIwR';7?˓,J+'VM0rݹ֚޶˂Deg%p۬ϲ"Z0HGL4bb4E.7@3#>rݻ]D"P پ.%~@!D"A"{h"ִX&&̪S gE'Qh%Faa%.r l =2# $ԓJi))+.@$ץ-Ƽh!%4wQJAH;=eYF!0J3'<8SOcۢ~ h$CT\Y ր~J)r 2LkRhM? ɘZk ZI0h|7 Yn&0tm^ڽqZpa@4.aU*=q@k>ЅQ'f{&OIV_mBAPqM_[!H24ypXi1EhЄ2q2@pm¨~[m4s!1:PZ|VVEAL`@&{pPȔRDoq)裺P[;yuO8uWY5dxO8go;x߯~Wl]Q'Ed  $+mjb#ք&{4ɇdb6yh V.L@)2Xs 9M|%MY`coѠX'b:"`wC(# !+/&'M5ڐ&.<"撨V@0j_}6]v/]jժtڕ~B#$a`wCxv,,,}X,S^)S08_~e˖ݻvKӂAxͷ̚kX{.0)ܜ Nխ{0@DD".AuTϖRYn;kv,YbūyաCbkXo붲_lwxBknڤE!XU@XP(ȕh5&Hky=fGF"c4!Tm6D!HAf7jX(?F-Z)mMsgTvJ6"acPaY3uVk: V}vܹs瞃O{F!ץ?<|7,{Chղ~%!.[֠ێحg׮]:w ,-<묪j x̭XQQFij3 6j+{Iդ ubյ?PZwѻO> ~mo.o6_}vҏOҺՐ;K߻uݮrl G6\#t؈:hwM|K/|?,^>P{Ͳy %;׼<-?IRJIE[aeLr<`͒X,Fj JQ\"WA"HCDJ$Kղ,r˻p\] JD"e"DA<.:4 hA4-tH[b=pެO-*[yX]z~ YCϺug OxY:8ֿ9劁<:hӨ+'֣K^^x=-Uw;8*'Tk8e1+?~;1=t j6"(t&u`ں%.30Iy\);TF&^}.E_>EÎ;olhS#IjOe_?:5AoS']?± ڵg}:d.9j_@YRw3hKf5rkgɍ OfLthn?ᩕywoUAh%vl>7}mFp1a(P<)ļ;t@d-s/+ إ |$"jZ%M"ץȹ!\Do95`yv\$0Ri~؈P\j]+S&m0J95f(.@O%ڪ4)`AT*U+qrrr>'R UM i#@EȭڄFiraZ(FI)PiaeƹK!{eٶuZAբf9|?:l]kzlmǍmvgVШ@Ǯſ*Z&Y\Em@A9YgFhk?}t]bX|7|5u{Î_|vm ?t1gF䇇>=&o>f/:.)򚓟9sc?i X5^;6_~p wunNq:=4᜴ _ug~]h)SRJG ðw_ξ㏟6?'q~ןOPɉ=m!T96اŧ } e,g{`(WxͿw|N/OhϬ:f_έ]Q<kZ9ɗ nu]VDM{vw>A޽?uˏLz;v&w/WCZqvESW 룇E7ocƷydDf[fwp5[`M7kbm) } 0Uԏ5l`=u cޜzUu[,i܀YOpחm nW}zyfvr Qugo9zJϙ~co=o8 7xOx# fP h0dA\42B]BHH`A܆POeI߲5&*JECFz!ԠXf8#b6VuJmm FD d46<BI%!5y "jx͌ab " RP{^ Cy9J)A$F(U5'U+^x*T IDAT&{H)B"Zse(&(m0fl*)\kiI](B*TZk.RP !P))l`}"JxʎWJO=zȥ5_3wCAע{KJB(0b*Z ۽cUG= UYaéAA,pE{}*f\;›}x@Baڏo:ߏzSv.ӛP#zutJlX&~~ʰz#y3<ׯH߀(p/N{6_9[^ɝ@0}/?0)Ď|Ӫ)'ZtM߿[?聡}ڷ퐹=Żj#;(BpeZ[/4 c[P$?>񮲳od-Zp|K֋Y08Y(-[6J>;}YךiT|̀/wyǜ=snUh:[^?ۭyco۷%|Kt]ÝhYN9 Ϟ0`ԩ~ƽr#q o{|Ygگlۋn@H޼%= VE'VfEcJnVZxNMѓC-aK5K~:-}jIWdd,ep//֗G),Ͷ n6^R]Lh^S&!eSf2' ܽ~{괽ؼi3s 3f|q!=D|2G!)VkV/^ǑA%ߒbl?B8J\6ɥ`TʵHH( IP:jmːuT O VFǑJ[#݆U 6 u+ݔeeB5`VRqά`i+)5b1)emNZksi+lCF+ (!4n&^qfiΎ: vAzKG:~k R0 !tB+]^^>ۥ{BT3r v_nSƖ9Uk:1SgU݇USL}#Yq;2o1:|r:_\3W. $BX6ko+O]ۭX7$Ӽ9.x tuw+ 4OcFW?h| +7 W9aVwZ׼͠ ^eYZ^g<=pjVMCd=wSn=3]bbMi=S+ >NdQzea'v}^Zr\cnn·QJۯXw)ˇ-ssxo*c{!g^}-ѕud.6>,|֝]9qPеGT+AޙgЖtGwP#B BJQAͶvdye~UlEkV5fk*~׬-xꋓvkEӲ:=[Fi[O׾YqU,?+kŲp;vؑ>KCBfrf맺;VɱCc:n^}yo8q}>v1/I B#0d-' rè0DRR#'Xi7itYtA!j#2ó=$A=QJs4dʓ;d1Zk^0*Eb~8u,:ڂVbw H!kfAz @*ԈM~%v+ G؞V. 4|n(0TC/`0BJiuH8fyOT cǟ۰Qޓc&׮i3l>&*8');F׼[{nnE#kfw#dkc]=UvBJv7O& +YejYoN9(85cn; @BW7^r^{X~2uA'F߂US|棞fɨg?ks:]w/6x+ITOiʺX^dK{Q/zQMEԀ)V]Q_81n:gDPkznNlW3ckg` R~}サ!C9wKm=yٟXs#Orta-t#L:Q&COۍ*ݥ~Zl&6:;z\yA=o{FEW_UݳL|ְ ۭwr^[gvcN3:Q֡!vm2~OlWƟoLnhm?_&Q.d맺=Қi?7nˮ#~{~ Ms\a?@,4'3ۦt4Qvt+Xw9 Z[]#(!iSM]!H)tDqpZee/M`t;S-k|ZFnٿ.6o˻SX֏6_j_p/}fص>wڨS` |5Y.nl{j՞<7ĦF?yy1[ϨnTU[֯?j֖{9>lv#@U[>}clۣwZjj#v}w}mchSu~{oJ͵k\(63ŻԪ ءϋ];{ӍŁ#˯}ɿo^oٯDN~nWdNS[g?la0u0~6N~Wg ̪utZ`\㳗7;4z)$_=sSUj}j1qύ9םFֺ47|^n5?  OuޯW)/v_} صK/p9c >9^EQt٧o2jf]s- S ;@;>"kR"LEJ`QTeEe+M   = 22- Z6qG$bNfWWҔFris.-2FwDqG Q\6^cd>@)!3766kb!kȉ9YJY vב KZ!h":IkI9L۴t֣e>3wsn͈4q)Mpv6Cٽo8裷~->W;B9MfN˽[s?#֪'Bs:U:K?kqά^!pT^5р*go*,M!>q >u.jʼn?}W٣֗0q-t~mo<~^jBG+tЕg~z̏6u;a'>pA\<Y*D?>" tdU.Syk<&`mX #5boݰ bd2h]ԥ̌Za?߳o޽biX<5}=tk ,fLM[ Gן&[/O=ovuh]rOowz?!nn[s&9LZb.\?x;<'W#n~~sЬsϘ4+\[N<~EOiܶ'Yq͏j'\瓷ӑǃ'g/^)ןpҹG>bp]0vfX?S6+yī/~|եy棿=򮟾pgX\C #'0̞}`~Q[TW< ǿ_?/zmXiUEyg&מ5? 3Vo3qdHe)}ȱ=+z&͏9j֯_gko׼o&~[3^Yd lfkֲ: "`=Mݭr*rKRh" 1P\U^|k7-'AZ.3S4Ṷ!}RYڬWV&{K5~yC:|=N9aQ~¿l#0^?c.1l],E;2.zxIjE߆m9XN V6uXM 񩱮{{DMK޷Sx>m?bMZS޼p[s{PcV;twLꝆrAog^dgYsf~Dы?P=H7^syf̙łPWWW, bG"XW,2QB g2>/ *yB졨$Fu0@\. Y3r3Ase.QԍE H+2‹t d aD\Os9REQ$j?R=m"DbfN$MR%ucYDDD@>,hFnaq$ 'i-9c?&f)3gdq|{/ZhPڦ6YiNwg[Lv"S35CHƴݷ֞ ڡ0ڡUp7Cadgh k+P![ASf|A$`N70)^!Qjիyvv/j5bH&j4՜Z?DMY)SêG\@Be'QÇO:Ulnn>ȉ7tȑ#(ҫ5ײg砝:[3Ԉ+q?"H ! R@; (ѳB\"%hKwtYGQ""?⋧zs!VϒظԦ"咬[k"A?!0.!Hд\MzVedˢpn˵-B$飧i_?~ƍ;CO;4c̖[naVO͊qoM~gޝtmosߙu} x XHItO"Qf-)I_DPë*X>FX.L)@w9Cz @hfHN*)TҗNՄ("L'[ɉ x@ 9jraA&%)e]IzZ8rd D8=m.QFߧLMYOٰ}j d7pǰMdrZNF:I:@0J $FL-bV Plw]yq- R[Qg @nUg}3gμ_}ѷ~;عsgZ qEgr[m^xo--M(^Vaേo[u@,Y. caUP%gLdeHQ|+[!k\0] +ds_FJc XґƘHrBQ}}5zH2t(1S"01űaAgY45O*!=)i:$} r.Ml-BC\̼8(Z2CKss"Ҭs5R*R0#!YgSQ@Q㲵eBrhQ0oX;EHx껆[9i]y[ s/NJezqߨ ?lnn>=c= .xz%Ҟrar[EѦtXoT { Zc2 1^^j*j\zXˁxXVֆ׎J+0 hk Upϕt6MSHzOw\$I!rg-@;@dHJ~>F^+LWHRR"X*Y`N@Yb2R#SP-D&Aۑ.~Bft(L9ۧpFqd Cqf2 $b6[a=75|a[|s wGh[󥔡klGl!׾=-h @T.ڶ*_˖jtrÆw饗ve>BKK˂_mYܖ!=t >N xsE+A:*2OP*BA|J=@EH:B xfqoO>tLVʅAs8Vz!sNVЦ$\)Y$ItNslB:"]xq-.z9-fľ IDATkA+$tŢJ M"T* č1ό3^{-7},6O3J%kmTBCD&"!Jf3JD:d$4q$1f\y!\m^IgfϛELL>mLO9'>ڎ\Ճ'_7` 9{x#Ǐ;鎱0^{}WkzmjY^/[n܈ğ߭[78أa=ɓwqe=r[&}΢Gbt~\(@aHNVi]b` |_EH՝ RqܧW˄JR*JD^WDRD`i{UyIE^ *yWPYȴq2DJ WEw&A1r~.<RQ$%GwީSN)+dSiDZfa!wI(h}ÝG#u=fMḍz|Mo:xkn2#A(6{é9_N~2~ĝuMvYCM &Kɚ[n嶜s.JIDF~ۓ4[L-6^޾K4Y{͚752f𑛌fq8du΂! IqIS?Ʊ\x@e͑T dil: Y_`QiŴ@98nS=G Ҫ+2?RP(ht:O%$$ku§PFDq~'7e%'>)Tr}xZ욗vGqe^"b"ys~uٍu3\Mn1s[@rN"ƅ֊!3gWPzϞ+sf=uW*d?((:+ zF焽R˩V^H+x[vUrV,|AVcp%*eCÂcJ#zaTp %4TY,1R`% ?h- Ew-DP@oE*o;fkp1)`M^N]E=wg}&l 3gdVk- -Ϝs=٭vꔣz?+#F@d7kr}̖C[ þ?ƳW=9~m{ַH kvG=nuԻWv0R @KKsidp rCr-rm6"-$/:wċL̡/ !ab~y""[{WxW-|زj)uP#~^ 4=(&J!i 6H7N\U H|\ B;HGr mqvąBACU޳&tpE{ J񨌏]B nꐀOD I [k[[[lʌkBQ]:u2dFm4OxHXW9gӴX,JH(>6:l̕GABDt1p[bN~r3Gh6}(q/.區Ν~ !F'~*8k2eL:<%zv f'\yn{# -ܖ=Ŀw_|ŲZn}`gd;yߟt#n uk "8Zqf,Qt&D6ƔJ%UmV$ cq'Z13 $dfǎ, ^At_7JU0~ռ]Xȓ oX,j.B2lcaH,h^9 w)!I0J+n5١>~=ʀT666&֭  GoE2~dpNxc5CAGXB3CS&43uYy̨-]0kXYoD/oXȧVy{oΝӸ٘Z憬!3L?zm_[nK:QOJ={r_v}_tyg DŽ^9C"B$189* ^^,V;}Io BUC4ibg= %K!)7e].|,3I>Tp̮\Ƙ(q;[7֭ Q)I"S3DX= VhYѷ%VQbQ!Vb  Z0(SPB(b"LA[8hvvBv9|ɢ.+:RZʦbvޥC2}bݜYzfMu-Fү "$3xaS{P,T22}[n-M8I}g7s_;>M,a@XGc Wl ,_JWy{O)vu^AHiٳa1jU} ' kaA=zw"ᢗ BffR4!lo` {-KT%p EPV.MlE(Sy-ˠÙB @ٲcB$ I#6ӵ{@)0mV0ئխ)-V! 44xQ\iC.[nĶisfQzh]wg;;S͏PMYlD1 #߇}8y]P5;$K9Pٮ* W _|Au#. RNQ%(jE* d>iC+k3]uuRd;#jA Ք%@?W(i6 9 <@ʑáU AMhy¥JMmEmm=:tdِ XMIa;;YڲL ;FDp ZCG~'ڲX bbj>R(qFn=#s6"̋sm"r[Wmg9( 9G>cG"LꡰGv,l, Уo *#9P]Uizӆ4x[sz BؽJْ#hmm*WuuZK|{qDeL=((B46Q)M3_M,Xrs\WW'TQh `<(%ZYm}}:vH yT)2Xį-9G`e ٽq24I %Ic"b24̌TTÿΰ)1o2fiZjiO Zs|hafnjqcn[ne1}W ^{} Qw;gsh"; F(*z }ū[؛GU9Žj7H ̴B7CfRc4 ."+H5 ȷ%KP(ą~ CPgYH$ >u8ޏl7D&@AI֦{ iqEefqtBvjn Z<(qL*4aifOrJ$ Ev.LStH+E$c4!.MZzCDcBm~؈Mz텧qoP}.;;1_[n֎!k/=S#u/\w9D$#J(Y$"CR#p1a*@ +WIvB탕!96uA!FC dce`E 6aPTla9Y+IO$gЄW]s q 謋XdX֌閳Ůh p?",A'T ?)P5\2#JA<O b}ʫO~*o$ 1Zgg="!yE*I H0wڽe^[Zu{F??e=rm6zqž-ܖ+seh: ]PhB rWoA UU XBe[1г hnAPFաcc߱X`c}-Y0~-#FUF{hXQc"_O,n\9þT7QʾX]}RBNY@IŖR$eE3D޽M7x#>ofmgmٳGkkK:'#0`ٱQUScDEHٞGrO_oH9'V{释fzK2b@eKXo!rUȻX,Ο,MD(8hSA^DIcEI4ƠO' vHZ+#0I~Է|TJ>˯jҤIBaݻw_guvu~9sX6$)ڴRV5hbDR%t_l 'rmSK:1"vZ^YE1ਰVPk|NM1Z >OA * h`jA:4q`C}Rv MP IDATjVP0[v/6w4> -r-6!y+7F}bT -C?o{Be,i-4 "]!*ߌL :+65EKY3"f @qWS)̘@PL6@kjg$f!LYɀ)2Xhb0d+[( 2(j Eu5#% h"?ʈ-;&i٧~MM_{u]:G3J|I}}C}}}kkk}C1uQsY2dkuIټw˟ǻ *.ot}ǭװTG;M(t8r-rmэZk"QY 0j4 A-1{ @+_9 )JQHBPE= W1$8<-L!0;ʃ c|Бf_:-peڝ:a݀P}=8[RwD`Aǃh'_k ʅ3C՛]2cPܬUz7|_ZBX<5>SO=ē?80o?v1coN r"HO?4I={4442BƘŸS&& CDG%_7^vהV^YxI]An&0r>ާ$O[nrh\[AȠ-xMX 2 n_IjBS=A/YPY=YzȵcЂ@j`Pg,h6Q*&MUeHE>RSk<鸕WiYuAVRM~b((eMn!̖ht}7xzV"bf ,zzxӣGb(#ŹTZ"0hZ OKI c7}欔݌3Ν /ǟtҹJ=\kU/LSqޞ*M|T*Qd%䎕.BU"M5h~@EK@qt?[JRHG#1ccޟRWW?qxcȐ{I:YY[k[[KM]ZKzuXR_s矷fƞŏ~WO.7vѵG?wGfûg=r oBqW?evX\gUկmcM^zóm{Z vIs蔹lzxm75%Ν3-r-e`U?ľ$auk9'\ R(PFCʏ/RP,mERh++TB6Uü,SȾ@*zΏrFŽ _s,e!;YtU1PLAXaWD^9bMR[W&2@&X&Cu 4M[kKRTR, [ȥKiYGi$l= |&psÓ^~9szz;gBpkJT]c@B9ӟJZBEHcWlf2nW|;Σ'<Mp|kESLе^~#__r KR= W_8pmW)F_þG\56"KXó3$10!O@3bQk)*l]"*ą cIb>#BIJxU,3Ŀp0f Fj2 ǧBQZP! bM}5xd7w}!"R3~;l5;iRZ{4kuy m ٣ÙWm8R_-r-ܼ!L\: o\C!Oѐt=QP ڐ5^&$m;+^1*|"LǠf@} j4'B I9ԓrh4pqzSkf@&Θ8g8"L折,t,-Z^M'| ^] e"2h~ ,O?衅TX8`@X,2 Q֋$>.)`~S{cWy{?)oO: džc}kseT@tf%+=[1>p;]6ns;g·mW/k1b%=eFIkM(-ʈM6r-rm٘@!Tc$Ql1dȘ8 ,a?W.(BCP*3$4 X'I`=da Ej|,娇$оDx|ծF٩ _Ίua}'c K*%B)aA尜B/MZΗL =FIp蜫k(KI^t M)[ aIwyγz3c?߳{\|>CkvG=nuԻWe9rV/Zo "4w.y${"h1cdytMVq_M/e;v5]qI lyvoWXD*@׌+;U` eOGXVT:V*%,vFOujHPՓOl:%RWW/e"wd·B%Q.z|G1?=F*xM_3F'~*lk}ZB%@WEg#^P[JQ'R>|݌6] TH-5-89 ĸ5®o6P(G6z0˅#qq "dt׻U%w U܋r._ 'Ɣ#ŏXi*e h}>nNg2/-5"@Y1bnN,.YӼb]};ɓiwfM]u>=d>z۾[|xk)nawd'3/״?xvmy?{[opO}w3·]K5v᠑{T J_ŀ5\1E&iޓ?^{Uf~~N#zl׺ { ^e56xaknzs"X^uϴҷǃ6dNaB_gcύzlvp96. ,[n-QC;f폘ù@DdѠsc$hZ@"$Ɍ22@S^ |V+a7}`Wmpt(3jЂ`uB 7PQȧ@_}]zGTB](*Az`]} $6ƴކ%H?hr , pACkpEs }V6Zg?U AK-#/vck}1gZkTfLqБA j\vE|Բ z_'7{SϽ|;!0w9VI 6@뻷ݲfϩ%";}E϶ ׼ +}ʿ}w:oˮK*Ҧn?5/|^~Lux0Q-/1[[؝6ݵ'_Lg8-r[YSdfBf2dcȦ^&X MP1OkfƵ@gu2] @-=$3vSOϬN:ps"*I!XG|N0$t . \i}03%+T,K744_^9G; 4oB\(sԬP,5tL~K-+ [[XC*Xd;[KN31r?~4Gw=eƚ޾oxp[mۓ. ٯpo 2y/κ剏s&t/?=|͏N˦=5;}܏SrsZܩf1"EhyK&o|c╃NZe3v>;程_lozs̺M5ơۛG}x_^9yË7uеq?L*q5ǟ~cG\s6= a;:?]6< Eh|ӿ.ŜONݠAۦqVs{;W$cLTm~y;.ޣ7|gw3/?9+G<ߧx1 f;~%(htօ}1-c:ḿbo2sX3qKKbt?}ʉ>4?<|أWo3q>>SFtsv8Uwdb[9^!u6rw}1:Vn嶴,DX,MQ  ԑ`0 :z:3;Qd G9߂W#z>!!LEe'9,ZhZb$Wa` AOfH!t::& pY83ܚtW3 &.pO*hKd洞`=7НB@S.!DegYJ.!3lFl¥/͘٦i]hecǬ|/zXiw#ܠp6iF _1'}7]c c`14z>bG>?9wnq۾Gi*9myƔtN=#/ xz3|y;=q5ssUw &au8_'+fa[p)O1i~Oj}֑zi滟mzg4wp7:px?/!' {]{|t]Z鱓wWH?~ۭ}ݎrVWn~󸛇lF27`xwOymeoG!GyzM{>s7icz^my#_{3L#obL7>q67>492{}Įӵݴ+Q}_=Z[s! 辙t~ܵ?nqۯqRJҙOqi+Y %r.OՒZus-e`"1r#"08;b(<\6&bOrQ+C)s>!aC]2&h+ibI0܇0=nkQ|mȫBQ,BBN%F`BWЬN/U8W8r!mͫ$t2]88s>CLءVrK2}Ve$l/n MHBvF_ A(:Y+4-Q,/ݷ:߷mS/? _XLɇn`Mg'Ҕk;p˯47G]p1C1xfJ;yՆt [bD#Qn#h֛~>WL{ƬV7^ǃnB~F=8uL~g娊ꜞKroB I DUPdQQ|Q\@\υ;($M63ݧG9s3ht$~UW۾e 3>G^sK%۽śfoW0&U|p&Z[ zet2VC{oy( IDAT.ב!=[u;񶯉ޭ?xoج#-?睿8舏na~&A]mѾw'_Pc],uw}І'jyaOwB*C%umH*Ec- 7_=oo>}ɾMY^q/߬-3Y}ۚ_X]WPz95$ E0A<F#"2dP 3ap("u(=0Su D M#J)$x sR/%>7͒isLcz$ѻDĻG48-U2(ff$'Qa_+FSwqNʘ;HǤ=:'&@^t<۾&&KƗ`Fr^UuwOj}%~/y?'xH^qw?3>v? N~﹗~Osa}c^+~I'տyd->=-~q'OǞsR8_mߺBx-W\}\p{Ͼ,EFo;˶Y=4>[W@k~`Kpp,/<*L,롱CXvWn;onǷ7,QQW[O]Fǃ~kC~~ܰ?{%Yko3m7|םriύZ/ZFW3n:PaWe@/c7.\]6O|#N*}~`Yݳ:4ZS|1nlڳPB t Bd(,RgB(ms\;\AAIQOv-#F,LCN#$-Z@=ʫS8.A|1n[cszw(,^3$D7L]ܠzSL䒑cJе%C1ȟN| f%Ieιf2_qc QK a>퓀+_ n_R׼7͐fç|o8ԥdAaez&ouϗϸ|}>p;w'Y&eoSq.?qu [~䡃Sw~7΃>pp =[~xu}˞;˵;''mv_|nOj uA/s qk?y{N~Q3fM9#}񧖝|ꁻ634k‡7EG3'nGlK]y҉ _&x;1~,9C>wM:զ7s/k96)ǝ{'uǙw8Egms|EZvJBm.Zm#[`ά"~C@z?= z̫.ȀhYup^}773]L/[ͺu *WFS?LC"_Ăef3(Qmn =QPI !J=!:#\ipt4Q~jj%`K.RbI1&xl)){n'Lgx衇-XӣaC~6gΜ=wiӦqlb%s%1='C-9sfxGydƌ ,rϽw,yxS_8z돳:L}_r?1k%EXwXZRbY D"w^{G^?7~/xG od:69x t!D|eG~]P0S'jnNP&n?}5kp`+ rTYix%L套׿GYp˖ɷ&!C`@.ۻ\@MN97H+{?y՟QP[uN+6;vk_~.qXy-J(Jzzz{_+R^'"w9r9G*q*j)>23#HC˩H%1cTqpg;"RhncYu؅șSlqUݧiG"_MWea`^=vciJ֝sU,K:uٳg̘a{+5(.tFFUIQf q 9Lں&3cp_gd+p#"$S5m2|_q/ɗ])? W<|'x4ܗ~nJ;$+ە`='tMٻODz;2ۻ^9+N1T:>_Zף:n/ ΃䊿^|>{y;ɻwbu<+TPFIb.[D%!L#CDBy"$Ԩ "%e6Rfʕ"k☈'&brK[:tNDBo5w1f%ϱ/ SRZ9ˡ+`2wqpO:vj PKG8I=8$t$qa3+PeDT46Hye^Dʙƶ%b7$vT_Rn}ж@9g/Z Ou[6)E >e]WYcxRB 'DC٢mVZƥϧ2a aMV/e:+P!iUjq/O6`UbM ~ UNhwRyR2Z,'ɲL'88CBe|tݐ8ipJպ(WҟK`DA [ F-V{hL]Nf;{-K,NL4^Ok[B *TPEh'9,?xS 8@-V%\d#!x"!nw(R< 0^!SB5(HٸsJSsVY=aEIᇈkzR#,j詬:\^-WbQCz( %n0ɽGS\mkǞi&2$z40Yc}6+sӅn>km>G? =|{sƹ?oޚ c?;lm7_Ӟǝ>y;<9s+vfYsV}mtۡqYז[nYnw_Nw|*^㞳-[.tlӾ֒\j'hnp[lp,2zޱsӮuy]zT׍_9-z޻[u#墓^N[o쭶zOrx^*TPatb?ٲŷH4Jj^c'}$*TTN#ͻfcgSw0&lTLBw,Qn* eu-H5,%wW1] S|uY![jCl5Vm]¦2%J&u챦Sгqi-5}yDĺ}6q}-_]Qo EQsF+ovU4޹_/٬t ]|I{,N8s_}l)?yo;9!MS.9~}x۞uݡऍ|}YCd5N]|0ﭿ7-9zͥv]'~B/eڣ6+n9'cZ[!\? truo9w+}l=vǟw\+TP-J "8*Y C:~ e'z$f&f!! '"&ܐuJ%,rwp([t%λ쀄E-1iЪNI>u!QC"lQ)"ćfm 3 JzlA8 ߶P#V@P},n:bT\BV˜WfYMHgڷ*6NWeJ1z:U8=}.w޸9s^}évi}ժ"[/ˇnbf|֢#?z![D'sg˙=>}=%м?w8Qv`JԻ kہ73㽚Ϛ^m.r; Bv8yY@ 6Ͳ^so1.c^oOX<>w {wo;ּ *T5ꅌ"&ZXU@ݹ>Lu IE_c!U[$qDMoE!``5yˤTy.TdTYn(jus/ڍ1.ڪz^ۊuX7ZD.y,BKw(FHb:JJ 42To+M֩5PADA, ,uz9~̺pi;!򟯻4_N?v?9ea|m39g#ro]CpEh=W -(R_^;L%̗3vP}oYMWs䤯}y=%/8`_nY}MwEZ=k opm.r*mP>:gy,=JVd0aIT8{w_K?56>4W_~%_xo2l>n}C霛Oƹ>?q^}O z:릑%~â-}8e;sNFF=ag=/<{C/mϲUvշw[m݂yM{}+(=WݖucXa3/ޢ-]A?zg8+C+n{|Ӟ|-[ڎ~w~h]m-[qzK~WóvY]ş *T`@fLy̚0]94 xDbD.jRO+qmIM4D,+BHI,"f5v:js -n7vG{Rw'7'p/̄bN\DԹ|XF:X3e[oJmӧl, PQejqB U5)ymDuYHR\ﴌgTXyדJ *TP?'P,CLfk2̙|Ь1ct;DQ:!>:`b@5(WQjj2ij!#);GꛮV;bgs;e?Ěo[ -zOX1kgH~z1E Q = I^8uIWp1D,}6.XRў.>Jvf \6أx Ҭ&g-S%*TPB 8D@b)DDafn*.rh?G}K2흪UG&wBm#/ q!+@!Uqѷ|WWl8lD1d",owvBqK66o% Kާ-~˧Q9#5|X78pXB,y4fo)}NGK*a+:hB *TP L#""GNU@":GXtʯ7&rTӍHLurA(X`,W94ά$<էMpcI(qо<톕k9~Pw4XO(8ߖ` k0#bV˲L$C2ne8qI,Ĭ=&묵[#8J?X4'+"D[=+lx>J}XicDFB*`D'#ƈ"v$C`6pVnz/ƌE+o4Ɨ Z=SBy(B O7m]x^{Ne_1WӴ)mOzܬ{Ho9jsW⅗ڋk^?LL$+TPBxl;*>G6L *&J'qQ%D`fv-t|LÒ*NDկ~cϬ[ m+xful= O~ӡO>YEߝyK7=iwϞ8.T*TPB@Bѱli9NC,LRXބCɕ@fYfDA}oS9=|.D Z@`NHHFYx.[v(/9K{[nZ#v: 0zuSlU= %&EED8)ׇD)dCTbQVpH1 pCD(!HuDX͖0#?`?l6fSCQ03Ek[.@Uy>\zGu:H9{[:/̏?;cEv ӿ}%G>]_kx>j)c7_{+<ϼ_;ϐ{w?ό6`B *Tx恐PdHN";m;3{?CxY$eBystԀ &GE &vP'#A uf%y^*$CUeAÈD#`|ޝ!=D4IFQ#OUUE9r vZVEZMq^'xAlVb{8sÂkLS ssSwW6ӣ_Ef#7}oS_u36fO!U{Mjqp jvꏯoUWPBW "1U(DĢ΁ "D4OZRB-Yl)OvQぺ{/ݢ}l'D*PMǑ^"qO83&9t'\;$ިDH3!QA 0`w֪JF!#k0h4@u* 20i㰏/$-2AC"rH>Q+j${{vq--dqڟ{ߗݔz*TP3H2z*E>ţsW̸TvtZvHNX4s)*\yoRcY,ܜ^xR13#AXADۈC{MkB *ihF Rڥ`9. Di)gVOdQjs _Gh/B#H&##-B2Қ$cˬ AIӴ 3MP-zEQһlE BL: _׮~63fy"HRb! U$1;7mS.ճܸ݉\~2g;̓ya]W֛EW~}=[Wuo%PB P6Br@UPzQN(Lm$%:+K - ڈXI!/+vLzbř]l(o'mKP\|(h'YPDŽ~ iJuREM#-BdjK{Ӱ̖R.W@M~ayw 8]؛P&:$w)cu2V(ڪV~6׍6c:uJ"U4}~!"ljU;߁믿Su_K~yzo;pΆt}Q!`b] ,f,tÒ8wAeuȱ.{PB P,IQi ,^?v@e,O3…  m]d *cCd ,=ԣ8@ϩ6$J;P,mK"Qlv$q`r乯1f̉4feRlk{pj,?, !1pD dĦg1YZݳeͼ=(i"zOڊCf?kwX<Ʃ “u]wGO{3qG^OylCϹ℃feSN>]g}ÛLV>{^_QG^5Y *#}OJ_ JqVk*# Esċ;= LR]*CBDDX)~jB 9aq,D.#B$<oͱk9:+hS׭v^0lIpۖRHDqI}ݪ0*Kc#4T$%/`w ~T+ ,d¤b5 ӦM>mHC.(ڕ"%0773:j?:Zs`t ̓F{B *TwDIfR;j"윟܌J(PSR2*X#_5k`'֚fF$ױSY*8]"5I9m-egCDmYH;k){YDٯvVPm#`lPFIRP@{xp _e.c%Jwt&|$f. 1h+{Oh?Wk_vt( $/ B  Eju1!XdpG^x׍WPB .D$n8+Jͫg罽)Ok'--;l%a&YXU'%,\nQk W+0} ^zitAhڒ 3%dDs^i+RSNlYa:" {rj s1,}dAg.ҟ5i.wa= Os@m5#]:A#HXNP ;5j===PB  /RS2ZhwoKʼ"?`1. NH3*jyX>6LRo!W|H$D=Ms&d;0W3WS 0 3`O*t,܋ОVXAe9"`396ٿ), @E:-DeKCY`tf'/FeoZRN`!b(/ vDd h=_{{+TPB6`؀HD!QIb_ -QH#2IGjZy>M1V@s^9У_}ࢮl޿nD͠^IQk*ΓDΗS%e'^{Q= *TPW&!TreF:8)If*e[aix칢&MCx84fۓKB8Ͱ+7#ǓIwY<drш 8sXVZG :y^FY AX< *PR":M<XPjE[$6%#D,S1$!=O<5{qt%Iv0t˧+ओN 9ȵ\/O "RR+*_B *Ӣ^j# -jºcש%%`ڐȡI']aJ&Mm֚md* 9J-mImr0<.AH8 eA9304W\x #"z!"sێSC| ['ۻ뮻N>=fZ;divi4&2, cDDB3UXY ˲f Оgj[Yz͒B^2bI+v#1\mVêhm *TP /R$,3U}7>v @RGMGU.js23K}D͌h[b6I+R3z K0s$e&ika:xlnEޓ#y!G RHYl}LLLD@W^xIbiӦT]qt<drش%ޞG-`E<-ZOޓ+=Y'iAD@0I/v9FASW3O^X ĺ>qOSO<@0%tNy$PB *Tx&,ae.gYM:E ˉI0KQmӽ *9"s)Ӗ%]9 I9]4^NF@ 粂 P,A*igj![oGe\׷v3gJ B'?44y+EnAKY34)C!^+lYEFGGujAoo_EAi^VNbY$X# *ؗbI9GOeR=)jAh5S O(PP%e Lcdd-*TPBG(>&G*De*p1'%QR=|W# Ǘn HKdi]k,`@P9P\ vk"!|KȲ#/{3{2 skFcppV9{xhhhll,+?q|||xxXY}ʔ+W.{l9sVr3o72jaD$hLfŠ+FGG/_^Ν;}tG}k>oa ( Zt^ <3g>Ԅ;]T۫lO:U 4h\rwu'3Z=00700P+VxΝ;e`򡡡+VL>sH}N˯1D1^Y~UD!oph)*_B *Txd_h,J0#DDdqT,\h㡌gVRh ^08>+%VC,$S}IQbBD_&#s9'0v3y``e3\5=;s|Sz^w9O,{l AC@ׁh \pUH!&7R5(! w@THAM%I[Oqh!4q˾d ᐈ۶޸/zttll,˲z^k?̙3O>11ח %˨jei>#{衇tk:-qU,d@?z|uppPtfY׵xU=41ZB *Tt̮yICbv^5d<X)E@Rݎ4BtYfͫ|ˌQS E,1> 8PDB<mߵEgFV9FQEQ 82Y{\Q xͱDUU[5,q]8c "| 9o4!zBZU̹ 7p֬Y}}=S7xctQ˖-S)FSׇyժUccc˖-˲g̙̼t<a||⮻jV{򾆈Z'&&`dlbxxxttlllh5F_ߔ(*nf$V,zY.}LDz{ fn4Y|V/|hŊ1qE z;л~YB *TtB<`pAI;[圳qh ''D)~ZT;l Ԟ^D$r8CNRkSI1o֦c^=},HNKDJOjX~brJ2-V+M2e1'|oooxbl\5:`{@EXo,o+qVzU`h^*o2ϋ<ifn0C>Vϼ@̙3l6C?Hea7˗3s5ɥ'( 9odTQjZP4V516FDj6j)< ּ1uꔞL1[y'V8hVObx`ffy*P8|&WPB @FuzJFfޅH^ -Q[:ɴTrt#Gh)v-K4Õs BI[ d[!CYHCDĹ)}'S[.@QTTЁ@k^8M:5Vea΋:B1:: > jj|Ppooob|!\026@F٤̋`Ԁ) z}D\t;',Z,sZ]u+j"iL4ZO <ZFy^hj[*2p, iwip8׳5MajS! *TP3ED>y;wDcGD, 3̾$T)JLLAMTd {*yKORtC@Ih$j,͟s3;T: 4%œsF>Qh+i6"->Dj|Y{l6M\jUa___ԾWƳfyajfקBV.]'yh49l37$Mτb9y3+^-H #0  ƀ00X_H X#1fh$fXc3lK{m2z8-ˏ7"TVvg5xJʾ9q8]xAaeyG$d難2sӴm۟1ƾ뺾V?crQUNժo&ؑlT!s]M+>^^j }8CbU8pCDs裈ydyƌ3f̘1ÃSNrbaRa)trJDx|p𜚆zF43}~cFTv9TzvW (WPB%Y 1g *"J,+6fJlm"b~&"̼ntȪTSeYu̇Sc{}{XTt:6E᫪:Njwkr!ž5婠m-Ų6!}<}3r\.lAseUEag]v]5MCDhJ}߅ʲ_.iٔe齿$NNRoк{ǀ)%Dv\ 0:!F@ETc13f̘1cƌ9v?d!!*hc$}("8 Q05йH\kk1s  =_8ȟF6r^"YbDC$H/ IYr YɖH@D9lILl q¦6ގ13{  !0sYٻr8b͋Hm۞N'K-JoB^93圫zһ>t 1eyyyYf)c0FQDB}ιjMӈHۇݾi\syno_?/h,)B4U@cLA*F,_g̘1cƌ3m!koFx˟0dcjgӈydj嵥33h$:ԜSɍ`N*89P$&[J;`3q[F Ps@F.۴t:YޗeY>nЧx8By^t]c0+%RuE˺m~Ze1F+].r )g͍l/l{J-JfvMGFpq(X.RŢ(뚶1GG.//}݋˭޶ֺ.xjmB(0 8,O`>@UfZגiyZ)j׷ADtX@PpŒ3f̘1{LLD4yA1O9~1_D(VěqVE@J ŌF~L)AD;;03u6 }MI[$"*Kg`8t:۲pcJ9X(( RpH 13ᰎtsҹ"ƨ1I+" dkkƺ O o'}="Jֻ[...wDEcttIB!6bYQ4]ɫT`Oau]j1Iz@PYEBh1V?Egw~Sf̘1cƌo3j_CD7&m/-۟#4 "LB۔թ Ga2"Hjsb~@_x`8SO?gUfEXDmMDyvQsfgAE `EDthABRTB$larz\e9EB* eQV+`cfqTU##숖eYbbf%,tiSߙ鐪Ɣڶu]Bڊ02RUUBW\.VUVGms"/y>ŜsVPb^|i҈I|u%"Tu (V믿[1@uxxx#Ęb)F@@S|u]U,HRʒм>l׌3f̘1ヘFKk# o~gW7#)ss"r) S|&DRvU h"U~PrMr`,w&4;?ǔ;I`ϘfN::YAJiZ&KTMZ=-&Q{E1%df5ð"0f!;.7^MQvy}Եa9g9CUMC."ZVKfD(%:r4 TUen$~I*!c{RնmUQ=qU֧;Im9ʲ$䪬[=z8soo<9_ ]nqy^ݿ.{znOR_ŋǛ^TC-֡,KCFB )}9*$A$Dîg̘1cƌ3C?/o=ndix6ۋ/ϩb! " TF{ǎSL#b123)"Rl#`{76ѹ,s&IXCʀsrYKǁN Y33;{t6G#5 #, A1\a; UeМs n=LDM\mT&iłҾ=[F5#"0(k] 1SQ{R !t] _ED9ź?~3u &! BTuӵDĜvy&OPVEUU!˾b9W/{Ut:u];NXձ=$hk#x IDAT%:fѭ뜣yN~?vkH(L&Ҍ3f̘1c_ywKWպ9(c@Q^GbQScKJ YsR }`s+^{oaYVٷ)qQV'Qw] h![-ܤi,m`\-q"G@e>;疫Ǐ=z_.miZ낎1܇L@IIuXVad C$hbE))\Ϙ1cƌ3|3'Kg>}?*{1L眊( PUQ "⢮s!F&"f/Rm 4ՕqѦX iȳb9rY/%d.9&PGUQZ@mGaD,꺾xEIJm`/"zK؜ƬPQ`W9?l3dL䶗Ho|_>XWWOn?ſmr-3i9w{{DSJJX-.u]K3 6Ms!xjM%B0_///9?z*4iVhupjC+zeftl ̬+hxHLQDr z1&;[B甮<}c>7sIX}k%|z\ E:쏿6Vr`Rr +'O wpwwb (:4Ptnf̘1cƌ^jٿGZ!n `dqι} ݋j shES`)WmvxNTbxxx઱;!0mgfzW7#2 ^U0{k@MX@DuY,ʴ1ǯޣGt:,daY!bC[@ o|֬1(+$gU?6ۉ)~˲>]Gt.af}%2Tn FjTR7GA@ľtdD1 )u{um ShoI05*]׵(Bz.6O x<Ϟ_w;kaIEg̘1cƌ`"")+v5;co۶b"DĜ%ƀ0O5XVUs>GDZ!#Yc?1V/}OYUFڱ[Ao{]gJX,V~Eg x#Rp8؈'JDh3q_ݺA/LT1fI0cu6Mc&/ LJ]r9窪{U[dvCYm!=2xm[:>Jn9g;,Y8:f20bU{uQ13#D[UTYV[o^[9rd uݱi1Xr,2四뛛b rQ}fU3f̘1cƌbĔ@B<詁rlB 1Hf6ϟ?Y' ;gcTȪcVN1ݪj?'ql۶Vwll`,J]VB(`EUØ>%;9evB# -oHUU1bQ׵;$FUĐY{Yvs,="A2ȩO_!uqH}}1F0i/c[ĔWUo֢ǺӤSA G"rr)sSX<뢬j}O_ bbܬמs%˄WKզfE]DmE];dm%9zQ1,I%TWeY]߫Wu3f̘1cƌW4!" 1F#ib df "E _}2r2E+fyg9""e90J%eiF2C V01u$-0{HmeY}oO9}F\su+:,'#DMGf\)r؞LW1/T-KUAI2dʓZ+0wU}bC777ffd7c M<1 'G;LU caHܢx`r΋En@NurJ R UዪTp:Tr{:ղ 8u'{k۶("g eVArJTE:$R11o??¿GsO?/RVʏ3f̘1{5G:(seYEcTJp;9f.FxQ=13PN&VmH &VhR׵U6?DX,bUUMS:RfrӜz>t]gn:@ULH͝ ;+tapG;g5i90'RlTDAtڭIZ E)1b`*oS A$"k\->tUȃ}L0xHB9^{`?u]sᚶnwY P9窪.<{+_srkN'Ss;N%|=x|UL{.$( K~:ueYzvd2q }}Q|ރJ/>N՜*/ҧ\߭לw5}1cƌ3fxhwX8-V(U1|ᙘԠB`& aX_V!ۮ.//'<^U뺶J|X,\V{4MYVLdRrv!Zǚ#*,i,H$]|EIlB)BD*ei[A:VKx&8 B3`I1sLISVGlE(F'êz뭷>Ӷm\sԧ.w}_ v<BŢ2bmRSlYE],x5[ns;{+tI PӧO:(J HtbLbH)ByoYW~QsMWҿ3qvG??3f̘1c T?n(K[)_L9Db1x<朐, &ʉMb6bygqgPDUBPU-`8hbsTE˘RjBN9gezx*OCU UcQT66F!xjNݮ:k&bX,&/S riwd4% "y*rJpc Ɍ!~cTDmc0}Z$l^0f狺&)t:}񋿳Z-6UQRYWWWEqCc{BĻ,3(3WBnDdleYD֫Md,[kWWCB84'KzUrΧ UI?~۶mE%!vD Ef$~M?S*L~{t~[s0cƌ3fZ|a0ݦȎzD\CvEP.Ff?cD\UsN5/aJFYC|'9uՒ`Xlۜ#"YDjX@5Mԇ\A4I&IT03 B:GǏ2h"& n,mYH(Cm#EIzxr9Gfv8(y1!B*" ZڲcjiȒoG}#tRZgoSͣG>}^Uz8kN'#}eYNOϞu!tm[VeD:&"* ѣ?w2}]WBAлB/T&3 cvz?ɟ^~M`Ϙ1cƌ t!QQbG4M.-6/}___w{$v(bZvQbu{:}"er&v} XYzj۹| WǏ@r~?y$TϔU]{!DEUBvȿۋ7{[?i>!43f̘1cDnkK$"҆wƝPW*1$U`?f;Mljӂu+N;^+>$j.c8m}@$$,M.)W{6?'?ߌ3f̘1[2#z:ȄLΩ*3)"2113jJw|uj:hʒ#y gj SJd>,du~݇<%!`fDEDYT9IQT) Et<̎4 b&PU&J;luzs뵝~VB0UFK.9z<0#R{EK_3랮_l쭙zHE$ч0 ("C`lUnWWW9BQLɎ9s,˷~~cym@Ub캮[瘘r,]Lo}( t*cHXAo딜UgAlu_"7+LıPՔӤʲ>}pEFDu]oכx3ٝ79ؾ^~?cƌ3fA9H)٪b{%r}裪2 "Vv[BQ|<]9b"`UUH/7[CeIJ,?ɷ )ƈ@)};vZ/CEusljܨL1cƌ3fnۏH&9 h=- T]"DtnWSgNr󘱫ᙱ0L5|-`ϵ$mnNg~n?BtMߺy_t!}le0z.蜳zvn\]9GEG V+D4+zzi]c~px|MSf!"E@Ĕg"a\.98f" 1JeY3j 2"x_tGۏ~jQc,2@PDR"']׽=zDJ׾~ %KxG?E~{].:D,Ssu-!w;cƌ3f̘] $s~/E-KDY heiX=i`"Ӌ ) S9n#N1&| a#T!&M0g%2{8/ۜ%B K0Sg:~ XeJ)K0~YSJDEؽx<cd@b1([t8ڶ9á?_TЩ" 9ie91 ɹ#1}h6tE ]Ӟ*,˺^'>ѝcS>|cc"E)x|bYVlzO;cƌ3f̘J˿>8{^UAWeJDx+@D2V$9{`EVHЇ1jb TXM]apH$%xӻr^4ګOˇbc/l\"3~!b)#dIؑĔlCc4yxc4!AYD 5WuMXu)Mìph>Xzi8@OD@("PvNz;ɓd>ڡm"o n%ef)fpu'=t]w_ɓ' ! hJ)s*|t f̘1cƌ3'~z{&@D@T bD"E&d<"2;rP=(|b .)%3 jn5yF QDKl'@4:adbaA}4L1\ӳhtENNU'[yLڅH lᔒZ- sEq:E(QǷՆGfO+䜳(J\QC1! [b3ѓ'OhYt:9B]ޛ[RUgksM`7@3+2AFDC~|8ĠI$qCDCTPAETFzpN{}{C7M}ΩSj𮵘rn9"Ns$ss"We988Xu,WAsB]w6mef޻߶J 4hРcD앯=$v}cDCkfWiSh1I6~{RsjW3JQŔRVmc=]w1`d iF!9^ڪ4$(×a+$& u=J ̧:Yԣ, EU%Nι9F'k{'Y4cй2[!`vX4HUU13 K%#vm3 P_/:Y7@>3CRLWF^ !Ʊ*pR& ߇;` UA4 Dmq6hРA l]L$ 4hРA;L${Pc(Zc`PA[1T$7Mү&䉡^hZLIk~LxD9d?#Ҡ$D]QOu1BrU92x1Y!˲sZbRX 5wsR Șv3gY\vE\1] k-Ydf leY!!(~ze_nׅ!-:nb {_{dmm]rѠ* "@qȹE" Z[*B،m= 4hРA-T(@*~\>2Ș%@c zfA A*2s1,I$3s¯jGpX # nY4ĞܵV)OiT6Ѿ߭ cfl,gY0vcX-KWEUv"MDQ!V^|MvnZӧOHy&ȣ,5}_3#kyV\=nMCMbɳ:6zmdB#bS7t/zӬeU"]p }0&s4؋q]d] s"֒\ Eyd.8:"g̒8HNH;!c*:@ JBW5@r$tD#P |ȍu(XQ\F (آ(s9aF{_WB ջD=@ueJv:ꪷ/A5 R +˲/CDK9Gp٩,S 3Ƭ]v̙;ʕ+k1:˽w D2y] *fbx`fDZzuߴ~<,2Sb97<<<8884hРA plB Wy- 3 DQB@.H%~Q ,BFxJ `UEMDX!Q>$5MTf,ek4VXIi@.;240Wj(d$2$D{ɐ]k F+V∐Ja 0@e㽷666GxmHn,8Oʲsɶ\{t"6yϚ5k̙DdjРA <_1E y3vbIueԥ oPJQ|6E0SΏHzQ۔1+q_ J~1ruT,A&P(RǦ~ m!`e 0Pg#9XTv 쬹sD:HX#Z.t.(Ґ 0icY (![Wy1,3c1?J y^V>n2``! Lf:2yZ|f2#ԇcc0Ǟ8 ) HA7hРA #%0CLLP#ZKZVRWovd6kѴ)4Ac\5(.~0fujbNjׇ|X:l%g&_+$>Tq=xų"u2]r=PeH,˲LdzubA4cRC8VBbV;_ Cf3UIʹaB{BDksrI!2Ir*^2NcLa@___fǻBmذaӦM,'Ty:/U^̌P4hРAȐ0YY@@@W @YM;S?E_ *Cdc4fBT{{4cYb95 T;Cz+H!7KwAWZ*{b5TBzxDCB`{ٌ,,#2D*;_e*WJs_EQ bp{#"a #yW: ·U;㹪{E1x0BUUv,vw.t: !)ɶmNpaΜYzvsx̺ɐN)NmΜ"rً+>HbF-5n<CI6yxW>͇w{Í럛SEo-<__|!wг;~W]nsVf yn]g3l5x>c?sM_h=guLn|o59L~b1$vR@z:BL)0Я'Q@ ˇISj@ HPgQ5JEDY12Ҽ`teuHd䃈Sj Qgf,]< ,ë{()dֲ 2Nwic=`[uDd(*!FvWy_y罯s@e~_0K hk$fk3$;9)^IizS7*ӧZY!Q "}HSU6TV[|*Kʀ< s1uAӑEѶ_}wIxDI'Y>Q5$.+3w:c^$Z- Bd Wzژʪ+}pC+cM<"f],;8@tWG ^}剻qzˏ;h/ySpч= N9W_r[9;o$wVO^p/^r>uz㴓Ox.oXe/=?aU;OTCۻZsxڻ};{?p~GԿm}^~5y^|Pg1t[o/~ ܆A쉠(2k%UňΕ YH(1spOԞHFsI*Y|zTW e&P#? rS;7E^QE[ =C}N']zݏ:_?zħz#w~l\U\Z~_<{Crѓ\sCy;zbP{ʼW]wk.[3ɿ\9ۭay4{lўkCrx{G_<+ng?{y;8Ͼy~uչ/o3Hןi݃0p.^]sƎA[okW>s巯s]-8_>ٷ?߯z;2^t}x~{ P=||׃t5a1G_o]XXDo'Ox%^i߲4\}A?ALu=x>y{^?3ӎ9Uo#^k"/Z7xaߟ뗴=Z֍?CfMˏ7ы&XvuOS_З#7LrC˿޿ֽѺ|=~^psr`wx+ 1+3QdzEפC\u&=-ڟݎo:cYy}իdzsy=y܂_t{=^yA3F}/Lll|? IDAT|qfٷ3zܖN!e4h- P|`VD&)')*RC4sT,>jWkҝ9a6*A̢rRRb4Ŝ4)kTHɃV$B1ba݀)Oh*JOz;Fbib%HL@DD=𱒳D)=dl[e鬵A cD 3r.dY!3m}V|Y5Bf1ߚzLfQd =`.}@Z`ŃE@XҜ\-ɭ`Ѷr1=#wwްƸC+j"d\y']p\[/I;MO|ֵN99W]k7ݷg&)υcGyKn5̷;s=/޼5_mCts5q} uj[ѧ{`ϻrW~o/ } Rf7 O/=W 5xO0)6>u=ZfҜc}ύzo~!uS| 0 u|5)РAg s& 0,g𞍡(X<$shjȭ( 0=56"3dXZKQ}`61vS S:?/u["E*밎D q49ߨA1Wf  M__vڴirQLѥO.]#tV%N M#)dlNGh%&CeRV $0Pq.ݽFT1=ܪ&}aQ\z|d [w j~([^sK+nd >vՏw7oˌĉ#ZI B` ՛Z;tuN03Vt`fשE`/Cf+™D7 Bf1D,L!M-Cky~Za![r7>;?KX/oz`v}_k<3{V5sΕZcLSjq,O&A"ynE+{/*|I{[KXz ;΃<288ӂ.| 䧷޺kYGDd25Tqo?ӧqW;R*yxx8?bŊU̘1c֬YeUUuG9z ̝˄w!dYFIAA{BQ7t+_}Ŀםk>j8xXciL_錺Ot0R4ODf]8w|Y;\*Mn>_~$~;|'W&ݗ^+lCУ{Lg0xp0bAΠZ^0fz'0+zS B fS7b?#Gm4h0kW>)>ɏ]K6yk E`OWҕݩZEQUVUYͲ2jv}'1.$2nG3>ۯ]Uْ.kls>fn׽&.w 1^neu0 μKiOO#.z̕et[a#wpwR4>xp0PS%>c:_I6gW]#EL0±oو'A[QCHLD䮪 }YD9{UvQU2cLNz?􅄱<0&?F}l* @Tڻܯr H# j E(I,}V: @hy >0sQGM3[KZat:0$z DTݤg ?kO= ?Рt[pgx^z𐐠 #Q" "n7axW>͇-|u릚^|bsFc?lѦN|q!W_ynoq_mڛ~~ezcsۙtWg35;~?Bk:A~8y5(WUsSڣ#VDP?&ѷD=楚ZE!H` HI'PZM:Գ?j7-& DDv½V03;/ᄑ٧rnx֬YUUh,†2<o(@[Sa\/ߜwKZ6{E'-mvN|q!펃w^R|k-Nfjp~?8[la8Ny3mn  mC?!x);U+ q1U J*7C (.b#uJ+R=ڲ6.?e)-d 5FEtm$6MQV@C/=$繵fJjAGD.x\ jPYtb*!?Z-k6`SWD BLT=4H>VToRLn3KHꔾr%Qe.ƠղΙ5VBD7^Ҥ#5k}_+C=nӦW#?۟7uQ}蛷jC(MVEYk3fΘ1}ڴ¶s0#v[h.KOzߵO<㊉}}/Yt8Un g4cd| W>?QK=o kw9w~'^t֌tH=qqp`ǝqf=Rn٭ߜ~ҝ_xnZ3t1f^0v=f1p'j4;+̨xȢ9'v 1xV5{/P*S<Рs!`#3ދʻ PUjW&Sݿ*sSdhk5A8bnΌ.WGjZjGu Ub!ZS?oW" X2F#32=@Ε ( k(ʴiΑWbOS=71 @Ÿ}#-o67STJ:T: YwqӦMd0mNJI/탿e6lشq&Q[O8DXxWnƌdfEnw6nV !trppiϝ;hUMadP6ʵ+uW_y};欋92o8؛/cX{_o^w]W8;?ׇʓg[3p$u[ົNm_Kwk?7W\|;ggsyЯIhhv ?lO5ay4~+:{E{VߵZ~hEw\L^y윍7}E>Ʒv*npn:ХnjNY9})FvGk k#/y?|.OMc7Ha`̘׏i{4[i'8m̌^} }g}{ZCzM9 bK={S4PF܎r  4OD *m%= վsJ I&ThO 6GFUFI:eg Tfֿ*#n˵Se<-"!8L-KCr(0_Ӡ%RK4x%V*˒E5>c6R\ daFiD{"y.1tDdʖEĝviʕD$^"r.{;-xDzwxoԿ~,g3fjei|͙v)0<0SO0oYfV<)e{ӦMC34<䜯y9h!9|̚|ϗov}ȫ>/uFqOߺpz}"v6! $䵣oUGzi~P{U%CsyGv5+^g,<7;ܱ\^Tf݄]y܂_t{=^yA3h"v?  ݳ?][FCcpQ`f9kX]:E\k:טY^.E=SwL~?ꊧM8{66ęZicV~}~̉ϓU}6 y AtWk[Ѫ;q=c{oD},х4hmQgі$+#c#()b$Lj5T$D|2LU@AG&vG$8Xܯ3? !ITA 3 PCyKbH ZYc IQ,$ {O` 1Ŕ|c ZtD=84zb'X~\EQԻmɒݞ|*BYV sJ1<#0pʹsM!_{W>Vcw^<''o+6|`z=B+OHyj{Υi®{ݧz7|Cuu1mӗ>[:\uwt߂E8pk50y="d?Ǚ8x߼7.@ʥ?cd$6z+?^Lt`#zKX} &ĘxO{6 8^דLde .8To&OT'  @5F16 >)-y:x"B|!qdBTO:Xފ1O#u?+dݜDwsG,98熇t:PH)R,%9nyhhns6A`P9hk#W`,F{BG)]ɨ_#"Z#pt1r*_wt$aƍwqSO=%Ptm"0Z ?g_ܲ[n_;\om7߲[?e7d٭~lm~_λٌ&xò}.*VWܺǯ 83/ \_f0}000su=̥o`2 1imwG`/]xy_3gDs=2D%OVucb3v3bWl=ynaj/$]oD{V5 ;qK}$wH_۹g?=Θsz3XtqI+c~}¢b5썴ӁirxzĻ0'~و?Z8zc/Mxw|4m9L#0 sn'y2Aכ1qРs2ԕ)cBJU8L=!)*Pn)_xO1O7I=ھj)*)q&u MU af<أ-cIb g` sS%*Wt@sj@B4Zyƍ?X^hu~vwqt): (4ZR!20eyQWE.nX2ҦD&Hek>38ޒ%K +Gy"qeD&Yȇ/>s7`]&W\;r, "H@"+ƿ"@r%!@D21u0Qcg0,NQixҹߵ̀.H[K(i46|o G tyba'EYOiwďXc1^'j R 9`LqS;%?]?v5_Lԏ%A+?{{@frwrكOmnEj(ZE_/VYgYVKhBYFHy^KfY#Ѝ(]W zBb>}AEmSGF +1}MPO@imӬaq$lY:$TEQ %O_ '}H$Td9g\ExUUI6C,Itp@Z8YՊ!PŠ(TRQBE@ZٵkQ]<=6HƘ,^ %v$EID 6^>>W&LGR&]bṗώ֣Ӟ#q&>,6bzrMkwzÔNȔ7q3=w:fcZ`L>̝WK0MR3Mi{ D|i &|i@t1c疰K@hW\nc"!{JLt%j__\P%<ϳ( k2=(46;Ƈmo³j. X{]p8T[ <"?BRs͘9p0h(ɽc*:Yѝ4a(D!YiJ>@~>jhȁJ=$>X,rLG] SyA/BJDd%7Q\:8pYLHDX=vZKERntIb/RbUº|hZVKQ!xQ-:RH31N+˜!䲂YY}%/8`,;e{oMBdǍ&<<Ҡ`d^mu jDL":CHF.b)U+rR,˲9 \7C ``"oc(ODjsI s 7 N0词5QV }U""1eD$6:I#R^E1HALtl֥ s9@VWx ]Is6 qUUIolr3U$5$*J R1uTjРA 4hJ󠴟n^B !TeeZ1=u*3Ն|WI>~LˆUUPf;Q;^ē(a5C, /HEԫ _#K I)?-h vhh:q>nCedʲ,U!"Ow 0@"[`ɨfSڳ5R?pL\ZEEeDyUNS; [3f̐B o$#p *3 DY:t4hРA LT'$@D ΊO!J#QԦ,i=5Д#^BuQ&T,4 u/Xԝs@{"{=f&th!"1Ra":ۨ΍Qter9x%0|Rw)1ae$TKLdei@bJyk3v^$PXJ6"뜓ؔJ1- ` Ƞ;EʯrZXr.UTkYM_gVE<%ػ|@qtұ ,\'.1@\U3zϹ͡UՑT4B@Cd,  ;ؠA 4h9D C9! Эz.pc "F ޣYŹV W#f4$,KqK]W:"b` 1䀙seX3'ZP<]KY_cXEQĄtjJb|:j$] HOS J.,KBn,rxxx``@qF z֙LUU*3I͠}NWY40iN eg0hРA 4h@X>,b."DOHƠB!ĞhITD$fՋ(.RY *c/-J15рaϞ}@-늠Ĥq#!6nDŒWmIuBb``DL [Z RT]>c1 kT@w_#Qv[ƣ_p( PNsj24t@ӧOԠA 4hk\D+1Pjg6˴s^j5pT'rHJ~X2Lӏ&ѺuB' wSkCpDTAnGሬWldb} 5ư1.bpHD*`̈FYcӃvڥN&4.[B%1_EQkh:Ɩn|TY$S 4hРA00fYgnZkBF@:8"!Y@)^Jeg!r1%ha@B5& <35KhhêQtm1XD1TA2UJ3 y;h˩nAf !Y!~{JT B vө:c,}gA`@T6q@Ae5Ũ|h FŠ/ $#i Ĉ; l3s3 |N/ԩsL-Z{zzҚPp2&qsNy JZr9)%cPE֞2mhρ9OBk 0\h_2Le*SMY{6QU Uk#|hZ֚Үt"c@f^ V046R2D>xɥ#HG'ݝ39\pBM =f2JǴRJ ^k<' $6Zl6˨)yr!貮}k{Hr.TnDRԽoy`vٯK_v|Ǭӻ5g{:hO7Nyܗx@ _|ߵݹ##`nd=9e*Sʔ c j[0 m֊ @I4uz@[ 'ŝI:=/C0R9$G @Ks4>#$64jƀUDE1gjT(Xc₧s%De\CGR!RcFc 2f"0y+**RHCW 4TZ m5hF߂(J)]6Zk]e<9 SFr m;0m2Z$Sܢ_^]7ɷzeǢME߽/I(d~ wh[٥TK|M(I{g=e_9k=-Ur\D{gj_iq U}ڛ[0yyT2}~_ثȰʍRX_9n~Es "·Q+e!KQJi@Z""2)@H5q)!Қ:kqA~}Ƙ0d:}P6dؒGe| ιays1u]g!!վ$atBH` 2?~dZ'gɗ4202Pu D~#|cccCCC\]vڵcκ;w666D'͆aY?[АmD@Y?g . lO1(,T?4eڱ~pJ)Smֽ8{}Pp_to2f}U}Ҡ#&uJ>6n1b\4vQy~\pjG`٬N?wwu ߚI!n{ymSO8;o/0kvޫW~gݷ,7'\OMgGu~}ս XKcuE|`]4nz̑cn}ejW1fMrŏL9j_XJn٫A.YcwohɵP)ywĔmRG v=׍8˞z7eO+>/uRv*Mbj>z7zGYc )8SPڀz{3g };JMW2db} rȭ5bdFG8{JҶ#P #BrA0`h j.jeT`!}K._ϫ'g |)_OFֱ1s u; xOnY_q]^tȀkym>\,WnĽK>Z_^+vJN -"JĉR6GuPh˧]yWז-y+{ t/S&X1;/yފw B􍢐KrCG]NB%8+pǫE&MO:/e*SR*|̇<׌!R07,dc5@#kR^(jHinn IDAT)k&5Zָ'& lzDIucnS\{i!Ncle֮J) !U5HQ5[jB>Ai%Bf)@=QPCa̺A&n\ץa3 d(\$-IG+c+RQէ ɐ|149 20@{ F86k3W^_|ӿW{ϯ;{ckMieblh?A5m{huzQR] x!gH9 xFJGTpY=Xo~QbzWc!VHb'_d l6׸~L +6˺Y3i'ݐsב3d1aޚܱ],m^0;e9` ak}ɗO\oE{$ֱO;M:n.}PӨؤȡ[.E|KNW2e Lr`H31Q .s!'O!mcˆF ĨÌ1T&$gh_ALa0ko n%a1@$kXe@RJe.PeLj;@ R <8Cuu#h1Q2KR۴is)(@;31FC5I9) N)%YQpH)="2h2'(q'2㲺ӿ5/wO\:8]I$u mhi-M)mfC#͟ަs{lq,>Z -,͟$5i$>oE{wcTz^ӡEGAH+:6=KCS处2iwŞ~LD`֊I)҂ )$H)*)dD!lRwƅؕl j\rD;^H/"9p&L%ȀA)%š7;~c-fCRg a@2ҠQ!0rca1EsBTUU .Q:2%Uh VF+M !-?I6ɐ8UUTTxd2:~9" 硔}?f P`6YX\xjo2}(XsokD8Z'aNCɍS҃vNs v^\ kԸڏB@kJJkѽO,m/g-aǾ=ںR3cIS߲ 17ϯl{otiQtiU۪~cwn:5-"ObEӹOs 6'>͕=g'f%H]7"OS_gmSs;h&1].٥+vhܪym:KQU ~ѱI;ߝoTR~h@@4@iU93H:1`r@`  < ?oFi4 u3"2}qh42\('rI:!q0 %ԌA`,]aZQp Fы}U % s΁1+-o$sc ,huQr>sd1cdlа jX`Zh"!UVe`$cAzAN>4!)zRr=יnu0T!~ݜ |?S qm/[ NkN١kʪUO>kyw=5guC>cOy#}[>^W=~+9nʊ~? yx闟yVe:գ_?>·>}R_O9ǫuMAḶmiIt4ł_fFW 4vl׋MpVݱZPȼxyfkFLUkK3CtmZ|NR=0R{$['>Q'?޽SNkf)>I9 ;i&T~ڔT% aNX[:ݭ BHpD%c^YKMB}h.V?Z ǡՖe@5f0D0&&FX ժ1b$ !#91G{8)t1 C1pa 9A;1C>a.|æ۶mۧG+bXY__ߡ]8˸dK$a@d28ZkD.c;RPk֬m@CE+^uvZO?APGxAo֫.PmN1(EA>Z.Rhd W"5m R.zZ`qÉ16eȯR*?<ٛuZRBPk}MoaQ׭Ad)KLO2Q}(FrD G9Fi."B"H+&.ԧLiHr 0*2tLa)0`"1ׁUAp.H؂y Uo+;\a(0/kChI(:Ab " 5c̑NL+p]u]ו̢p1q}T2L{H2H%--^T/ j KܠA4Zkb%1ɡ$Unj+1Җ crd2'Ek DK(E0fb'8)21*P:DPAB0 I6>Bz(/ qƌRJ B [  U $v [veqJ۷bxgv4\!N$쁨(BOM̘f`I+[5&juZ'=ͤcd\sm@u]R:a1Y&*IXuthav'FА58:MN0 L 4RʠQ* XGRpJmew!4Z`TG\!hTaZIwG31j+٥[~믜~yHl"MѪj`5g<|MV?wN~SXaӦolpuG;W5CjKuT 4DH7.7*cRJ+A~7h4sЅ,ڥ<䢱N9?ξs>_^pͥ'}"l\+>[nԞrAJ]Nᑣ3{Bi+/ZȄ&eۆ'E%b_&]2A-D G&6Bh4TPI-6HCUyֵ "v  h3FlH!6uL!NDĵ ň2o!-&NH5!GP~@W g?=9LPiOs#=!=ͭBǖJ}}Uʓ5%Fv ?544r\6g~.~X6 00jLm./c1 `Hg c8UUU6.0JG:v/ꌗ_M'/`n>\u_?Bno>K._ϫ L{7>~:rW=oQ7.hٲ~yB&شxq\ɼo-?wo~yɪ~=2免 Mm;3аYWO3߄䖺k|twѫ.Rf?m_xnKgSOQ]NȄӮ{.]޳iѶyor*oyӯaſ}8_~œ?+=4+}vCTr>eR[-3(>sl{^[PdJ6+fDٴ{GҾjӼG_f踎2HQ @|ڕw5K'*eT2}5CD!f0 RSvjmH$="s@Y͞r!1-JL$'6s!¥"J$joaHf} '\'Q99 Y䒧azGٷ[T' !|_ K2)E0ƀEYT 0 t]#􊔒8!7+lfCq٬-[qc[8!"a0n5>Hױ!cL)P׽t„2}o?_2+}5L8G#ܡ'w-!KO/ɤڰbZ 9{hGG &8ƅW9[/xQg6M5G_|ZJWtlsۯU>=+ 9})>Oݮ7?x_11os%ܲ">cP#Eo]%.NuL].cX^iV_X?pD1>_S˗b,~)՜ۖ~N36n;w >{W [0H,~T2}EHP]\Ϣz\&ʩq*0cQqj|Q.}wصsI MtW>IZ}HA(9ͭ%&Mqb#cLJIa D fzadTBZ?< A܄0Ph1Fs@DJ)DybPJiH֒[dEeNKRd2dQ+13E[S__OoY3;B0 m G ܑ3p 4"*311USR'DdM+x3qLF7]>c/=xd;X}qoq2.ܹ/?q_⼛W ⟟WJ)3&ABJΣuA:*p㌗.1ԀFW@iI2)͈uw3M<͈Dik6U|ۆ XL! 2έ)uR''>t%EVcG碬i3o>zDɉiz~ROls|n]‹qt+Z{,fϼsE5P$L_Ь5>⮅xraTZ_-,g6fL IDAToqf.k7hd7!w*g܃{Uk֬YvM:b|Y}i9$$֊)mvOs 6'>M|e۞]KWU^Y k"ԋyQ R2E9& S60PObUA:$V$ժ[{)ݵ%s#^;ɾXkQҴb,bP,N&ױfyx'[-} {b)'\pCh8̠ EV A̧KJY k_w5еMeUN-U_?|bm_9~gX,g'_v!=yѱ9rkBnmK}wK3CtEλO7mVahc=I=Go֭M7͘vlMͯ=2=k꾡g['>Q'?޽SNkfŒ. R2_H\3H(c(9<& !1|ͤGɚ1X2ޢ)to+'UIbm "@cUM|0Xm"!"0R<9ѯ/ !P[S ^+ DԀ(P$; QU+cP,nkiWe*6TCz۵gTx8 u]@LZqhsZƍޭ"Waa ٶnʕ۷^l/Q`3TUU @)V MNUOHE7Nm c P+ F|.0t?$ NBjeH^$0Rɓ<{ӂNn˩)gP]D0^SWs RJHp}-.1QʦYPk,?9ߖ* ٩UF(VOxIƅu:X0Y+y$ib &6Ԁx'_SS*NSjIt/ZZZ^N5h%K|+M:} &W7'k0QlVڶ&o%6f3Lm}I3,|v)tVmX.UVLe*Arxc19ɋﺮt1FP3+ENb3ql/ZMȭnCtF9RZ|?!$=9Nmrdk XPPf wK s. ζ7Ov֚;]J)8c̍oa@xr !<BڌuC暵,ʊn)6 { !RJrf/ z+8wWzj4 ntUU֡nl$mĎv8o-"qsLj]_չӾ`K_Glod|[LXAw׍*?~R:KPXeU2}m)vPBHJsB @JD ^*,,҃IOf:c6[bMRO(19פ1 (B|$~{%pKO$lɚ(B[ǿS@&J#q+4 CabP=3uq00.֒[KGHctaRVTT6Ep>vvb; y6oZd!6* W}S?,nh;f9?wĽ5NKuNm:W55q@+ڃqv_=ʿl/}>}/yB3qґ[%%L:RJ<s jE\ϤXcnreR9{Nwd q^ e^[lG ܂yF!,.[E&ih\.G ݡ]Tw? \.}}X+I?]m۶m6m++Pdu(ǷdegM4z#6&3) (À\.qcu0qEXm+!@'!c_vxz^SSC"Q0 C]FD4Ym2f^׍=`*۟]Oϭ!G2V hwQ@UOxJz+'oظiok7lڴ f{֭XKOH;-jOA8-Zy ڛ_9k=~ n]o` %},A.X=ܚ"g Xի2lc YCHqҳs*c  :EQrZ4`4ǁF[p̆\.* "R\.W__cǎm~hF?fs\$>ngQ;^R.+F[ns_L$#Q7[RDδ1@-l@"Y='Ɛ1\PeYBY8FZ~A /L ?jK?3vNխl}ymSO8i9i;qccs+ vzӫƘ^gV1[Oq MΡpF-w{گtvi0u6_^p \pGLۖto2{;|r o8􎏲~~`ÎݸځS76s1/Y7~xm={ <4n1b\4vQߙI!6.Gԭ{jOq?]p_, =8W|tQcw )Bp.Rt}ڭ{^u߲1)6,vtX*9r$ymGd[^x>S@ BvODܼ8xS{n1IלrXN7Ϳ9n]kƭoK{9,_lͩ7ͦYh#FLǾLe*S3dݵQ5{sA5ƸԿC*M466cw.HbfakNj EcMsD! *XRZ*PdO"6ЈlJ)Qg\zdzеYJ)J۵vnm&kc(`,,k /ݲlk2xANil%:Ƙl6K 6_v0c ){x}挒K"1Ιz\PSgw>}H!>\u_?TS~tTSye_L叏O`@%ֱ1s u; qYmfgn:>?;]9:~ɜl~xr=1jFX>v=,]⭟W?uWnĽK>Z_`ɫMn}k#:ڪt(e \>ʻl{^[ՠh)\%];烥˖n^5uq `zqg̾Lg X~œ?+=4+}vCxgU,I 2t_6Ф3l {k>X|o?4Vpv{u4xgMEd~ݫvfm#;igj!i*śhzss_~Ȕ6ѩg)8y;ދp/ϙ:Jg:e*S#uMbRs[ BTd*@)[U\-WzO^IaF6wY>'u}k9@G.p]B9M[0gN;|̞\̿tCㅣ~\щ*xb{ϯ;{ckMieu?ƷB,I Ѧ!hm7V2\i'_d pS g[0Y鈪.=JmJ3e[fgm?`Ck5eQ-THѶ[o 8yb|~OQ銃aαcuGugV]nOۑC9`%%z.Bpy-SQy9 @ͰF1m#0 (q"SWBp P#:0ɦRZ aForߝ7˱zi:yH/UMy (9mHd ۋQDmT"8B[4q1Rc\8`4A 42['aM(2Zm&1Y6$/*9oll$&YL&c1܂"XTzVCJ!Uem۶k׶-BBDd0x\RV& UvSMe2R)A!Ҿd4:r@A`WSw/ٞ9?fZu"3z>҃GwCnpsĥC~qދn2݇m6ۻgbOՉQo`AE+"3n]c'6s2n⻅U} SgIJPeSwxSNtEd]8GD^w34R #TM+ S9J}j!i*=p'9P%I \g)E!L ?~.0KMsE>M)$xPS.6ѦY]_TTB}=apgp[ ?k7d~oyݱtoHÝFI=e/z6+Lwm ;֕j͜K "`nmU}1;Mt?"m9mxC%t [5Fv~w{Yry Wށ\hXxM[-z6vOCɍSTSYYR캅;cV&Ua~^}biFn{ڧp. :ˇ-a+_xqu-lm"ilk3bS%*- Z-m%iqZ| uG.r͞f8/q[ܘLe BD sJR6FH1XyqNi0 >1ZL*xaC\KCz^^LxFbL#Rh bAzx U>-@Rk(fZN,jŵM!6 y(n}hIK{R7N>P=3^;>?^u܇keGݹjSpЄ8`zK7NxCnC#zcs Y?,*4| |ɤ>k;Rn#w{vveEz3 IDATNkN١kʪG)m{w_^͟ϔ3Z-EAi-9'7uG.EȥzV dRAZ>!Jh@Ƙ1LL,l4L-V6ߗ1cg%&欰X&EUY4ͭŸKŭ!2`1cAeݥAg$ Cvp?hksZm>Aڨ)D1ZkǕ698|1ХK}kSG?6lܲeKUCdf6k{U)n%E,1TUdnݺb*!GѶm0 -r4|UVѥK>^}3UUס 'x t10T*D\Zn]6ر#UÎ:'Cj:P#yR fE p#4Cm1|ȳ7-x䶜fB.̹s4% =Fk"0 p)0D`WNiv֚͘g|In ׺SzS-m)ogpaw7>uݳ<;k;wg,fgĮ׺f30{Sm돳%8 /Y7sg3LhsE$lA Zj)PpNԵ B8g)%6ź14Mc'$0%j"eئ{R{2 f"Mcb#F| ۷97u! k^?dg=\ݑRj!F[ BcOeX!2m"cmBE}Bqx;,ĉSLTcSre1ŞuLmIR=LC][NyEB 7+J!"!HsNkʏI!'h;%lJ7p_/}rl6ZU62"kfu'^g&Bee|P(YmDشpU.ޛ& @DUJy(`L=yG#Ç{Aa WCG>P6RqX)% xjdiP&=8lԲW8$v|*..[Q lstMVU$`?[ksk*4fxȠahۊAKqαbo;;DAl[洇]1z"jFgY *4lVZ(UM)%;1Y({[HKtR`ґ"Iלx|^\.eg ˜F;ڲx < { R$IPIT`$Wb\#GLA kEi*RJ! Ŗк {E)6OW%_!SS|sB P Yhuimhv 1!vgKw>G{3~V˛Nz!s8s̩g,}:,X)uV)):DP_|d% ʶ)&dYժDԠ(a溮Km32juYM_V4ǚXh]E;md/vhI (MS^Ok=2i-\:H;gbjīwnXֆ[4s8([9@[~HQ;G=3!M=$I$M4M7lPEYiuT`-SbkcxD@Fy"WUY$nAP}=e(Y$wibvv׹f>`Qw|]Ɯp='?fv?q>?vt:)ޛ::u?խ.T)#;_{wY?#3/6VFWyS^_O:q[gU.ΟO{ݖmo;?iB4C/Qv!3~Fp)orxZo)]Gnzo)@K;M7M{f$tGn|Q'\|]hygzh3>%qg/O;;|:kE<X{ Los?/w<3.}3p~˿]_xu}5n6:;tp佒rR1PyQ'gCmHx2&&3{?[N -e;>]opԘ',gD'H厧oi/ds[ۮoO߰Tgm^zECp}~Ͷ}8)q?e;.l.m?v _I7'|Q_gv HD!"4h cIYVu6}YMm[ N2f8bC9:S@AK%)Pp "W30MUcHWJѸBo]CשM䭛x2Z./Ǔ\S-Ph4ZYYFPr0?iL9/Rak=#LUڔI5)PIYg, {1=oybLjJ)-R毳/y:4:aoP%`@ĺfc7}+6u͓e6JXzᘿ8}D{%PZ"")%;F>ՆyP R?sIuVg}xЍW{%>в{7"ԗwDe^ͫ܏oz ß{KNܐ(a a=ѽi2Lnv0 76j%>aö/ͪgo{{k{p[牿s澖^WV^ic [5}Ʒ<6GV]xGRYW+7^} ;1S_#Q̾1x/ԋ>29osAt.}' vsL>ٯ֮[WI;eV}ys_Ys҄%t>7klo~ާ!6>g^$O$-N>uw`qkjw܌~%֡CoX&h-D5'BI"!E!X$IUWJ4MsRZd,!hkmg:R,K 6fYVd2Tqhe'yjRy΃@ш=4S-EF!3[.j7ZBJ $`sN)1[$Ryoء hZ7Z;D nX\YSue~엿;E0}:k :K:Þ_5>}~ww6?-ipƇ'NMRJ )u0k믳C8*c(@QZ ZI)ȋ\0JB;JpۗʲTeU"SUKih7{|]W_y˘B ?~fyɡ/7[{4]Gݬ“;o{ӞzO> >HxŽ>N嬍:{ 12b34jQEyv5B[(LTDQM,ˊWo+`]YD/Bтkgy;hPR+O>ʋ{'q`懏}W}> |S-樋iR=Y;mzA)"#_w_z;}^>";/J^?#a&ݬ<_\zg/3!^ǽ%<  _aߦ>6sdy//_?gCrImzo>~{.ӽ^{KrexIϕ͵N7?/pljcwoK5/ [?jyoWY[a޿y>ݽ 3ӭo'C6:;t#y"DV* )1@4@Q E+*^]W! rY;kbY׍A=WrPxnnN)ŜuYW\\-..6~a,ݰa▭d%g$9?kk*h2EQ5[lpEܜ QƿRW^;9@"`%` 7 IDATJk {cbsĖJ)Z1ss4??iF,ܲmqii Q>Im&49 !Іc^;RynY" NNwcnᏜs7R[ FnS"wy 6ysm׾vm{35ϳ쳟u!;tY6 !bf<9½޷}iiӦ}sا~+^vQL}tظ!>'-!'' Dc$@)B!@A J8& )0xG@5֞UWO(7%bh'z;swsM?6cd~^<f@zbd&7o~ wwqLu"w=v_M "}w6 7jO1|xԌY 95СC[|'9{*~;Z 4UZjlڴ%֊*+r!RDX׵s^{"y3uu+Q9r?77' K{CY֓LDF|PUUu]sxx"9u9MZ<+dr#BL7)Rz'ι~QԚ lVe6 /eZ'Aء f"R~2|D䌉> *+5ʬTQvv9J)V뤪* )e'wN?aL]ׁ .W~L[]1pxwyZQXv:T2@{. ߛ|TG8o\|&ծ1׾3GkVHm#lru:IpeY, ! E!뚹Fz&stJ)Nz^7=MZ)M,ϒ(,K#ph:<o*!&BJA)}f;I@m @B<$nBQ㙰GQm$7=8=6cx(i(|nMa@a4I)'ˋt΅RJcMf!8[Bn#-| \6ЕbsrGkTSX8DrJcda[$IBi 欳N.Qtjr?2{(G ܸ;B t]ȬC:t@@,s./rs^d2@@cBB۶m7F$Ko "Jva[e8?G6[lUfYmX g),X $I1'lyd)S96-{ clBbPs0qa:}9_Uep%8ڸPH?ʡRJT4u6RHcg`RWG%ҍ6Um*jUAyv^$ WEe): ET/&Zcu%^"jǰ ṃ^xj;tСCH몮Lk-dB0p4%:Y42r0MIuֶDq=2=PE^[k`QK 8c>*f le$PB ssjƱ1fͼu[F ;1m3.kBa^{UNz!B$My"αI $gmŬ!j,.]Z/^hFۚe*|Bk"`Dd;]g!PY!D灨붜ZK@P`nÃyv7O{8, ry^r4U\+K㧇Zg:t?u5:M,k&뭮:<޻4IAiZElUkDD!1GQ9E9`fۉHh(d-Fq_`0`7L+C[6P%sr&cD Yk4vɯTS=wTLcu8J*@],A̮-xM6m7lذiӦ^8C6m4 42$I,ZiU(˒MD"TUA2yV;\([EcѲ -*y8y!JB@4EÝ|)-]XFamiZI^s9{cEΦ\h>iK B]5jw3ֺcpB@!)$ "OZO,{(ZkLy^^#_|_ל?'@_ z& JoS?]}Y Jl.'_OI:woӾy{-}i/3ާ}NQgL %G}o(?xo}2K|glXKN_#աCB*,C!9˚?*I.IbeIRJNwLI=p{uy-^,:d5l$l:Ƹ;b J"&y! 8t>1! ~t&۽M39Xϡ, -8&XJפYu]}x<&,~5w[)|b@B RJCAOZ24W,L&}NӴ( `5E@ɕR $SA&IB>hKe1?!|Lۊ)\bܩ#5hF$Q(̠2ظm^G!y`+7^u wU 76j%>aö/Z8<;tСãP@Z+r(2I,͜p;gi&)XkB7UURP":"r<罢6꠵ ly!3 [!2-,,eb>`1ъFD<$IRd% 4Mm[I4dYG1iWVَeuÕU3TUMĒ$iivb{3mhǗ#A 40CT-8u]/..."⨜pȟkRJ&ZJyָ,nԇ4Cxll',ˈ;y˛6ۼZ9j?Eyr/;wh~hT;gub Ey"kmBI‰djG:}뮹뮹KI_Qg3(CPku$ޮW"BFkE2i&Q:tСãD^sQZ,+l̐1ր@!B1)DkNAkr<)˲?7k2 +ʰ8RL.)NZ; _yL XRk 9Ϧ975lc3gV(`cZ.O$pRGָeU"Hi$2[Wk$@zc4jZBDXSrh瀞UQɇǁL d:8?KۗTK+{[YkZ4gg8C noEQ%w0 ?|Z+kw.RD&6QPJI!{+{.t΁W(@N3}{ ^w\ػQ@Ffswu>zU}/#,o}Tw|wg N>ٻ7}osw翵+СC*p52Md($iPEhdZs\kL]K+ˎ땕buyeqVSդ |K(r2K-X/'I+`0Qu&iZUOPJE٘LO$U=_dD $M+y7#lvqo $~n>r6=| :tСC(u÷Ji^/ [ QkZJYצ$I&I:M9G#UCy>"Zk9$ ]^Ra=0)yhDD~4hD.+BDI)u fDٺ;?lÆ $r BhΥ<Ϗy ׿~ܷW5 b婧>d#D4ֲ>(7[dŭ` y g]y;d_PJ#$D=q}9'$@ PYBYHׁ)@H{СC}˽×1ǝo^}~{eY@:IH$M,I IH OI1Һ6*M$M;f)  /8dNMW)!DhRQw.xSe+biiyi:٬m;e lėef*[l !@ xsRh6IOl mWf1Ƙ&?LZ-{?l|#j=ʉI yWCS:̴3gʄ-!]ͳчh-'vuIBSsBR sBeU'i|ڝ b׿P*nCE!UYW~,B!ԘkjСC@/\*ŝe] :CY)d8IvD\Q\rpāfdY6Ui?rhCD*7b^۷sssш-O8 R$IzG|Mz1gzkR  >o`h|Hۨj2yueY/GR_",mj5qofCV,,gU>:P#@ƚM-${ Ncq!5'Jqȑ4KmrB08zljJ2 d !hIդ_ؑ"zGqA:tСCd Wi!v]e~~~wRʢ(lܴ l r,[Y]'EVIg3ցy>"A0,//QJȍ7զO/rG[77n[^b>=uM6ed"OsK7FgB-~~p.6pxi.Zk4h?es:B)%[-ZXqn%-[ARhԞ4MWVV \2%#g9|>3]# >jNE9Kq|cI~|u7M,k2Jz@H"FW6rpq6'/ #ijmB*ahLRY1 [2D\=e4Mc1x⮑v/4kV'YGD(DVBD|S ҡC:t6&Ip8dxReeYd2.B+(i*Z{oZ"BE\:.K6G1XhWJy"[n$Mkf!uS [ⷠL&z2iƘHjָF`Skă%:RyȹR)  8P*0*MS pvvgD%"2iIJi0e)Mu%'D'i}ksQƱ-4h4η+h{j9RT%(P,M' ,,QIB.СC:tx$Tl))mQEQr4Mʲ7Y:\Z[ }3`[Bhh휅LQL&YcHZYlF6FVJfYZM,bei B$Jhd2IӔ}h8Gዢ`}Opeyt9 2-obq shno`]ern*PC9dqqq2yC0b`!nr.!D:2Լ\>,˰Iq/D%|K7o=JKodOBh;iZ(Zkk4DC:tHf@c@TZi{"CŠ?L Mh}Q4Rtxr(G$ !xO =UUiqW3u {H8eKbҨ;đ~seY!Ht(gVX}յs6VHtee輯ꚜŶw>pСC:! iJÅRJ[0mfcZx$BLXpJH)0^ֺT*XJ)jp\FKe %50RV9T]@ FJIAp]Rj, ǩT*jk1Vsх RJ5`sq"^S d @26Y\meElb?ow{?=K}hje$<u\K`U*3% Ќ"OR&)bRJ(4M(u];.[ KPFJ+]p1XNXKm,X~ƶGT=b d @2 BIIi!3F(Esh6-qTV+Iq)+`< C yAհR\qK)ccchn1iq(esJ4A_J;rPt:JTǡbrCJ4M!A@{\f$,CމGZ!.cJv3g EPn缟Zk KPSXJ.84Z+JU\c8od֒٥LJm@SJ}Zkqk{r1I)XuAno/5e=@+#4RLYk;VѻPJ5&@t<N|DJiBR> CI&RqtQe AH@2 d = F gs:XB?J%@vJ̲fUV:NDEZELTf@B$Zku2blfR<o88]s]y@$jRz ~r]/Za00nqmELj aH,`b}u;ԎXBrޞ XtPVιGsF?: Xe7ͩ{,e;F VsJe RN)bNLJ7`Qe E\.6ak3n2q颕r([1Br^8bIJ*ط̀Q$i뺙ZRd2\qQ׶2je@2 d A0v<2tjt:u+tc:#YG2s}ĝZJ9N*X@FQdA+h84K8cAr!5ajIB%S2˲ܔ'4Z9geeRysc,0 Acq]@ k-Z4ys; ω8'\eO2Y2,SRjm4y25HV\dJ,8;q$β,ITIC@nry \K.WcKIxKQZ7h{Ig>{sp=wttT*A e9^DN ةo|Ȓᡒ©/;]?Iv~>}'7uo~76ٛxG^'v)︫{8>_uq1Zhv) =>|{{lV{oɖ= d OUhI&RJp!;o}LYcr*c q4͢8n4'[&h !,wtXQZYvq0 ss\.t axhhlMO!B)aEfjnT`,8NL3!bi@fial6Ӎ,I0DR<79"Vbr\*0D8 VeiVSRn/@?H>]vEQ?Z8$Rj-ȻMpH) =k7Zx1>!YT9h$X$0222==&2.16==ݭHSq`s(*"о=cg):"yY G[k21-& > +ifۏXQB{BRYf]R(Z-Ơ1u$~_qx =nڿn՟~ז '}0Z/\fnG_՟ K,?o,Z;{ǡoIݼ2?vPB2z .Z-MkfSJ9<4l\gU+Jjm,c)4?1 `~1F)/St#rL@zHt$Z1ԢRO)H0N6TpN#  wBp<}^/-a_.FY&mILxŀc PF9X5Ga)rJٹs'V{E2q]uwrr*K3cMRer2Zk%}Jss~_5:՟e[[Ӹ]Кk9:g՘ֽy__{J揝ttH_/?G:`> kzcřnzYG=䐃 ^{g[ƐW=Z0w5H~/{UtNv{S_Gy#GCʖC}-_{=b,.㵿 8VCăX}k[efmSxo~k{ŧl8Еv՝S_u;ag3bÍz?`''7v:I+i|ҳq%7)c'n8}/O;c?L*[fg&n8%/x3ν{j{{wQ-׬Ypw4po3P較odlν:l+~#?m~kF@7ef ѧ҇bc@ .V2{Rf2Ve{pIqEaQeZV-\.hhvҥKў,hIERu?~7c fhX0 ;J8(!q !FVX>F~]lM>11e!dtttdd$˲,ZƸWXQV}G?[$غq ksړ^*A۽jkƐn'/4Mf x3beg\6g2]eY*H9aw78#4L НjhJiZ QJ% gߗ B5F'I,c3AJ6Qec1chMAP [bn.Y߾9}?zq3{j쎿ߜdLQXOC/W??8+\\#=nřEG>kN#ɦW紐DE]s,T`@ ~Oo3ly#?+>_Oou3ʢO}v}ouc} ?_c~/2ܿK7}g{mo']7-xu:^t&]!zUz7#z59̏~xW16vn8eu'\~ zLMm%|=8O f5<5';}?~7~qUN7HZ:nF╟{ݫ&ߣodFe| {>ٯ?y`+58~k@0Σ(1]s]hNU_ժĸZ+,zi\Ns]6&Ikyj+T7sD=5ݫR2)^j?ϽǞG_~T?PF(en??omY0rZz-m_w恁~Kn}yk{’nT?x;.N:I Tm{cG]ƒJ'Mlhm1Fg^%:Tb IDAT j54 iKQ<Fgκs.cs,xf.8z($3j W~HMj̒\Q?ÝcE`dj-dJ@QgE4M6!W-c4"Ϗ*SSSZm||<'KO$ZcoCvq4"E`"[&BZ-)%2ʹmDn#LDX;AY!11@c@хLN!tnv[Y/" =dE?:?,EI,V"t9i-\BE'$I](FH)ȅ9RJZk%ǂZΑ`<>.D,=gYV*J_TVMeJiB 'F[ىKBيRFQ{ W8q7q;ݺu+Rqb\~dd< 眢wK@қ,0S$ "rX3 c@)]r LDQuȋ&WyBeQh4G(MQ.Jjv{+ $I"VDI:>9E跕Jj uQ27nsv}|@J),ch ;VZd:Jik(i(t:lrkWoX 6T{w._{{\|#Z?pS$- cXcoz$1 \̅ a>r=MKэ; y}6f*omAU t2 @O(ZY_'~+0#B=k>s}_ʮ#^ZK '[n'%cZq7m oQ|p=?nw͛Sk-vI]pѱ[ˏ]`G[Yc:}(:fEV7[ko΢c9 P/ҧ(fĹ*MWہ d P&)CMAR*ͼ8ժ՘۹kcv]l)j1&\/}VJU* cDm6ǘMFƵHOoA"Vב' vO8nZ 8dqnc:Di6ZcD^9znqDȋItGa&oi_K`C1B9(ZhZ肴l^h看M׌JC%P??󚳟K-K?}/8qݕKVv̺e⋹/gw s>t5 ]0cfZ>)Nï>ֻٝo5/엯m}N_K\}\yq}=i﵆O^wrw9y?__t/Tcdb1Z"|ˎv!4o|_>}g@53k>{_{#/mDn7O Ȣ\pݡ"}}suv 7<f7"<'h}Oz䡔r9}h~C)k4ZKG\MSNRVo PJ2ф S%K芡+7MC`ɒ%=}ΝR0h Km۶}!d^)ؼy-[{$ !@+[~Gr)88f+Xh;v₏ Q/ R:&V盬V0FI^3Z 2J|$?r0!aMe>?~yC]|d!`޼?Ƃ5 Xx[=UY tGڐ62Ff=7 ,c,[tߖl617.hY`"-X ZW3oт{Ssk 礝kIo⳧TY|W=3g‹_7yYOc>odD{:l+~# >SWM-4xOPL=׭?J8.c{w=\*18xJ@jUkE)sFSB=ߋD)9dԂ͐=8Jc$A)_Jdtaq)X!4 Joa4n* J)SI)ϯ@gƒb ֢lAfN@?w!RHϓ;NYI;al(@)2HCJj/&&H 41&eڟXdIecJB Ƙv3˲Z͌PoPtN 'dY½XkYw/u]us RL&0 9X#AyaRJ>nYs<ϷJ `T@PU !FG,n0Kо5+O>㓭\`P6.34IGpO3Qp yκ:Ƃg.V4e3)gH,R6wrߟ/\pѤ/Lf]ΙgFf<>{,,k{E(^sQ%9Zԫ?o9zd{y؊WF?R"Y~yW u; dp %zf)v|FRfi MRyh !ʕvZk6[1q 5I$I>Rզ.91jEu]$̲RB:N8J1Z 1*y c|*h1:@N ثE$ 6tsc4A[ ca<6W|#*s2Tt<|-BIi"/gc{}Ҽ^|봶_荿Mf}6~_?r^ Loȋ?w2Wsnz?nJMzSC'}x [!huh#C)URYK8PJR yry79#n7jjQ8Ir[F Ƙ0 1)CCCh ]ׇ22W*dRQZ^Zlhl#Odp]VU*BQTTFjѼ-L ܐ^C2D砠bF;cBkTZƺ~1}uZ":sQ*KS1Q.W$>ym'p(si4R 8:m~֎W`G/=Y۝Λ:"Xކ Zue ~R~퐕JZ57.`V-X2,j5ۄJ7xݣ/OsDե@htuˆ=GxЋ轫Fi{'W;3{-Fz{[@2' Rk$\*1!@fQ!QJ(dY@l\{fqdAn}|G(5wEgeY&\lc¤InkcJMPȲuRh40^:" JFtBfXЯj5`unN}ZX9&}x|,cMz'[~X!L,&\0S;jA1:ZRZFkS0vp+,@m"*OYdYnJI9S7P1ʬcyJ j՚z!p !8wA;1Yzڂ'|_w޲mvΝW?jM|޵?.{ɧ?σky^]c^_gS %yz_IeS-৿gy=cgW173/xygq[mxBt1vo?wzIzɗ<&'nӾ;Ͼ?U8_c[zm t;ΛwU=_aW lۉN_+#,YOpܡ+?;-|w6W=}~}rc|wuC9hѯ=Ŵd-׬Y(6E/hg{ s޸st׽a1R~y_w{> N⾠=uǾ}yo/e8s^هmۣߚB%?i@dK)`q?3)41(eZ$M,5Z+$R8GyhiAwc6 Gw4xbR {\H@"BLTb udhj4Mz\8xJ)t<AShbZ4z;&4]LNN"{ȞpGGF<9(E=A u1gI@hfqpS!?E?!ח$ ]"̘rP+_Fxe Ͱ Kj-22R,4,!0 Rz&n(`=B2RkV%B*$M̔@ ѽ d>G; n>#ٯ?+\x_ʷ󳯽|Ls(4>w~ gyJ=L~'!˿rַ/:С3gߺᔡmp^<vǍOQš^uӿ>I<~.3]Х~c7־x_G?+ wJvӇϚ@wMל/W??8+\?β&۸K7}g{mo']7~/#?s[FrG>s{3}%לzUO%[W_}7]%twJgv4^0*Ͽ|:9)V;j"n AoNl}RN;x|vv˧0wwG)O:2>J,M+ղ1sB.BIiM)c&ZkYt[ ֝Wq<vg7o},9W-=  e|4w>у׽oE.%h3cc_x̰vQCp +zozk֏{3GgDEB0nv:s'"-럽?~*_YyA?(h_w恁~Kn}yk{’n屣.A`_qWgv,j\@ v.8z(7% i9F IDAT+^v¨YwsمwlNbF'.rSb.Ҏ~sAY4<e.N~f#(9^{΁ d O }ZcqcYj,SR)HJ*Yf~תFc VT*պcD <$OOOc/֭[sR]v@EZ\.7 źi]lYجW)566B4~EQ^o޼y׮]mұ1F޷T*Zk[fX&Z+V0IN399<$GuCl.Jc#c6MdEZCT"RVJl6Fc|||׮]v*Rj5@ZIDq߂a_B}eJ+6q!)%%3eYSSXȂ~!nhɮGJv[{nw1ԏ=k>s}_ʮ#^K:P󇝹M'a6z;S-Z?pS$-V8&]N -Rc{ԏ9cҚO8^zټyם^=ܬL~Y+Mc!%++Aiوk.gw s>t5Uo{s\/~d/;#nMKl xd,;ktR=(pSP??`"߼h=<* 9Q>P=U9rEY@2']0'yBE>,kỌIvm)ee&G(;bXrwңNǦgJ)?<<94&XcLAtE $IA+VB#6 C`!dӦMNʕ+wر}vlҥKrɒ%YMNNbbthTU"Z%b Uhnn`'BJAJM)%*'R.J%k5f9UA\p g9OP#xszZlwBK V(R)]kÝ;wrNWX0}ͺ`#J)W,]l]c=P&]em_[bO=M,")ȲLʬ\*Q&VXcۦ۶m}oŊaVZ_ )uexm c,/=k6@黧kѰS!K P> {23>X PF/0FIP#06ֆ]ә;56i(c'sZP?V)|w3@X5kc,bZ5~Ij@ }z6; ϭnS_2W{ߔ-8Yݟ|=^b9;)_J8s58J~9 P?qˍ%=ӻ޸q\ky!@)}%PB=s]q(%Fkι&|Y[T} ,d+up^jXVZJ%`>\rP="'&&1r9Ka~>+FEʕ+نf4h"9 nD)>"ugjjh ])1sңlϋ}Ѱy?*sL B*%Z`%RJvkRW}=CRjDi0'oYZŤijFSJTa53IucL ['0`)mJ) 5MST>F墔8Ѓ$ٵkWfR…h6/FK,֌(I3?jB3([Ѓ"Q>EOϼfﻱY ZxFf.iE?" MQ6\3k9{#)|Gm==x)@cY[ӽ>z~Asv4̅WRvM8N<@RTcf` |)[b ]!юItKr qDcZb#0kmތ A7hcfZZ v[zj+D Zsn_^}\`6yhjǟ&Q-p|.t6BZbE"o+feYƀcg8mTS:8;q1,YIqRtzѾJ 0Rq{> b}YR0 ͖ g:FQY[VJ?@,dy BBF_yoϟ*ȓ(%d O-t:3c6IkƨֆskRI%=CR =l8Hҏ0 &<6].EG1 Jwp]8^ϗQ$I6慸 =R)ckD>ab-.5A ZB)!19(Z8-:N5$iJLF)%`Š(}rEq$cq<%c*M-XB:ê\*66AKhC۾#IPJ%|!QuŊS֭[Pg!cm&q97֦i*mJFkl4\-S]Kqd O <<CJIGnYq1mXcLbZkð 26:hXgY?crLg!mHzrA=sCq%V }K\.Ŝ<< <55- LDX+,@RM a@\.۷[4 J:197=Bisp?;cfr&Ms38WLxΩ뺁18jSUwL&ɾIXB ÎGDDExD\ЧSA@)k6Ce^oݫ~;=3= `SuNU|R ܫKDYę3A }쓛f@tF8\V>G_ oAtMO&&ۖ$.Qp5|P(RPMiJSҔ4)py@(26kj^CP$&hyz$AD)HxHmc.mHV!EMb,F$ ZXGJ*ڜA{qqݲe ֱ@ma7"eXFʦ>cPK|`tqB-DZ&88J9 jac9CT˅T[m\H+z{*eYT4#N'Z CWI,H$|s\m (ŷ \G+/*l'h4˄9dE N5]bD)Udؖ뺞y#~GBs;eGO?{E>9~'>g4aUZZq~]~~S)Ce%NcU)M5^|vM|8 />fl{}q+3O;}\>gw|-!h#]wagVעEnj~WW:&0󐳖>@iiMKt:~Ѝ*ç5FysҔhullrRF@8$HԈvH%SFt=a]4bt:myM$T C#^CC;Zh}TP~D=RXTB:{۶#H4UU5N'IߣH$t]G~Rw@<ZibhBrH$6fdge2>=B eWHa„ }}}$,:8S-$!)\܃[Bj?010W оG`5[ExX`~mGgQ1]ϯ_*eJ|Dž+J-!kSd/~߾r\CFFs-ڻoEӴQ t.~TQ =3#F -i~!A@u@ξjǟ_2K2V^Ԟҽ"PxSbB1pmY}7u+vϼi8|~z禩ǫ:=sO[sK=/g+*8[s>Xy{~.z>u]b6h߻aZv}^sҔx'@H)(G(!VQ2J@<cU*x<.˲pl[ӴX,V%( Ym6aެ딁eY<qzzz0,;A >Nr9c+М] 0'"L) MjYV}0VDB X,*2vbXT(VJbydάPM ^A\ \|_n2Vq"f%BC SU\FD)@$0-:^wEQ&LIqlyimPJ,+DW!@E {arlYVK͛7ASF'@јx븂D<*$LHzA(BH=\\}Ǟ_~>i 3mn5<=cOc='mȾu̞۴S7K}fΜ1} K-'9z{2/ wʨf2MfΘ1uھgܺ2F3i4LJd}, u='M6mO-.u/,5/8]v.S9oo`V-f|Eo8oIG^,Fv?IA,M<ԣ k_xPjE7ӢJ;󊳧9[ "$ƤyLP/·6\-Ŗ.ZS \ ec-Z~/W$٘rWon=ᝒ.v}ݸ#wudHg[d$7Yeث{5%aU29½.]8$nF:MiJSvBTUUd5~?@ 8N6e|bb6-G󲫫P(@Tڴir"eva7oF8ZFpOjdhR"u]leFKK 1@<B}PlKO 8rwbP}ITr.v{L&_LjժYGs iSX$afQ@",@mƂ˜¹pY{{;Ry3G߂+4-BGԶ "c,c#ϟm۶[-j 0F#nۖe[PιeYq˶;} 8'm?жsįXzk.¼8mnn;nK~Cjc#VO,]qǯ?\4|[w)zi퉭 Ek/Z{7]߾pQ=n\z#_aҐ4|A>ɿ#Ɖ5tuu 湛NxxXKhf[z[&d-:.~jm ߫z'|[&b%4 0"GtG+ח"c;i9 2(!RiXIeR(d^{t_>efTfdg֢49uR2%Ae/oWwo_4٘ud_'{͋Q|c_[9k3䞚g j IDATi߾r ?$Abu"z :~{_{{]q)E" wh^XXje%f[`]EѰazM|qS"2S8bwe7[n?tC#۽RM厇۴ͨm!|ׄ_?㷟m_ӳMzdz_w|e;$q,1 ;WxC=h$#ՙV:?p-@vpAmTgyNl/_q>g0~IlaavaSݔ4e4MIb#"˒Dk2|?ey$I~p!(j+i(2WQ\.ad2PvxnH$J$y\.( u]ORhU}hc)4R }}}3$ i\ƌXH^TEARuޠ*lB= tC[1Qp(j>PK@+>VAƛjhh4ʄ@um˲4-"jdxhnYV.8躎yuj!l+EΈ Rf@t:b_GHBXC/2fd!9~#I6Y>!JF/~eSyLJWXu +V< ǟlM^w6c*kpϜZtf^~sͲ'!F(c$1-״5P oB^׿”C[h|"i 1G4zz* lxzs{G .9ѾW/R=r;ܥhc[soW_)t\0Q#a2J:ZlCHi(v/o:Sxeϳ~-vaiJS#|_QT"~,UH1*˲8,+D)=R`G Ţh 5fKKR`b6-ha#9ϸqyp]BR8d\.s΋b.?~뺥RI49D"ب jP41R0hr#?|09uUU  ΀`4QBYFr% B+ \ű;?j',bX[[i۶U~!ї"Q*DNH#)J7FtM:!JTjkii=0MgX JR-SNap?+V ]U"UܿxRT*Q(A@]S]# UQ HRx<F"M纁> @x r{NHjǯ͎U=Y0#`ZwP$ek\vƬN7/m ,L:Í f߳3E3vm+ 2M7з@rrjB`o|ΐԂS߬5lvpui㩑В8߿|nϑ6Wf uUy3 /e޻'ziKEBEZ&'% }/:{ŗّHP_X[ꋃ_YCϏvp SQ/O4Nzo.  8OA~MiJSvBYFQ1f[8r9ۗ޾}{_6kD"[lٺeKoooww֭[o߶~-[r>D T* XrY-s}[lٺuMlӃfx,ˊ [f__888Bcm\.r9$\.W,"RaczR.qL!˕e=u3"BF⫵`އvp8_7`5mEyv.fdG" L7;i$4341O'~$Q]W=s qGU~(Yfi?찈u5gp]`i>vX,W*A KC]eB좰`ApN t=8c ^.W\w]ںR}>(3cQp6ԟaW?RY:;/j;vZ]D<0]rIJTr.\xwkOf0:]rߝ]t,}y225h7\ygO8דƶ͋O{KH+ͥF5bj?^w>lxt'0h3On@).VʼqߔS?g\2moc_YK )G}C^)p}cE?I2g{NKo9K`_7w 46~WN鐉Ywޯtiozr~vpv8xm;!w=t~zϼձ$ dГ\Sԇ݅4);|`Mƈi)bD"HIar!D_OQ &%T"e䱴ms!R=W ڨ!=m]$Mae,b{4MC0<&Bc! P(yx<;!dڴi۶mCtPýTU5M==W*Boq0Ш&n7nDӮ.VD!$ !\.AkmEͬ8]Tz!=Qx.aBX |^.e( ll=OQ &qoF>_D$p#jH焜?Oir1溾B0A؎G;vl$ TӴTP(DT\.߰bYXHڵk{q D @jm+Tb8 B@mqRS?(g2\4DAdHuMzݐ ;@{2p؁ګj4g͇GyX*|I(Se+\Ti1эlߩA P?ORtAt6FFZ;?;:r;:L9pT7)zيzYW_|w[镕+0&1˜(!hTuYeIW1UF3YR `4cbC{h4R`0,B a-:e;B @;,;Xi,!ں@G -X,^4  u(9xT*%l6xh8wZnavpo펎܎0Ny׿G8#BSݔ4e$5 #C<5=J)jP#(ȷMs+ z\Q(BԘb+ǑU@Cs^TtX|^ahdE>GH B Lʲ\(ГAWuγR$l6$I^,k񽽽H$|R*o!1Eʄ9!Q*41&YwpJWIͿЉ!fi*x뚆JJ(Ty q:7H$ l۶ }0 ɠ&qP*K8gITU, a)w&T*ex0Ѩ1+p5ڌ,򽽽---AJRbhU A QMiJSv)MiJSPʔRYVe•u]ommu'HfJB,j^9v/Q4h"\tH4 `-HV.ՠ.@6!L2 (d2g0!Yc"Nh4D"xT|ӦMîVXgƶEM)aȌЭQ@X$0!M)` ֭[}OR\tMca>iÈ&}رG)îdu`FB$UO#7EP(!SVpNTIF˔r9҂,I@5wG=?ZRoT*&I~ %,[ B@)UnWY+O߱+'ܔ4)MiJSъ"+Qkl6kJ1È"f4[ZZ*Vu=Jids]mt$D34Dr:6Mr^B4M$h{RApq!0`Je2l)~4hgH$nww7Bzzz$ hG"$۲lwww,+ VUU#=HHs  DBUw$ {u! @ww7qP;hڎ.}8dBJD)+8w,[R.[xbY,3BԺM8P*T*0F.ׇ3Ǭ:10t]nPP("9iU)SܟmZkK:2dYVt:=v^'zFDf^+-8}?ZI'}ry#%oSKN< OޢE;&0󐳖>@iiMKQZw;B y8s}.P4G"fF{/;[Jr,>f̴zhph5)z6jĈO)d2Um\ʘ\q\6u|>Y*!p-r]P(`Y (FH$]aȇŒرc$F5MX cL0htرB|>~XT#j̓tMӰ&Vlb)#Xy{{ baZ{h_P4NK#7أQh0~89 SEIJߞ}_?_lTVS+ LA)+$K)-yJJ$QUh؎<ET*Pb,[vEQ,B|\ u]oii7nڵkU59YeYV$۷c_-~.Gfl9F5!o@j Ƙih"wJ&G}=ԡR ikk}T*!'ܲ,c2yI5лcU 1_F1~9Ew/2T,pYAMeYT &paRU5c%5>kp]=9,E@atwBL,lYB?ugf>_T*kX RV}$oZ5n6c LH2>}ڡ2ۿ\|I'My5msy.8%OZl(eu?>}YM_#_1?<V1~ݸ{p'N 6_1Cg^J?DeGO8K:e}ݟ}[v}"w=oث{5%aU2..|9פl~/Zg񳗟i:>v ـ.;z瞻O}OAmƆ9({drxmmꁟ0i會>Ճ/p]XmaM.^!hp,.Rg͍_0X~=5)EB9WdP(kv4SdŲ,(c,몪ٶq @x=" $I$ cuvvbqT2zKK }}}eN&[mz c|>;cƌ<4MԄiT<@@W԰!$Ixcqb(z @iQ븅Ap1nFX@)dYLX"+ jʔAhJHز׌ЌR"cZ[XD׫xZjEQe* ᣝC)Ut-c!yG"X,2c6"t]'HhSpt?t]WUR 2&S*IrCEB!YVrB]2T*L&%,xT UD0aD"3g͘={v*;MSUP0"#yL_\"YhDo윸~ru[/xtoޱ`Wк?>w}'pt.3_׬]_ɕm8]ʷ^Zoo=ymc9b(;ο>]W4ܼBp7?J5~ϾOoyū5g]ۀ9Nby㧮{/+.5P5Q? խjضG@;)ȮxϞ{sݛ~oPD3'/ܼ~}"I="ٷ6lx;_]7VrnT?ZЊ'cג;{oWm:o];x>~"ݫB]6 F?:H=jp=pu>v;=~ϼSQ}rƿ~[k}r['X{دaOZ?S{=w旟]/t_l2au7=І~{a~5CO<3ɋ@ܟhgLWwUYSUrYZ%t- IDAT\X*ZkiR)Lx縮zB/lʕJRB\w<./Iܖec{^g^4P焐i͒Y)c.$$L&Ǎ0D"N8d2eE M|\\.gY˲\*2LŶ,!$ <OueEIR `B0G\.f%O,P!dyݡeOaM00I „E(hLz{{%F(r1YYA'HRY Bjjs59B>7KdsyA pdLkkkbbgÈ8ML49|,K,IS55OĢQ,iʨE_{t_>efTfdg֢q #tdU"w&u_cO8y1 ]}|k=g0ʉ˷squESDHno_U9Tghrh]WDdpĸxhH,IҲ3Iew}N˕5:sb  y珧?i˱݇ӳ5:c(0h g }yM(l!sZr섢F;}@O=p,1e£Om\pw3# ӿ:^l>W45"3 ?9ateya3O\vn!٧^1zCg_IjKSE@\`msyb,c1xl2M(,F1D_UUĦ$y4e"̅@ SƏoN&IRxq45M8,_D'BH|"H$B!g2۶8UBM*$ɮXeF 猥XB ˲8mÀJ-5#qsDOi2, B! КG[>c̷,WZnw-=wAwTkx`س7Ҷc87[wcƎ44_DdIƎAGc\0 =UUmYFژ 'MJ%2FcXN1.0.֗"_>֯& #|u]dq]wc?`> d8رc@[[bÆ X׋}iQWx|ҤIAtu#R},>E*{&B1ieYmeٖ s1(B(f1TUe ;aaD, j41O; &a/^s  \.)IcLfLOOƍ7lؐfض80`B"IUF'L@RXHڏ\ps.E4i rU}w}\1 #+pN)K&ShԲ۶aCob5wJHjǯ͎U=YoA)>%H֔ Yco^6X@tm˯.͆:n}z}WļcǾqSW~Fs fVe ʛVo enC߸[]/)9t7IcOsگNY .W|oҭ\}DyG/๕~9cU̙j<#wU<}^A_|ݟ|O^8鹛^pv?#;RxnvϾ|cMVCrwЀ$}੣݋~z`㛆=;Bփ z"1f)%~qQ߷vIlYmYZe9jZ"0qErAH "N<FhΚ5kBS~`@h4N3 ~zf̙sa% y<G D"aYV>,k֭x1KVb>lH$@Ȱ6 Ӷ-[X,ޮi@OOXƀ-,+/U{c!8J$&SX0{3O?_|Qεkqt\ד$9i,+lE_;Ǎ) 5vvɟZ'v'z偱ێHVTr.\xQc*itGd)^tMu[SiiOoýhȀuec[:bclnN"{]?7;ipJ@s;nm4Mi T*=3F%I<@STE1ܠn"BJ%4)iZ[[`k4SU9yXjrh`.Xƺ.b$If=(c(Re,[?^[[;vj Rrׇ"r<L , !<4N!TL6EBUUM&ldV8 _* ~* T !iXl~MǞԦL&GB|M<[|K. ,hJm7ZB(8F%"9P@8P5@`"9’Lbqݼ NeDꦏw_)&]pd|B}O_spy1褁6(cS%V&d "aV'A @MEJxPX:u5Pȏ pA# S[p2GhG7 K)pTwQug>Ut^wkJ@n l'k zJ!Z~~3/2הJg+~xgGV2+zw֬Y#aedqGtY (+F4"p]ӄeLJJ.wV<GI]i- 0+e =n2m4M]׵yDhww7vŞ X,b5,r8N[[h?n!Ę1c4ML,jgg'aT$v{\6`%BR,`"A D%B A AWPBxl DuD0$)jG0ƘxX q֬Mwv"׍9!im;,')w . 87 cLk+#;>p?|w]_BȲZ*HDQ/+_uD,n;3A?DLt=oټ9w߲;ŒSM D䋥w׽kN<(!R'n&k{]}x ԡ enE7$# .#a/6xZHP;[·*AkfK`g2ihuDŁk ~:.x]kpyзZuP(μFa85ү_n?_mMSn=imm-Yvx1N:8D"j&I ~vM^}ܸqR$<՘r*V2$Q!r*1I{U>hc$4M=`ް,H(UU1ofH9Ji\,cxUUb|I$%qIJaBH*eYVkk+)dUW'UUEz~7B4?Z}k@չqBm5Ou9E M <ϳu]H$QsBe۾Ki9sF"Xf-)VĩTbƌ/R.STՈD(K 6k>,"ԩS߾};Ą0ʕjmir[o!rkJ!즛n:uѧ{z=&1&ʫz7TrIɖ{{zm&@̝S,2̒%3R ;}}ſ\ɂ4);# QMI5/1Mi?| ֬bW*X,J0%$8`hu=EQ"iE&QXbydT*m8ٶ # a)BvJ%MicƌBqjMӰaZ ]ף(Ry"&lP*+b1yeYr NY7q@#DQJ#H6L]UjR 9v*DXe`!; FP}-$BA`[ظw-`,E@%!pB u|*|3 x޻"fFUx<64f-_c󟻺zyg]6F "O0>Ͻa=D\( ֿ~ض3f|y6-z0h+:/)89ӔciJSE!+ JF"vI%8w]7qd"=nܸR4f]י9sf*u]$}*SceJNCSNݾ}{\mKu{{{d+ㄅAԶJy𠯯 Cɓ'b3|[0rYEQ(T {zz82$ PV* B.JX,6f QU}op<$*C1,˲,;>s]o,EǶ9tD/ LB$ p IPqJ16f3P.@%IQaBS'BuΝU*0V5R>IpFKe++u(lݺG`'s J  9YUY$,_\$ɹs0&<+\P,V6eTI&?+ܦM?ɓ?O)MiJSҔ4W|G]QU].pl_\dI*MMUs,3#ޓewD"[o8sL&H$cd+z` іEQV\cƌIRS*dYF_mf!D&q] Y1 $ -R/Ӄ.rz"R8N&Ym몦:\&Nӳav"îd4ɓ'۶N1%y> JG$߉"ɔ~ |}JQ M"3#AzWPUu4 %준$}Q-u! 5 g4ry3t$@EA(zDP<\zU{2U!(BP7O{^{u}uuwu'@&O=Uo* } $`0Қ (Jq۞C`v\ٶK)R+a1f3@kJTIJADŽr他 ZKM@jC͂rcq$~3M/_)-wݣkGx3NxX"ֆ]תWk;v׳;n?vlcmmeZj a+ADZc(p3,prǝH]#5|W}ƿ蒷n{MW} 7pӭ/>>e>pCϢ (#M(*e@H^w20 {\MrfVTj4K˜$M`?$C;Ξ=tBrӽA4 =;z(l|G4Mp8Nj4MZ G1.NqOSJi\ư `dmUU<922qjGqƨҲ,u0fgΜϟǶ.//l}|"7@qGQZJj: 4`Wd_)% M< VW$sI)Yj}Z=w.M0[[[ d5Md !hkk 630VWW)ct-ز,%247 )eyQ e:A)gIɄLp*[d_EkH^?̆ h 49}ERtD8纶eU s}U9 IDATYYQ*S/2e4I,aUs<A$av%,/? ~UϹ=~|fr^J_{}x^xǭ_@۞q};{H?3/u'Npmw'^~lj#͍I{wэ/lg ?)P o>2~o~}&AlM_7p_+4t=_}9Wk^>?/=@v}~KQ^9=8'zJաwc?zǭSw^G׼_엿cY7{_'|/z׵'_?sWOJ?@-JR8{{{YYƉapq!n8IFRh22ARJFTj28@9(ft:fȰGCyTBŞzl6Kr^߿0Bv:6 vvv&lm)eZjF}u9)`0pBà|vmie8(.TVs q8K @f"x#y󹮿RA0^*8NߋdL`0Ǣ>ե`qd!c*3[ְa7 xf7. F`Cи߲ EB\-/}3۶=߱LvVggq EQ4,5`wgO=x' )Vh}˯|o~a/6μ{~6}ԧ>\M>ӿg'7cyGx7oyssrOhZʅ~/1_k]RZy?uŊ|O}ӟ+m~Woyoi|ow J)X"͵&@EbϾ /Z/0'=bE2 7RwjݪJ(LʗJHt4c/I(d)g0 ur!PG}l6Y:NVWkegApm8 :z{g:仏npVt_hl?D=Ouo_ox%k_r5,߿y `乯K>P57溄R8ANۋKY4i9،Pa{^z3{qG2ywGWYoymد}u`zj'_"ڏOZ @hycɮ[WR1-Yw5~AWW@l#(F1}6V&I%)nKΝ=xv,--u:q0UD~Rj4iB4T*˱yR *[?WTJu]w)-h8DBZ6 8T jXگT*8FJe:/*Uʔ3QLi6Q> r-U*(8Zqh4VVV8f8MS4QWT*%]k Cg1f,se1Z9C!s q-7>9T,u}l 3`}}6]p0gԩSy›9G*eY8-H37'cT@afj+UnI),q+m#"<eqlTs !8Z[[[=4͍< \dYZ<|M{?zv_50[-К&3Q~?~ם?țn 6QI4,(OzqD9\2\Υ}ۏomu11lU@gx3W+~|ϔ_[//]rw껖B'ax i1@\@4Jd+a{p4RRB,Bu8MK)A^Z6 `UXضR蜅3%I魷ފDǧOƚm?{}dpRt:nt:|a={V~I{___1' 0$I' Zu :ړe5͕P sLj!0$RFQ$ʁ0 J%$Nlq&\'PE"2Eheh'I= 0ٶ P 871Xk0pǙ,c9s`nmۦak ZK:;pÊ<8R繒{eeUU*yBXt R0mm`S.6֏ZM!>eYI0z`8 G,Yj (5 I$ Brɱ,2=@*e(I8/7zR}ww>zvY/=Pz|%~D$g? T.s\jQ"i)c x3=xg?sy!DEǎm;\^]2_YY)Js'=`c~ߵa AN, R$=0874mܻUCEݞ Xk00$c6H?rᄧG t4~3 Zk|i" [ X gIBMPsɌ"e/HxNJ)* q ř1 ZS iqΕcZ-umäqPd* fDSM XJ%enfTFt 2UZs8EQ('I8@yV ?uU\la|MczKMzv/ TI7UJ}_jŎꝿe@oyOͺq[.{O~wY7te޷qp5PNRvVv{{Zc4*k YP*:{۶VUs{{xj}w-ʲ̲,P:dvNJ1!7CjtڶRJ[e(֠3gh4z,*yޚӧ(MGyjcJ%lXz2HGRӉLjZEZMJná#g8(1Ƅ !X2ϕ<JԲӦu!RJ%}=?VԼ^dwlB$KӔRۖe!$M sh4l˨jQBY´֖e$t\nd2k5,~Zjۮkkg04gY$l >0pM,}r,vj|ՠ,Bh)]3M18> eRJ,2Mc}}]A)a0 aς{l4ǣ`4Vp{k:?Qo]ueAP=wT@%@;PJ*@B_$ EqmBk<ŷ_k]T8N%mY5,C)Ni}4}ggɓAqtc4 0 FñfB)!#\k͑p/KJ)BB[K qQ~[13Ŝp5,ZXzyX7<"0WqGi|(4pӸ<)$dY R H̙3QR*qf 4n}FV? ʶzl6=էڰ,˲Lqص"=ΥZ&c,˥eaTU2-%l6I\J'S˶˥mYq&0p@-(8%zi.KvʮH;ŷ_k]B?ߡ+>ޮr5#}|y}WSo0>a %ς|7ؕ7!+P@#xy zQ5q#W$MۭV R\*y-TJNSƹ NƓdL)s341.0Dq|N)9cBH1M4p⓳̲,=i@NT}<GQ[[[L0$I8+++Y\RJK(G:S,˥~n{ޱc:N}nJ2 Ёx11ȴ"j)F꼒RtMAlmmab`YQDL<4RJ$hלKi2fPL˲!hm°@)Mx<+kDRFsVTjq<ϣYLyTkJ`ܿHp,mѐdqjS3˲t.J)ee$B%vl˲"kzQ/Pøuwi5UÏ~ $/P@_m23MVg x,Mgir86L4M,c'I|Y_,˒"-IZZ^AXy\F=XζmT[VFAAFjɓw8! FR9g1"_̼jpmۨ6a0JAB[q|abgbgpYkMضmp aEQgKINF3M Ø3ҪăAM ѦiK^V*3F,PJ&8ij R*s `}AY NVНMYzRJa!4h˲ͦ( }ܹNgYt-;Mkj\PH(B)5>;,jީ/78VirJ g x2Fq]n>#?p, fRBJ~;{(+a$Ҵ^ ֎c@Y ;eJƔj"i<hiIC5~hYPVc Eb*f3j^',2N)pA0!txOMR3Bi$I$КsHsT(cJ) M53i&$b929 ocs9Rk!R*MEM]+JRfh`ϣ&@Y.S 8=^ (c[+++a4 ˲83PL 2HAVBT9œ5 ",]|K)!n Ӄ<\וRLLȤiPƒ8"ך!Qv~"r (P@jWl6ǣtO{K1FS+Eq]IQ|9QjapWl---Bzޜ8A  a/j껮k2jjp8j5IVJeV Qix 0ƒ$<<]P?˲@ri~g>dR.*رlBiEqZEF R37aٽT*2\ő__YU> sstYi"\.z= KI QR2& b+F8#* ,B4!sBe 0Ir0Z_ڿJPKR LF4p˲1lhEQFMϟ(ex6c]Hqכze.Fœ,R9iv7y.`I@q-yaJṚ$)(P@ g-?7EGY ?{aE]K8y*ϵTH+뺝NDZmBɹc;Rk)#u \(*˾k' jq s|5lx0h6JpQ_`0@!4MM7$E)]禙$Iq]4M=Lv`AXšdqfFvTx4R4`P!FE=hШՆ!ߗ~*ZeX_RR22$hƘR9ƾ b<Jr )#twwW*y'{ !Rd_ZY2ϓ$ʲl!t BmRj HQJ}zZ 3!ЁXk= Ncye<_5_~}J*~'a~Z(Jj:j Bt`uu+HiVEi*@ (%/OĻw_˿Xy1i[T\y>ʥRZMq]sǎŐڱxIp'cJI$fswwwAo6έdj !P\1q(ҏ$R^>}zgoT*y^$hU+`GQ ynBG0ta8Ne)4IB۪TkQ:֪nZ-ιeYy-jR.1f33cz}Pѣ9Ȳ, CKzdΎ8 \q]Fe"0Da$cOh4j"emnnraR>6 |!JicaLqn` ƾվ?:uPjY&@9!E1y4L<ϓCiBRHǹH,A/}F<5Հ8%OȨĔe|Bk!ha$b1Ni4fӱm)$GL2&d"ISL&aT*Uǵ !a&A`hVyk;Kmoooo3L%֮[oh6Bz{?wf@+M8(Ot=Y (P@O8c=ku$ilwg~Cju6YVf`0nooc8;NT|WU$}l=z}MS4C% N>7x1]ZZNE>^o2eyN>u]t,^ !ZV+JȕDyiJp9&gT°޲,ì ",](B.tj#!s@ZHRJeZ+6!$M!< DJI J0f @;y=L4JDJELq35W~ z:eiZ.WVVa !FQ&ļU,ϲ< #ȉ7H),^8J<ϥsU?Z4|lVI)ME>wq$x7 /Wu %8/ξ{U@ (/[NWoRj̦N}v> e($en[vG(nYuxlT2D(L 7pX.Q\.&q8.cTRu`>N,FXN0 enb0QmlldY].lȪe4 cmclGϞMDr8mnoܘf5ecT*v8zc_r !BN Mtyyygg9K)kJF1Ӵ<7m+^gB88(J8^csM)!$l6ER6= tb,/9pf00;㥔@H^o4ӧOdpg#w}׵ (WY!qq @ح0 Kk;LZF!Jj,//oFɤb5Z3ư! 5Rz: !ʨV4@"DXtϲ>4\( ZtpȄݭV[[[B:.O`7k6mQ y[/<0Jdsg{L'[BP$l*(J+Z)E&0nko@ xs~ח/{ea H^cTr8R ZMIl6'ȳtZV8β ؖaau;qp udӴquϝ;' cW6Prd}muu5`eYZ ^BibwRsΡ1.A);{p< gAVKԲ,Nr}}}uu`:fYV5rȅH,s]= 5 ^Jd 8^'~4UJَiei&qEQUJAm  @#dtB@Ns9!0Rp@__2FR\ !B!5p$! (! rE{xQ-u5\imZ|em 4,Lq?cQ l@kʘia'I pp}pdb4,-I {|I (P@/AlFRa*)8:hBp8Q8wt:ڭl"|DŽH;~HVT*Q cyM={ꙇO3\s} )o ` d2y{{{Jȑ#~9mf\ 2dԫ4ݨxvT* NR kkk !8cP\.Wj4M ƧiJq<2L4Cygjޠ/,+E 4R 2ȅ>\)@0$ke<2)/Dy\`'a4/eY)SEU @)jxaz4`BME)P@ x*CIY*,Ӝ,K+:Qmn^XZZv]GIɹ uKFPn 8 é:㽽=$l+ Bxyy2BdPΚ?n6kkk^z#G,ZOF$IjL mooofmۄRƘv[)ZOY8vXR,&IYʥpX{8~9gY2LiKZ(JYD(/ɗM\ .,Ж&Mu&dl?Ν$sZ*O>̹gyy84H0xff&IreAk]֢(]qBhueTiNOMیc6ҿ7oP-;l6rQ DcBA1FQBlN:%[ycﻔ$Ihml^6pdd}2Ғ&ފ_zQ`NXO}+t^zC=C=<q~|Qι֪hfgfgZV&ałbzj2$Ɏwm޼C]IQZ9 kK}}r i28lNRfggm0qVZ0/333LRJ)5,ˍFq^״1V\W)jv'ýVP.J|.M7jUXF=MJlsxe2v+$Lј+/X%BD8a"lfvv8J|6ö](!|>/D=Z3ƌ1R !$\om:M)R܈i,imLҧL)hx49{ƥz衇zc0fJӴl&/8+B>8IZ 9hjj^WkFakZ-vetGFFvqRB333BUVB9eرcc:sK^fZ8gq`fvVk])W0t\g``aIR !YP~j(d2V-Ba(!F`DbJ/%$ظ6eW>1cBXvG`{@=C=a:r\6Fq>pB/n޼)?Gy}}}IaX.'q8wcfQ|Xڸ~C\ƔXQ* CjbIѳBeYX?XJvBhOGFp.衇z衇rn IDATzFu~Pq]NMkD&&&BBy}ŢҪ/pH.J:C "IA ȁCV:rHR8666qp__jLfq`xxjׯ}xGy!fͨj>!dpp!y}}n7m^Jl6B6vJVGxǎs4cؑq)eFdT*;?-y6i lUWJey00B `Ҫ&LĔ?1`ɱ2S,8@6Z2D "I>333fq^$z1VZ559iٴes=? ڵkӦM{jfgg1Ɨ]vY:xƸT*ݻ9/FQW1۷{Jy׮]ǎZLh66B(I"qXĘB#nP2B4N!sJi6(2Zl\)eae?Cl`iv2'Qϴ~mݗ9}Yϸ~޶~?E?YTnKM4 p[ ۼxw>@S go{+޾ /O}" i=o_t_зf9_s7 ~g~0Jꧣo~svlڶ|Y=g\uiicg<]Sn붇za%RFca~.M4MB<>>>66F-;gjh6 ۡ2qvf(>|匛cfgg(ZcLǎ pÆ \n͚5|빵ZmffGٷo߽{mMOOW*jVҗo|czvfzv&ˍq<>>W,fy[]wU }4۶mg_a;vN=dn9vlذq%RN@o ֯_{9[o߾cǎky hqFVQU* 7oBi0}L&WE"[vR߷l c}R%1þ]<.<ޏoo} Mzuow~=䉉F:(?߻$]WVx恜.Ogσ򯋿|G~{_w~?gvޱ 9/~kɓ:sUM~ n}?:?5 pqe~ovӋYq`=yt]=Cw^Ϲ\2RJJy'z|VT*IBg:B~~n>rlCmf9?lܸR.gff1vv=88h=w7o|;w޽a.l`` Ijw{f,{=|pZ}}8M0%GFZfMјd2CCCAgپ]vo۶M)9/\r 7mtW'I8300>Tk=00daryq @,۽y/mR+']=2ҧɘ(6Β3d*Eٗ\Yo /d~c?)r~W &:w#&Ct/tׅ/}Ň99]= yWSb+;׼+_psv]zɕI| ʬ;Up %_/>]\~sCpLJ_3/r+>`оソ{[F+NLm?i=L'WP@f[^~2ݕ]>VI $F_reK?18K[dXUOImso|ѦS];/_xwcDZNP1W?p?~b4, ֮Zm= \W)q=x& z-;].ˍ[V``hppp``ݺ<2lTrww`h5badboxa=;{6 ZTlre % 뮻kgs9ghh!rz裏FQ423by[AfZw9>B9q]9|֭9VAP*6mt}}K77 BȐRjpppxx(5#׌  )䲹\ur237 ],ybv']^))rOɞkNE] Aw =ul{#}ZT3/?Uu5lI&o^vo/?_wnyk]k߱SS7VGtr?fo?[|p6 }lG{ޝ߶Oyÿqg+ԝgc \w>#_{?f[nWw_KB3P^6bk.-oO_'zo^?;r8!ځo?^UM/^׬hC @Bv[9'p_;?گƝ_,z枿g,FyTJ} [8M,nx <4Ƀ>ګL'^?|%}䕙+BrkpLPjE߽l".mn{yqDza%vk!\JyeTi~FmZiG(IJD@V- PahZkLEI"8|V9WV[-իwm 3VB} !P(ZNaVOϿ>wG_Lx[41sj*ɞ;{VxݍLc6q#cẍ#WU^ЄyFInyxYj;#^uHNO?' p „j|o|'h sx /[gi>FWRE 0ͮ2'+µ#7/?h!HN.;z̒g_i7d2٫nV׾ϿwHle.r#eg;?.MgxGjt] { ti7鲗z8ok^ZzF,O !Sf\0RJqR_POLLiB FjE58[e# ɬ^q6` WX!7oD) |wϚg|>/tlllRJJnݺT&V+ukK>' FU* h)0ˇKRQT*q$IVbyZk_ƘMiL) NrN}Rr_~0X)eJ1^JN4b0=`t%o t5>|5>~\}ӶND]nYB)sB2lvv7tYdrF7;߽+zMoݾ7nY?ra/ `̉k=&.Bc @gq!BtnH݄ӎ|(c!f1sW$gLޏ}O- 1a9p%VtHxo)Xݕ\ᥦ҉+̽*o7lqATlwkn%g_K7up'Jg[Uw l}d|C΋sl]g봢z˯b+uV]r=3%˟&@RHp\㺮q4MƹL!!Xf c8nA{8DQD,,,ڭ$ Eyw]cl6APJ&''$Ilݮ*:vxaز>wrrRk}!uFGGAk֬ٷou d2Ƙ\!Oi;wd3JHT6衇nL&ùn_1ϗ*B6nbs~UVA@rA977WT\}WJI)O495BcuX.|.L6B.sbW, JBTo2(&rR/r}b1_(䳥9٠^x\x9b1 d2`ygyZ"Xǎ>tnws,6F1YYx6e0!M*F4jɕ:ÕR:m Bia@L;SЋ,^9<ԭ=x/=7w+7ϞWMg[w`Eo|ꗮo=7n+3{}!8W_knO]iͩ_{/ r nSpr(?1;?[zחK|_>2]g~~*w>y~ ޣ駮y[^6_}[ [D@NHg_o~o~q_vcl}Z w>'~W_kӿFN)ֿ3<^~lm8qh]vo_{衇MYn$lFGFQȹS,D"0FFu*ժ}VFq\!x5zjc(fBH+rƘGyR(aojƘ0=w=4395<O)*2A&)T*P}R)\DZo90&"4M-[kg$MdYBBefffpݺuVǹGGGoRXlGa,JSSSJ4Msܹn?tpŔ'|/f͚ZVwu뺅ba2znnh@TOpγLٌtvvzڵ[lZ $f.*ggg a1 ,8!s9XdP1rޖ Bt~a s ֖ۚiڼ֋@k-X6!bLv2pN4 ZKJRhIӔsBJ5 Z#?ŒCHJF?-=b}={{ MV_􁛞+7ԭdnO2]xrN)ҵ¥z:uiːO„vNlζwT[3N벒g奻v鬖ʟ'g+y,:C?XBL&|pvnֺI$Iuf2RFNs sLIuVr]qv]Vggg-S$ 3zttTEQ&i4@L&n7:4>>n$I})&''ggJҖm[?5! IDAT&<˅BjiZ(*JRqÆ=ܝӮNOO9rlY=<:|X0Rʾ!PJc FFA@BH)m%PZkι8$I0fE& LcLf5M(jxZ<s(˷OwJ(?i~q˟&@1~ j.l&q\Y(B45`aI!L$RJ!ѱ1X&gJiQqǎsl00f^ZNOOkWZ%TULc,V*JƘcǎ^=R./h0?A!$\H=rq l߾ ػw/lxhU>}3t l}s9]xȑ=C=COHExY1:vQRecc4cLJVjpp0 CR@䜍yBpJ `qr<==h4 4"(${a$4 6Q8;7v(DvP8>1~hܴycBʁdQW4;R26z([lrZ\.GQdajRR:?7?770]nuT0N0xvnnÆ iNNNp~,2sX:BBk{;-Wmֶ R/[aaAc8eZy)%x"F<&R+( ;wҗ^oھi a !\ezgR)c,I4MZ(Rź+X=~ƚHے .z衇z8{X)yB84483=SZ!,1OӴT* &":RkG8CCC8 C;33cE(\gّT*#Vj\Oؿy\0҅BT* BC1͓RGB0B ٸq֭[:%1jH)E1X#DN8{Z-cAN\ײiB6ֺ݃ m\ 08 D O'JGRιuY^'xyC=C=&$NZj6R.˾q}pVT0OzW?(19qطvr<331]+dƦ@; !dLp>}rr 5"_.ul@O!֚jYLZcVoBb-upw:tHJA^_7 B1& f%Dcꗽ%rw=!6?Pu>RZgַZ-JȺ&ihyj41䮥+Rk4{&@=C=V/Ѩ9Jp΃ X\M.̱cDzlXXXHs>:ZT4 l,zbb"sΫjckS1T* vf.zDŒ1cLݶQ8.JiA`<דRD[uJiTJBcFEku4 \q|>O?x >>~8cccvwpÇ cBerZcT !4R( 61-f~j uEKc97zC=C=$p%%Bl9k3`zEl.(MSS|>ぁA)8vh41JQD}Yj\.;y^$Bj:44dc X>@JYՒ$SJf)ͦHS)Bj|&[5UwBVlZSAO)us1!$8UϽRk1ˮ !fk׮9-w((DX(-9cqJE%5ca~ZGR-gm]Kbf.`H)@A!!Tih6J)y໎خ )Yr9S,VjA4M)5 qk}%qSηcH4B)8R侠;;1?%X򻼇OJoBASJ8lEBad5t6=Y>4s!`t h6i1.6l?B11p )]]1:"-wbR%׋pU'I)i)ca@kcT^ne|v 0lG`!u]Bp$q&azR&Fk1ƹ\cBFA UJTWh:_40@ cFZk% #F &Rh qcTN0^UJe璽\T 0 B` F bL[Ҫ61` &R:2<ǑR1&B ,v_DlJ) Q0`aVc c.Q{c:U`W#))c,MRL1ĉ 4cERE[F+%;H)9l4c, b!1R(TN{3hPJ b$qK%% !`*#("&Z/~ZJϊ)JQB<ϋheFX) 6 R1Fie/њ2*RqJ+$FaDMf*oC;׎!iZ<gpF9a؋wzJ]vs8v\7c%Iؐ#ABv[ץR)%<@ bc/fDÕRRcL$P "=i1`@*I Jj!F]֦L%( @%iRLL!)1Mk-ٓgJiRB!)%%cVJ1*1Pj^Rn.E}_kVVSh{v0s 6!ehJ)%q"Mʨ堤TBVy&I,15x.ؽL0k aq4q7 RJ8 ZAj JceݰsR& :-BVnm J!A%c :#M( Ib1hJYJkJ%2jAcƨ5Nc(G1=UZ[mJi9cb1j&c%fBH}_Ji?aFQd݂Zgl6[*ZV6lW҆hV [eᡡ!q8_g!28l6%1[ c$p]͚5SJ18gzZT*Z /+R$}FBte%w 30!dʆ85 WP>ssssz3IR:G}X$K)%msD\ƚ0m8(8I(I; "<.N KS884' `d)K} [9{ g0@ | JTRRIA1FСÌRjqy&[i1)h `?`KylL98M$P` ؜םO;uq\[#cRXB(#RBHkEe9HD-ѥ|wbh53PZs̋c#DhW dd Բ`MmCe4Th7a$MҲV1+x[7 q٢ثq 80@sd9&4_?w_R_h¹V=PY=088~hŃ8hCeR&/,cZj)Rڠ0 UxZkaQbB(rGg+B6`L$Vl"[c e2cR!D.<=gQi $<Ya lE_C+:R8`)!B @vALE{؏%'>r8JUbP) k&!E$J c,ϼ q5[QGQUgŏl}ch4,wH0!c:;JI+ZJY}j+%T#Yb!Zimи}O)2l6c_¤#txTڲ7%k23RfUvhΝ(͖`QYT1R)0F'z,;3Aq)Kf5fI9kFͷe?(*2<"t샟(%6ff0*őrь|!R$CK@@q&RrZ))E9ˉ .{Asò՜3"M. 줟s[вy%sn !Ɛ~RB?1')ѨKo KT#2)%u_"2/ }~cl>TJ]`΅{Z/͚R.zZ<ό!xI=N"BR5FJ霳)H)a'$XkZ9gJj.xW*cAJIժO)A/HN* j9c,eGAq9Rr)Rr1I?um%ƘK!Z;1BgW9gd"r>hS.D8pz(81@/g~Xk}@o Zׯ_yf۵}C/O䜅P~m\٬]OCa?6MzxswK(wm7#Jɭx἞\B=7j\j=N9%m4m)0Q\]@D(X%H3D:0sΙJ%h<Z)$2*>oz9|~/u<_3b*XkEd6ZpU'\>mӬW})RkȈL'"۟8 @JA3"Zs-Kݰ(F99ZkcPjJ7k|zҲ/"1fv)1IM9gIO?Mcq饓R\ Wg#Aċ>s3"v2i*h//9g k clǔ&,[8B5MضMJ看.@g |K;@*ItB$5Jq%.2) ꋄhXBΘ^nB1Ɖ))rεQM>>>!K)Bcun9QJKO &ZkxJA9MRyZ鱡«W4(  c1bbp "Q !dbJ@ -U* IDATzJ-̹`V}R0R;Jdh ;"q9 GYcb9z4͌1B17ƒ/4픒mx:Zk84ZsDZfRN$徨ysɛ=m+(R)3N^2Q"M J#]pfQZsv=cHCwnJ)'=дfG!1&콣V{ߔ\ʬ )R)`,L, f!hBØK 2~09ӪEhNJiY SZyw(k#f/Dc)*NMzBo߾kUp眇H]n+jCO|>ZkKIBqR|y sc !5>}kD(( ; onnynL;OMq캕jteJi%ijMx?349j!Z Ig!zG)9Q"W6R?C&qKB5e1Fi0-=Jrj$Y<ϳ v]gH)9LG>ӥ '@H\DE4s$ BDzƦR] y45ƨ&|)5H-<)%mL6<bww1i#g2TJ]GcSkDkK!ښRJ6X0 $7K-PAkM.>S<! BsnTJ!D>C ι҂RȶKpW"&LL%~*Yk[u9G͉~3 *|It,pwʜպWRngRg}~H?/ "B~&vu3PZ;BZT)sHV}<#c%gl Up1H!!J;|މQ}K)1iӪc)TApN3IcR.4MƑ_u)/c: DIbB0D2+C]{p{bԥD.ŕu{O0q$ot`Q9Hf@řTR+,yK),jr"1i!>sΕFr+s'$@pA}N !honIFBd==zrs~dZڈ 3 S Vcl3B*#"gP.2bFodH .R/{_JqyjJRk6thZ\iljs0s.p`ΉsJ+J9cOs.9yCL@LNi&dM;3~u)my6M-3w`-C`]צzz*i&c͗_~{z}J\]Gw1ic\V:cnWr"?LL HmL(ƘO8)&ɍJ <ժB'碵V޵R΅2JjG!x4p<1H)kŗBΕRѵֆm9"')qJ8d!%(b[Lի u!fPOA,c )qDdLYK)I+UJ>ZywW$st$>>i)^ᣵg_}TWR0 WWW0^0B8c2 Cm۶og_kf&EZBv圇BƦC)Z_2ƾ;`nkdǺ:ZaZU)RWoxO1AH>z3"BL9%s$F\℔Zkd8#mX)R%*LԒs-)λ/_)}6XJ=>K糛 ogKiۆan[kxyo(9hQE@$PJeim+u:,)L~:S2bȴؗ\ÙaEJ}HcDY=WN]ב8F"edc\U!mZq$08^m<甴ZTGRi–r{O_DGeBn&BW:mt%h@InvNdLe" c.eFYJiZkϧ32v8ЊsNXCaJ*ڭ T aPJ9$Z*^M0GFzHԪ1̐RVi1F`H6VJc ,bĠlՂs&Z hri]?,~>xX-YC$6:$@ ٶM0B4a Z%e!~7ڜ3ޓ7qWǏ@&E)Lh9ccv8jٵyaS4%BY1@) s>轟tbmZ)gG ۾[Ƙa8cmclǐRRJ)rWWWq}ιL5|]y?Nq1vϥ~aHD{^e8b̜1ikX1D 4ZW`nip)IZgOCd\_JmTJܤy/j@Y Uke包 AhM tHR+#&9f^I!`%V RZƐ&$]r!mNԟ VZZVS-U)EwBVE+dzBJ)C(,s.㤝!$B@8%Z|(nz[%S?~|TK%kRɔRUkvxR/@@e QJ vq61p<\rJλ ts*bʔVR2K HYEJZ\1S$+RMY;d 2dBq39c3΅yO$B8cj΅9E"YW%!{DSrQt6d(g,oaCMkSLArRiJ*! SJMv]=>>itlp 1RƐSjAd)&ɳ(^WӉs>va>}d1EcuyPm3mb) Rk؆r*( tb<OW1qR 2RD #Rt,I18}cכwxsu RHQ_ ^8=SlyWr~U1)m{s}=4mks΂qJ#znl{4wJn´ĸRR).T(!D;OG% s.8a3.h6liYBLӤjvV*9\ :Zƴm{8($1ږ\C8簸a&x}}-)jNmsZi'Eeʙ!JpƭZ MUqt! wtBcg85]b(ClǨtRO)D7ϛx88稳ʔ(RӎTCԐTRJ㑈\%4KT6M\m4C?8R̓+J!0=0ΥJ%mA@&Tmc/^P4 m; NxNvRPO4]׍\Jwᄋ{K0خw74?ٟӠrN01a>kmv9ꫯ8J)>Ǜ+xūy)x<VQR^oBs)"8 g~M)%#I !/jC 1aM3h0#e1Ծc4A\\_g6,_sͳ"B<ǘcj~x%)h(pΡVRD b۶ww7o~_j_?Ur$\+9cMӄND\T\R 'IvR"B(%I_˧|9vƚ-^R }NH%UDPqEPJ/ڟ j * cPJJ_fJ9GRK۴WW8Ǒ*m[%4)x~HpOEJh&9爌9vўJ<< x!RD +qF).&2I!7vx)E֒sRݞ__}8zLL`Bp!A*BC#DFr^s )S0SZ9RI)%M)^3})$8kۖ LQ*{bRjJ)HU!Vs^K11Rj0<3-%#b) 0t]7TKnB|՗B%HgZvj%ZR2PDWg睇ZKwbL)cHZ CkdMp&s|>R~2ƭՔMIsƅa adTcDD])9猯7s"73@u H/۶.6}R!"y$>fC( ι\1:(P @Nm R+tbqSNZH*\JbBC /]J [Vt\ T߭B9.Gy4 6$ԋe)sNjBy!XK1Z*iqtmWRvmu\ՆҁrN0d}%czcəL^T14%笍n9oZݛ7o#R$[ժF@XT48qUœaT)ntv:z$M{^\+ e!2CFq;<t-J1Jۻۧǧ9!D^i]J^m#%nq1'%U$gHQьsX4T?==rssc~]LH1; il 1pssB\Niq0hcnooi>~4Z[cP kjx>!R)E__ÇRJŋ yba'ι`/xzz )yv2<9tssrP|SJMcj%gM IDAT4OpsuZ?|pEjEd#M[i8Rzc<777pW._W} << )QnEE'2*͔¹ֆr)%R rv~3|Rk&MUII 9{W20݁ !)-.TʴAh&(tAB׵_yL0Pmk "Ng 2BhH RڮB > w#WT|xxPZr!J1%v 㠔%!FtZKU u/~w?;u#5Z u;$)r)&)v7@;UHYK^Α )aEYBH9G989g9Q#Ieb4W@JWvP0\Д.r>o\p-)V)'DjZk!8M@攗RJT2p. 9R s/7Bpm̺_b֚Lغ;kϧScRK'kjc4Ӵz-xÃTj8s1g2mcy8\r)WWWOOO)ƻ6oFIe&z!]n)%pvNk# ! <0{j\3F_]_k9@fw稔bLZ)Y/ӎ@kt>Y[|n pj|:Lc.ib[N+6>~ri6Meb+c<5!E0RL 64MJC!J!l dL v9'< 43B٬%g!T)e\JFO2tm7MUN*0lz&s<[kBR2kSN!FyFۛPmRJNdlpޗ#piZjc @qc]#jo+<]mRn7#tΥ "c^B3maX)G2+9 }W}OKa/_IͥY0"gWZ')e4 r)'tb̹|rwqdƚ>~TK9z1ǔSev09uNg ؛/C:RY+ K.BdNi%7s1m#]M{c"8S!LZ "R~Ι=!)$rVr#8WJ`۶O84 {R0_-O>H149t:duP9RRjvm/p9ju<ǓRr{uնm׶ݻ?圕59J[RJO=Ik(9‘RH@O#fwؓRp8P5lzի_88=U5/K)޽#)\fr>OÙt#V:7|gBD.'\봤lI!Ī_Zk4s0%Iݞ9ghRȒF5MS gMY- f^ /1.?ՃsfvU4il8Γ6RcP|uk|FEKZi0gvR)ső`IIA&d\I);69UOOO1F֐cvv0 1xars) v%Hiql&M\*B^Mw!'j]l)ҟ~D5$K|)&R0dw74k[f!.qx:Bp.dV)Uk aGd1s0J*dcRq&r]\! 5g!0C@kFƂ;!{[k} tCs]۾~?pε)FAJ1HABeBМsC1fSK+ivd\.\m7/_b\ٔ3|]L R*Z+m~e;O|nN)}8잹_ZalcxCJmy Q* .CHoM)rCJ)E2B̥ C_1]o P-%xȽ wRJDJ&$BJ<^–RBHZ\kP6M(asd| )A4癢fWTJ btK,vs>?QZO)%M?~bSJ[kmLy8x~YZ4M<όqc48svHYLmjSJ<_x1 ef,@ˎSrN_yt4ҁP|)v\!ֻ.zJKZIRN`M1qI,Z+ζt< _>wwurʹr΀@+UʩReFr"hK4c͛70+ ֞'ƹz􊑻}GD !'T:wiuy4}HΜsÞj[`)wi8o߾{p ɽ n{cL:so6.Z߾~3` *+ċذS K)傈yZ&3aSw)jFӖ;B)K3.ALag @Mc۶l6hyc-EzԘR#t$$ {+ 4s9/38/"(eɋ%]sU'(/IBH 9+0ƥ 4b^SUzus~xB̺1F=v4M#aOKh0JTJ)R4uSz|KZKBwi jUye(bZ(oDxqls3&C*0`eѷ3ƴ\pTt<1&s.9 "<0.b BnZmb_v6MwZe!8^jf٬+c4";4Rɳs {""()!KFuZQ.{EkȄZ+bZ)m5IZ5ئm۔2j.]2)#bʉ^AB!yu\p=^rh(}")OXj7)'kr~ի:NiQZ1R I!FX)V!^>YIy8bJ;)eJOXj/~qrΧc_Y+sJ"B a0BnSYF>vR~;m+<}w}}g=5p8 Ep}1D .1jkR9yBa|zz|>Jyv1gB VY)YJ(~%0rM-ilH ޶-qDswwu}k6juR Qtju^ 'ӝ9Gp)EGJVsRJVSMcWWW)q8mPݭ_؋{B4?q^{hha 9#<(#;iqBR K;kk[ZTa %nnn6wb@msЬֺcE&ja?Ow~jRZ1dNDiնۻvKB2΅RŋRH!x qZkJi篾{<I|9Nzzڅg7iL۶B|1$!hY)յ]iilw*%Pjθ1zZq!(U6B0RfͅKQIJqdެVas4DVmwJm7MujinnobmH)S>Zy_V>x%1&t<*Ǐi! cM~n?Rc7WjWJp)49}ḓv!R($cC8Mn[6P8qʲLӴrP3EA%ƇG|k~D!S`f 0x<+6ۭW`N 9sQZ뫫g]WUE)9zu8TRI &{kc4\ Yv} q18>_XE^K8m><<'?aTJb~MaC'YBJdb}op[gP<4iι4MiRRc}ߧic;7 PƂwi:3 CY`-jKtZi09p/=9WJ-kc Ak\EQ6fZX,#aeQ4M޹i\pimb Z&30?c0 Y lyfE^/<͏4T4 ! syOG?Ch"giCs>Jhri6ewJ7},M,i,ONֻnZ_\w(WV sO??~w?#=I80B&?C?SJ3ߎAJ`R)JBHk}bc Ak=&Ag *tB,hVy>&bs!B!Z 2zPIw6xcJZ g#c>@M(*.I!xD B%q*sk]s^c<Ƅ9g!48gVkED`!pΝXu.Im :C t Y! c0Fs:aa|W.UUi>0# eY͛7E Q<&Dƶuiw!D%R Q20DwG Jy OC$El1!4:ȹ`Y#Y뺶} ~$IeJ)ĬA(XYg4I(!SkmlF?_UEC$ NmiX%୅S$HT'xNw SQhsYo6j}ss A'`i[rbQupw΢,^|!{m a~Ty?pޟl΅PJyƄ4mJ2 PCccC8B8NH!J%!1N)qayk0.5xf(.F  %ZCA)c,u均c/e7$K+yl&Lp3u]H) >0F?Op( (RqVJ%I"ow!R0B IDAT|o˗/C^79zN'in[hu==sp1Mqa!ͧ,Sj6X5|v;MS^JHJz@k#%zR!o^,K%${=<݌1ŢK)%(>}>8/ ֢$ bM߶=!@'kJ38W?"փP;Dh1N2*LyQei춻,^|E1l6(!B)_ŋ!ͬp4O4IMӔe(jIOi&IQB6m X;#%u!Ţ8nxu2Qv\.kҳSfXc%0އvC(O{[k#FgggKDqss3 TPYE!}cyPJqeB< fMӄs>O3( )$&+)//bzx|x1ҪONb*4-& %:Cs(Bk1qG_^>C)%B,poR2ƴvjQ}ZUgϞv;L'bقOt;1Ʀk2Ƅc'R1Q~h JSem1FVJ_m{}}=c˗!ci_AknC}ӧOm?Vgm>{1X,ii?p9@B80KS?rw0ƜS{Y ! B{O{eEB1+ bhuE1È1>:;"Bf Kq#$9("q^scSc̑z:_"|v*qcDYQ.2y׳1&YFCr4`HuƘ5@>#Ȳς" |i`I$x&hߥ!1Y]Qw H444MB4$V/aLBJ!,ieL@i1<rBlB4b QV%p8@Ƹi<[k/0] qǠ{%T)Roڞ4lBƔ2P?!,RJF9K81XTY F$9: BIqZypzm"; 9oaZdY-` `+jnBX.}'*UbYk0A 1:gE=MS1j^w bV,qZ:޽0 1& >0N1mۮmcz,noo g t`q51c! IbV54,P\iQmӛ[B A)QIgPp;ks:էO]-i 49뫺Ldq,W_}{v+ŋw<_'r"c l2Ӭ !$zO(!r<{g>}m!ƺ>~ׯ몆}~(PJ wἃ\J㳌3M&!xw4I1%^r}>ơi1FJq:4D%MgϮnnn waX,BvրM<%D qvv܅6mþ1Qryi}E( _)e@lL<"}@Oa4S34MsD(ONB mg!JWnﭳ )8`bFOKdێ6azC,)!OWQD×\L|v~1v {Mp !P!%ew(aCeY㘦__|I(NNOi믫,ig}[c!9U]~˪ԳNϺZklu<'I⽛!t{wW9&x<χa\8ky)@(BiZ-$-BJasB‡^t];ZQk<103f:1)!xZYcEQdy^u}eYJiy v~Z&i1a1LCLZϛZ{^BMiBXJRXEX,sιzҩʢ\,*IQCߟ]>ZK)uκ$Qrη]gD%d~vuzQ?au$WWWP@qv<7m+osno$i7~S6}߿~i/b 1^^^'y9mγZwہ c+%"O VPC ^(~,W sgcDAȻWWW![g->B~#Kb ZkP.#%(SJxDcyauroXqT@}dG>Ρ P5729 | OcQ@YSہR "<|c.x50B&WR:og i{|*0F0J 1x] JBH",~} >J7O4QR 8؞jC,ˮ=(c]Ee)%m 0Mkaڶz=swh1o'j1^0f=`SVX=N3t&2"}c[̲s ;(E4MsaBS00cl&3vL QiRUTkG)izAH69BT,.nd$MqJy "8<)!I,TP2FiDz7M34P%Fi AfueijRJ1QJY*^B)cZ\ZM ·ʢX1zM:{ubkۻyONVz8Bs1 i9箯w]QTR)9t軼(YUUm6;pE{Fy9c8aTi.\-!D_hi !uj=:cRfRy~s{ٙq'E&Ǐ.]^^^{3dYxxHeeLAcH)aɲ97R*Q 1l>sʩsnXдgp9(e4ZzZ4I[オ1hz}<L0 rWiB .s(g64cwiUb0ڌW_!_.޿{0xv1 c< 1R4NJJ{,$IWJ1%2qr (?|$ɫ5wM3f8eU]9X%۷8eY&Ά?}G4M>99?~G1vp?Mٳg0DZ,۶#vo\ʆqz.\) gyٶ-FQJ1t4ruRz _|9 D>@?ѳwȩ$IB8"cty!`ްnUX.έ4imuoxXԋ%}wߵM"XbXEQV;HA;L c0 YQ],1Ʈ 4u!0V^^^r@~Gk<<"0MӄNOO.1e khGI%lB0u?@ x#c~'OБ1ZYI@q p$l"!"$gJShmѠI1B`ÅX!r! 1+(Pa>Rʜs;!"F1ѹs9Jw> ` )X aTI,U V)>S1CD1c` R J +1sXUU c*eI)R!Fzą J&/_;/OӬLJG Uu}sC(V嗏!a))!#D[,?{W!v[c|/M2Zʢ8??oVI9V[emn*eDk=0Խ T2MSs)`yw2 UHj3`1B$IrZ.,RNh# )ȳL !$@QRUUUUU]uE )<"D)s΂ XUjZ@e 8ZWU;=9!TUq//N,M ?J(eB*eF(RJRꨮ!P,!G ug B<10 J%y|8 B ayYɟ,}!$ h`Zϳ֗u]?<><8q컾iya@1.,aPJ1ΤyBiȳ,!s!e1QqK :{b5Bam1u89=S0 1DLC2뺦yq~q^Uq?Ji] NJ9np`!dٳ X,z">Ӭnm}wǏq8.:( 1)CQi|}kLJ)hU>m0$xYYke8p!aT|}}}yq~yqyr8ف* IDAT|g94Ͳӓ3fʊP_Y%=<ʲ<TV%4\Kq1oPJ)k0ͦ,}PJI%su]w] ,qP@5WՕ"ƺax0 ֎H]Wy^B>==a !Bu]c=,% ɦaߏav?1>?kT΋]##|Pn†7FL)(rB{"BM!B˜@\!g-0;"s>N,%T0l)Q909g6Ң(C(EqqqC$2s~vu1zQWE^4]7NR儣SA>c N)!bCmpDQWWW.%$2jk3ֶ d}8vy98!~ȲzL^ 7ZϘL``;$RFP 9pF019H}oY&8Lk `tvZ%q{'6RLH~&hQyQz~OʹO4M8#xZcD@Ck%)6K3`㪪V/@G3rmft%z,ϳ{wpzzzw˜7ov[ a(-jjQ{%dUae)EK9CViR¼ÎR5w~Z&R{lf["|GJ)ed' 40VI>}ʋ<ĸl1!;F)L*wUu}{jXZk켪ii.4B&~(|0#t{wWaNO1!aK15eijyz^qB4s>e~J=8`1B^42MB,}^r8M\ \ YSJy猵]ם_^\\\\o$J׿bWE(]<gy]W4v,\:IS`Bg7RJJ)ЏskRӴZ.oooa9'~W!fY,˔߾/8T)!y9GRONNnnqg>}...1F!nso6ɉ` caaβl\j=eOoWbLlŢS [0CR)JqYl1}KS%BӶ8,˼ =Zey}97`i >:疫EQ lX>~8>'4 ܔE^*Bt{m 1 EiI^12FaV ]&؝1dYR~i㓽s,tX}*Q@ Ƙ2꺮kY# /RB% Bi4y#ˆ1zh܄i)z!hVy(vZZϐl/W >(L)ѳv΅x ,;74M4M4u:7c 2_."Z *wy_V5iqC PQEYEQE? (FJ(QkZ.g~򓟼}b'wi<Ҳ,0΄s!61(=Y <}?L#,TH) bh 3c:ֆlia}Q,[ѡiq!p.ത+)4~NC0B^9%iB km{h_ޫɲ,xs}2]U]nfr@$uAb` fUw̼i?~m M3#EAD?td11Bꅨ*3Ex3ZSRy1mڪ( ]6ʢHalUZ[cZιf:F#K:3V;1R*a&Oʽ7 4J}?L/e_u]!(iyűsT]UBR&GsvJ1!m1&B 52ڶQ:BEQc/mZuUm[qA:1D.p: (4J !ƣQǢRqE>RS-^ttFӽf#znhf^WUU%j`<)c7󛺩///jeonoonѯB9==ڦ)J 5)%Y~rY OBBemMӓyeIzquQ+Fe@(?t6[.Z0xi]0f3 NOY!߼yaUZJi$B L KFh MfJumfzGY)ajӵIJ_Hdq)]]wp8RVU54m꺮`@RfYHYYk8NXK M41:"y n7777]%I\.hia F[k!p=0]qKE8Jf^uLUUuwh&)eu]o6!缮+Xq)%ci! _?] !WXe!*kX] wp7FC *"[9Щ䍂aR;!4@T.1%a송a;uB~a ιgi$R몪q80!eUB*cy?WZkF(cL ЛLFQ+(!X|4u1. &U5Ti%> ˙G׫5˥*4 ! -XkLDmY@ǀZ6uX,h8cq?,;99BsbڎR'?c]EA6<& na`2ڶJ:U犉<ϟ?͛i8Q1Ƣ0r9!ʱwьP6BJ< ox6~Yk@Z0qR=ch4ߝzA˲`B޾}k,?~a^mSGq|||ڕ8}5mljzwoE-D铧- *MӋ !oߵ] (~Ium۶xcZGGG`o\"2:L˥Fb4/ߺ%O}7Uv6I?TB^܌cJ0=?<ϋ¨뺎wY:oaKvl\.[vIٻLb7'~vm+8f]׹.KڮR 8upz0B{!} k-mMmGaF!IŠL&2۷o5p~~AD)h4$ (8x,I˲<<9N"t֙Cn\`P1f00ƆPIl0LksNJO< o:~mp*)UUizvۛ[Ȳlcr|zTuqn,>y1'߸hyF&cx:C_ʘ*ɐ$ FTiEz^#`RnU%¨:ynwxx ƫQ﷛1YE3Z#H1u]q8NE!.v;9$"mtVI r$5j3\kYGb~/zt{~u 08J(A+ƣ1cϿhv~58VJQBU}Ƽ=1/ ."EƁ1fdYF1Z,\Ȫn #4NOBP)ryEXUJ}kB5M.9b3Hi.ǹ}격Bi%sk1nj0:88eYQmFa$$eYskR>z ެ7ILkݴ5!rvvv 1(MRFRr}}3pחy!pX] BeI0Z`}v!6us~q0x^]Ai];zzzlơuF[kGiy( vnykm RwJQܶ-,0(jfƁL<*1FxwwaeeQB)Qk(!4Mv2JyqUiNxD }_H6`/#v[gYY׷f:3ڮ|~۷o0Jk$yG78v{{~xy|t>\[Sh4RfYV7UP|`R6rzˋf6VqBsãuw T2ISzJGqe9@Iu5-KDq{ lo}?mo۶xn=Ah8 Swt}Tw1fkMj<8#\ͯ%t֣t<_^ڶmt^}߇4]U~|7o|RP[X1ƔTBE!̒t۹P']y{P}h<E!zam"<}Bxu5Ox8nnoVz HNOO1Fi\_/b0iZE>a6;$y'Bl9>˪(Q^e(./.Ҕb-B(I4>'#FJr``11dYJ(JQ!ƘPJzQ&QEiqRjݬ~ IDATQ#yUWZ sK%:"!BZ1=@>| >¨u4z c)rZfSBhJ匱??mvbWmAeUYa)"X;N(z346kBHJIJޞ#>uE((3*BJ\ {˪MgG8Ta(̳w|WnRށ(8%u':TQB1X$MӄQ&#}?2(QX)&i NNNe;daYA4M-8˲}GZq!@t Rͯ=ƫՊ2Z7uj3L4=vk!dsA"&1PIq/˲BEn8+Q>{sl` q0'OeQBE/nnc'ǣѨ*ǏR'''a]]^-WKBHrfYa}`XH)4 nnnn޿5zzxxxٳhզ{)/_~I,㲟̦͖sR;zSB1ITu]׵5n5lI(ln:90zs{h.0 )ꪗ2Imb 3 rN'Т,뭐2 奐7wwQ`lΝ+eor]edM>YR>K8qG'Gq ?wFau3v$I$&;uFwGc@g{{{.нnSEvY_ͯo_y{nDͯ/&N46mx,<8B\^^VUjViZk?~|{{իrM){˷o߭כ 8>쳟g٬ﻶm\=^4yC*bڕZy~8Y6yQb0:G'3B?]@~%!QBv+@cn7 q_'hq$=`Įq>c܁O7o#SJ)E+)?@ 0cbiۦ(릩B0u=;Cj}ck{BJk!0ƄJ91vő(zwx)eeYڦquݔe妕1=1 =q觓 PIeֺkKd a2M&* :λPRIs`-xKm0}Uu]Y`VmieV. @8[ D#9y)ڶm;kW63eYeh4nZ:aq}sݻEYBȏ.!TԢ);ʤB yM&x<ay({۶iڎs'Ft] .8ꪬ*$2.Lk1/`0 <jJO~eY!aKk Wjm[Ǐ{.V(B!_~UGGͺJB5<ϓ$a!<>:VJ)KFg` ?,]Qe1bn?btw;`TbWno02M^__Yk˯;~v۽߼yq?( KNk1u\^^BmumvN9DM/DQI}?#7Lةh!8!A-yCw& zvWx2vlJ0Y.w7MQL6'I,ߟz*?߾}9O@0OF'{)D(2_<qrrD5֬kg tm)ˢReYywu]7Vv50QJݮiKB" ,{vTy۶P!v 0 4?BP|~߾}6Iz~|>_u]ӧOáhm,4 9Rk˪%a4 4Xk9on!ƚo}wJ_~jnwͶm(...>ӧ*Bp'/4=99yh4ˆsJo6uF#AQ!HFkt}Gy+d%2 !01ϵ.&% CiVk!'Ap}=w.p:n\__/zR!0_xL <4]UW0˲rYWAhվ}hKuFC`t,H$s?jTo@WkZ0c#y VHn@)ţ ͏_#s"T !t'B_;"tw37xp8 `!\uYUǍMXa /2FmQwiʪnڎ2Z(RR0\{`;%sic$~cRX.yB$ R)u۶iZkvZ#TJWAo۾˲PJA}!Z(;0 & &x<ƣVAΦn$\ %8zcGRWWw!Z@]m4mכnfEQIc!MۊLjJ6۵|EE\.Vj`I7ۛ(rX=;{4 v5z#g ,Ks75a΃unz;N0H„0 TEYH!8ֶM%d9<::8܇u(ڼxiEQ񾧔x Rc$f<3a8|J)aUUw]{rrT'<988@%qo(R ǣ.s T2̘>Bu]qo߽/,]R'Ϟx0 ,Ϧ/?9;;Kd0ʒu&iD5\)1'w,|oFqˋ˲:s|rG19o[f8emy5u=ј4M0BDV%yan?z ͚`b)v;Bغgl6M&,h8&IkcMB]Qz۶FYfUUS$N,D/ʪ|Xւ aYӴkQǃ|PT2J)%@l4J`l2~y^Yceڥ.Uq> ]8LFycƣq'iãc)??=aURӧO<5m^Q-v~sm54uy,躶jB_Wu$I5eU5 @B`4`ȳRg?Cx\"ԛfB Dad[,^z]W58#}_7o_~gnYGa%qŗ_&i&VH'=8=I)Bߧay C1F _iZB n||~qy+cRЭn ccls6QwE ciڶm>}AUUyviZlۺ͘!RB2J>zLEQq`˲pl۶}ueY&ϟCh\.ww|>Fwwjn"u08}BHQ=PJI)!s*?FwK1v&wmO6ߏU Rt! ;L0!ƌP?`!ӏ[('Bt}$BZB p'wpWvt0 !tw1$0>>zXY?>?} #c#3}?`aeu]u B0(J-ei l:p%Vk$I1`yJk)QJ)ݠ.<ϛNӴx#b R֪xu_|y/_@=Q6kpi\ )!blY^]p1v R!$sLH ljjLժi[)?4LƓQUZ> VꝛBRXlĔakQdJU8}DŽPJ}f!|? ]QMWOY_1FIEl8,˦ߧj)ٌ`?8y ۷B`0Nf1akqoޛYc.hð*l:#EruVJd4Z]Rh,3ƄQ0Nth7,I4_~~eU]' ծFY/뺬A~yy+aYK!QY''UUw1B~f̲(Q1ooo[kQR5RaB/{.f* BRk$qwN+^˲DO'W_L\\1 m,BڦVRq1"hc}w} {bLdXJ gggg8^zUu>)g$;?E>}7m+ &YUUhJm1joooXDItQ4"P)-h1Qu}z7/.. UZscLDiZkۭ[y#gύB\e۶ժx7o!ʘ d#|?缝L&庪*cl6 X:$n!z(EYLskd2wQJjmgʢrQBۦJ%,\19oe!YaJNT//N08''΁T'eG qV?=O D!0DcSkcn׉!0`FIyIDZkX|lNa7;c(a įc(!kcR #1!Ak  [}Z-҉`!c2a+c$?qvW|x:MXu4}\HBi\Ve/$zLa"ʪZx<bWH!y/؉"Q0, " m;!B@KP'/;q!mӶʑU8@(NƘ@@)-RI(ILkeL)q~ٔef@%I)84Iw! %:.0!bֽ1ZvVU|4M׵]/Y Fv(J0|J8JX` D@F˲,I1y( |a׵8gg{_!ڦY.Wc뼛JvSUR=q(βvg !l`0xc1</K9n߃3a̲A+-WIf郇'1?\,^ڎ-$uݼ(ּntږm7u_xlZ!LAiUUeU⸮y߷{d2ull ,n;eRjRnk1&i(!}^YVa8β4݌4u]K./~,Ͷw xHI Ci*fiJBdPBڝ6l<ɳ,⃃}ܓ$ ܸ `41A= ~?"V܊R8`B^j%,㺎~N'᰿i7!1hv:mfi_Uy''geY4f4r]jEqd0՚L&iPJ N[&I(5ʴ,q(EYLXxާ/_Fa{P04nc4@,Mt6]%)o8q]dYAGU lnHRSgYvrl aUUI00eYjx=_,Nq\h|;aEaaYf4۔ҋsuoܼͦ7i(@c)oeۃ~#!v YRȊ,˦ ?qq37EQ>モREQǛgg=W_ ->? nq&Һv0Z.N4yY~ɧTHgY~yy5˲97 ~UW! B>w;ַ͒4˲{{o3MO>>hZzJ\k ,Xβlkkk\~$ {o޼1 ~a1f|0q0TJz=M힞N&<ϣ("p;NDJV ?99x%888RތFiQJRFb YÇ.iSlRa*m2IewMeYw-1q)1BJ q}YVUE4!paoZkHlo}nC ZVpaJ/V}ǫ* N@),^W%L%jFQr΋p$eQ@i gZ.aa[!}Rwݲ,eJ)%̠!6r&Y qskݭ+lXq]⪪'@͘`byƓǏONNRIZze[.&=j+=X3ƚFI& 'ͦ2fZ#(˪Jj9 BZ ,066NjE?'''uAZU5a]qܾ&OGaPARd4nc[L6(r0X,Ʉ1f;h0R=O'/VVJF㪪*oeBknwde;ht !4F ׮^I,;'IUUY#Do,+]=+l'eZ- ...\1 ÁUsB|>3-4lDI%mjx\,zpcH0N&B 43( jCA)RjP B8rDpDpXF>B4|A1REABMg,MF{Chggg8j[giB__~ QƬL\[V竗/_j߽{/4v͍Ap8 eUUUcYh4 tnGYQv!TU5! ?1$Iӈ( 1%ZV]UM *|fYez[YBd"qod2nw:[WWWRZk8oF1BhPJB;;;vLׯ^-VU]UuyI ! ?<<O'e^t}JbVO&{Zp8~:[gg`~1X{>9;ZO&/}WWϞ=tZS0>'YVPJOOχMfxjݞR!4*^T%xA {GIrYuYBx*xIQÇCƌ/`0woիWϟwٶgE.u]\-qUUZ!ug)5RB)E P4nnnJ)?T^{t%Yvo'PJ)$[)"f0HlΪntCgPڦq@5?%`P⦆Z[[c .iu q\GDɘ @)6M|"u]SJB$2( I%, N+&!st cJ1XeYJ(U1&BLZII.aeB4 ׵-+=ɲLI4] mxBxnԩB*$ҔbܲBPRJ!8Xk\U0(k1 ݎ?x]`$2s&d8RgϞ?/²lbz97M .m6 J$I?A 6+)8E˺c Y,{r\,R ) eLMfb3h0i$B157lۺt?XJeٓ'Ol> 0M|$b{srB˗J.=-10" }ιP1ƫF)<^o0L0/~yحViO޽{zi*/99i󣣣*?888ׯo ÇyWv]n7EUUWWהpJ,?|rvv$;0 'Ye0(!q9L /i !wwwlI$I(j,>yv !ai22Lu:4 WWW4N +oXLIi?ǔRu 8>>. <Ҷ۶˺$ 1cvn (޽ ðib(¶-JMno>ahێgg3Fͨp3N#MFNgwoo6r =I{YKj2 1r>\:BG_lm,KjWWNs4˗/Y}8q]޽{VW߾?NUUyy^R$\<=)]2|"lbs2}OJ)(0ֆaʎ1:(0K 'Rnh=PUoYy?QJe c0<ϋ,˺DeU!DsriBF+qcJ3@pݕ 眙&]ތnB4TcqR)Pƅ95E!d뺪B*7BHBHW2?|/F]_u,KF (,˲!Z-aEafY]UY e^RJHLb 3׊4M jB\ρjZF6Mc clRS*UJ(fJiu1B5!qȳȻ9oHӴi8ceRuR*àB*bp( SJ0JӔsro+RKN)J',q;???Lf_5Ji^ 4*rPp1֎4NgW0666VNw:14R+Z)!XY;nv/GyhPRRIǶ)&B6sڊN <={8۷G|>/V+6MsX3ycY4Mm^r|﹫4-˂eABeY¢wuut<8\y^yEYr]'/?ZeYF <(A(`z?359o{η~ʲ<^ǹ x !l׵m[p]L!3MP$<2UUdq!AeM4YI)adaYև&Klbj"NOO(䓲4ϧMEgaf(0־LBh˥MYƫN+(W6s IDATLtss?rt/Z-W|gggci:nV+r{{N!jmmmuvq~tt2M:=OY5cR4Ţ*KUv,( BO>3j-lo޼ZonnOr6Eu<wG h8=vDJ"4BNAV r4Xo%̲THom&ZpI Z×AfE~||MYRǶ=Yq4F뿮.g)Bk9iܤy'}cc>~4^ϱ0 ~4b1RJ7b-BJ+TB9!q)QZ^=j=,)u]MQ$ϋ,z\^^B*ivݫ8naBz^۵L+2fY@ve}mۖH)aQt:%PN&ֺ+ku>J4]!w|7 ZułR"jw:777J)f2=}R*IA>0ϗEQ 9J?QaWU5ʢ a/ϋ( :N+JiqC޽ \- 7Ms2O{6zg?hggݻws6I)?9/K!8~WK4ܲ,)#06X TJZ6<6vbnv(nOB@Y##5%B%1"c4Z#FZkmh911 C)m˲, cw F4L&u]lZQi3aNڎE1,V !,B1W l!\x<.˒&ϳlVWiЬڶ]9BJ)U&yWutX I ЌC*1R!I?~7.nnnfrJBtԜ7k?Ya\MBŘeYa6c9sgZN~ma@\0/.,˦aXy8?xş{x:N 47 cXf|07a 3˲VF⋍FJ0 ǹm1N863d2Re9_nt3ٹZsHQ/xyyB=˲`jat:%v=Gy|ͶOR$YAGeuqc<) VV2yߏn- Ji_EZ-+AJ)`l6s/"jP0:4E#֓իW'>|gg>|xݷ|_~w~.BJ \亮ex|rrr~v&?SyeI<}4ozjZڶt6+)5MSlīj777`4M׍)L xX2fjf,NOO'>|/ !۷o9q>eێcYVfu$vvJebxmQ^mEX,iQYBlBIZ^qtt$"ǵGqÇ|\^__7McYvUUŋYeჇ^/i;;;{[WZ}2B [R2ڊTUJʺnZpTqem;[EdRJ{yyY%Xk,[5BmsU 7~dZVUwo{_Ӹ cm ( rnׯ-ƿVw(w݈ZV"|¯Y ܺqH /ݝ%Uz|o@Nw%cC&p@O[n"ò`nfBfTL@ a!tZUU:">Q5)XNm#DUU1q@cF1AX@E^% #0t0 ۶טhdeh1BB;kR,KbP?58iZ?0^N1ɲ,/P(4MSf6y =Hb.wO^k]L F0KUUap) P,,K`}I%TW`:N뺖H7MS!)<s0Fb}q~Eʺoo˚'?qMȡ@!0RkN]@cUJmmmuy\.Tj0^wrr"NJgCj;;= !DI-p'g (q"mqonȧi w|???BP جeu]@68VUJM&3(j3Mկ p]7I(A7VU fc!atBȄŪnsΥlVcL: ,{io,@8˵Z$YfY}ppom<}7 Ƹn !vw ϟ___ea%U40vu] Cq.lYi `,nw2t:4ٽ{Fe?h{'I>>2"̬HfV&mWZ32h LOOwuWUםe&vm%ꡭ&22}5m><8I( UUMZ꺮WVu{{|%|6\ɴ!tEQWOEQ HG/^ sNRZ:e,xI"i!IR{sEUy(B!tvpps{qq~ZF^wE!H"BH01&Ţj|w ɲu߇qMSU&Yݮjiz700L3inpEI8:<}pǢG4=99GFㅷp]=|gY+ Cql" Z޽{~b4?PUb6N6m06 F (I0=zTkԿFqqNQJce^lNNNܹ1ȶђ|4nK)5 !9Oa:(ʫWNOOݿw`YeYi!~mXve0ubU˲ Y?F,Q(0*+a˗UITe۲fY`0C5qZ<+aEQXy3$IdQޝΦ' ZVF.FZa?(K˲'Jb.WJ>G+~x- ^Nx%>u )AE|*$ LŲ/$8p%zeeY2^uB"%(ʲ`#A\h9Y^kr\5!t_VeQ4M!qEcm`j |j!)"YxbVdYVT1&!#\:8;9SL$qjUjvf0JӔeY/84sl6 !;*^/Nbh=`dYaK}ΞaJŶmq*JQxB<,{R C7 C50&l6PBp,<|Ɠ'O66jjJż"Dx2u]*sXRXYT$I}:q sY,SYe45]WE MӀ8]Tc u]q\jUl-IUUۮ֪fh_/` 0De8T*p%QpjbB̊i2+]0^GyҢ0 CU($Q̲:MO$MAd |4ƺatr$M$%ŲmZP],KQC_ ׯ/./I_>9) 8W(β,B]:]O!4˲m{<0|{vX4繐5t]e4 (TM (Uʢ֪C3HV>F{`0׺E.d8LyBH I:`$,/(۲,vY5Mh!QJB(Mӽ=@mA!,0 =Jhx cl:˲EIDZ5+*eY6 Q0,18UhKs8^} `Y$Q@"+dKyQСGcqDÜ31-s^ ,"^!5:fTu !V YUd%AFhՕw=:Xה`JEo\=|ser J)c%QJQYk*{,+"hMjBi!*eYbDYJ$MJ !"#?0EY*gHY%[y4/ !Q(K*&VɣG$I)8̈!"IU0H c뒬Eq cH";;!MUD`Q v=w 9iCɲ$`tűn4&DiI岪|uuy.($2T]+Yi@ 13İ&aѩ `E 4M5 h Lu44 sVEQ뺮B&Yc y#IVL0MSQJX XTwjFө:iH}xxxzzz^ggga(B$#~jiId:ϲ,˾xvC$뺦 WUy ˊyXv [S )!ضm<>߷i n;NnG40}~]\^5M"/_p{{h4F *yI 5ommڶMS4UDjA(Di@ (jjt:`BUU$$9 CHg܁? 0EEA I9P7 G< g뛫,lۮJExR)qlEQ(ͣ8LQ{ffGy; ˗}h4 0 #jXzТ(`t:N,3&wPޤ4_,eJaس4ߟ| h8E4 YZgQ[U4Mfe6EE?{,˲0 4-(̦w~o޾=;;\/Yi'q|hU,?$ + @TM,Nz7 8IJ,K]y~G,InUҌ1Jd,'iG, iE.l>p2K(r=/ Ɋ Q*Afs( BpQO&|ѣ^ouQ\]]N&㢠IV*f7 |I+33s$DxbXUUEUM, z( _ lAxv=w0p=o>Ng,onn,t:0X7g/i {wy)4ZfoG!Tի~m~=|6;:Gna_ض& ӳ[rdi<ќuMf BnAiV,+<χáa+,s]WgɄEx^Hf07/8G}V8:w駟j$eEˊ!笷iFQwT7D 4%ieYD1SYQ$Y D4Aln6LJżX?h4;;;5$AQ;vuks4MQ$74$I2uuUWhQV ^omJqnYV^(IcMQ8XY76TU ooo1-l8m'q$iOzmHQ<'lmmU  Ìx20KZqZkHF𿄥ۣ #/Ei%\uHX"Xp0AAaqxՖ^e؄V(} [}YЕ/W~2hŵXs#VpgVfK 5^̖4'#P{ON-I!u,C%؀T*AXR T0.B1~˓1&Bt]40 0t/ Â4Ͳ<[JK-(0DQX !DicAfIp ieI\QJR$Ift]4$I$YE \D]*4 YQA:EUuU+9:t4)B YUU2qBHZgFj!8N灵6@i `Fހk-V04~M:; Ar$|veE1^P))2SJ #ȃ/HʲDiD!c`dt4g4}UnqeYFsq$Q :ga{z@$"˄G3sA^SUjf3yNeY?kθ빌8N(tg:ijU*]ٟmo0dIظёo޼)KzNzzxsgy<<:<ejf9-?ժqUѲ,͛7F0Ǐ `647+fQUGg)nii1V`h߿O>4cxb>xV:#+eYe0FQYFiyKS m 8iDZm;a0Ϸ6`,#HMܼшٳPX6`<ϧө뺞t:NszzWWv4͛<0N~iǀ^jXz;;;#>|p41f빌qAásFEDDZ̊i~ @D#.bYy^`Ls8/A(ʫ^1beO] g`ZaD/!c`eVM}a%FĀ@qi 7!X݅@#Ckݒ_dhk*"Iጕ%Kϥ(Z %>h~y !`pRDQR"TI6t%EQj*4hY,eS AE.˂LQdMS%Q Kj^7,/ݴA#%IX^Uբ(`˶2&atIԶ*QH$/ EI4y;h8ciJ00="iYiK19<>ŋ(rJwwv< H$V2`e) L0i{Z!q hE9R9笤efQrIƄi6H[__A `n?㥧J.c%"h Ⱦ,K W*_87 sFy9ri$)lWj?(J̑(3xAcqU0vjy~uuX,9˲zJ҂IxA`yO?w vgh8Ͳ`hێiYdJ)Nߝ mggw> $l1h==0] #TUyyK88I% nÎa6BDzF)u=/IcYV , *X)(BRi۞LeY`>giYu= h.\ΐHJ([tU*ns||Oյvh_:~?KSEӓ4+ >Ph6gAӧa(rϞ>=88Nӧ{{{v3ܥHxX|1V6hxq\QE9~s$%ϳ,˪j(b(m/oo m'RA$Zi&犢< nmGU癢(ycevMb%+ժ38RZ|p I ÅXf( Mu}ccuݿᇗ,WսF奪Yj흗/d4z%cNqwW_Vv1Eq$VYNgӧOOߞy{}}[g>8aOgwa]t6G~ׯ_zFU2vdY*V+Jvvvvs}ׯ{v]Ajَe!- ;$)MܬX={O>{ u4-]SmˎM"+RB }x24/K^\;wml5/./}?@Yu}}q^Osh6Ȳl<eblg}^_z(4L'9~0JCS}qzV9O =b s.Btɂ 1?`5(qY,a"YA 9炀UUx|(JcGεCUx֖%W%ˠX2dY՜NaQ.r\ЕRcc&KE,v]Q$pN \4-MSJ.b1Ʀ+$I4MWMӲ4ǢqEL8%+ʲ$IŲd!BHRԢ(òLhaN38=ߓ]%8'y 0E%{m9njER?Y IDATJ%QkiI>˲GD8{xUI4W539GNg(t:U4*;s{{ v˗[ٳgWWWWB: mA? pؚ9ymHnԪW׾>ov2pw }cc<]MF6s.`d&4\$EaCOz6}nfXT'O?#܏@gld*Ѝℹf`_~;u0f"NZ8u$IJ< â^`J$ $Iv7(;̀Y !੟ev( Q[$(Z v*0+0ƊA{/c va=n#@q $ .xX,@EQ(/ht:B7NuUe V9V!}aVs94vae!i !$(|:Fe6y^=/ci _~WͦyZ(JBۛ`IQJekkKՋ^7)z^?|Yp8E%Y: ,q "NSQs P+%k xɚ&pϋHӴR˱7V_u0 y,u]oGGGIJMSyp8>=}#q߇b3u߾=Fo~J /UGW{clf9A\ހg]p,]oЌOT!ɓ'mZHWWW~BTUh4TU_t:FyvvV,˛[Qonbs.4::-a,icܴB Dώ㜜hVl8yzpprw{eY$0l>w$IR<===??=<[$:irY)il\ a}DYAEi6v읦iAmj y2E*J^]]izr:Y+òv>BH+VH@Ee&(EQ `A%UUApѣ1TQNF `^>oH ^,MLRZrR$; qr~~h.!"Ƹ}_Rd47|#`1Mӌ`寧*̎9X]8^b|ެEgE(ݍ(ij`0PuwͦiQCH!Jn[r9fywwwvvzZݻwmÞ-BĖYsqqj8/IV?96M7sݿf4h4(v7~_i.IRӑ$aggg:`prrX,t]Y^.c,hY,fz=ShB% bƒ$EQb6lnn2.//(JQ0Q ,` M:`An j&iR1 EQ(0gQeYV9@?o++d%]?&"A!EAV7D0c( l ,lD i)V,Qi=X?aTX**V q׏X"ߙ9^Q'dl[`G 6A_Ѓ+f`k&Ѐp#s.ˢ(y^PJ%Iݰ豢(*[xe8N&b1KQ//40IBDX?\AȠŒ"8y[hʲd:a 1"iZeQ$Η%BY˶, >$JZ`㱦ꊦx]z2Ƃ8*RIJ,7ZfW5M,BHV ȑ$ J܊*y 4/$4 Kc#cE#`"? 8AlI4M|]jB !`%s( JRmG>x;P=q@(7oХ&5Cj999r Àx~_ۯ^@(Ǔ.E4u,koo&N2#D 4NZW(j<~xfS,noo @`j?Pܔ+XUU Bةke3xO[9¼3Vw1Lj!fcDJVu Ba(zn7$ VdXaۿOeeCH MRͩ`-ϰZ1E"2t((N(=X,QU~|>_,>n6fٻwʒ"vvv*ior677%QEGU.c1KYV1K$ ZX!I~kdiꗗqijY__EQ./?Cv Jb˲EEq{۟L&~ٴj1Vy.Aٳg !~77wz1A*jfVkṔ??}׿~?XWׯ_Eau<+?b16 O^bHPaR)$$ZF#q`jhf@'|4I$Ivww -nwSӴ_?XqHӴR1;~+jpkwww8N&t I>>~ dYj>99Jc )j$IZnITQe*SUUsz0OUk'iz}}繩{{;%U6tR1q<Zmmmz,K˲PUnF<ʲ ðlF# |(j5xCxx#0˕ IJlY˒:; _f3E9:`08 aGK(,y4MC%۶ɓDŽͭ 0i@ -MEM< 0#(>}A5F1ww8?xaEGGLQd]~mT`x5M+|?@0s!0**#MQj`eӼ( ô,۾~Sgۖ($ ƈ!^!ID,gmjUyNLUvʻZ"0F,/^Vk?ׯEQ<<(Ot 0I߾}_zmYQ(KYL4tͶ*e!˂rnn4oZ;;;Q7-)w I2;fgyQwcǶj}mUzEF#8{H+M 2UŪ"Y,NS=Oo6=34㐽3$Ud ʵAD,d 謭ݽ} n躮}&v~&IŅɓo߾=::>?8;57κt:heeEWՍlr=Vuݗ^u:r|FQժfu;jY$j J9xh4*iZ-9#Iҍ78h4krYV4Mfi+Itmm-Zv,777jZպ, 0Acb>q[ TUl6,j1C0i"eY~vvF㸓d߶KRJ:v'3hd۶iB4MBq%eK:\bQG1YuH4(E4 ۶g!DńbƂ0sJhqIe/c 0I˲Le)MSaiNGRdUU+++ $I1\4͖BЧc MJ)9* 0tJ=j@ S iZB)M̶˗l#t]/ '''_|Eߏx0q ~xp GQ8&x:ঔZF#4MsGqzvqvv69BH40}_TuYizrr2߿ g}v:lEVUSqu=YLEj7nܸ}{^߼yɓ'T,aL&R5UmMl>88x%ӨZkgOwvMˈ0bRxvqb)!&$P(wǽvO>秧n|W37M3  Z?}/ #˲ ˲`.`l6zFpTUvj>,I.Yϣ(di-:At~{fža+5R*$I#!X\9]MӀgӶZ^,=jB)se IDATZM a^\^^W*,yox|޿`'O$Y2 EN4 hy΄tt0BUg@b)BG*( i-O,˖emo !$YZYYWI~ŋ|7N(fkݻw,ZVTj`.afxOSot:W^={X,Z- /^KD3ZG,b)ׯ_fw+JELU۷o !YqFV}gJ[0lsH 095ٳgYzg-zYe|֍R鬁چYz=aDp8zr0<_~ɓjBBw~[NN!J%˲jŋn;N/..yv!FﻎyXUUY?Ccww4ͲTӴRt||3d2q]W}xzARJ J0]כΧ4Il6L1[ca5p̋> ZU5XCkfQiycoͲ4I4muu__޿8ť,Kgggӗ_N^OP嗿%0`YeZ{ns0r]͛d27uDTV:BO8Иāu߽xO60TUt:U ?~dY?7io,bDi)@acBH TY0XEC][rpf ORJr~HW6W,^25#S`i-Z\,* «Bhá.5] q(Zk;^8u]}ٔ8~@xZ&h^~eBu~e'W(029 HclyzsђuUBTUF=%|Ǡ|?$I8eI*Jj]WeY.E3Q[VUU"2\,(r2`8nykA]( b\QB,NdY5R*r1+4M x:a:L;3I4Bn nwIY~i rUcB(!TQSׄY`PEQ$a{Q%fx7Hp-? {jY6S0\[[K qpp0,o#JihA"9zBTM!Carm$I҅\ea$SpɊ?,2qF c'HDsomm79==.TeH۷n߾K0I$Q̥ͽ7,?믿Kdr\v!t޼y9Y0ƀ0TU}d2hoF 0ƚfxdl:JQV{.8JpKe1tض#N)>VEN%GmɨYaoYh4rni!(jL*c`q!2IBJBd \__o[XVAeHRU~az'dY !(!^|BUUΙ B \(XAϲ\.3'i\4MYH )l:.0Ǝ>-f\芎…y12yN![97 `6ް$qr<"AiԒ$<{4%-ɲjTTB(8rQc3y,K,Yu]_ 8'Yas Ass7`D!erCp5UAL)yn6B(IW dCpa\alƘȅBRZ׫ժib QEQj_ekHrem6θmۜ0 㩬*03<>sIjssӧ޽L&ФT* jY$*ey[0|(뵚eYis`9eٻwoL 94 CEQaD,$IvoPJD!W<8) bq6 !ګxy^w޵m;BUUKRR  8T0#ض O{`TT, <4 \D\(!$IRB-\DEB8c4MMUpBg 'I Fc.BeR*oܸA)$U:/ H RNc̳,ݻw'''`$qT@k(߇]{:J%IZ7n޼Y, <Nsm! h6f3ꜝJ%θeYvD!rJp8}?Yug6OcPiVFCTι(B)RV (A2L9&﫭W^Dei h4e߼ysvzyp8i616ONL ÏNOOPJeEW*Iz$$Itӓ|id2)Z}4^ I$ŕYáȪ;f E777XVdJȊ"I2Ea8VDYA2( r`˄g B%Ie9Oӌ =b@ !k'$1h*a$`vqmJh/ױBxeYjF$Q/!8e}Yc#TU 8~׻lἎ "E-|?^`KF{k#-MS0@9BW@t4M(I4KS뺞eY:y,3! !0*y(J"B(4JhC3 M˄9!0Bb#A'UyB`BJieijZpi8#OFUUU,C1eY$۶β,KS/W*{s,D<`(U*$YjZfcUM,K< $ cBeY_?#(@5 bB {$xG)N8 #? <% j4<4IŎP$SbTYTUSU(<$2MUQ5-N,ϡ5 8<FQȊ&g}jw,DT$P, ۛ(zj^ EVk44\ @eEUs8nDؙϩx;\eI !+j54jcYcVWW0F_$Ѝ rS*~( 0MSTMk=00L0$N MF٬V{{7߿?R\ܽ{7Zvpp0A ^yl6f{q\YVnAizZSdlGG8k>Znllt:B7q|>,}~`YVXNB0TUsgN( X/JKVVjB8 BX}۽ͦp:RB8N4c0C("ϙ$.o:s 4w(.y`2mmmݿ?g6 ,z#!fݞ;W_~/Kۿf_ B Nl޿sƞe=uͲ=ε흝(J bB0-ӤZjEn$2]i4JD$=;;zHSTIZU/FY%Z=M( fQV= @<\=::j?#w-TUP(J4TEmuaPr˲\,tMS6e* +빎Aprzzrz6N^~=BBv{5Ϲ㸜窪AEL%ZbB)Z0,pyЬ;<4ZfXLsc}͙ϒ,qjzg] dY'1b0p5!h/DQdzM/ !)Nr!2,O|]-!@[zB`b\^`~>^%JR"84&3z! -0  _?0hWk,ˌ# Y-r,$G f&R =H9T\yМRDDYWjp1Ƌ2%(F!1#EIu]=σL !*g EQ Ä\wVB5ĩ +DQ14ՐJʗq,7p۠+Y*,5Ut4B ˲iBA4!iO&S ˲h44O&,MJ7oݫ39 N_*5]Ӵpva04- I*r,I뺂bH֭[YO8Ţi% !0  !dfe0 !RPBH( `kkKnЅ,I2c :ܔ-!˗4|' z9 cr:N u]!8@aX#HT*J1VT}޽W^Õxj:˹$I5I< 0Mt1uFaYI`2<{BIPsv<ϡ8N\MY6 !a4[M{w'I!7ؤnonnya裏(!eie)XQJƍ73 'ibYV~'kkk%{t:z3N߽{{v~s8;q<׍ { IDAT(8f|>WEU8NVV}|?PB@r:F,KEN0_Y9[]]=P(P $R4bFx<}Ν;z>$Iy/Lӂq7)0niݻw_|^0$V+v{5 Ç4MU("4f$?8NVWWkjZ}/!XR}0L|t4 sǡiV$,t]w4aeEOEO$gL\deY!X`D ˊeY$K"˪h*+@BȃJ$I 9g=NSƄ@8@ttH *aLUaNF`!J */>W3EӒ: Y aee9,:W!ײBK&] .Ȃ~ML8BTw ŬY[6MիW`GA:A,5MUU+$I V00 of0_.ۍF^o~}M77666y:`#Ec,cv?~wuM9l2(SE,d9glw%_{.4oίc0dG@ ^a7 !A$>c /0H@m n%^Dzj5}z-*K,Ҳ" 0K,"/yXe, < P%s1Ix_PʲRX, D1RR pe0 P" q ]Q!HUU]Ql6Vʌen8XR&:AgYOӔP.px,SdM$I& 7$.:*-(|2\k0W$I80 EQɇf_Z^Yx<VV41o<>>8D s$US>C!Imv7dwy%A$HcuUꙞ7nV1! _B )4V/t}U)4>3߇Y~`"os~g$ #9*Bs( CEsF! s8%$YeMS#DH\V+#J,S$q^kݍwǫ+wyWP:=4q9@L}fY'(!c\QI}l2Eޒ2 bf4u]GE^EdXyQynh& x뺄$I0/`xss`EEQ [mے<eY9L&WW@yB($-eYZmRwݛ4 LaD) O?}6 .//v[x|rzz>;;a&IEf]VKܱT*1Ơ 7Af͍,+5Ai@RR%nq0zL$I<χ!cΙE1,AmۄQ! ;',!z$i+ggg 6(u:n ,{n#덢(j*d#ȲkZ-L4Rzxp8QIZU*r(NcYnnnq&YQ vRpƂ08 .4Im5M rH1Vf,Kɲ11Möm۶DA% clZ(󛛛N2}rh4j6RJY,ATU<,y~{!s &ri2޿pE%BL"ϋ%,;!#B:\m{zgî;{p$T*ٶ%IBf]joIdY~^kJ˲|sxᚦV*}Q0e a|_e6]^^yAf Qfwgwޭk(2 ӧOt]wZ{mssmWJ0EaX ljLJI1tr~~~yy5}FWtppݷ4J^~l"$㫫+I߾}͛x<`g9[.-|~~y`0oonnl6n1l6t;;;;++MMӒ$Y[kiE.BvnkGZZ}W:tKfY;w(/on纮M&40`ǔi?fnZiR4)A: ɶmMƇ~0 `ɲ&NNN ^^.'d2! j0 5MjI@ %VYEC7RP9C,PbEEs(ϊ,ISB09; ,y 86!PV`a]@#^`B/# IDAT3l|B/BH Fd}l-؂7rxX-<yǗZveDZ18J\/;pq0j ^x \ A@J !i 0)I(4@PUEQdp}1F'(Av |r y;;0\Nak,IE%ILQFeY2 *4f'(m1RR1yTBXAÄIeYZJiDa(2lDI䜳Er`Ii1WDYs($ 빔Fs:340cЃDYM4MU C/-BҒ1$ <@(aeEgyc\A~Ǟ;9iEQa'AfE8j+a[EʳL ,+2ayW_-7_MY3Γ$EQQAa~qaDdE.L TlijZ `6EQA^g$*15q9ȇd jqg{',9qrjjQIZ8q<EEζHC׷wv...(~ Bu/ @ÀK((E隮kVO~ݛR$r`I W:u:m*y1{7\G,mmmrZ,^z5(o߾<0Ay.GR !⺞(zT2(-$m69(ts*x4ǣ$I\Ij/(e{8N$kkkVYoM$JRVUYU5h (qe(cqYw]7dd2QU((`Y kWSJ&T!5YjlF|QF8΋"IbǙ=|F}M_uct0~ƶ$.Qy^BR$I4 u1GXQT($Hm4 vV2*x<ataAѣZ""bZ8qfUHxUUdY*渃a͕4MsQZz7o߾5 }?T$~޽v*+{,UF.^]]_}xHιix謯w>FU??8888<<{p>ۺsggwO߻_7jnYZ}`0t23m<}`+kYfYZ{ͦ( ii^.[,aHi( - ^yj[euBx<wfelnFqRTE#D(ROA꣪Z0U˫.;j0 q,di4Z;,EQ͛(gK=X(iJuY?V{]մ/bkί~듓ӓ;w6G̙uYfΎ0J=YsYTN眛Y*/Y~~~_v\' htzzvyyMT4ju}}lt]ÈsۋoߍN u0dža6&"ImY?{}}jcaOŅ,˕J0?޽{A߹Q۶}p("XN^ <"yy~fQQkq mOy?PU c8m+ӧO Cr`,I,K)o̲<$ +<'X򣣇{" )K [A!YQygYF\J G$j/p"]CErEL3EaŌaȏ8`c8- 2`Y=[n^bJ@ޮ"ZFl`0{ /^|rۃ G[t_jUFK?@d<,7M#J` l8PՁ |ؤI1bf8$1e&I<,/BnPfO,sapXvJ,Ae-gY8p#Y-ۼ>۽:D`9:Xz:P'I5GqB80ۃrE"w+ xSaia IV|0k`SyeDA 4€ty$Iy"*mhjZ4qC79@4-|E qcA$y8P,Yce٣G`Ś i$i~Dz$TQJi*20GjY&qZ,˾ 8c\.f"NI4ME\^ B\.K(5(buǓ,z>gYɲl68>_c OSq>dh4/΋LFaACAu]!dssSӴ,ϖ"Vn^(aWbj#(ժ$ɲ U犢fY8B Oјfзu];wY*y(eV?gDQr\\#QTr\Rfd2ё$I c3 ݻwzjޙͦXէ `8F3}˲"{`(Y]^^&ifF,˲_yq@|7o^zlll;L&7?88<8w`Çf$r0RUU "&=Ay^+ m­jq,Kr?qssZl4$Q HQ0L&`I}qq!B&~?MY666BWWW;;r9(FG={aesjӁ(J$eY fwpkuŋy~`YrY,fd2Af3QN'eEQd2eY(fYfۖiqSJ%Imj@ 8] 7le 'әeekY&A)#DET~1M/^=?GA@#ЭaL0#`0&Ƙs( B1& f_pGD0,M:F#'dY+/Nyt'fG-zQw>0 ]>$Guw"^<zK$̡B%IR A -~Z50TUKeY2)eӅmf`0O+,r4Ui6lF06Md:( L<4,rI]˲-.3Ìxp4e:hyRQdYF%YfI׵ivE$K$jE^wwfzvuul4*Jm8NElѨ^pr|"M'_UJd.a8LSFmُ aDy߿4%)g`b-Iwvw,lەA(2cLex:zuuh44]eB>qV{-TYX( $9>~_隦?K8GGGi4wR55/r渀 &o <+4H6v_[k]\\\\\@lME;3N1sƲrXai@]Txydz3L+j8R zgy2F%QK@p A\S(,alnv"U*e)"equWz$I8gi{o۶ j.d2M<!7޽"ljjsee2Pإ)Lv]؊SMSO$Y#DfEq(JdR[o5]===U5: 4\ӟ] ۽ h0i+H<+_)45 u=̱eYGGG}yA(d2|~oq=ŋ)ĖB Bh:udYSs*4aD8`>4zgxqLgQ88ٛ7o^^~믾N_|yvv6NL>:j4FRARE{{{wtлJtvvy08]wu]>sgnmnmmmR8O+$I㩦GG^zEilx\Yi 5jJf^u-3U`*Ea$(r6FȲd6cLQr `j?????Gi麮(A8x:O IQ䕕Aᇛ땕յd)x< =NOO X[[;>>>;{oYvRٿZ]]Y5Kn[29U[1gd9˲$8aX83QA$FMӖ`^^^nmm$,3JiP߇a(_u] PepKƫW( $WPJKңG>sB2 s.I ct8Ze:alvʥRF#D ØMƚa8yEaȲJ9LFqټ `6s,)vz],˝N0̲ZR\:)H`6C0< Z~g\^\yiEEQT2ʊ$Y,B>y$ cEQVWY$(I$wJ&DQ_<o2Mc~uuUkC#`&۶&=rT zz}:オJeuuRJ)O4) mEY__/WUUI$3ʊDQ lR)"0Aa"2&iz)eyoo1!o6=pfE ޽L&|yyyxx-1cleeEQN80 4GQV(L]+B(^wppi`deX6WWWa"2ƞ}ll˶o߾t:?zz<!up#3 p}ZL&W)6 M,cl6߾} zeXlfݽ$IsJ,IgϞ~ﭮb4jOO1KRiCs|2 û{ZJ{zk'|3H_Y}ŗF^jy^y^dp(⧟~f(*eqZk4<˒L0ƛ7o %?ZCD<|ɺYKzQ!\xuҖwh(Js2 DDx)K GF+L7r%_%; A彘'2 I MT^j-,#R*2/(F1· A$ GRa .Q C_O8Q>O$[2ILV&BE1u0,KeYbrP),Q%jQE^\DVmTERPbX(Q\xaJf e)1^0F1(*"{(,rJ4cPfYdyxa:nz0,K!DGۖ-榗ifyJ("h9,IEAY$QdIc@0"2 YfQBH^84(Ng!)!XdqaS zUu(dEtC@ e)b\Ixt6y($Yۮ!juݕ&hi$w6:/_It6s0yy~ɓӳJ$i <uh(׽^u-(gF8Z*o(]$M4](i|`$`0,rLUjjT*VuqqAa4E1ضzvV$I4iy1FUUd2q,Aj ,(PA ) & N?a"\u xu]ݚG<FoQ|}svs@a{@I5M{yE^T*9%I"<ۮix:sڳ&p0%F^+Du(dY65Ç`0€Jr\^mI1 884V321t!s_1bTE5]&,4+DeYŅ 0b%#KB`$ĸrbBO>zxBeyskqJ2nnn988o|oL&8I֮nzr*~0\RPUa(; t(cRRJ l@YDQٹM̭KA,kkkK>CKǏOBae`\\\i:"t:IFXQA$ˤvGQl"d -B+??9aaBG],")`w9kE0S۶?W_}SA4VR}_DyFQqq !{=Bi[w߁>0 84`2BC|<{^w|| 7`;88M?|ݻ "$AI8t: WWW NNN?ò,g?C}v!D$q(EexE㕕0eYo8}!0 '`0M/!L4V@=EQ6;kkk/_0:(00)A&Rbi ^ U(c@mM_uscI2'>={lkk+ h$Q6^>_-5Coo,IТ(vggga|+A1Zl6{%cųgFx< FRk5B(cb(J%0kh( VŽ{AtWEQܹG zw߽?=*J+N{ȧIVuͲ,̑y~ogw4ˣ8p>~x:j~xjl}^4,L&{Vh15IeY6M]E,J%s}71;d }qR7{gg۽ó3M PNpH}<˲t]UkZ-F~h4!Lh%I*[j\ymQa\]q` L`t'Ozx쳋V<ϋxuu V^(|AY2(C h %n9B- _~_ԒP,Ip $^ %/&,C0DN@8syiyIt;qiB'( #0``i$ Ȳc\ HUdJ Q`jgle( 8^F!4]V+B‚ 9'*#0!8I0eQTJ9c\\t49 tv"ft0i Ȫ/0 Ye ̵|a`?30F%Q4MUY UeUU0Z%s| MYcRyeR) p A"ou:J`d( ÒH$SF8 `ooql<ejal5UmU۲04I}aŪ$K| ݋s$c, 0 5 Q zŶEInRwUxz$VuΝ` ѻTU?(/AugV%Z1Z*Yo6g6-qܒY`uu%IZ:YV7M`i㜝]Ov{Ͷt*"p4~iƳg? $''' 98$FÇ,5MCQd$I6MwӧI@\(IQ~iaD)]YYA1VB`&o& H`ngF# ?z۷ uҾ4A (`k%xA󍍎eYiUΙT@ag-ֶ9IDQng3B766TUSW?}TUՇi~vOU-x8n Rj}`69(D HQeIjQe^oԪժHݽ="x0޿xU*Ea%q2]ύ4MYQFws ZXx DqieYϞ}9l<of]IMDYV0FѠlDQ4튝$1ݻx<9-Jl:$!lnnpge,Iy$YB?A٤C õvK%Ƙz]n2L"kݽ; L{u`0y;FZ)|ppfq ɌG>Vkk$`~WB۶'ZͲm$bf1U txpxpVܹ m4BPDT'OFQ")aQ*蓍 4kj{95MBϟpfmWlۆ H rYcsj:M,7<[[[3Mݶ-Jir jju:B60b4Ie4IS Q gYI^y! 'FuʊY2<߇iaĘե}t6 PմaU,dV4_֩{eako?<9| XRnuaRsȏS8!#B ABf` +/K>sʋDr%.c@v:Z`|򰁻Y csgg!cgY=t,4˳,â]XJlTBm9\1Kw 'IлB` ᳳ,b`8 0 $ RneCTК ^iEQ _U6FySZ-8!$E}GRABМJTç[%X,_FA1g-"18 /Bb̓82N0 B!$X$.:4CEpZT*!d<4fy~lb_8,˲4g`6EQݻ ]nnoo<MdٹPBgFFd-Xl5|΂6| nƞqAfu`uPp,nĢ,2 q;|`H)]&뺔R8Ţ(&ɓ')a(uNSQ;{̙"Ga{`t`4b8U%CvQB!||\(t]x*~Y7 jtxx8,] 1Ƹ\)}08:8BHK),y C(_YUT1V\ׅM0;\먰(^i:L$I߇^u>A+pyEap8f/QժsυM@?;;zel:L.Il6[yX\.ww$a,aO&),rJhJ)t}? :?kXV?~3 2 8TZɓGnb\~*|OZ;04Eb,ŋ_PJmwEqi%v1.J7T*vvmZT*kkk1}K4N( d<'IiZvqq B(@x<<8ؗUEUpa.7Tv=BCBjL&R!If3I 0pARTip82GQEQWmwݝ;w63-nЂ~>GQ2S}a㍍۷oan{ֻwC 4/!*JJ)-U*wnSJqA}7p>w>USt8N?u8Դ~wiz}}(ʽ$ u2x}* WEh409H}(J"BO>IŋHɓ'~˗q隑Sܿ/^p]PUU', {޽[qP˗e]^^.pn b(B\.,˿o ۷3TkkkA<~ضn YV[r(`4REUM̜1> $z(Qii'''JP(|׶mW틋 1Ƽ $`Lӌ!D:e| D7H7͟ovo?{I$#$<*NXc c 641ƘLdEQxe #X`k 9!]QVJTDBhF,$I7zb$8e؊^:#e(2B#ՠH(c,NcqUs; >^` hShUҀ[qCȜ,㚦Fٌ3YF!c,aiiB4UE1I%/X.|Ѫ|Z^28DOP}UnjJe՞lYUT] GJZʯժޫW!E ː(bQ3qUMId<T%DITtwziQJ funy6666c)d2hskSRAmô~$a IDATA]QJr<CК]]__?: v&$i)aGbA6 v kkk$wE{BH.4MUn#tvZm{6 N$9c4ʢ8 j6RA*w:]A\[4(vkccc\_]]Ngp8lZGQ,ɲ~]rw8ʲ,k:fh4tU+q,ժFQ pA*rn:}^^^wZ-ǝ=u{W$*r}cò,TڜM&emSB[ϲ>LeB85MͲ5K4E8Z G^M'N7?}ɲHR'ci3~û$$EQlF!d^]] \/(8Oo޾m\4&~jjhf%DQQ$Vw;~*a^6MSbFnw/^x~h0LaP8<<|샏>dcc0( IFB0Va<;L531|ccCUTQ&IN>oݿl:u:ݗ/_yqq 0qvEӓAh4vvv?}v{BHQ ìUdz c\էzgw\6thP(zlV.m4Mx>{A<[8$NRZ%IZ+~LSr9Ӳp8 `(l6s7MY)Xdf3DqH3,gR8)3y0D B ` NeM-"Ke\(uPeYeIӴJy{{]R?4#]` }api'Ie(i0`-|BF'؜PMAX pJ$]7`_p+m7!/2 B}xDcٺ^__W5U˲DQp]*LآQUu$|4K`0$ܘ!3AH9sqްmIqT]M2t=MS eY3`W2 Uy&7;0JǦ$41B(*AEK$m 0L}߯T+R۶>|1~=x>?íb8v׃9/c׿˗/}7 cwww}}ݶgϞd2EQaBpEI24M3M&Fp12Q\XX'O\]^aU!p/dh_,v\&(qGa0 ΢(&cEQy+4IdYUnݚL&s#td< ]eR}FY+q᠏`FK,ÜBPU-vzrz]Fy ^+/^<>>a& lTUЮ\.G%x?{q>}ʰ, ӻw 0S04Ma½X,>xRժ;;;@mǶULcTE BF㬊0 l6Bh:ض=dY h![GGw82N0N[;;;_mH\ R_[:[V $V0 Ѐg3>u]$I>/b>;==Uk{{=̲P($I8$Ij5[nZ.W0i& .y1t LǏjͭgϞa /$X,ڶ#Iyp>@Y/.iٿC)-Kkkksw.j* -0G$eah4\QV5Ngc E4,Z- Q{B<8(p ߀z7e7:\W-M __@]x!)zf0(*`B$C8[iAƔbBGgR3<->o8.;p'ȖHۆ껯l|ᑫu@zgCx2S %F 5eoe4,#K?PLf5ba62`e1!`q9@7t\ "D QVۄ`YVRnN2D0&/ c(<ϋ/=C",I#04esB0!(O#p#DFe`pUB%oYyEU,˂,NEbqV,KQ0 fiq,Qת穪|xx Rw=OV( vg+rFf\.eh4 RZc]\\|!Z͹s0t|p4y+_VF\NDAsQXjfXDJ$Ilvuu3+Bp-nN_26x^8\eyF(((q ˶GXVly|(jn .LQY? ~(7Ϟ=Md6R$ bL_~;t: Ba 8MSJ10 ;i>;1QU  aF >}^ I[Vs_ !1EaTV ݽi,`4}v6^8 $URc󭜕&LRH)1MckkRp9Ga *筼ii_E,)HDJt:LAV,B)$.aXU=>>Ͻeq$I(uw\g:IA\.QSOxl6ybkk?Gf Rǝ?z'gYf膬HO>)ai\*󖵽=Ͳ,Ä8ʒx^l~ťiAz#O>GZf:Y5UUTcj*swmm㜝fׯ_n,+\NcFk6[`$Iқ7oƢ J$V=/oAGh8wuQ,۝ˆR1&R!Pd$*V(0t`Yr_}W/^{nT&`z᧟}Eѻw 5[-E00s$Mq.$ɦ ;;;{{{j#LfѣǢ(yV DZu]hfI<~u`0Z[~wᇷo~G}͍'OrJe16Ná7r<YUGm/Bgzxpl<ZV 0٨($mFcH3t#e,IFaP)Pl<fv\Y6O(7}'c;XGFa>]aU'~շ^d' ZȮjl2 fS 7ߗ,ܽ?7$(G҂ބ2dQi,B|BaIEIZu!fҭ6clCX 1`8_?ꗅ U//Bl Jۃ#-UuqPA%|X3-SQ)VUj:gσwe H$Q40Z (qD 3J%1KShf>(V&[-B~ Fhi =B!e9Њ8K9ӹ;(Mx.4;t5\BKb0(˸(I,SJ3ǜ؆}Y0iFaiqs`/ 1}oMhiJڪ8ٝ(EQD1q|GGGp:$ܝ3ƒ4ea<Yq u>FN3mI P2@ 4UMe)b0vwvfd2k>]ǍTeUVDQ42!ع\.MShzm;NRO&,I0Em۞M5M+KT!I0TTM4q8}UUө?Rjj^y5 r!DŽ0]8OOO]圏Gc>߿*0EQ<2I!5RBy'aݍdRsnd2۷bL& _v9BHQA@}m۔Ru l6;::~ׅM2mvjw]`c߽{DumK"cX,d22رCc9DZ]hQr&ƸP(i(Aq 8 E|i_q۶ww, {>r'vAfR)EQٸN|38GGGbs{cct`#hp8X[FQdzexF eh_cb( ѸL&癦:B<˭jYqEZMWc,o߾ `>w]=??p͇r^ST<C^?VECU^wt.wwwg0AsKg&~_tIzȴ`~^QMӌT*%q"DQJ8An BHU;N>oyyN_~ `iwdY[=>>~ݛ7o+B$8w !s&l6PAdiZd>?|b//߿ $p!C2n>gC#o2NN/..ÑiO>Vee^w]+ʊbaNA|޽ŅeY˗o޼(oߜv7o8ϟ?|Pj666*{8~IXW*;Oƣ|>eYkK^1޽Scoڷm[`8r]{(fEqyu?\.xݻwBA]ٙeY},˗<|߇mRA}ga})˲L74]߼y3W9#ת5q4]E)l\״ EdEQelsk<`J< &?w{(sx+tJp MB+@sA6Ȳ XaAA(Cc )\L9U(CJQ绞F|Op0m ҄Fa1̱hSJY4 4ʒqQ'I`QƳ, DUL„RQ (0&,a0 Y&P1FDABq3(24ISBi2ؐY IȊmwdY9k4 B(ʪbr"+(QLP,ꆎPVUAq8(\_)dYV088˪j;Q9r1Yrf^׵]YA$ |j6Mӏx"`8x2D1t2[ͦ,)W}}ݜL,M3BQjqH? |4ea׿^[[!&)=Ͻ_*G̶-+/bL|U 0#8e\*ݻwoV*׾Gils=2$Iʳ:HEIR0Ƃ@dE ZDٶ ewM0 Vc,V jooO"ѣGu;=-˲ً/|k6@1h4nڌq( $aT.u]uY岮` \Q !4uvq{38.-Iew9\.W,///}t]'O<|?B>c[$,8BϛWkx|rry =󖦪1B=[zM2ywppgmm 8j5v8ba0YVZ_7tm0w;Z=sL&t: ɓ'pǑ ,׾tvBK۷nU~>ݽ{^ ,K?O>|'OsDQ"<@0,bPH0>R*nBAUUONNm{ c_\4Ft:}ٙm;۷nonlT*Ikյi(,BUUUYVYhjǟ|Et:x2yp8 }󏎎vvvdYLӤl]eۣ/+{~cL4Mz4Bt8fYL3W(l6ӟD|>_Ւ$b,=>>Y*7jU(XV~{g+v`4| > ðP$Isp~%(rТ5C èjf.iZ>gjUx+\}5sW&Y3_eGWw\aUvWO6A+tvo4CP73Mx1SB?+:HcWyufP!`4AWgK:]: L\3lI%Z7j ^_ |Y(91 s10\8nƪ ^|)^2#1`LwA$Q6۶#c* ǔ FAy\83ΥE21 T*:W_kkkdJJ@>s0U O r JE(i6?<:h4o>^kkk<!fw ? ?,tZntc6r΁:h4=JR%ٲ 9J"!& &4MoYǏk?Ea|qрh8т .+20 Q!pR˙}|20Lt `G(wTUu]j!yGAXd9~p4,j O$IE枟ew޵۝n+Բ$I7660F?Kuuŋng{{nZO?\.zΝrT*rfB(ym=;;k68#`0 ?I 7|͛~_諯*\WNg^{;w@b\.z3`k0<4.!bggg0qE!DNi a-*Ы+O*rm؊{vveY\ǖTIj534gMN$cAI cQ8@6 ?f0Z(`gf;_/ْg/g5UoWu YpAi pH;liG:J(Cd `&?:EJf7AhB)[h<9I$W,_ 'VAKxظjd l5a"a^z&i t4A7̫B`wdQ&\S*dY( =tR `2x$,Gq (NDI>I8K$ܺ){^GY@q0tْRU(C,e/*$MQ\Q,%2W&"!L28M$(8N1$8J(c*`&# ^cgʲ ,Ɋ$+( hx2A"k %X%QdQ1"Ku,Bq'I\?, Uǰo !BL8%QWuP*Ri88VTjZ egc;H>!D$x2fM&Ӌ  e[[B?|<x?n<gwoW^DrX,߼yn%Ax&ǏBX4q J ۩Tkk܃T]3t:EBgܞNN @SJ@kiD$!H( C&uuu};et6<'qƑ˩ d(W%ǙNf16zo6H"B|>{/Il6< pɊP(DQX*T go[N$A$i^ ~WV]1MSk~~xhh׃A,dqVyg(ij]Y(uݸh\bL t>z߷,+3 M-MUdIjFc<;xtv~&JR r\>fwnFQ>o7o^˲$IjǏ?yT*0TM PQ3󢄥UUM3/&e=zz}ݤXD9 }LPDR9 ͍/!F}{{R)3yKQO:hJ`0x-j"$ ggV=LmEzrrlw޼};Nw˗/TUe%߽{(!hmmͲ/bkkۻ{P5 ^|uq@1u:\Πav]מ(!z}#ImQ`0JSJbӹ<:O(-o߾T*RZÇGGaCQJ߼zᄈ&Mxyk;inmm[@Lpӝf(dYF -q3QK ([x#05Ifܽ{\y.!Yu{ٹAITM1 ö'i4k׮.c i"w}UU ,˵ZɓhDKt2is.P(lll 4n]Ueiy(+++i鋥r*?T*q7nK 0 $#pB<21uo .]jZFv= %Ir]4MQ!܋&b۱岪ʺu˲jV}ﯮQʢ(VuΒ#&EQt]ZE4M)Bض'EX.w>|h8/шwhsω8y$,C$IS7pph4<ɓ'gǏ=z1Z6??+2*`?~/~gϞ{vAFqz}yynAx͍5E$!EQUUNLӠv^n=!Yv;BX󌒳u=ˈaoyӧ;v[3嬢$IɤT*=||;ryuuE$NރZ{h1ݻ ^Td 1Aɲv_zU,MONNTM=99LƊ8cfd-,,ض}||޽[V1Ƃ B)(8>>>뜥i&i$\"eycÓ ^ٶȊ8׮n=~^[[4\.apppp||5 req ۷6663?~077oێB4sk(Ƹl\t;'I$(r12wXBH4j5ɻlY'YDQeYb1/ܹsg~~NufmqhvOOF Bxnױ%Y5UEO4z' W'r&>.˄(Ia!ZPJq'O^|t0EB i8ā>tm3-E,k<Fۿ6R1u(lׁx*Bn;k&,ya5ͯ_{Xi濾٪S8LYΝlLl K)cɹo(J"]!YΉ}{4aNE^rC| fVp_|{J45q1(vh7OE$@Y|QD$ɼ;2M B'Y'd`<6W;f9uS|AQE J&XQ4M(cfP'q, ^g fƘ H(@A|ޔ$"e62Y`3"^SY.L_,#(QQ4Ms AYUEBc2IeI[o(!PF3J1H YNu.ڌ,RkuA2ʒ p~?`tM3 #ge B!oj!+Z9C0jyضmqFR,P(trva4p08IF3 LI&Gwv/Ko}϶4ťRianllh4~;R1 #۞4y]m.rV! 9Z}iqq<YVa}͛_04Ik$FeYKK /^=(JYFS8Ojig$oo_LUU 荛o4$I^]XXsvȶNpzxcc0~QTl5;vssno~S(r9c4UͭBU%I`aaxegwJ%AS~F3.:t:YF< GRZc3|>_Y]RJS'Z-gV+q+ <[m."BOX}OӴ|>RONNTUں8\snmuP&O>=9: f.OH&JP(0t\c/yoOx駿)NӟѣG''Ǣ(0hX,ں"^u])gTU |_fnhe=Lx0g'';;Ϣ(>=;`᧿mFYkahggq7,67Fq뗟۷_~uΛFsooo2Ai??}瞳T*eY{Ǐ!BȲ$˸@EI2 ##DuIH\(tUAUU1 ilƵk !ar?{I ]O!$ FaY'g3 À(J)OLt4骖lrިi$E1 YTZq;svvF)V.,OBz}b4KgOv:]u3FrgfӱEQ.]ZCX*x8,MSYcif;TU&)y7L\E1b3gah90 r>u]4I Dj$خɉi\׵DUZzzz#I!<~283L4MUUr͕ueg}fۓ()j쬝 w ﯭX4M倥Sж(QIF#~տ;|׫+ey#6B7onaTVgϞ?NtҫW~sFan8ν{oJr|޽ZÇqӧQ:N8d2jq?|0F#:*!]54Mt:EQyj5ͻw6sۼslQa1&iq1Mh$I 8M|>oYZ: 0qIwE(G|;o.-Nu,R8LNO~ߞJe(R)"EA 0>?~!ӧC}[V\nee1~.O<^s VӴ/_LvB)!4fsiRF<;m"1[oat(eomv~߼x|}}ݶ~A8NEt:bX(0B!. D)ERgG\.Gjkkk7nw{F!l4ssMn[(XKKK(ڶEo߾UV\B)(#Yjk+ !lr*}4Ea!,> v:J`0,E) ssW^ٶ8x)eq).ʭե۷o'IŋŅf-A=/ǡ ж]7y>*z(>|_D0z-0?~/VUUDASdQyg( H;O-BF(Bf@'q uOEq(Qj&Hs}ϲ,&R) $cW $itEUeAN+M}~~T*ُ,޽Baꚾ},3/J`B0XaF$kkkU2h Be{zf޻wODQ!ިeXTFBA7p}I(0hFEAbmmnIR,s9CbeOL۶$Q DY|r4Q$ b?_q,Ir~vm۶yJq0 4[^[9:>{Z[ oͩʣG4]/ a*AgcB8{o{? mۖ$!xhPi[A,4sgϞlllzS(e>a~d2...d"aCq0A9_~?<4hIV{͓cׯ~ZmVeNWuYj!_ꓵUF!Oq$nk jqtt^.t{GǾI7}_|޼v/~pE \.ẎiUGB@i~!FQhfniYQI(`qiQu_~u l,{n쬓孂upxih4>:9 G''nZ}]x 'Q6?7? 4ÚƆ)7o60`j"Gq\*8dl6zn5 3Ip'\8x<. \c:ERU4,jT*}'/^8|>Dž@J%Mq>QǮڶùIۓAk;Ngg^I8L0 NN΂ ۣ0v'L&~3ãEQkjZ?SLgOc>u{Z B7'IW_y^ν_Uu%I<;;jm^?h8l4NgwIVij̽z^|:^i⢕ϟƓf%I)xLZsYٶpd??O>}zΝ_Ϸrahj%x{ݼysss͐CueQ}ǒض$=`g8oL$+i_`eZ9j?9٢8fq pV^O?A(.<!TdR Nk׌1N/Xs~=a8kEQ|Y?@p<üg%>/!lŷMH]ǒ8Gi*I#b)-WIcRJ8p4 y93[a>OeY^ww .pMј[\\tDQvApbL)8닋NǍ;n~jdIuYDQY$IXY<|1V,B ~8ZsJpUB!bk1%p8,q)r>6jh4<>Nb(ˢmے BMβLMҔlnnbOOO1P 6Uq$J"IFjA>77W*\%cX,/^uu]?;i_h48~?(J)<4[nAptp|tt4Gnll$ ~cB? H<㱕._0ZR/_.rxܬ N1޽{,n(VVStcʲ /_y/)QJ\YYt:ǏZ-]}׫jZ#N?s|eWw}0@g:{Z~~8+#Gӧ??u]^,˪,Z-o,Fi'Irڵ,cַK~_6O[9͛7ӧeY><<9_׽^! 2\.ɟɳgkZZhzj-~駊"iFy*`0\PA>oy (\vM~O"ꞟ_Z^t]7?wx')y۶DI^&f1Q@i_,뵺eYb n Ꚇ7cYQE) Ķ]OEMbYļUWi,[i,K4ƣQ Jx\(BQiw(H{ȶ0 b'IZzpx躮kfazou|.Qq0J)mԛ81̛۾2ܜyoqskk_N;ódbӌq⇡㺵jUQ0)(e䜪3 =I VWWdI&dƨ8(`~_.Eq7k׮ nݞ2a./z$ cYZ_.._^,k2S dY)J ܍7JO>(#Q,u]4 o7vw_ |>(,KszTs{~$)GgqG0 8梅B@|_eYE " \0jK|.g@?dbk!X]]5MS^E8Uܹsd}}=M`n)e~~0Y|Ͽ|RQE \䕴,+e9v0{G$Q\^DV+ϵJW^e<DZDZ0z㣣NóWsZ4jj (jGJEӴx{Ý'/^}~eVph|n5l6NHԶ4MؓDU4]ӿ+(ELl ϭg Aȟt qE)Cǣ\iV;8CY%G)-//Apzcw|E9>>>>>M>"$qBQ=t9J)?]̲ BceLe!Rn ~|` }#w^0BH̲ 2 +"7jv5BF)a]4MSUs1\ ("J&+,k_Z>v ?igG_u~w~_u]˲=σP @(2k[bӧH8 xMDe]dTuY,s!$@'YP~"n^ZZ١jɓ FҒeOhuVŘp\1 e,\@R^u]svƳ3.$I0ƊfY!{}ˮ?UUm6gsO߿? BF!iF(˲,SAII4i*, Rh4,͛7)x~~!3;RR˿ׯof9'dFfɇH|xr%0oNYo4''Gׯۮσ$I"$tz(5ﺗ'IBNu]7M#"BϞ=F:MS8]y^6uXk1q[ݮ"ch\ӄxG}xttwBq /{͛7_sa>ov{q`pie[[[ф!ey1_*ImX*7Bagg'j*YX F8סHAju2=3 s$OOBPČ$(J1)FL<1ƓE)lzP(\4TUdAgAiapAFH)^AP~ni~c)򋸩(2U|Q:;0'OO)!DziIfYeZ-m31NEQDh%588I!$R^MlP82Bjժ*+Ņ˗/kt\.ϷZ~FQш>Y׍DIu]ϲ dTE]7p%YJŲ,$AEԁ"덺:I5EE&3rn,h puZ*={v]YRNOʕ2/U0i% |^uHH}㭵(|i\Z\\|rZXU\. UE)Y/o4MܽIw޼s}EQoxLq\7ch h4 `ۓ d AͿI/($Rül{8nO>m{2G 4Mɓ+ M]׊E&veeemm;wfY6]MlwaGGǽ^RQ 0wvv-+˕Jvիhyի߸FQ?;;c,힜L&v_~j-?x@QTI*B+ ׯ_7MUUm6M0tNó;cMU,˒U ˲>}:>T* ה8+rQP+˹\NW*ժ R1ðVeE޽w9ׯq8_),Aprr Gq•Jnw㉦麮B)K"O~5MӶ'It:[o!"ҥKk$I*zi xN QVr93ϱ,;:>!TQTJI|>=.E)\y+h$醪(xlg(|?M8%IGX3Mh4y2/߽͛7GA9!/ yBH*q>FB~nwݞ (]~= óŅjʇK.IyNǚj,u@鵫W-џpyy !~Es{䱢93v}ϻqz$γPc`9޺!r>SO&R4n.5Jĭ9MÌXNNNlϬ893gƬBa0xS*$Ǿz}^5q0 Binj CtinA pg#.5)T~8NZ%38r*8"MQ FfmooSJ4\^^bFQ 0S (+XVP )NS, 縄fn^_sob_YFl- lj_!˝:8]UfS1o 簴+hxOy!iD(2a$Oe@Q]8$RyDEQ4Ma(d3%Ie!ZJi'%I"9I9S"<;w+s9o`JQ55IR\qQ{UUUE cኬ2fIMC@z%LQ eY8%1$ 8ɲ;PM=RUlr֋HdIUU]dYd!t.F 2oFHqe |PU$EdYfb(VE%qiF%IZw]W%Il{;;FR @Pi@BPJO 0VY7ŗ*po(oݺÇHӴxLh!#dVM&ׯGQt ]È9q9??|_Ea(d(Bmق 4 K&L@<ϛmmqբjz[xZ=~m۞EM&VO ZiB!NS~6;Cӌ$zPHNQ3 qT*1xX)!(?::V EBHv]ټ0Mq\.ɤ8}Rܾs˲|vv[4fQa5,I&a~_r40hz?==}(J կ...x+nh|xx>ٳgǰ/|t:~nnADf24B2Ke9aX9ܹAQ$ya3. rhyX4 #(k\0~rrbm; r3on6,ťE㯿ј5Me٥W^YT<8Z>z("M\=;;T>䓏}B0Nqzz2 KA=͛7.א27kGsssL}aþr:v\[L( I'IFN{4iNnV٬ggg |*%~0 ѣw+`ZOgjunn6j&۷m۹(tzz*"!DeqFLFuxZYYa?i8^8:].=zn}+ Ϟ=/ X\\\ZZں}Vm6[O>OG h۹\n4x"\.Aߏ\T}jzttɤWVVA( ]deiaqfffgggii |W76h4E-˚׫E> G>iw:O>9G?ϳE?QZm.ǖe[YYVQnKQ諯><<w,F^*mXNT(J$I;w>B؆f2>01u]wg*{n.7h d\ǝg9u]D!- \no |.-??~|vz$ɭ۷@':Eї/_>|wǣ8 +t]=/ܿ?xdmZjL޾US)wk}y=D"lGW],aScL40Ps37 h͠QV0e qru4GSLLAMShK G}BPO(@Hf  q(QY!IWs_?]w/LIB0" }BrLQ'qy6!(8VULqa)D!E3jFVTJI8BDSQ8bY84M*N$IXx^Y}ϥ(DBuÓL a(ǐ@Q!`E 8#(BCb+ HFwq|CZBb+܉ai8Vxh-aaŰiiY1 #]iz0 r^&RdIL)J\(XDCY룈L:-ϰTZ-EB * +OBDQ(*8ǮڦSvh }P(*)yqiQI) C"Ot}smi2qyA`faaa8IZIEQ$"a9 ͵$Qy?[Ea>ǂ uIx(d2nۦiƈ*eY5{q4taeOy!),A DD<'q &M,GnF1GP_7a4} ;o [7ldnB8NeXqq04! IDATaJ4 na 7ͮ08Dznarf&dX]մt: `( ',LDeEAi f ī0܂ӷ"˲~3Z( "c/u;kkkbnooml<9(2BaX-?3˲8}rwynUU4E3\nq0 ,Gkcs,Y]__Xu (J[ϟ?yF:QL4y{ۖEQQ̵ ò4juIY-A#{w }ݻaݲt:{?ŅJǘhMLؐe$r$)It:۶a$n7xɓ`$e[FVq7BcUFQ$\|{^$$IkAD>??,1v+ ÅNh4 t:8A5M֭[QE40U$ |ATUt:Ǻ󹍍 H,knnT*`gg(uYeEes( Bd $  BWO<;;pqe9 h< |UU p\E@=Pm{N$xh{B8cp0t]˲>A p8x{{{@J%e}߿wG}h4XEM&7oض-+29t]MX[YYq=g{{{qq1NA!h{{[0옝u0]I`YbNNNeY{{{_(^{n6}6s"JAFDb)^ZZVL&NϿzj4u:L:^X\m|ŋ绻%( B*ZXXfQ1/l\wvv/_.//;dVVV{_?ݥ(n4$^^^4mf&nnn:#IG}xhKDY__}e* '||BeYeA鴿曋wyZlɎッ$IrVZyNU5EQ޼y8.!ӳ3f"VaO߈%&~p`S=) q.q?s1ñ8De wai + "\$7c05ch. C& C($T $M4üaAQ!1Ԃ^ ث!},^)+ c$IXnbZm#DCIeP$'I2 !1UFDQc,Hƀ]!I4T EEEQ,i8za5.//sQŵZ hrEQIRnOH( XhYbV:lT*};wUU=o^pN-O82 QgYQV|?::yBҒ(N"0VV;y>ā(aR;gNgk4I$IҴ`0Pa34VmODe=ǭjhȲצ ޽kׯ!!4_ZZv/Af)%+YUA$44|T՚s0rUA84$ffggm>88z3ZNQdzeY^]]]ZZ$n ˲ zJQU-t'I/..>}atZ,nyYzP'O<{ūxduu(NOObf;JZ-յ5A=σoa(uKjh$W=iewwWf д(H  Je,:8znX8A_ uhVL6FA,y$jq(3ȮAh4hZA=.EQ}AGQTaeeY9|q)hZQ׬~k#U>c;7q tԹ eZ_ 'x(&}q|mzN!<$ \e"07b@]MVpF āqTBEQy?ky2'IAc8!$d!PB0-4|Nɾ绞E1s !33Y^02!G,ð>"STB~.`q 4@3\ ̋ QMӂ#Df\EQE!,Z^^ann\.oll*"p/s|gggG<E n>3f }w`0Y&Y^^NRZݶmH\^__e^I4M+b1Q ޲}EQ4Mqff&CgggjǕr9"]4fјZrΝ8!-:33dIh:ibѶ,skz\fhIJa'''o=jYQ8gff! C8l&5\.i?~j6R1NOOGA$%p&y ˜ضjNQU֭[zc pAVN>ӓ\.G)ggn/[*]y^LYEQ666Jd<߽w\.kYVQ?}UU˥ʝw7T*EH|ϳ, XZZe%ˉ$"2m r(yZz0-fA- 0\.~~$ 1P|T*{(n3 -,#c(6-Cy cLtFY\5{I@6qq8% ,x4MJhXݘ;U$\>GugA۱j0[[[_}yQDQA",AcNon4VVF/l6[L&ڀx1-wQLF4Mg2~mEh6q=?{C?>>8rp0躞fJ>v}ppūAQx Me7660_|B4M`r~jR)T\.W*htzz":::Z]]Fyqhiu49yϋfalmmfNi3mT*E5 (FcefO~`L/}޽;pw0,UUIӍFŋӖjj۶H?dYիRnl򉎏(шvzj[Q=fA%'ضe2QݻS5:y(X,FQ fa(۶/.Z(v:sqTU% .//u} Cc-0Tj}}ݶNCqH:3?qjYy'<rEewygaatZu}ooq}שlZ4M a1C(TU}~qqGEIZIzrUsssD啵ht]#tٿt]^9ua qMsssR((㱪zف[6%$ǏW*' {f8a<hteY۶Fv(!5P{ J0`4Vx0V9BY$I*Gl14(8qbtrz*Wgg VJhv`nnݼHjvl5z&HR;:8Ȥ~ DI=z477׼(xB!|>_,K֭[A;˟V7FN?~7`ZkfAӢ;G7z$_~tL$x+0ch ,|vdj#ua\i5@f$˲3h}l(0,uǹ$l0!`J8?*''Ϟ=jzqqqxt8N$aW ,/,,,(J {=AdY^^^o6L͡(ˢe%r^(s3%__}& IDATql6={at:prDGx~~hdYUT*M˪ q"5;; b&>{+B۝L{{{{{{?ӧOR6c}kJ}D hYIE8UBN;<qAR: 0>9>-;fρWm(٬&iBT [Lcd2t]gvyyy2ɲwzz(yiq8/,,DQ4rUR)P(JT.wwwE3Y;nEr\.g2Aãׯ_z`>4cajF~ɓ''''KKKjѣkڤRvmvl><| ($IT*h2A˲q 508 wyy0Z;Yg^gZec%ݮm9nwJrQuvl> WU5N)b bZ5бXx^eP2%07ܙ|nkQ1& PTBiZAQxNө0 y] K[۷1M DMOIhOBRuaaOOz~w麞i?䇟gvnRӧO{?ټuWq_\4iyUn&zn:^\Ys94UǺ( Lteq scѣLn4ʊ2,d5ma~X*W^xb}c( iŖeFZ*!Ԭ?{[*Að\.C8\c|Z!h9cYVlmm cW9*nR4KEmt:ǂ›(2e[eN\B_^^vkN:<<5/_`E1!/o~Xwyx֭w~3㗗(*i2h8 x^;<7s=)KЈEѰL(q 4M'tEQYb9NDIҚNsx2ir-тY>\,j5c=/IDBPׯ$Y4S,WE գ)qADL! AHQE/뫫(xL#* Cs<BaZmY!˲%>CӃh{{KBȧ~_2<}ͳgQ`0q|qqzivX.힜4$iJWIAQDׁC0EUU) t(#|߷,aL&|wĚy"㸉>կ~K~/r0ʒ 2w5Mm puuu2Z64pj6-?'4M-//C &a`$T*T*%I]~4MΝ;+++BAQ`0XRdI+ Ġ(j033srraǺ34)nZv{kk ۷otFiI4=wxxl6`8ZvE`пCX rj<AT9^N)ΪO4=jd2qGeI׎@f@o5 p]iT-+oYB~vS?r`D}V]]U]}'oHJ=`ݿc_<x 0<޼F%n}_u_y?yN;++##3{|NSV%Yj:s'JJsOR~%@v!.|GqwaYViɓӓS4 p'äX,V*rLIMөyƍju˗/^uͶA{h42MS/_cZ,{nѰ,{87)0?W,OZMrj1i6?fggeIn4A(I2poooooL&cerVzeѢ'OvvvVWW Øжmwreʕ+_AhN{xxiڋ/ øvaNhz7?E%IAG1!@QBHݞNTW%0$N$½OfYR(%ImuEQ4 jQl0 css0򛛛eݽ{ײFp8R UeY iVչy@RL&i.--k׮ZMaY5f&4O0r ÄQ,*D0I Lp=O h\UU8U8Nm~V,wlkYV" pA ({wƍ~D!%G}$Io~Y 0Mr p2,gYyYu\!4??Of#IHig-(fYFHƲl>_,+M #g Bڑ$'BD0f-AkI۟_~eTR*DYF54ñ(?N,˲v1Jc\V(뇇qq(I ^﫯n4JrvrF)򇇇baP>}_~\ZZZ\\<;;+hwo2r EQVt:KKK1^XX bq0iO&*ud4M4m:jeTBtnR$ɲB0777qYyYNE\T*5'''/^lc biFQ)DG=~qr'8ٵdoKl6iyBhbYփt:秧BqhDA$mr!Ob1vOOOu]RgfO>%y^;wP`08>9Z\svZq]믿ݽzjT:99qMSeHcbc)vE!IS^#dPomۦ11EFA(REaEqzdEV/EAq°,rB:t7'zܾ=1!=~R8mYt}?z+82QLs__Bzݎ$IBEQR_\\s=Ig 4MI,L&Q,0$IEe~8\Z(3Lټim;qٶ8d2-;@|ߟ/ mmlgR$(3m/B( BVL˴8e[[[7o e[i1!(ȊrY7$a,J75 9cYƲ,L$I<'iEaXR.J?MyT Sk39v:YVUugOF#۱ffRիWNqOx< f:??J%*i88{BVzaR_ey8S6iC$ow]s!\"C.#Zi=ߢ*[ry ?Ά"B !BH o_14$PRo A$qeTg"Φ[N$ 2 4eY6]׉,p4.80 @i Ȳlnn.IS۶(E qQP@ i"e88~c`X6b"Q zsM iEQL@P? (dJ0fY6C QoatMCDYB)!DȲ7cBHfٞ! @d4 H04Nb梥iFB?0Pyeeq0 Ay^K4"H$˒@O<'IRNXIrs)+MӜ yt:L:s< ꮕe@mۦЀ B3,P$˶(㘅HyLU8%D"0M'>iw`24$u(Jݻw*j$hٶ74{h*˲<Ȣ(˲((H|e>I@uM jۦi^YZu,Mi*/,O&۶ }x<\ZZVy=8qǔ#kSBe fjeii(Aᇏ8m֗sF\<9=,W(jwjd4-;vV[__ۥƪzi:PU۷oJv8ggi,'R\{>O>\zިߺ}#or9Qo\]=:<yEU8ll?zư"qDI}?<ϵ,n+b9QJ2!ZREA&Ues;;;k;NN&8-YAxhZB^)W+q.˲;ϷEAmκz8o?7/(e͵FQןL&{{8rFhlvVkumm2p8Uzu0b@999~`(Iu:A$_/Iro>+ ;۶8??}fiVs疞u:jt f05 Bi*/PkZ{{,a`NMꁀ1OL3 4M8+ B1Lɔe8Nl99;v0]FArT,Q0(7w[&7(n𽻽ko7.G t-I8]׿x$(79 T51xSDH\.GiMaL,dE!: m"biL <1DqeJt RuMd8圲tfj71B,r^JSLšyBs !?O_x1>| W_}eVE D|yYòlVm8Ɋb%z>$q8] #aEQZO,VTUzQulת<C@`zIi7f7p'8ggg`9E1z*piT*F#Xk_~-J½{_>_qxxHy/0YdR84$I\OSEQ:ueu}ffјQogff0޽E6շ}X>WV).54N-q(4KK73 ^| !TUuݍkm3 8r{lԪ~vM ,1RCQDh4FE,--N4mjsƍ# j{{i,SuttTkt:Ͳ>3Z7m' TU- aR$I"eAe`T*QZREL.M&qZEq0P,Kp0??L㸧O~B(Tj6WWWEۛJӴxlYVBt#wrr,b1 !j6,MS '9($I(e* urrF=~_R^Q4bY.,&I:|---i {iم65>E4-//'I/666666nEYkZEnWeM6!Y>:6pW,qbr0! *hQ(u]G3L8s>^} (7Bō(I\%)L&cs:0~TX]] hj0u͋>hEW**w/d2Q4uPɟp0`08k q B(jVk<Omu}vggq}#hr,K!/{JQnaajijd,PenN-Iޢ( #aP ;'F@CayP01R@BaХ?@,'` |Ic MʌFB4*B )碓$52q\ۍf7ATɔ@H?@')Nh,*rEa@5&)$!B{,9-KSeUU8B(#3A^)Hr8a$MB I1,c j<3˲ I"ˡ$Ml۝L&A\׋(c"DQTSˆ L0, &p S7$ $a%E _ũeR4MI8Eg9^8 |qj}󼪪y $"HrAU~R>>= ( #@$$In߾-{J.AMJ3<~߲zpȳ|7t#GoHB QRGRѲ#@FQE)ʼnan߹k@Lja7@A%IBa*)k8a9|ߣ"L$4LR=͒,몦Q"8$0˲R/Jea( YiǢ fBfq\R8csƑ HprlPݓӯ~&Y?g**Yf}c=Å۷orenv]|?PV_rP?|uu?}NRh:e9C$QռG ({zl?ZmNM˲ {>_8<}Td=14/׻O>s8ˈiZaU*Ǔ_SAu]NPΞF\8>>40 \ݮTJJEUǏw4Mx<$'?' $IIYm\.F/^߾~zgyQꚮN{rrF%QV˲0vTU'I08<<84~K0{O|:IȲ2'q7];fsaaƍ==<=w: UUS"7+Kj\*A$ZussqlDZ4Y^fڶc1eY׏8q=4M:NŹ\{ ܿeY;;;p:5\Y><<* ,=AUUePeQTa/-.^z qAx$FFYQ\K4I"QO[7'c;iZR34x9ts-NO$in~A SsbZkAptzJugg{iq1 ,K8B0@? Β4UU\(/Km)qFAEydN퍧Sİ'm]wލ}o%.S;0o~?|'Nw qVXaHJi8UsH#D~!0#-躎1$AԺ/@yBc8sqobhd/P2 pRA 8N>!TTuMU.FQa,0 GabB ܛ$IhGYI$(pp'd:,U! J,$$bE1m0 BIL!mȤiB8ʂj0mcrq`@Ѕ7N C80I(} f "P" UU(#IR4M_GPI4MEE `an_JUtN8 hFBݻ>=vSۢ&ϋi S q< Id:14 7^GI!d<$%!c0S]EE[&rFZ=??x!ƀZB4Mf+z>Т0YF$J$Bpv_~h:4h( <8>8BlAU(W —_~iDz"ZJbX(뛿կͦggn޵kQo**2nP(?!I)wMC۶bEQdEOyVKd2)Ϟ={b[$ɨj8_|իWϩ܄Q^レL&r<4MyX,k5dYͧ$IV??== cIXZZv;,Fh4_k m_#$ Ȳ,D' !T*ٶq~~iAl_x1ffb+%(JRAEeزU_|[փ$*ө5333;;ŋCP9Q;ɉ$I+$-O&plYV.q"Fy5Bt:FQSq\N$E@GQDn(?p76G~l6YOy8 pLsRT~, dVdYP HFb!tttiZX__x)!35 [ nݺE߿Cβ,cUU5Mw=ӧ[ԽcK%IY\\dY4??jjGGȶ @H$j(Z,#Ϟ=8uj5ᎎ<ϫTJ7nݣuqXX^^x뺮zP$maҠѣGq`8W_}+WLӬV˝NnR=o-vVuvv&I)\.68^]]E޽{'OxeUSa9= Cխt5diz%ǝ|ƪ#WѣG*qN'I69GgggZ.'(/--u[naEi' sF#ˉ_O8y^߸q#MSqiCa(}m}\.˲HU=1!YYje+++j>#^vիWvB&1q^ 4q3!+\ӧ^9@Pl.,,%IBqԶ8gYJ!, Y!T.B,M8Jߏ8B iP;t7, ,EbPlw ƘBE^A()aBh n2/˲|  i68"u c8M4Ä/Ƃ a4M+ gg Y*_q<`c jͶnܼ~-p ÎFp9ёeK B233S.W (j3'VӲLsbʒvRÏ^xNeu]o6j13l<3< v$ y5ǶmgYvpp4b^xy|x!b`\fV, BYznZ ׮_+ dYv{xh65+KKg,KRZ`:5+at]>99 իWϞ=mױxZ*_x?q>oY$βt:kIgfŢiNF`Ȳ44GiNIvZQd%_ȷZyDZL00ɚesY%Iy^$&qvgjbX.y#7˕j:c[QQ|5$I.K,}BzFcӴJ fGn޼>t]EO~,Kt2ax6-^ icRl|u;}{:ðAN1@0.H9 f3! ,: d\QG7 ereYe IKSpA? c 0" D%p(JiBH;j =oM8 ".DRLu`c0 h/JY!e)Lcb -&!-ES$I$F]@H&I,--9c6u"B,#LӤ  1<&IB!a&qDA8==,S\;Г(jXT0 EQ,k6go߾(2&Q8(򃀪(< a*MS]>xsq|>Ƙ6(8޲LqXr !Wsg2Fx<;4Ms:Zsa}!Niy89=>w,Ǚi(孭m{nwl<ϵ (я~9qƉד$y)6z=UU*tb4{a;99X]׵mgvvֶ g,FqReF#XS?@c/_"%]nmm}O<9o0<>Ibjެ(0o033c5߁1 8vQUuss0r(I( |q0 G͛7766,mq ]7 a )vSMӮ\bYؕJ;A,‚((z㍍oFʕxLm㸴/q$r Dzp8nnnt~e˲ èV+FWVGw=;;T*zѣG| CUU,SUmssRn۝ϟSHXZ\XUťE*#뺮fggo޼ZT 03|ZI/-.1 eyh2IvWeACݛ}7rwƾ" I}ˍhIeJR3=vW?9==>ˮRJJ+L2@y$RIry=:U0yBVWWoݺidQ,HS5Q$RUu2Ȳ<33Eyq^_QN#IR\Avj6ny^UU}1LUUlys|||vv&IRo7,B#`0r5[r_JJgd,Q>YeI}'OF({b}G*(r?L\i/ϗeUE1M Yiz~ I|f{q8a9q0ܬVkv۶xY߻NpF$[RuqFq{^w{{;\.G3"1F^X(wiq^Τ&w8ZŋV$IIQVk 7?o]׋x}^4-U{5'_gҟz*waIB² !KcGB ð ˰'"s0 !d BpTV0 LN:BOiq|]#.qD%E\N+MMɥ4o L. 2D CY @J.#@"1F<ϳ!8_(P:seq˜@y.@piR$IĔJlϱ$r,K!61!^\9ÀiI\}? #974XINmHe2yX:gX/oB<ω@iG4$qJȥ+ϳO=74 @UUI-X7 40`@& bB2M4}q˗c9,kfj& e#mۮjqCv1@'$4zlƢ3,CNËvs<8ۖeZv <_\\@Ȟ40rÃϟw.?xjN8$K7nPnm8i[ְrݮijUxu=ϣ wc$ꫯ(c`0y KVadIm]\BO~yǽ~nn. Ü9Nﴻ'I'2PU{w$Z;חA Κ-9b9LVtsc#_(0 yn,{w0_tmXak4t{x~(Ǐ{8 ,q&qxq ȃ7HӔzVߝ~'ow߯OzO& 75+:>ʁݍ9@ DUL Ա>c81dz@ (1_W 4tLݴ1#!aR'5N* &ɣUj]hMLi @R̲ !@ uLj`Dq4xzz#DtEwG>U2w 8N$*pE!# 6#Q}+K0,Ç"ѮtjUy @=A$YE M2Cza"vY*f>O *4m~NMXeXv8:۷\Ʈq*C]E^PUUu8$%QUx*8G`28'۶Vrħ]Q0E!MӜa/~_J *jj!UUM0UUu'nEBBH8R2i:55%p0 !eY/=Sdʲ,iʰ,p8RI8L~#\)i(*:?/~/y~H$8'''SS%Pp. < fIv8Pl6u]7$I(1i޼yӶFA2qL3i<88<2{cc-p(JPE)IãOl68^ZZDW\.>33Mo0nlXx nݒeIUU!@in\.߻w\._\\LOO///om`Yvn^V\NT*-,,HT(,;??ma8u9 ˋ˅B,2ð\.rLfB^E ,˚}s@ RdUӴw81Ms}c]uw]LrܶEu?Y,;??rܸqs4Q(DA8NJW_eYj9eY+++vB@X}3p4rmvyNQqh}Ѯ>2o߾j5o?)'Q04UQhtv~ZT5M[_uEZ(IrTje*WnݺvyZJu]4pg>Ocp,UP(mz0rN'ː㸵۷dUŋgggV*7oΖzť;ϣ(VVVg鳚*g՚:p!GDQ$G<ܼqemۑ$q\!ӓi˰=.gx^XXX#`@O6NN9S4u0(,bEzݲrϟ??;?ϲL44M 0+GQd4M7nlq >??.,IREQ ?Ǐ[}T,,..aMm'c]E-~p@"0Amh`776,ݻ7ۿ/q!Q\*>ǛY ^hol?y[o}]0c_U8I 7674v4պiSOtŎR 68JR/(Js{Q(q=۾\uuUz[7>-~zȕw] 7RO}L׌|0ZeYc$i`AXBYApq,8MSZ+ә4d#zʘ"h!,!B0}??0J,I,CF/DO'` &XeY$t2$B>yBCQ$B#"I!!r,e)2aX@0LIA@XUT2 \z50 C 5U0Ce!/yXNQ54}#i~ppJQy^Z$Iwi,˔`}~I!,x|% IDAT( "nK!Hq5d2=OQ 8}zOx$#ev=Vk e[7o\%j*y4hy3_U!)mxfyyzAzІB$IbhFy1GtMt$ ("\iVBtU裏s՗/ zryu}c!X4e äiFIo Q|vl4iJ~$$a@Ckoe$8Ͳ$Mr'bI8}p!!@ x^(8ZqϲUfxad;} h8"r9sqqj]fIl5[#fXXNWŗ/_rJ҄ swrܘϙ8`aZn2\י}86 M۷mt:0 s-`0@U*(:n<`ێ=v<'IeYE=!;;> ]U|>ϳlUdMݗ.L{^eVԾ֝jzƭrT4 qumnjoF^Z6333666Bkkwy뺮uhhM$ Ӑ%QxdI" ' BNm+2E1L(Z3rk5Eu]9]x.^8(QzN嗿k6X=;;˲a70 Xfbaa`}qAqرyH0Ӥu:]ם`8:>nzmIQUkh4nVW9&p4 Ps88jk++IP(4"NϔqR)KLB`ێn(.I/jv{=A>W_5U]m!4Gh4{2x kk++TP1i;aضi9JLFc5^4NA<||Bc~yp\.WmKDQn\*k_P_DA}Bpr|j?sx|?oi.8t-j}ߗi<Cr@io Ie (%2秦J~hZm2LMMc{<>::ydǟ fl{ !_W~ojjڔg?#=E5;[pyjRǶӥ%a~EQ0}cZ\E,˲fgʒ,AiОiTHJo!0x<F(<{ldtT&H']\\xG'tp$y(~zl| |^8a< Gaxqq8Vj2 cYCƲl5 C'O>o4 vn$m{8v:Ƕ8. p,Kj5˲孭~||<NSU8 lF۶ ]4M2==Il6NO9;~~~nFp033sw}??|=aܾ}ޣXat i<89 r94m;=VUP(P{!˸;[.;cv$d>˲z䖖7ٳg4*j5YV tز$e,K$1FʖTHՕR---EaQEQqGA$(IQA$r$mo~sF~ϟGQ$q\Kvc&{^&iP$Kr\D@?:>`ueޒu*B7Yv:GG===,'@@A!X*KnáEssuES_z8,Ce |~;;;aR|4h !} uj诣w󿕐qW" g;C#1 + B)0I0 FeIp,K% b@EQRz B)u"$q!08I(=:` @z>yauk)F@ji2e$&ȲJjI(ۇ@0 <4C#)"W  4M}rHeW@%I54 :—eH~2e4IӌP4˙9MӲ,4Ux@`dIP䣏 I3QUU 0ĥm<0Mc0O&BHdS Ys +˲iaϝ,--ݽ{F޼QA B308⳦i +heʬi4gg+#M#cEVaV dY$_۶Vjf>/UrW^U*76{t:pd/''IaYM~bnܸ1&i50SZ|y>{aFijG'( ajMs¢(rZGN:nu(*:NEG$qv,jשk4,kH%h4|uv[)ollJq.CȬTUh<β̶,VWggaavvvZ$M4]jFyisn߼svZy睇b`2,&T*--̣,d0rв򴦩,Yh6&dii ea5M?iDQtz0¡5yիWiaa,..NM$ɓ'rx,4i.$78(ZYe4NƢ(j*WGglooi/_ @x|ttdƽ{4M999v qon ::BtXeY^àݺu'McYO|ͻ[[a7 `\.r,+>|4l60|>|xme<7hLShPyA?m; CBH!_ #]S337oޘ-$IN/<<|!96 ~4ĝxM$y޶meg] 88 if;6}7Wۿ===LFr߶~~Kx2;( 1SSS9#G4!*aLf篺sv۶5QD*3 e 0mk8Y?z뭷(Dass4Ir\@!vm?~~ttL!V/,#JD LGǖ5TaKs}~﹪.ќ\ \v'!d:Oӄw1,Q)u˲T{05CucvŹhJac !T /%Ti+eKNBXQ1tEQ,4M $QeUL&ԘV۔xCI'^4M4eY !3K7aehiBTGB1 d%.rE݇/%& Wm[**e ,xjJLF}x.gYFi*˒iEq{YFLTʛfǝvA@48cp8 ðRPw:eQj !d`ʹ$Dq }>,GQP}^q5z$IIfYLDU5BWu |lۦiQ$f qtPDO)TTL<! T@!Kq!n3qd(~ByqififYky^{F.!D%!òtPMO @(AQUU4@\ם. '|2 =zTTs|8nZ^ZZ]]vy.z}xA}G?f\UU:ٵKx4B4Mm4͵ϟw0, mI h<EQ4>DAr'#\uTlF:Q2M3ZDZEw2TuaaA8wvvNOOmۦC~珎g>} ,ի۶S(I7n躞) DQizT2 ݻ06o I EEQF>;;˲44VfV8E)j{ٕ۷EQP0ngg[J<ϲ eY=oaaAﯭ HWq=<$Ie^8~wie:ZVffVכ1 C]LӔd 8x^y~BÈ(r?e_qR}XK]O)JP%ϲTGs(t{{T \$;h\Tk($I| u^]/y}Q0s]7 Ck4O|?xurl:::J,BS"cAQP :Oj(I `QwT, Wտ'k:Z:qFy}%U_w`^\! >BeBd!Y ιA:!t7 rYW^(h d 9@@e4MEAPd58N1F*@0F ϱr 2iqJGAi5McX2j2ZZ !EI0M2e)&Dj4L<4K <!05siLikzSDƠ4\\Fq 0dQij,XaESSAfY!XXXs62DQ?jrh0?5]pBu0, 2(Yְ<|j< ƘN$1n6[wV YAԸu4Mx^`Bd$ pf(O8ƑI4Bݸ0dE12cmۦ/H.cfY)sU,+8n.mnnGy#W, Gqӳ?3Y={&d24Ҵfgssdr{몾qCV&]XXZ\\x7M38x<:99&Rqh4ZYY/T*)ݻ?Ν ޽U,JwFi\fRhYngٙZ 0Pdapxm4ff !kYϲF..rRYXX`YP(LM޽Ȳ\Z]ZZ :Nf4B??<<< sڏ?0F8R$Ȇacm۹hL øuBĻaeY!gO>L&zJrrvvK"7MDZGQGq0J,+R.gi: v{HQe5OL4L7n7;[Ř`M5LulAV֪2myyYseYCdYO B@$$KQ0^0el]ϟ0p u K{/_ j2={fFF.j0aɂP- ~RIU$ X S4,ð Jf_umG.c|B*Rʿ'|]g=7vZ )쵝  %(S(r9+( 5Kxi18v ;EQ_Ngێ`E2PM $)e e̲i!HΆBeE"2 1,˔ʰlB< pyѯM^]4 :W g08ei4ܛ5bSP)nbB8U2 ò~ay !"T*q(BnKPBH֓$bY6ϳ,y(J*jyf&gz!Iӧ_It-*H,۶A*Jq #kѣvC EQp8~i.lnn4Ma>쳕C=77G 'Q,AT*X$Y$IEh44bMc$;^w֑YwuU_Un^Mrcd 砼سc؏`m`2!,` %Cb9 iNy]UuYy?FZ>dv拈q.uPU޽{,,,LNN1Y^^> Y,!dffÇ ooX[oMLɲOh4Z_h4h8kUo6BT*AX>ޡ#˦wK/~ ^J7۷oAA v3,vMm4z `mۈc $¹\0\&w3y= 8Jrynz8 :òRt:GGG %B! iI#('!./XLeR<ܜ8''' T*ULR(*@, =!dh!L$F?%닠b $<8×;c7ơ<3Qy@ ^`BDn( ǀaðyG}v po J("QG$5UDq 95 È4Ee 80q\q0H ǀ@ ˱˲ !dzо0 b!"aL. ƘU3y.EQH'} ht,2%EabL8a)LP8a(IsʂL$xG)iYcBe(=߇ bY0,!q@Deho}k}}c,:8lrRz1kNsQ1 A$p<2z~A #rt:ϵZ-*A 0 ^t{V;jF# iL$ pX(rT9UUgEeDz (v|UMK$u 84Me,2>1q' HlvrR;9A,;2Mkd)Ju s"˚ "&8W2a,fl6뭮jjZ4j0e7s<2QY-)"433v2$yK+|wޭTR)۝h2̠?99X^@SUNMC= TJ?w32|>?33+AP`Ywnnn"0FF"^ٹrJ՚Fv'+I"護߮N~.1~LMU\3Mk0heeonnpw˶m]:;W%IF,{nnիA  E{~E^vg(z420&zcqݾJlmm#8ڶU*li4=zhggguun:%"???Q"/n7MdT˟ɧq\.O|N$[uڵ}@ǺnZ-@2ONNjQW5:KŢRdN!D1J>rTW?1H$jiJijGaardR%c2!c41+ ]7M(K/GGR1Z㹑LJR\.iJ p04|?Ћ+Iڋ\_R8>O_>?_5> O7T*d4Ƙ}#!`@>"` _ u8mPq}IeYfEt[QDa}axO&lw"Bd=I!!8ܝ Ts(je=a$IR3s5aLja4MK&)Y"HAE&ts,B> BH0`Ȳ,˜)8Yev(@ db86CIU\T$zU:G@!xKRΝ;]LJxG(=ڄEQ(|^9 E؟c %ql:JX ayXǒ(.//WUAм ˲(JvB4 Axq?[E p*Eh%(+R81,DLlۦ\802IaFqL(cX%EQlǹ}Ԕ(jRߓ_(vvvC!D|8 a$INMMiF-BVVVyje\ضQ:yW!'''aQ0 $ "vl1MaeeY@m`(/2Ŕj1^ZZuB !e%%+K\.'6T{UUE J%a nmmٶmYZ__Zc= @͇0LMǣ(~a//DQOnT` dY6 ju!19u\>Xu{;ju?ݾ}{|$"qӃ0(e0!`dF^LT*%p<0ޙ)=>,XO)˲v&˲@JyGsd>DƂ y͛eb-ˊYˁap=ӀL9ʣ mG`c8lz~ ]8ciXe Dzcϱ$&Iu9(ͪ>" N;0GCiK!@)}a,8>塂CAPdC v8ʕ33ׯ]_a\.+ A0V9eYǦ?(Jd2y.B,ˮм8ˌa1I8U555Uv;V8? A!":5;4B!u0]$ ! iMQ"ºnPz=R۶CSY& DBnB3,+ B"1$KrB!Ԇv}&?T\(!)aFy ZȊria=ӽnh SL>H2t<B8cBٺ ,ONNC#W^}nBn~lf}cC7`0ד%)>c?0OHtҥ[O>FFaڢ4j5ׯ7f5==cjÏ>hsѭ[O4̓Eiy25xgK!a!DQEuI套ͧ&mREI `i!pzz@D4Eg+r&u-!ϱ(]t\T8nVlۅ\cD2 ZVFe4n_=<: Z%T*?}W m=rLӂFQ$ITU}o@(r(rLKl>Zzaaac q;;Z}uu\.c,Kv+ƸX8iU%8nm IDATA2ͽW^ p,!!$\6K%A`vJU] (W.-. !2,'BMS{n[7ojp,m5[P{b\eҚfFZ-ziڎ+JJV֪'nr2] DABy ??_KF/(Sc9<0\/Lןa~)pAg81ƼQ(4z͒7MIjtE.oov:muEQv]' h 5UKRj!9eYYYȰ,}pGFNmQ3 B"5cA@ CKA0 Z$}pPQ(q1 q(i:D~#g>-,%",8  RdRItjzt:j^yq 1-+ Rg-# PzԉD"Q1EQ<+ARzo۶:nRU>="zaD C+MW C4\v ьH$oo?5ٶ1}_K(]10 %!BWێ,^GQD@ j{Fia`Ye XDOyT;v0+t1}c}饗uAHӴ\.w||,IDy^P0MKeB8W\;<|p42._冺ot:0 5--HDq˴nwu /@8>eN_l.//ϲl/;sR=W_r`{^"P5T2d {[[ZMUՕWۭ^1d2x8.w7 }4u:RDY=i"mYV,2 vǡﻶ3 dE. ?D۶a=nR?)!4zV*ҚiѨV!}0|wtSˊ`^F|~}b`zXOa3J~1^2}ps.L|~rq2!E)BBx{h#{ ?11A 9de(O\zYi-("Ş?l8OhpSS?{% >=rrxу/jˮ9g]I[?^kӋ\\ x_/e ~m9X'O~)w.N΃ǮOS|A>}G&> p L%xg۶LPpxtTE]Y-pprrbF\ ԏ)BZt[֛?~K/QmTZ__ދƍ\.;Rȍe|ߧW|Y t[oEe1sݫ7[hO^nr9ϋǵZR437w޽陙Ƀmg2O~~pg=>>_YYt:P(@p8Lӗ.-lllP~OSnl6kԷwSt P:á$I{{;˲if}X/ lvllb8{>t]wff͛ ;;;~̐eSh kF:d)L~.--{M jQollLL>>461Mc,ONNAl6 LL.//oٟnwbb41N?ԃŶm$Itah40_2>R}mSoj ٫_u9y^3<.TtثX\0$]zHZDB8EX qC9!Atq铝NN~qSqt:"J$ Jr85)áaY 88}?,BQ BTi,z=LD^ e@qizpJRY!3lE^BDQL&$ٶ !4gMb!W*(X*˲ǡ϶z|=__A05F ku:q #ϭ}\VνS2nq7>|1^\\:<3ᇂ 3=kIZdra C4- 0UU0U㸽=e..]>,+7oެT*^|33sra㌏}qҟ7;;"iZѬT*Nvd.;::t:drzzֶBj7iaZQ=cY6NB3 svd2DT BqT.]F\[[EAׇW^][[O<935ytt BSQU^~j:dw&T*0?06V1&$$T*5??OH|l./ܻwﭷJӲ,*˲vcaoݺ {DZ nĸ˲ƍw}饗R*TTFZF~r<>y\;!,--mooFVIJlJŢnZv:-奥ZqܥK qLnݺ: LrppL& $ p8 ")Iͣ!cccœ !@1~e|DgsE)ǯr27B ?I_|?6ϳ83ߺ[l\(TPH,l֪ϿxIyB! ]ZZb<˲x]4 qcujj?KYd( (CP99Nx饗Fmǖemnn&+W5-ܴ,owwbb,CV>xaau8q*JSeРŽ^lvYcK NoDx'2}F(T.'"g2GDA"}4tXmcO\g~.{㍟j aafiJy^XA$ ,rWUQ+`7m^z^6!ԛd2E|!dk<ϯ_(kUI6.J}GOr֭D*3O/IQ??p<rr m<ܤX3 GB 4Ot+$Ydy!8Qd2NC2Ax<^< g[Y2dDv?<9_O0~Vt3Yq[&t"aY}Vb3eYY(T奥0 SixN:/,KX~OMR(  (1AД Xx  HȲ뺎*"bEBRZ4M/.Pc1B,D8:']"*}CS_e) BHQ,f2شE331čAh6!!S;;hIg''"baw89X,^w߿_+JUɻ; <_e4MIRqm{{{ jQjD>oZ/󎎎.]vY١AU*:Vބ꺡t:].777*cVL&/]Z~Uk֓d.YZZ{LeY\ PĉQB//MOO7C *Jf xbb\QVwiZO>TZM|>~3B xr]OjzQ*\3Mģ}I4Mk6ۂ7oxɧw7j,˶Z-]׳Z:?xH$Dr^|>ϰ0 }hDQD xfgAhZd1L&EQ2QJ 4Ms\^N,S mY$s}PY8/&q\/2,?_>n_i]Я,TS' tN8QBDG EAĘ0bX?p&HXb<Qy^@y@IJ*Q D1FA 8D@Xg lۍcI I L 7337oLNWVVVV#dYd2j___vcY3<òB -4l6: ƤRZmԯ_RݦH0lIIȊ cP3}h# cREAjZ0p=1aX L&ficÁ ,bZjڝfM 7&Sxi\)0pmnolWOw:Ʉ!~p؟L(3hhbL0&F8U*~I':8V5])Q ðs9aF57?`ށ: 磑^*{ `BɤI5sl.N&LPȻdG#3aǵ,۶PO&S9v V fkdZQ C5N@HU!m%Pd9ǣOO%Ǘ\|g;`ge䌅|U|'jigge@HmYx"R(c&YEu]״ \׎c@b 8q OM10|!APϳRIZF5w#؎G>?I 6@0(ȞaH ,ӌHE<,T9ϳ@n/(&.fLn; !LOM2_t_˜Uտ@{w^[Z^ҙZ}l޻?wGƒad*qeG_mʫph֯%0i+YA s]; `^>/Jq'%)QYy/_n6Jڵkccc}G#hA뺦evww]~gjKB''p3== BhvvV䫯J%&'+=s(EuVX8<<k46%|d2Jѣ|>~a`0YjZƍsP(\|ʕ+^wQUl68558Vӑ$ÇwnVs]hAf>,u}qE+Aa(b,V*׿]q04-%I-^Z^\XXjݻo&B( IDATϏzVK0!U[]]%=e`uuummla ( ƣ("8tJ pdKKKD4*-aK"U0L,R Ѝ'V9K% jUr]^;88*VKJR1GՓ;w(J ,Brt:-IR٪_וɉph[[[[[f1m0rł eyä9`b6%wbgge_{8+ #]Po=qjp8v$Q{:a@%"ļ;BA~Y!I\<),|떎85s"NU-?_u' 9;WB'6 >-g!(Z 0*8G,Px=T*y^F\C9!(^A@e?(ȇ ($Jٶ+Q§ DbH7=ʆ E1f:1 =ׁ( Y xg23L2IB]׍1,{_8a8XeYàJK?G0xIo#*|yԶWM_~!/`;0 7d_'~4Gs՗L,^Z=_?yS/8۽iYs,sF|A,c,IjMt9PEV"Oj5y+׮qTc9B !b@0}?08-v< nE<]vMVAhW\Iw EɶmEBmGңol<J}X~7|rpH$>|xjy۶S0SSS67q`SA:jb>\./"0%)ӺV(vvvBמx QVhlnnn8_E8B:h]'hƲ`S=N˲< I9-rr|pp~٤^Te#z,'TUk `ҥrݥzu|>nA.Kj2 }UU;ε+|~(L]}E7G# ξknmmz^L&_xᅟ纮EQR({{{qv) iMOO#{^٠"|eY֭OLNNr"?yPT nY#/]t~j^xᅿ|z="Z,ʩT*J<ѣJejg|amv{A G9f-" ]\\<>>v]P(j\.o|ݻw>?55uʕW k׮<#J;T666jZE>f"?YOՙDQzojFl1~ p.Qybx>p/'|ւ|O,cbD:~1L:BX;gie8ZYJ*d2>P(A.at*Nu),bLZ G1,ˆa`G>eVc=v°f'xbӦMjGJA:_UQ_җz!Q_y'~SN{TjEq{Np8d, "DR4kjX.ʊi?r!<{\02|T*uoKKl#(33G_;Wk1A/@H04me9i7 "`2lVGPߖ--gG0h4~0L_LFQCCCR 4nYeUUws5x:.' '1uaYcX^rx|~~^׌xt||< 8p`lbyrrڥ8m=6n<::k!lr)7nv:ܾ}0BhppGPnO?TUT*͛z7\R___t̙yәH-kl .5B!Ñd:PŜvB8v\cccXLUr261fn===EM{-bbJ-9It]W`0XT6lꫯvvv#f{9t]VB) J`(3336 ce!la2:w TB#A0 j$!gy0o~&^ǿʚD r٣l48`N(d0 $+Ie1&RLDbk+<ʹvZR4]زcR !c̭!(Xв, XXe%K'b+W*pB`+($yd0l6Y9,n^JiZϵN<?1V4L0c $dai!ݷЃ>0bX };thf!!HY <8V2R.Bz^x~ԉcW _;dtذ R DA!D<3`kJF) 2F( m~ߎsj{O 8J۳cǙ!]UU>u޽{;qr, o۶mff0zr K/300ϗe2;;}7t@VEߛf%I:}4ڴiSTJvz87Oc1~X%'˳]]]ӳoqlƍ㎎ӧOL-/Dx<4رR:==ɓK69\pa~~EUuUUc>@,ۼqkNg࣏>jBV%}n׫+169=U*fdvitVDQ2x<D)Iv)gϞ5B`enk֭3ssĂ 5-@ 0;;k$trio5M3 JKIJ[v|t:;??_T;<<޽knnT*pJӧO/-&?9899Kfgg dkfzj4M] !͛7WO$;:d&c,i,KRHMÁ`Z5 ,"˲ GCCCTjlw˞L&C0- xr2`Px\TR ø\YIζ`a?vk145]jw~wҩe,Eg~~1Ңf#h(Ǔ+bQZp˚ ,vdZjZ'FATPWVØXJ eU`k.O;-Jǯj h2{ 翁"q͕_.$\v/gn̲, "Y00(*Jª k釡%Y0 P*˲1 #c!|cN ĢbO0V- ˲iYLA( b]tܴ .@>`MgYBBn[Qr*@$. ꎅ1,q;x[;tle.Ɵ߻BH WI5qd8o~M -ݝ0*%eˎo=:vI 7VCC0GNI!rku,?<}/D{ߪj_}~;eݮ??0M~7Y6:pŋc$Zwx=?p?O&&&Jҽ #{WU|;wV-%*3) 䉬>0p8:2%TUUUUsnmr9^hV.w(\>[x`#(0(c"8XW=]smoeOz*@e KJ'[P){ۻKE h@zvP kzI"(P '"80L)&P)%bB12\o4F؅1šlw|ߟ5M;~8EP~`tvkyxj,vмJͶ}k 6Ilzvl6xBpĉg϶6s=A1335551 B$,--1v{8lmmFHE|qQ4M{g!He||rI499 Ax{zz"H{{ǎ4mff3@>};9r1Br)}c(X,lٲeffwuvl6X"xgu]okk㙞GGG{z8O|>d2^RpjP( kEI0`0 _<\*r|X44]h$(;E9zR $l6Yfff#~WW(J p.jDo{"2 P0%@@$B!x.d@ I(%3L`L3<ńn$Ijpo~A*XXQ[P@0.T`cLDUV1a $ bhnw8nˎZ[AIA04]"R*U0 " !&RJ hf"BgGiZ<ݯU<"2iKT+0a3B ڥoۃfeCYbRonU+=@S|>|ѦPPz7K7da_kQ4mnn-v5h5mv/ +ej;{( ChR_;l0콫~~޽{߾}_y an^t A8/^loo߲eHK'l 0lܶP֭pA1V'Ɗ2cA5c凇KKz}~a!X]gfRbqoڸyKV͎ʸo 0MCT.bN Jز[!dѢ]-2 lpz#B@T-K>_p8D&.]h$-($H$BID a-Ly@O AeiBћMBsy]W*BaƜnwT*W/R4immK&O۷o~~qee3\xQp$p8n#G$[;;;_]VquL|cK(ھ-}}}~O~jvil\UT*vGF.^>\>_ܻ1ʥG~Ύl:}ɶd2953f^e1뎿yz~}w\t:L&BNq\0bREIK k׮X,vR8==e8|O~JiSSSR4$rn\`0866V.u -.96OŒBPTn,&t>ymݶ}7|X.9wQJqN8JfU]7,jSkY\1nki."8{u_ò~%׻_5-W={ޱgU 3W3wglSn3f$ duƘ6- 'Dey XUU eQ&5Ţ.!Qs2+Af<üi"Z2cs6|o 0 &JnQU!DVSx@{[\C 7mԯL?jHqT :%PF.>oC֖JD 4k"Q_?UXt_/^bLΏ {}ϩTQ5TZMmsQQ p D\헺 e$侼U[n *]kLHKk+=]vY|婧:u._eB w:DlQ}-1-1f].g[[ۏd2r7y}(\~IY"677Bax\h ; IDAT.^WjP,rRf 0 7W!~5^t㉒$I&dNwhqvAAaka~Wz:;;]./plњ !YDD@(3 sTis*܊ (eBD (ɷߴ#{wΗKBQ\.W.58qD8r9JSJMLLiI.\@Hljj*˚jUQd2@ٹcn\.z===;wnmmmDB<9@$=6Ml퉥$%,1j'555 P*a=== ۷/.. j'~gnhf'\UUXkke~Æ ۶og6?qDggn|`p|||jf3u\.ٳg```###s.!c\.CܹJ^_VyR)x\d2hSRقk,NC,RDQlnn&{2zks4VS9z>I%Iz|>{CEn7LSմZj^9P7iV|@5m41 8x˿Jr2޸JaJ6. R#dY0TKsk0FEBDUG]y `FW4M(nѴz258c@ѵޚfaf(&H@[,rBDdH1a,Bm6V5fc !2l6ɲ'䪅#v]i4G}nO._sӀ?/O{DNl9懀̀  ܛ([Ʃ]L[A,ɲo߭o @(^' lͭeΏJ ƺ.[hlNqlZr& ]M&[ArMD_޹"RGGGSSRj>P[4$Y=W#JFO}S+˩Cyoy5\J,-(<(s}3d1&2PrlVѕv0jg;lBy@fs[6mS144Duvv)4?r1Luɩ8ei?ebl #J&^Êk`>C׾|2$I0fMb_ Z[[AݦkB@)D Ѫ@$B)&i?y<$"$`(t5ML.fedbk[;vX:=9mbT?8wnXp81T⢄};zT\еySoGGGGG'!tzvnffƥ8"Ȇ BlĉHpX=b]]]v˥86LNN&@ {n]Emɦs/~IHpPJ~Ą隙}Ng,ti",--8fJG(``x]Ν9s뭷;E9}(*ŋ].4i{f344$I NrjsP(S3z#H" -7gĔ.UU'''1ƑHA dY\lzjj !<{avT*j5έaÆ楥%#ITKK$IJX[[[RvKR\YZJO躞\^bZ9P4-U*'NɬtwwsXv{. oor DCΎ XK$IǏEb6,\Ȼݮ;-BKB,KG ]=sss۷NOOڱc~~~i~'@`H2u0l6K(mGhD}"BlaҿH f?.TJI.gE[m}[wweBU,jwy)Wk,4AydB(eR^ٷCTy!R1)Ccv֯]vj !BV7`6w9R0 EQLӤxf 66779sUmlm޼_l޼iZ?ҥKMr\\OLL77E7n444 H\aɧ[B 2G@Rp1&rT*283 s{| IH)3-Sb`@GQDBAЕLVSLZ.²aCg*j^i-A`8FQB9i:)A@)h̄( wON0 O6W$HT]A B\wcwe2A nJATz~_L:X,"^fkqvv0m߾w㣏~XtVJVVVV~Ç[[o@ Æy^JacccNgrrٳ7oZAhnn޸qㆍǏ,X,67G~eYzZJlvuǟ~龾-UVA---Jp8LfW G(JR|^+pxƍp'rqM}@R׫tzq͖c1lin߾=Qy?%I0)R,s=^W---iEQ:0M[6t:^8\*N fEJiOOO\v:[GFF͛˕RR,_8( F$Se#mrG?QB#1p͕J\( J"IBww|pp kٽ{ʊi333{n36:fx<ҕBSO=%ܝw;VVV(KKKZ*Ufǧ&;#(...fSSODjJ4moo?yt&)z][ nuAfgjvG";11Q*W*BhȑWj\ ٺˍ+] o wte\V*gk 6W\ x HJz/fkZpP@ܽU2=`"dY@桱1BV $ J)&&BD3tBB!_xelYݪ(ɓ+[Hq2hYB R8wI2D"jH0no[`R`!$(rIeB,˲ p)tZUwer Y\z4}pyGKd`;H1A IZN@7-'-,MA?p3r.dU9oB`Qm6 xNOܐVS87:c2 `\F|޽{``Bcvm_ߖ+'|S7Bk"> ӧN|_׿0-?>?eK/u}xxxdx#v常HB)B"A˰L~,F`cccd[V"0 *` p&`La!2J0J_CQXpx~ë] 3!Tx񍑇JvEDcQ"'jum7@r GwpzB1&L1U=.հ(F%=@nfcչ|u˙t88jesIa^,nlRu\.sh^,ڒ$!Ͽim^c9r$+LOm۶uuu655QJ ԩSOMLNN```ppϟ??44iOgϞl6NMMQJgggv{?Νcm7|\.CUUbOݸQڿi&49IǣٳgWssiXLlB[[bsOSScl|r|yy9 l6˲駟\~dzn@=O&~g>uFh4u=_߸q`,ҲXz7\cNWVVVV,r{Sr\5M۽{w)޾L&#sF΋D)EHLRJڱ3D"XXE.KΎJbܡWڷ<iL7lǎ+J~ ׊;NrfXlkk BmvR*Y(gũ*7s16mn[0۫Rq8]۳떽}_TTFÚJi`` ˜ZEf&'b(p8p8@Z0 p---ƧIxPPĄ qŘ! _%n]Aĉޢ)p:\s7#vsmgC+lZ.aSH?2BH^o,˄9 pZDLO8JmiifAIꪮ7Bzjc'1^Jah &.ځY$I"@._Y.Wx44M%Ϗw!he&pe}5_-ʘ!dUpPk3{IoAb~c-Wfi (Ε 7.wlr&&T&`ծp9lBkj&41cUU^F\n ȚUEQ]- SŃ^/ר`r\n?p}Ylg˲};vOq9Mܼy㙟_J$Tjj#~ԩd2Y*Jf糡Phy9]TߟoNinnXXX8ybV:NXT ٳxt\Vw}7{4#G_y0h4ڶO?mFx={t]FrtT4 #H}>O@)MMMxmԺjL&!q'N'd2k׮`Я(ݻwkX`0(˲ZCň|>߉'rǏ2Msjj… ۷>CЇ>\.ɤeYJr$ "njp~EQ*$YۻT>M XaK&b1 ˲@-.!dYnnD" d\s$XPYH HEdYH}DQ䠟乸% :\ | BAi 4勅wg󋤽 cU7GϬ peed8Y1f " O Wwm(UeF* 82JcF) Q3Ԁw(ȍpڜ ܺvݩ8kj ( }3ٚkf0pJ!rnڶm'?#Go4u:Z %4. M).C IDAT,@z,ՠȆZnkk?l2ـ_̠Rui(F H#Jz?!hO!-V1x^q75 h4HqnBhf>qQBxdY$[[[/7Ie5I4M j< ].?EpB8*g4p.l&f Pժժ!Yӭ(J\4m\wwzǏۿûw0^5&3 OŇ~o|d 6VrQ_1x&ʚye>s1611/՗_EAt c!;ٹ??o^v4HRJm6rRI2l! =_w~~uƍssszнJ#_<>=g}hZ%y%@  )/-mSno{K?-m)e}{/MHHHlvl˶$K]Ig{gf?|]vϫ.tΝiP_ҾGnz^-sټZ4 JW_K(%$ mܸq˖-cHcD6/I(ڷD" .,.>}z۶m@xk]4P9 648l6jƊVl+(J}m8@@\UU0x PsZBHe0$$1F$I 0MBjr3!Iv|$r;vntCOt2{m,6y*CěNmA1Kݑk!}t"P@hI#"7Fa<%JF1P̨(A5&æk%}C{ l2E/Le}s׆ 6Ȋi,kKZw/58n>M瀄RJ)np>wQXaXdY^RU.0]࣏> r:kgqpXӴW^yezzOG_dž7*7Msvfz4Udixl!!ɺݮd:x}qT}}}(y<Qˎ|ߠ 7_L: =r|zr~C@XTVFD_g~/l20,VEQR&Q](Ge"1MBYS$('a}Bn拆$L,}^o4HZDl6;w110j!Ds!/h@ 1`f_~I,%F0!㔛ּҹidb~~~Æ&RT,$!Xj٬$Iv}6M3655MUUeY|WSL>?tСC.\066V(Z[[$IX,NL&r,?q~֭[VwY /,D1sOKp8E ҥ-[pWZáiZWW{3gxf'O{nA:;7=ug >_MCCիW;::VkX\0x +3ssYYGFF:;;\Phǎv횛fl -Hr9YzUUYZZַ59PWŖ"[mB|>ǧvBQ|6t:}wo޼]xsƶP(tȑd293=+bccȗGyx'dp`b8`0Ο;fggL|x MEQظqtt4p;4x<rjq:\axxjU>O'?)~wnnĉwŋu[֓'Otvv&Zr94ۭzI\((W g0u0aB+W,ˎ]vbO(UQTV ƀR@ԥ{ت~ [3kMT>qib97'8W?x㒯ૄp+QJ&Q+5a 0@x@}Η4 .b&S ݕ t؊5݄"LEfJ+W:RT^ !$IӪ!!dYEz(7Lj|64@}~95 q"9r+Cz &P inQ q?\?6ƀMJLS[OD~H&0kKuٵ;x<u(I #Sn#[1\nbQEEQ~7~ _^zߨ|f8Zv^Vvén->"Ȉaw;/ ? >|Ν;=vE'|iGcp8\S㧔Y0.ٳ7mR,|3>={|f l6QnEf_{ie-LqrZdYes7YYAD6olke߫fQԢzgwc<}*G"d"}wm%߉?ұ!%\@7p^5 }i|N8"˼|.kO8uJS0EV \yI=o[b`FJJs< QL(ajD"QG|rr2 ʲbNEQRᘝ/ x"9ylٶR@cp\с˗٬H6s#B%J0,K8DK/݈bbw=nohd$ Ǐb\&)NJR? 6/۷/y<ѫW"01`GG‚ܺuX,BtNLL]rEUD"Q,cFP({ァ^?=33ES33v=  9(ӳnLOSd"zccKcכ7w/H469!bϻ !435aF0Tu=mܸqÆffr˱IA(N$r6CQ,蟚fl۶ٱc:t8 K}x0u_MMWWammgff'v;cP(H4:2vÇx |na!B *5@fZr]׹Bs{С~wtt<_>HS]v>|?O>_Ͷp<s \e`S@z޿)KJ2w[M6m؈ʪ)Vcܴ1|N$nC_Y)d/n"E,ɹ|˘,Vbςf"@ VˆYt9Wi:vu:X{ٴ "1Pf^L\aVRK@2LS744Jbw޾#g $&1"H;Ju\/~o{QF)ӖaB RĄQ~0DL:u*Jvt:Ϟ=rv}BcccKTJ3J*Ņ%Ў;vM[fff`yiIT*B|ss ƔADnllnhhؼyW M˱h&u=Pnaa_"L:ΣG;w\.C 4tu|l&1M"AD,\X|bbXʕ+(B}-$A& b4\]P,+LNY E.@B^v $Z,]9E1iFT:E$fb1)jbA $AYUT-B^#g'$I*3cT c]4]d!$_K)pKYպ=z"zl.!h2Fo2 !Aܷw'4#_JkrjR 0$ FPcMf444AAט#MgtD 7n.erwftdYcdApAX+Q5iFXx<.\ʋ>sŋWVsXzS8CbU!7%B ˏ??><<ޞHw~7 ;uA$ddYz06"J&R5ȡClXtP(H]Z>#9r4ٙ9UٶeG}4Jtvv.-->}ommm]B!L644l۾ԩSZ)m%L666 j2Ϝ9ܺexx6\-xo3YfR MM OfB[6wrLJ1!!nw" Pp 3B)Ru \.GPuJP"aêRJ"4|>?441^*}`1.7^A|ȡTknk@"-Q`!:Y`kd p[{'Xz0eJg*U$0PI%@*5fAבa 0 j;܄+ϻt`c{|!O)1?z>חC<({pMĘ$YVhoooF"*eK*blb%qՖsKW 5u?r|su:x„twwE3jṓ;xx>>ۧZ0- nٺ"!B,_җ \.'Boo/aCs&ҏ[vY I8T+/ͫ9f"[񎭵Vxr`PP06N, n jFE֩b QPQdqՉnr Xx2eRB##}}kUS vvy[7JKBiJi(c[>2J10Bn 352EP.' ϋBpźښaZZBHwwwmmP(\reÆ W^k!r~zgffxt:{yhN27GF[lZ1791,jCpgϞd2vsnn̙3\pطK%G^la!JS6mTWWnsNUUO>}_PJڲx.MRi<(>kRi`B<c>oy)Z]744˳v;Jy\h40#GO߾<8ꫯ.G&&&boooرc ADD鴇˵C .)vIb^s#"`WG7W76??mlX9Ϫ6cb6F&:K$Ċvu޿hݹ\M=%ٳoY,q3K;m"bLFU!x]Ij`ij .2w}z4#b 0cT7I!$ Iozσ'F G*l ~10ÄQx @74ر#Ϟ?mϞ=|1t:G0f9\K. Rw܉)ٳgp24myyjU흛۴i(.\=piã{4Md2dAzY7|b nb) w#ccp8|}4M+D"#˲f4mbbȑ#;vlo?|(ɽxMo8`XΜ9׎۷駟###L#Q:u -Ξ=K)ݶm>d9U|~b{K,˺|@hddd)Ue-|gffX,eE7vd"pl333MMM8ܮ⋋f557& Iv3b/!S٪DbH0]ZZZ.y^K/)u˭&UBz#=g(۳gw,yh0i1F,."dY4M$MLLJbo馁 j]. X-DQL26dtz2*NTE5B@%$@/dQG꫰ 0s#;zhn` Q IWp9p XQfE#v=DW^wVskH8nmXVWQ9՚B2۰Ws |U3<Ϝ]6+U;TqV@HZbSu@4ଈnȂ `z]jF󖈢d$I!qEC RD\td,n=iCykܶc0A-h k5s"C#``Qť*#B+80E@(5i 7]FT .ob4*tUU7wz;ڷbH.Nu)皀*Gz{TUcbǫW0v36:j!޸f $IޱcGo_ߚ%.{ M NGc}82?;=9NSd>\4ևO<ο f1Bh9| E\55S2 H).V醡|X= /|_eHjj{"(1J Fˆ&(xpeT~U8.fe Py1FD$٬2BBV9kj@{=@?\SȎh2S˿f@)NJؚ`nT  T);.G>_]c@` Fԑӡo?ٰT:RQ0P(vLwSf0 [8*gb᷽m.:66NcΝ0v 1%y||r8{',//e?044 UUx UUUU ZV‚ժd2 ssP(dvޭi,l6Qfz{{GFZZZ|DӴ3Nׇ\w<onnXZ46466u]iŋV+Y433vP]]7Mg bO>-[EE1HD"Jw=s. .-.;l6 *s(~oaaAQŒE }}bP۽{icӧ^o |yz]=??OIf骚L%k­\.:f!]YYeY$&N ]Z;x]}h:&al:u*ݻX, H xCpM0ƱX  HEQEXggg,9Q !l'@\ &W@JE$y<HFY+nnjSu'./I|*WWtS ^9P͡}@ji39ֈ2{}5W=VjꫫsKW~ikrzQ_ dVʶ4*+ fju\STʲlX5T*c<\|>O0յ$˲,k!20@!R8#@)O(2heb`I7*:Eᙘ4޸у.گH\ 1hDP:3n1< B唳e*O("BK =0OS&6 E$!T&)rcI"9}lg?-*Ӊd=pŕB0!15ծT+ :r;:==54tx䅟@Gcf=YW{&ҋ/7\f |__'~7NH(Ile 4Ꮮ7~_l)OB099L&vg({e}?lݺ=ݻiZ_e_?pY+~tp` sٶ$I=/Dr|I^o0XL&h.s \$RF7cl(ֽ{~(:lg...RP(J Mq@@Ӵ9gXV~× ON֚>|tŗl4իnfP͛٣u3?ؿ"[5'O4BŅ︳ԩ3Lu<FKs H$ ޹b`J::UUy</'O߿F(333>OU] 4N{\.y^Z<R*\,j m%B ˢ$Is󺮇:;"?PORx$b;ֆ &X,P(cH9M2TMJ{xt6TK)TMdݸyh/-ca Ƴ%$NOԐY Yu8Ɛ)3D0GFfR0%X?wXA5h66 { %KjDP(7 0͜+. [HcyJ# ߷j*8157 rE`˾v옮`CtdQ^'V2)Yrz TMJ H3ƾz/`ӛZʲ^XwKɱs tBt!3t55T=xҿ{M?ƓI ?-eQd*/VUQt&B&$L@8t82sϽ꫟'>8qرw#w/]NƬl3 !d(!QD8WL:jٿw,&ҭl% -ʒjvcx[VzKoA HWKBE?_fGӛ7&XGX<>=9(rk`crxLMf'g&~/G>ўo^(>_={]3&30)tMRd4ȵ MMMMSSS6m"(J.W(Jmm;lBanoijs8l~{KK˶m3g.^f;vlw:}}#H`0xСYͦ$ v kgff"IY\v{>9W tumR r9GFFBTpeõ~zwCCCdx"qF套^ƒD&W^ ](Btnrzj|l"Z,Aj%I=(\Rp[ZkA9dGFF@w/3;wj6==]dY||^Q,###D, ۷1ryhgX,k',9 9ruttMNN )b>x38U4m{W-}q3g|>cvz2ϘdrO~߸?ӟ3&hddhjjBcǎ}$IR菖R6f,E׍l6hkM$)"Rdq1*4L`ZKEriJ)BaL0pm!4;3Gu:\d:;UP1^3r+HE~J0vsBG&uF? IDAT{;sˋl&ED UC׸i-L^ܿw_S.1.#\ȕ$<4" aDe08{}85g'M80d[ո9L6MϿLgܹsNsŅY4;;3Ig2tOOO>Wب(ƦP(t=g2s7vBa#HU;v~C#._fs:7H UU%_iin޳k]{===|gl(677:thCCCc>5ivb1bXV1&JHDV*T$IR0iܔWh,vc2[#"Hf$N,LD킱 Ŭ@yS:ўc miOAi^$g-Hyq(-5 c@0S)n D(c2su#Lf-07I4qz;=}SgAx==SO\U)]nK)T4O/!RR(VU?y{޽[0` .Mq Pvz]ޝbQ}}ԧ>㏏ G?oۏ>={y駝/3QOCCCǎ wyg?Yn5f$Yt:dEQBbQ4@ $I"TO8MTr t5\vp*0ȊA4ydٗd2) r1_e-߾>x'@z/=mNLHeUkw٬b1H)e̴횝ohhn_"D&"&&-c`7# "!VOc[C&9f&[8jftSB(GI3#6;]uferT*r: ][6_w} (\;#HbNnB!~@] 60Fx滾HssQJ8$Q׍_|_zuNvww=zryM3 ݠ477&T*Fxm0 ٳ/266Xm(R)ǝ(NOr7BH]}nNSx"UTU011r|R?\.G)YR % LHPWUB :bPXX8T&o;7uX,NLO Z$r HմT*inWUʕ灩)0"+cX8Ft:Nv;"LLL8p8,|^CQͶg>ˍ1V ՅO|=KȒDܹ\NӲ(+]1 PU*`'ZcUKG* a T4ՈSurz`pkܹ5؆gzZ½%o2 -kMs7 xTXam+,{%BgR XYo+8N嬮rH Bbdq!dYTU?sr|Ng*00Qd (a'_P/ FۺFa޺&J!7"  C\(z ]'LQ CvKs?N 0uUfz0 *pj P (bB"f7/emp%ٿD]8,IR Hdo~WOou4MR,PaWVV8 bPЯ}S8T&@gZuݱw(xK?{o$q ~H$(ɢ:f] e{5c^1cdž3vxBmR1IQ-AWU]]UdFdi:̗8|?jĐỹ37*R]܅ 6@rϡGb^y}ɃMу"Nij|s' G}d~_em?^/,,L&gg<\4~GZibc{//28\>ͦ'|2KLLL^r婧Ei'NDMLLťK yX|>HLNNql6H$pܹg||\k~ﻥ:??_(>|oW 5ػwoMHČEQL(&z.Qynړ%Em>"BPfo9 6)T@D3#ٶ]*Y@i=+m?ZM׾ҸI?$Ywe%mL Z|~aZ7X_dooڠpQMy.Xx3^۶~l 3Ћ?s_OC3ÿed ( |XMr7:?^;ަf_7~WuttQv|3qosʯĢG===O?h6ug}^*RrN%KeVMMO|0snm=BY!#vRaԷܱwήLsضqǶ,۶-aQ  HDRƵ٩{_?PeUS5vBFGGl}jo߾O}aokZUD#5dZx,\`@M @A)UG@fGGfVBtx>ȚuwwO}j~~~fffeee~~>33冇711Hs ._캮$Z-j5yEB‚8Lfrr%q]b=|҅}e>c=q5 _tg4M8n~~Z>B(um۶a6xeeV($q7'&j:TuETRNCuwwR EQ* D!x|ttn޳kƻa[Z8mY=5@̉fRaJ)Ƃey]*lYs\0Ʈ!AeWUUjݙLnyytGI%T@Q(9(rQ<zM4/2=tj!lM[#𚪀N$@ o1<B]J|}BxNI:>=wi89@n)ZL]*D0d"./}u'fFkk.P09juV>Ly#XB@Kyɗ˅ǒOwsw6UQdO Yo,wN__7__mM%b ݻoll㸯GG?o_b_b/M'㄂㺎ò%x6b7y YQdAl0Vzdzz(!eYm;eχB!9%EAP8J'Zmc؏u| DEAj(LB(RUx> Sz} TCjH R@((h>]dC:p_O az{)QGFF^u֥~4,;L]wm^(1^]-X\p}奥7|ciڇ?t:}ܹZf-J$}}}.\pRKy޽{sE˲4Je9}Ȳۧ_y啞L&dŋV˲._[ PEm۶޸qc9g Dg續D"Q.mێvrW]_Y) n<kn [+J+cZxjucL)jnA*!${o⋞8^ñhRiX< $Z2==-ʲ I<ǯʯղZ=mf5MFr>-v욦&''{2RTQ9idfvc[8Ay3޽ouuu׌F`@8 m`f C)A͠X!]ڎm!0^+M25gHE͘4z H~  E Ά `b;ܿ+L˵'Zvڴ2syqY6n|;Ϻcӎؾx;wg :џ~_ZƄc=OfځGq]!(P\.ˌ$ Rc y^B]XecӴmǽpؖr޹Ha 3pƦE0\3|_ J`UwM*Q(Zu DZW 7:8;J2?HEQߎA -˜Fc[M !_|}yJE6s}w4bsW.opB (ĵbSF UԿ&wg7gAR$bYg48דo~B*O&{_*TD<|YĢQx?曉D?JLZQdYcѐR8xfusyX֊7_zMK;Bo<=51 m~w9c?P@胏<ҕ?oYٙvi"H!hoIU׃Ƀ{q{{{~»{~H(I۶\211oߞt(KKK SO=% K/yޙ3gz__;ueYLii˩d:NߚY^^8f{VVr̙3̎%Hw}<EUT*Ӵ•JSh={%_~qd2L&?33sD,=95qcQ,ۮTT*H,//w-Vy.<#':99i@jRLO./Q(RZ y,$--o6/py J6Nwٽ7VD?ē'O~(zIE/7n۶EQERZYUUBHRI$Gu8b^/ J%N P׮ݸy"Xݱ@e]|5ʗ}kgϞ,KqRq|Iy Cj#ygq0g<>i)c'1¯ ,/mj0A7'$^w~ 7|\Goؗo~mw5z?u&]a+EE miUˋsX\.:cp5Gxq]yv$ Dm81h4nYVn$"O1AN)e bxs]Rb֣8 +m'MG|pI?FS2sUŸ l31;RJzSqb*%3 aaFe! By8T= H ,[7vb4 ﴺ!1ϪwJyz7O_߲N hO.׭9u?@_x#_ݵ]_PНҩOx DMnܼ#ǎzWj1K4:|d߾}Z-^vT*)+=ӧu]$) v2t瞳=BISaα0vq`ȁ(`B`#l `_e!p(JM)H8QLh%)umz?[\-VqH*eYgܹsp?JBNxZbB!8p`~q)Wd2~ڵw]xqTz5gϞC\ZZ* RqddQឞJ2mTBoy~/ӕ\GXVW.\,X,~s+귞{̙j:E@1Fd##Cju~~~֭C_B,aV%ѭ[R4  dSc\A ЃM9}rڰ 0nȳ9)"8RL+I(yx44taovvf/,bXqyyRQ=`~`4:!M؄ x<. sk-4i( ش@{1ӼTX\ns%7> ;i^u0=tmh) _L&;vD4Mݲe̙svaai=='4Mr7n\xR6iڻgޙ:;DQv]a亮jj^vmn~ayM Zv_R5 EQ:BHQBLAu݆,4qIIa3ƶY{va繮y 1JiowFkקf+՚QI2-#?\>;xG ]0D" [FG8033D,8>;v\86`˗oN=zO~PVUAJŲ,q @,#) f:MZ w0ae$IRPHO)ѵ2S/ζby]i9Sjj3*y;v py鷸鮾{k4A:߯gWO ~la  g9J[0zS$f=NKRP"̶m۶-Hwoi.XOG`6һJcc6N`Bq(j"4e9,'kZqWs`ʲ#/--ݻX,^xq;wsW,1]N>mf8p0ix0~xqc#<8Y(D2H#Uq]5|IJc qEq.;XP"HS2MJ!Hv F mY޾>y3JAJ|v@4V֬IuWQ$UU~__9m۶OG"S7_m[Ϗ3.i]ڙ3gggg]ňvZ,[^cjs۶h4zT*511ٟJGf$j4UR*TZu뺢(R4? K9J) I(a8u]4Udzzڶ]pWUUF,Z3X-mJ5G\ץ xoؒ\(xstgg~'?iZ^.ff&'L6M1 (Wλ8͛7o9222<<<::Jb3< /\t/b4==]%BH+C)f !n;hc/W4vO zAA_s;8>E1ܜQG;``׮{ܹP(r UU`;h{R^M6F{R^CnH6#v|\{Kx^Zlx:x#ՑmN&qblYnpZf ^[D)z-B^-E]fŀTVz{&gۗL&r 2&1:JƢuRHaC6jx7:QFn0#!}1Q{v!E"6e$IJB-JKKK˲AK' RBDB5t0B("kժa=M[+ q\8)oܪek% EJ&%r&+:@($֌["FxtdP(zAIR};9fo.Bnyydx$L_ժӋ1r\vZ2::q\>~  Ƀj:}#=E @&5P8,p,'nD)PJPf&P0L0MCLݫ)x\dz(Ry$z.X-j-!@I1?F͋ }6#UcчڭkWKiEsLBjD"JU4Ͳq\Z[>}zVK%B v@P˲: {Aر"( H+i+x!l♹g052JT\~iYTgfd>ϯp8s\E,W,6 caaq֭^?vؘeY33Lϳ=o\tgƯ_pƍbqfj'''?O---eCCCGbIUǶo*IɄ W\u0u:b1)x͏-&?۠;>D-õ`6v&B,MG[zZhԾr(۶+J:L$⚦$x{'P#gQA ahڙpo;D*EnR-?1:X_{ž78Q3܍$I8c#AD=}ޑ潇ٵ^_`-pg9^Hpo_REBXF7k?CV#yݾl@UH-{Y@=1!U$Rqlusťm0?i"_8H) 6󺄨Muۮ&BDQD69ضa[FN䗦'a @_|90JШ~n<}~ݱ>ϥ^B]CX 54*BXB4 ;(o_p<k]嬺!A,ˢB"tqiu\0b%tpppiiIťE))!O>|mΜ=K ,-.. ܸqc6Py^$),IRJz 3˘Gt(euӆgێnccmFqVjeU )$q{i[?>[*{Gi, 믿qZ*Umuu5+~EuҥKȑ?NiX Y]]uݬD<^(μO}'EW.T*L..F]߻D)^fRT՚-ڶyP(TT<׭~`8\J)`g SȳcHddd$N&=1eY:RG k%QOܺ ׯ4-㸄6t*(ۧOy@Y^^+[_|^E^X7np8<;qıcwww }xdO?!˜33*V71zX˱g `~u= -8-RӴpX9x𠪪q)NP0 n( `j~ VXڨ5;6>Hus׎[3u널O~XQH$Fgfgo޸dzzzRR MMMB!x<.$\Z׍jRBq\ugLg>Smݺ5Ƿn:11qߎDb}q]7&&n:a5U]*VIj)H(J%˶)T`t@ A) ,Ђ1F˨2N){m۾xض-Gx|~qҕjPSP2*x.InɃ*ZRBBaw9)Z(C##@G1n ˳{qkfR ɋ#`xhH2渹]Jiɒ\ՁBgaVٮtJG鈶?)iqx7T H)˶LSǜB #u-sd1B븖$B!a. Gb,y1 lvhh8rKKHdllBp={*x7448ysu%p8tPWkX@zmuuudddvv['>%x޲l&!8_*I1"c$}2 ض8mb"DuwY&S|Y QlVK[Saf ̐Y?:2kdIܹs;uOQjzܹbH1*3ۺM)]C[lump/_|Z)Z4-H837n(:!׶ RZg߿8B͉j[PJ P6PQ"p< H$jVUJ}^zfJ57ywR,*v" ĩ`*A#up.//;Bt]0F"0n޼ ?0qgϞ}G?c+3F!4=e<駟$ʕ+,OZM&{{Cxql/Q0 cߴb%}> `ote -(h˰|+h?- bWd<ØT& PD`FAV 1ؼx6cV^6ek4ﮜ [x;֎Z;R4\>G~%ݤ ķ 3C,v˪8]]W)ӶzRa uEy8 )ڔT0X58.N %xH(%0چz$xP3ۦ(Bip[loD( ;`+)&J$dVPJm|2Յ x:aQJz{R]s@4FcE! t,F)d2(ժZO$c۷UBq:nY[,#}nh5C-Xq\qRЪEr^v]۱R\(zzzx:9E$֭b8:Ŷܲ 𫫫33S+<4rnMNE9vrЍp$* ]]]JU;>J4Ɯ8I%QxA0aq<DZc<SJ RS4ghQÈ ]H൥ʭIHfGUsss}=/xAh'" IJ4K'SC}gM|ClrtW"?exdqq!nnn'HӎLNNVK/jz8hvǓd^K&qBZ[])XV]ޗ_z}CǏwd&Ǝ?ݻW˪UNu<ϳZM z T Me,?7<#ӤB--e vi HOîcٯEղȳ4[$UU#ݙ̃>X,5M+|4mcd"?#SSSs]8{l2x++eb2B,؈Sͯ-y7X,8>"XgkoRjowP33I؂(l8< qhk78SU( y*ݘZ}m?vd4dz;+_O *wc7[ڬ( q Xf'~/fv)i<[ǧp`3 !yߚy]Ib5=s߷? }$P<ZJB“dFOp݂ArIxć>ZdնG=w,W_y5]JqӲ.,pU۶.r,X,;wKoDш ''gz{vvv prrrddzzzlǩ*S] }V]Ge\='hԘ>vtffgV,U(⩧~gyy R9nMx`_ =pf^7-|$oK`Q{@ȓXP"d+rLqx7ʼnl' q8e *1E:;;wyƘHu9%=]_{B$[ZZRU\@4cy$?>uX(+]{٬瑙ӧ+rT,c `!D8 \ðֲg['$N +]80J;C0** X,lZ[۹|4I&;:qqveE_q7777B#RG˗>w•+Wt]+[;;e ]|ƍmyTr;669so +1/?8w~: L[n!2%ƸP捡X,vΝZ}ՙmP*=ߗD\`}''4PhjB*!h0xhRU׷I_5R0 *Jf܏bFͶ]((/TUUB%;K hᬫSêi###KKK)ѣ㍍LXUۤ^.BOOiG(zaRN)-*kѥ%'x,WH_hdH1i6ij/v;###;]9*cL&կe$v] HD, =E0TKD~F]ץ}M
© 2011 Dirk-Jan C. Binnema
#+begin_html #+end_html mu-0.9.12/www/old-news.org000066400000000000000000000131051250406103100153010ustar00rootroot00000000000000#+title: Mu - Old News #+html: #+style: * Old news - 2011-07-31: mu *0.9.7-pre* is now available with a number of interesting new features and fixes, many based on user suggestions. =mu= now supports /mail threading/ based on the [[http://www.jwz.org/doc/threading.html][JWZ-algorithm]]; output is now automatically converted to the user-locale; =mu view= can output separators between messages for easier processing, support for X-Label-tags, and last but not least, =mu= now has bindings for the [[http://www.gnu.org/s/guile/][Guile]] (Scheme) programming language - there is a new toy (=toys/muile=) that allows you to inspect messages and do all kinds of statistics - see the [[https://gitorious.org/mu/mu/blobs/master/toys/muile/README][README]] for more information. - 2011-06-02: after quite a bit of testing, *0.9.6* has been promoted to be the next release -- forget about the 'bèta'. Development continues for the next release. - 2011-05-28: *mu-0.9.6* (bèta). A lot of internal changes, but also quite some new features, for example: - wild-card searching for most fields: mu find 'car*' - search for message with certain attachments with 'attach:/a:': mu find 'attach:resume*' - color for =mu find=, =mu cfind=, =mu extract= and =mu view= Everything is documented in the man-pages, and there are examples in the [[file:cheatsheet.org][mu cheatsheet]]. - 2011-04-25: *mu-0.9.5* a small, but important, bugfix in maildir-detection, some small optimizations. - 2011-04-12: *mu 0.9.4* released - adds the =cfind= command, to find contacts (name + e-mail); add =flag:unread= which is a synonym for =flag:new OR NOT flag:seen=. Updates to the documentation and some internal updates. This is a *bèta-version*. - 2011-02-13: *mu 0.9.3*; fixes a bunch of minor issues in 0.9.2; updated the web page with pages for [[file:mug.org][mug]] (the experimental UI) and the [[file:cheatsheet.org][mu cheatsheet]]. - 2011-02-02: *mu 0.9.2* released, which adds support for matching on message size, and various new output format. See [[http://gitorious.org/mu/mu/blobs/master/NEWS][NEWS]] for all the user-visible changes, also from older releases. - [2010-12-05] *mu version 0.9.1* released; fixes a couple of issues users found with a missing icon, the unit-tests. - [2010-12-04] *mu version 0.9* released. Compared to the bèta-release, there were a number of improvements to the documentation and the unit tests. Pre-processing queries is a little bit smarter now, making matching e-mail address more eager. Experimental support for Fedora-14. - [2010-11-27] *mu version 0.9-beta* released. New features: searching is now accent-insensitive; you can now search for message priority (=prio:=), time-interval (=date:=) and message flags (=flag:=). Also, you can now store ('bookmark') often-used queries. To top it off, there is a simple graphical UI now, called =mug=. Documentation has been update, and all known bugs have been fixed. - [2010-10-30] *mu version 0.8* released, with only some small cosmetic updates compared to 0.8-beta. Hurray! - [2010-10-23] *mu version 0.8-beta* released. The new version brings =mu extract= for getting the attachments from messages, improved searching (matching is a bit more 'greedy'), updated and extended documentation, including the =mu-easy= manpage with simple examples. All known non-enhancement bugs were fixed. - [2010-02-27] *mu version 0.7* released. Compared to the beta version, there are few changes. The maildir-matching syntax now contains a starting =/=, so =~/Maildir/foo/bar/(cur|new)/msg= can be matched with =m:/foo/bar=. The top-level maildir can be matched with =m:/=. Apart from that, there are so small cosmetic fixes and documentation updates. - [2010-02-11] *mu version 0.7-beta* released. A lot of changes: - Automatic database scheme version check, notifies users when an upgrade is needed - Adds the =mu view= command, to view mail message files - Removes the 10K match limit - Support for unattended upgrades - that is, the database can automatically be upgraded (=--autoupgrade=). Also, the log file is automatically cleaned when it gets too big (unless you use =--nocleanup=) - Search for a certain Maildir using the =maildir:=,=m:= search prefixes. For example, you can find all messages located in =~/Maildir/foo/bar/(cur|new)/msg= with =m:foo/bar=. This replaces the search for path/p in 0.6 - Fixes for reported issues #17 and #18 - A test suite with a growing number of unit tests - Updated documentation - Many internal refactoring and other changes This version has been tagged as =v0.7-beta= in repository, and must be considered a code-complete preview of the upcoming release 0.7. Please report any problems you encounter with it. #+html:
© 2011 Dirk-Jan C. Binnema
#+begin_html #+end_html

`_1| P a0\pB`鉖i0> 4R{T-(HRB*h"l; i Hhtppбm gMPU*poRBhOK!YH]דY GGVĊB)lq^*mϵmѣ+P۶u}(rx\J8D*Dd]Ie+lj~Vw^.{gz>oxxP(߹sVQB⋣ccdrxh8iftl4hI˄'*Kt>-n&f뺢(ѾKiYՙ LNNjǧO# \.Wn޼)Bo7-YQYo@bّwﮯC e111122fO>ݽ{g>֨Ϟ}hF岮k\\.xW_ya^*RuFS,s?/_|qffgΜqF.4MtzussO?M}24<$0 c``ߞ:իիWUUsl{;77H$z{{o\.lld6|/Jׯ_X~W\ۿ7n$7|SUU]}ߧ/.-UrhZ-L 9sfLww~ZTQƆmc+uz8vsk\.hñ#0:ƘRZ<s]4x ÐVeIMt̐ĐA`= betxɝϏmcmNزtJ(cLJPqλ:#0kkkapeo5j溞4 eA&@/ ,36PPxa-rp1w?W,̩Sfi[NXr< h eg799qs)P,Fcx"nZ'^HR^{vwqsi⋶m';w 􏎎6tzbhƍkb0ɉ_~errgjjԩӃCJ::<2w/ol ~ɯz?Vi3ϼ+ɣSׯ]_\\9˥J*Y.>Ñё˗+///-RJ9H$8e8m_v5ˏ ߹s_cvv?{JQ'>boSB_}5ѝ?Vp="GB_l6zoo3̞a#t#ia 48έ[Ţy| 23F}ߗ9$Bp*B f5 !V-X,B_ԗ fS@yr_plŶx(HDYIR++>[+cs&E}P hs6·kxm!tk[pD|j"}ނ ۚ\D¶yB/S-=@`>%H(18xt ``D$,vpR06֗a6r[cOi1&綥_\`ܨmyŃ0Wq|Ģ*ꪪ:-屦ir25MCXu]wلb1y!qR2K! B*HuB$!tS@!gD. cLӴ`cS&gf#9&NYS40c 蠄8eϚi!MUvRJxT5 !c$x$XYYA@ hTp89zVZQaJ| qIhO&;;B C]**& ]+J<w]Rʘ98#t]?w|:6hRȼ\nR01*cرcccc|PeYJ`i`_??ozz~X,j^,IDcCC#t1VA>,˪7o޼po{{\.+ٝ?J3.}}jk׮UUE3'SJtR{m@Ҋe=s KB^tԩS'JU .,d2NNNZu}#|Qr8QJUEbRL&jcpp?J/?=iɔdAa"pl6z.ML29h>O4M@j)cL NB0ҢAL1n8!Lr*V14ML |%AxJ Ci${ ASiɪL !1ʇMA "ړ;w@@Y+wSdK^lRDQ%, sGB(…|l ܆ogpSp8 *K11q\uyأRzTUUU%s <$0B< |4~z{`Aݿm϶k!IsW_zOq~Y1ŽmT ܿmZ}a#Cx@q1~j%ګ,3iFn'D2BQ{H8:,]]]J!!LzOOwGGG^GȚccz}/G*@@)!BBUPsj[;>J5bj@ !r!]Ֆ\A ]3嚴JpH&ٍRn2!J\׫T\O1NiJO!X-#`?%[–X(jT5ZF}B)<ˍFZ}8c)G-2B9cyB0U[ !3dw7/k1j:@JWgS'O͛)bzwWW\6-ԱT3^G N}r}¹R_iR)q9R>eԲCզlr!b&j1v;wܾunO~:?g%Wđ񣓓RԹsMSO:\˥bWg*B|_Z^vuwwĉKKǎK$b/kgݝӟ}R~7gN?aGu}txrvůl8yBԙE2RF&Non~yvsSӧRPh3s,/KZqdb4TUy7z"Ѩؾ?kk;\>^\\uc_Ɩ@|۶c:!dvT4&R"ȝhs(%ZM|#,8(clltcy>kB@ClZQT!8np.TUYIt-9<!`蚦*0'(X"5nh-H[ K`)tZrBTUQcFHօFkX(KI~Z92l{+4ޟ)͗ ϓwn{@%z(A#$Ll6J5U!b cs9 h  R'wPr_C)bΒ*/Z@w0Rʓ??h:yS .SGG|8la ih,t JRA%-I˻D"F*m[;Wb,S`yNMUq+1.^eUB|n9y^'tuw.@@^UUɤ ABHq$p!$ZuWِz)U s{%3ap>i6~J[fDH?au]/{$6@t0_!rɜRH,91&DE@d2zN)cB!TQT*yґjBrz󁁾D^lT1#Fse/Lr%dGoo/X~7Wպ{Fy睳gϖoݲ,k_~]CRJE۴VRr F#M'4f3˩GWU+񥪪1<3 s^H$ͺ755NEi‚eJuM1/./EJh.LvF1Msee婧Ke0MӴl2;::,~ 鵵BkD".[;8vX4L&384TVFRVZd2lZh4>aٶm |>/BjK6::(J,;vX$UPؽya++al[555=333;;+T*xrӧO;yW\(<0I=w<<ϱTgg<ݽ~z:^KЈD|睭>V%jmai!HZ߲,!fBB)/4LF١{'=C+4C .r!V`]%\ B MӤV8  +&$ zepR t]" BDB iÐN@?T?m}^m 3Y`,VUu=`}VӅH,Ҋ֋Z@$ zD:[5uӴDK>22b EQ29x<.hE=&|go>&|X==ln j?s Vi|гV0@h ܭO!5m5<|'jX%9 O>ep9*H.͂ADL'?#UEOr.${/t:3U+;o BUQBJ1F!D C`!gDm[p;鞃@D"FI|ʸLu"P0VUQ1VEtqs9I%>@%_b/ne ࠦ)#IzEQU%">bjFC5 #$Q{NQMS"CQpWWx2a!"Htv|) Vy.=81#J&M!!`{O!}p0B򔪪FшaD\ǯ*x<&FfLRX4GYۋ֨}~V^o&}0>|R۶ \ՄPJd'q$`\Q*M- aLM14 ;ضӫ(J~wdWWw&Sz)8bƆG'&sh P,W| y2ZVږ|oaaJ/u{,筷ފѵtӶWi#k[[[ƢJi|X Վ4Mq'N<}; Mg^x;)!N쳅b4)XR.fs3/tuu%F377a WUDZ?_>y_}R7D[mY~N/..'W~O_GGG{{{gOku[gkjAŢ 9gdٳ/~ ӴƏ7c>uK1;;X[kgΝcSJ]׭>aQ!OV`MWbшah4c8DH B` 9gsh3"Ȍq<#v}Db{!!@+dι`8+ <" v.-߆[VuPa>[uO0dXIF֪9o23pHy >Ԃ߶>ɇIO!?|iTjc#ZHX>xF {>p8s iO?_>|n~5l9ɻmOY0 |6E~Y'-eI?mEI HǶ_$ɤ빌1)Gy>@ s#Bt!S r!q(a.KB([0`FGTQ뺾O-)K.EIn!a329R$-B:H[^&i $y (D"f$|ōLh0z^VͦOh fRŢh^uV9OF׮^j6-DQ˜}}}CCCD^Sʆ V, N$@utf2Bf7]qd|l#aZ^꺮*cLQT8T*5ꍕT*{>dBf}?LbqUS}׏эL$b ;@۲W67nܸs[nݺ-WE"ǧ^xu !~BHww,W F\EUffj>M߿744dkkݻRq{gX,6zxP9F#Z}zzرcG4ڵk$O>gxl&{ois;{ZU8]]^ZW*k׮_vg7rl^_YYdZuX,;ΞF#333ߚꞟs+;J,GcvB$܌D"#H%O>"d6B)!"DlہF"ιO!DsH2!|UM|&5l֤"S!wV:wb7E"|aj كLBJ\%rUMoӮBAavɁQu[(QT|ӐB(q' e\\3<l\Z3ВьWr8|MD,eBJ&R "Dz1vKKIENDB`mu-0.9.12/www/mug-thumb.png000066400000000000000000001256221250406103100154630ustar00rootroot00000000000000PNG  IHDR,bZGRzTXtRaw profile type exifxڥi$9n)t$8\t_"s_ufL/r pws_Tf,W{yw~S7䐦c(‘~KɟR?}}'(-]XQͽZsmr>E^bJ?x#VS*?Go J^!zw[I|?:h{G==22w[= ~;ZZ?wn:7{ȯ: AW1]@c3䵈}&Wlr9%e|2 9/ y񒛽K#+`yEy;ߒ[4 ;#2O֕#/,#\F~oeG!'pUVCTI&ЋQ9l$hHj<ɀ"s-M1=.Y8D&Rtr Y*ТUUM]vV>f6xuuwڴAFϽ}ڭ{o1s0gp̳:ufcU.[kˮ[mm=tN?サ[nzm+k-?7kT\翲QA"gd,W!9rԚ#s3UEjfKbG^?2ynH3soޞ?7Y!ا #P}gUIwLC{v[M=*Jmt9Dꪷ,) z@HglaD2ױY:n?)op\Fe9kcg'j"rBWmu2Jmcے?7\`'GV"@9o={'UZaa9%{CYj t@;}CK. YFJdS8n6CT|9-€[_c׵|fz#?Y꒳LHgvQr$MhF찵 V96Ng]Z j%6f PiBH٢>( ևk"Y;2}qr] Xg=7u(ljŇůDo6Q*Oa @DW ! Hbl)r~sܹ1m{{zvAP ,qؒQ~`@` sk$->"jk( 0LB6!c].mm^+Kg[>؟lDf`pu]0v(2aoG2iMei@4# &H{/$yQP fߓG}& a$pX`NIQM&0 ,px!Eg֥& ; obfU"<&h'&F`֬cp:5|fC`kc~RW+3֍a'< ҍ/FB.w@rѨs H a+r;O͠X܅= 4 C~6XDHZst"QxnOl$*0nO8EULUڶл:!3@)l]=ڝke9B-4/Q$o3?p-c.xjI+,"on0 Z2Ŋ嘂BaXgUc]l_ ! `R'ằ>g@K,+6AZHtZFT $A#JYNaah%4cd,4-$^mt{&zWVe MOLΜ=S4;b]ZtVfqPZ brzjF;v[Y3* q3cgX!SD^d8 @< *#pAuԶ h1jQϢG ^2Gz)Y0;}Xi &c:jZ5UZ<* Ejhj$ڗe |J7t hc|p79xHs4"*rW+KPD =QlHX?HKFW ]Q+qRa%sa^ qC;%t"D !౼.Y^ x -ȎWad@t\+I> ;2º 4di0NvN8wW4"4HDI83Qx!8'D L.4|f7 ?3U h OBPҪ5 Ff [bȵ!4Tn~54DF;!e+vG3WcRta c#Ipjj?lnn^}x?!Rg Zk^OG, ܬ$Iw4M9 TU CHB~K/0drA"X,p 5M !q\cc7F,Y>0`RMUo8UBXUTnWBI뚶~(Y6Jp(bq@t^E5B("Oz􅂪 ݛ0cx4V]3OǗ^:xdg?RJ;!c[L&`Rz9EQQ5FiMm(x*cLtEQ1FeǢx`~^f;q 2ql08 p.R{??iW&xpSwYZdZa`h’_1Msc9.{r<֢mQJUM[Fxig3\*eâ(?QZQB:5M8Z-yy1]?DeЩn/.vU0#gGmo{pӮcOPרNS 8U\CO{:/>3CzQF`c?s?3cs7o]Ͽt;.Fn& 9Q NfkCQ0 TUz.P*%CAƌ᧦j۳s{{uMӲeH` íE֒| NX2y?Ɓ.A("~#!+IIkMSsFSxHf\Y}kkM?X˦)F5Όcg1rrp !Ƙ?[Z^~?e~@l\x^N&+* nj1UӈF8(e裀3sv4멠6 5厯3L1`狶voKג*L+EK9caGnđ!*uUfK)ye`z|ǦbgRv ^`m6u91XV9j0Yl/Z-@8[ ,b&?=i1}XUUG/KNoG,O\w}>WT+򑃒!qzs{Z (յcǖOoÄ1R/,1&d?,JRF7L%,Y<$qm/Q˔%sq*U, VשAUȫL[8ǮnW$59L%zG& N9¶]aeAhUw6zOq5#Ojw#=z1KK6NIco!įQ 7=]&]@SUO)Ibbuyں7KNJ cѭ*LIoQ'ZKt}w`RqH'|pW]6CKmLTӈ1n޲U(*FTӃ=ʃG%cB#x16ML&0lrjL`zOXj$IV8._N.Ɋn.`+GUAN-ܖzhTԿܢβ,bd+U[j06$BH5jӼj:0LLIS=>;˜B,g ^׋eUgP\h#ɾ[o؎<$=qWÂBBFhi~o\b3XRn=JMn*D0WOv2_cAWN'|%5帖RSD7K ͼ19bPc2J Zf;DL!Ӗv cz3ΔRCb ,Rq!'KX2]|-+˿6h:YW w}BFːWߑKK:P~m?t߸ɤ8Bq Th()f᷎j:`z_9a_A mܝۛLrDpP,M;h5)~Z{VBpy&E:H*&lc"9$cNKp@lޫfxsw עSl2c}귩LUM_!FTIC !N_?;VBfbyqw"=`p{ow 9knxGlرEʒqۄ#)ژɒ!vU%%'Z,Y( V p4Q2[lڣ˒%c0Bqg.BNFm!@^j%Kn"c` nt*YX\t@deq[yh~IfQ3'g~!p$'S]:O7&GtLnk2qgO'Avn꼚DIҴwO5rJ#N>M t`W]_o}S&s?Jfm>_淿;'w8<1P7y~7>H|8. '7]Ym//;>c[͉n;‘ V"s3<V5M4@F;!wÚQdwp=7ύ Wu[HaK`Ϛ[H LV5TL[pˋNZ=f*Z cxN`7Yks:N{郫HW"(UUSr,Dcgaב*0蚦jHPp)A=!Gaֺ q%,$196j/V1Vϊt!55544Ŝ$1>2l.jR=Ԯ"S较 Bqƪ#/_Yob~jrjaƚ][k{*1&q^|QLDsBS_rh󚳼&R&}ynQ`% E ŦMy-^TUۺnjrn晹墲M-cɳ@ѽS]xe wܺ8c!r`WpPR/Yd88;H+z]N0F)M M` +F30%9<ͶA2H&0l"f9c2>:1DB0EBD #C rϠփ*/ܻ{Ôbzc~T<TLu2Sxiq\zyvaQEDŽ0-_X^t{';!~-1*Zz JرO5Wrr?öD,:<ԇ1&NUqv"sO?ƩZ~Gv dd0üq&na)iED0^w 1Ӣ$:cl}(pFef =N$NC>mԚQ1vߨ9C>nDFf}=ܨ USBFH&N'ۊcES~;E$ Q'ic&yV7w`2.+cXu7B8FuqƢ1I,d(*DҠi|ac#kaĘSDi* V3|+sq%3%FbR5*J;2j=a5SaEWUEQ8BƺbB TtQ2UU)ƘuGBH4YƄuQQtCE%FsgUt$Jr,Jtv:&8c= g#VufYUY}Hڞ[]ۈú\"xv|t%yýJd674.N ˚nrx0"#%m6I,,9==e//·"qI6s{g4"sAa^dqV>ivƯhNg6_ƒP8lqB333m5[1H!ɂTB tbaq.:D\n76DScʤHmBT 63\y"M&unʘإYBN~&&5+ ڭR*b=UeOmނꉞk̜r8kKrZ@o#P`ٹ;!ԑJV-vkOFHüDR0zXZ&8/C 1zi[e7?pDVY_C_}W{/Zذy-Tۇ<c:px'y;ό`ܗ竨 +Kaqf+s<@SՕ+(QB 35[ K'nɇs=[vĜӲr0N:+=-CKNd$s&3蟯y&bute<'˕[ ⱹ4&;._j+*-/6.x)'zjܜ`&n_A^5?Iݫ3BVVUg!90;әdv{ٴ¦ZY01<+X u NKMya*̱9(IO(`}kI( Otn*hơHN{&ul3t!ڬ{m0= aGB.Ӂf'~SͦKD)rx- ӛ6y&gC[11-oinoaZǓ-_$uՁf1o͵*$ kfG0('ep:9yǁR1J Din-xZivԴUE@6sb1FbItrD`qنedwK"q:,Jd]1]B3w96Uc^ml/xs"/9<&fkBa(j"՗cZMYEBHQE$Kf0bdsbUf2bD,0=:]jAD(+ +?*m.6OCӑ#{ʯS-1o&E7*)dz$Ru"ģ"Ո#P%Rf !lwsL*22r_OH$8USHҒP|_gGt%wT"KWyǓ)3 V%$5If5MbME%n?8#L%#"LK'Ӛl2ƆG/VӁ+fKtM[j4E9r"yltZ4x#M,];nk'}ښ<nf4?ff1F8D}d<XZeҲj0 3C+ @WS1Ҕ0ר=2B -P(*5%)caeJJm;kB P9`^1FF夲F jq0e h"X<e+dB&Hws:&b^ U!s )r(JZ5%XS5zM $4VӲRɄᠯͰ.e#QRby>`U<`m,U@3uVFUp';+fc7֍-Jcs cJ&ҫڀ1ecS4`Jf2U5n+2cp}`჻e+'woaj G1Ƈ| jd+cLUU1q?X4F)iBB8B <:==]SS*˜8뚞Y;5Mtڨ׆ jhhޘy-@@մOm a_<#<ճ>!tx{Qb PTXf?^wNJn9N_N+0^X WgV^iquc8*IҦ͛?QGUUdjY+[n\ONonŮ\rkkkkl[Pq:1X>O$h~ƹ݇_ g}.&<<|m|I}gRFl$Ix\w.v&:{z~)s'j326+໧9<=4Tgzrhl$8;E<͟ċϷz3(~ܞ"=~cz;]jxg[[Hf7xCM?1--notdp^adnF>qW[fU\em'CS!S}m.llp7I<|MmmJ?RTu4Us!D I&C@C#v KdžαF;\y^=%7?poL956b!v=Gw:+?lH~ET|̋h'~:O쨴lLF@><~"SZ3pcPLb0{c7^& 8E N'PP֖'Ǜ:@,/EtbD ;dS{]tYlm$SNMvSQuMc G~< wl .t^vUoju,-Oh e_CZ/{,LnO,Mf˯vǂ)WA=g6'ϧ.9—FΟ}L n}؏+ٴʼn/}jyxdZR-Zy杁@ʕW_yyu:4g+pݔ-SrjD-ꢅEWZWb{ã;|:K:سGWq~;{8ɭU&c_w}GKU[$+14hIjo&J՛ ]9{ZGgY]"7nb,\x<.3Sxr#? f%?82wxL>B3>PFm 'EOF[09Jl1YiSsђ|WY<b+.m&D:BDu}C8ɼԒ?LzrEÐL&`~j0HǵNǛ.Ft4eRQ281C=m^[9ř̽9eM3mu 5k7BK9_NEav8๑pbWRN{ٙƢ=;Gt67>\VdU״~{"'Қ͛s]OӃ}IuDBxB߹gOquvf:NG;#.]"Hӊ(iF!:ssVoMDvCbr sp0,vtI-UTKGVP# ux۰34j{4ڱd=D+Aՙ(>q KnE"i߱zrRU3ɪJ[+\Z=[pTuMQ3:x/z{V8cTl PU&G<586&w=GWw?<g2߲MLxC/>`9Oojq3Srr&GqzTqspeʯ4F9e)pYs R+M~d ωNՔWt3Y gE1NwMy6]QIM3.>5Hs\c/*͎ں3 u,5Mb!eN6+1TQ]!"js3/T&U>/r |(n(u '[[7Zjxޝo\#Դѡ*FiDUU@]]r82v/%z{dd套^4`(w}" U+uf7+aؾᮯEMyNL/-ν_itS|TSutZ4HWƓJp~ژ¡;UQրjf@5gg,J`zPQ@Mcs {[XQK/&2;k0cT#qTRyS>qae5Sؐ$ɕg}Bc0"j]L ]VgL8&$ǯW_9Dw_9^w6ʉ 4&آ`ExoaaL? @Jq$|lQ؎-M{6=1dxH<<4I1ZNӔޜ[,6mk׺׮U3.⽇ `0)sܻ[]M&7,–撋 0r21?7[PX`eu=e,ַo5c$I3;=,K&5 ӓc -;}:7uG1Ƙ*Rʞz&eO`>vƑmq]KaYY<u !jXUU]9^٤*iEQ,VlXcijs@,xl"^4"I!`2LkJrs}*wlVdh$'Z7oݱZiЍmv{^bZ5j.Mb oy/8)e10ypZ6"jj˟wD8.3 CaaoufD+qM8B8l#0!Md@s ~ &pO?iXb#0`s" QUv 0)5I䍞 ^0+AnAɦKKd|e~sk˽'D[_Hp³voqb00r;UK~0ƫ>xGͿs駿"{KKx_f,CϵwtIØռCn+Py4piJZny?W֎E%W:ϝM#딲Yxd.Р6FwUk"|{( Ljآ#J)Bܱ_|}Ka"h ^05N[r6|m~wcP]tk$iR -{q̌Qx5dίJ]4xő((Ϋ#%2Yczd9O R.txs 3:mSZzGZs vDzrk=p]:5p2~:[ׯ\JԷvq"\ڽX|v`9ڼw__W_p)%ޖ"r^z *,+װf!ȱawebO|?S&hT$ M&Ts[.]9y\-.,p-՜xh"/x$c +%4ײw}Dr=cƗh6f9Zzmh1ڊv4]. \ݷ;Ƌ~q+ֽV&_\抜Jgrhb߁g^ #Gw#njqagYgγJ#uuuhhw98 D+j!>'G^>\?sg)&ﺗ>d4U//.ɇ{[Y2XR׼m%Ozשιw4_^G8I39/&`zWǍ=LT'"o:z}V3.tkoݵ$t͍7:>Z<ʪƲ\OA/iB7n,FRBK3CVG~EڙDNWܮ5=3 s}p" zJ ,VqH_畎>lY(Z}Oo; 6a|9GTLO:vo6s؉o@Cjʼgy}ڋdճޢsv w\QީY)wBQf>glT{36ps[U|@ ڜU*%ޯR2TVRl#lըRw z7z*U6H$Ū7*U1h#^bii UgU4Z*Q2c88.a$I*JB7'bNơWu9*C[na:u^sv蚚[! ud2b8S j&@ hd]B (3/߭PW9!F$IQ iBg`W@$ZgW-: UժCU~{ba8M ԟ{I=AցMRJG 0'p#;rO* ?_B B5n*6ɥb@TpQ#8SW(@@$A1a$IJ5F>CA VVQaR  9^2+:rY|X*cI`*çHJeTJ$j:d-UBd?d$bsy)/ǔy'ʗşR?_<+yVyY~w" Z,@ Ô7rߠ iyů?*RY!6J;sundx16[PXh,,#{IXzPJ89`$^|tbˋYL rDC/b8gbDiiz& ,ݹ?RHE̘lhaxg2(*Cpv$58361ϧ>728.Cz}y|hn5ju8|[?<<294\Κ2N™šcnȔRQpqrtjޛMKS>V9\^$(Yʆ.' cybDTL:@2ŐŤ7R Ѯ)ȥc9W*5jPl0mQ1pL9! ?95 1REӄ1~?>IoyƽlufMl"^ ˊpvok8}N:}Y5\>-b涝[YvOkkc_2؝v|ZM :/cڿ?6_BqHR;ҵej"wItDNK)wT,,A1{K]|5x9TGsoo` Tpcm:]صcĩk5صp.' $YL=~-=py3[6*pY_ `ͩ'®& 3K)Gem%.XL8Q+%Rc$r(3f7\|mm5-tR>gCWa/Fhmj5HVg2PFq.ͻNFis5>6j-k݋.n8m#DnoP)Hv[cniopBShe-Aicws:zk,zJc z$N*JA )VDe*PKp#Zifl8RLb9Z۬a8N`68+Ж!Fg ح)R`AzƮhuSQָ\  |6]Im5$*ӪY&N ^㱙VkcC0.LѦo"l8n6[tZ,oo+bbg_eG{#h'ۍ#K2 pL9  ,TR@hI ;{IIKP, HJJ$1( +hAQyQ(,rU*2d%@2E^X%_$q CTpw&IyAI%DVh%2,T<0"01 $L1I.M/h(e$8W*H ȍǟǨbX^ 1 A$K@,/*iZX$EJA%9VESJ1Qp%e$%@21PaX"#kF JIDC"q(J2B7Ɠ'[0 $EdRl=4`#e BLJAH0g0$A$HZGJe b8@%X$#Y 1cеSI~*/VF)( <b<#)K2KUݶɓGߔ% q p $<(ٌvwvڹ $xQS%oT ",\ JlZvuiN,e+Tb<1iߒ7WK#2)fגedk$<73+"ƫDCwMMM.lL'\)[@\}銬Q W䕓aj_ [m.1N- Њr f4,g1rk3?8yuNR:ǖV#F`&X熮KF*R)1 +&L&Oc|!S⒩1,S\\jk/DVs, ŕVbkYISc}K|EVWX]Y]3Mt’_kWWO6Y^'2JRF[fʼɠ>>SsMo0h:FP( M <۹,.Y/_0|yucl&1XqN߻kKCb_ /TN'9nxj;hտ]>{s%Fdr)ΨZ{`߶Lf3Ѡ+Ǔ8I.^XFrjj2 M6~'! ⹓] K/_8|jy#6WfR.gJ٨˴V(-Zm\2)uVn湜vkGbb\!ƹc1j2Fs)QYʱD`˕VBhjYds XɘK&ZIdH,S`63S0P,Wb#r*v&, aŀ'Hk@ ./[N%\EkUs9\V@._FM2[P($Ryé |6P(8PB>[,+5FB,P!E*P\Q*t:)j I2cO %2%%Ȝgw!3 j6/9xnfY^*( QD00J.?Lܷ0h+9s+D@LFP*cl$$ >_Qo$#a}|"d6]~ſUTsoW`մUl"UUl!PT< @ {_٬D̈W5w]#&q"Z^"I& 1QD,ʲnd,BX(*qH@NxAEdJa6Ѹr"I" TQq +28.߂&H*@XRPZM $,/J [|p#KdʼnzFs  ZZ1t]Wnǿ8zK$G_fw^kH<?*[<ϱ{nՅ_ ľ_^i57Oux$e7jzCFtNճs_ENp|0y~QrH擱+/Nܿ{ۣ;:÷R=kؽkeX6N\KE׿_kdNWi1eo蒹F{/Τ\TV3֙AiY?3Ƿ\{,'n i27ѓI wS_;mi~ɛgn޵i!=G pʏ} (+Qeاw7fNG!~/R u[a%!Y$wcWSWdR(o`@6 &S&,>}ܠRtpVWmKU"-m5l"yQޱS4HJ9'\xm 駟rR1f4:T.2"@RguiTYҿȝ}͹ځs#{1KeE31x>]w &iZeML<"DōM_궐# `j55wmkO}G6SD Hcw1n+I`'+ zviq8;k6zص(K[![*L2}[NwюoಘN%se~}ɱqgKw<L}/*>Sg|߶! (sӢs<pvlpHJi0j\hD%lln7YlMr+%j64JŰzʱ&+:j[=z9UF݈nn&^Ohy== fbx@NB,״tXf˾ͿihuZӶkE)HL:٤f9yoxD68faY '[0hmɱ|疞NzdSQ՘_JHTnr̯H9N)m'vL0RffhR..85 yh̹^2=!o_O1»Ƌ\ľI75Y~'Izg[ BHd7Lg oe~F%Y$Z?lW %;@do߮>Xx=!% %fwȲd՛BAiA1y $ItKjiO9W>JտYZaI QkљL9xR1Ұ8Hb9{Μ/%㛮H k&fr-[a% x?}sFmk2Vt \fhQM"ʣ3n] C?Ovٟ~%43xyIJ3V ӟ{BQH2B<++VRn IFҙN{#;/޹v5nF(& Jd?yxFmz㹵d}v^YKJBiع_%\Q%&= Hkx;`*h}l:jlofhdm`q,, #@&gvF ;#KSqVu`[j`г׎NM׵76W]_Zlu[P<7 xa5׬zKrj B* [vVc/;vhrS[ךg{g pM+/}Gz%m Lcik- 9,&gcK 0dҪpBv;.Fk(ط`t6#iU78m&ey׵6yLNnp颌$D9&LilhsNZLy5uqG1=hǶkTb M-zfa2:mSGox{auja՛m59ԕ[ǟ}aGW#I6K68즎z۔/ɧTL8+>Hs1447T4v7L-ޫQBBu@ejM'bƎֶ~AdD:ƙ7}ouV]uGwm=~Q0k'C3UNh>aBWn9f~1Hv>&Xى2anoT\H^s5wj8@d 3ֆN#(̮:z?e9sjF IRYf _eʊ?SDŽ& , IDAT`Y`,( G@4K?$\~0P?٬ݘsor(ãszK"w-OFSʊA FFf2?wfv{Ż'8sR⥓o*-VlQWgҘhپ^?=:.Zfzhb.R4mN]:};gRչWVZ85)Hih׾lScƭ.ZMl{pbz$@Ȓ!_ꭞV}iŒ[;. / ?06/VSb#ˈ6ɦZsE]zel[ N5Gpa^ |aJoc@#d8H% b,t_$NMδvoI PN:6ܷWK}CD=p7Mٲ(DfVco7:fNZ(3zxa řp,SZkJ /,a8!xr/+K =Ė (uچ[k ~na#ؽGyH&c1h,ZƲeڊI2/N.`><'! 5RaN__XyEl8~Ȳ((݃~:cwmji{j0-X°:v%H_xz:.9e,r$d&___Y?sz͹%d|sooݳOM.oLll囸μ8DJHrbj5?w񊾾 :{MySZ92 ޺34No'Qgvy.hm7^{ȼ̀?)^/-+)ٛƒ%YHG2赛wVܟ ,M20tz0-H ׮Be`^ÙY;2kϟ8qC/t%Z}s ߿QNK@[ L^u_aXK' 즷~f2woh>Pj㋑酩)RܾpvqhgݛZ뷁i7nJo-VY^P*,˒$}O J/R%>w$ppg'SOf嫗o;L'-8D+e&|kܟ)KesoF&<;tgxu5MŲ{64¤?8/=3:~r,N]bx.K1u^qtQ"h[;jl; ,EQF8X(3Ih,lѺ苷tC#;ttp8*mNE<\|ips@^m\)3 C-ݽnǼc]$),!.?Vecqs9]Hs3ؤ˃?A.L\tHD{MS]1K[-fY.-vZԸv t;qbB~Od@QJe7j=u@˴TF`O]s׎Ʒ bd2zZ{=/ Q}g:P;;8e qj95+[nI]]w=H d7 KSR;LѪT$8Ap׸j4j(ڱmӧ.r@QPOZԺjԐ:X:]4 #QpJI7ȋh ]3TvܹU΁aXLeQܜnI`h4-2@L#|?°ugVɢ$gzw-2HS\6_(ʿg駎~W." ?W>~ A ,ZVS7ϼ Ju舀OXuN̦b'f*)c˫NR3Y8=*B*XM%rjbnl6&cEY +cs0znL[_@%$uBgWԹxwHMRP`X.T[/}EKCQ˗=9ѦJ66:|҃XL/h7+ќֱTq-MZFIF>?HuեX]],":<-`T1x¸U}F`Pb3RBt|nWUV׋NYJ#B'Ƚֈ&\4WN| TEZ%R._OSx^Cs% XX]"Y]gz/MM7$&6wuhZLA34Ց8lw߻u_J-YJWk]D[j=z/J&󃡙J-MW_~ͤ.Ǘ¡,p*3's[(Hʪ`m@[X,,O j_rP$I$yׄB|q֢0Qg\7`Di4zx(ժ?0WCQ1P8mu6å)V_sݑ{"v:v?X[kX"κ,!I+P]gT,*x)eEIMfB,FeZtFKsKYg5SSsuu."q!.JTLBXU:SIv6 &^Rh;=]IZk4.g.JgvjUޥ%+ciABA:ǶtvGf&Ju u`%Ë"mxZdz]r9U N.>)dX[O(੭%J(j9kvm鬿ywzd%] TUA@$!jӍ৶2!gE(e5,j" 돏tC8%g:x\;qjq ˜^$D+) K|IN6BNZeNo48,(ktBJ)aĐBAr1`@"pJdJ z2$YXWZ TAi2RJ\. &$-*BMA!_$D#I@x6WY-+|6SԹ J Z"3FgKEAIWnc !F*)(9٠׈"Ny)z | 9biV (b.#BB`0bN&:q(f9Vu4Z=ACmNQsxV>^ZѦM1ŸE$a}ػez'?{/C?@$ApƑ(l=$y#]}?(Ͽ7R"|X Ye1U~)jo8tzn2h}G&@^3%IES61tgqa*R&jw:ںZk  3%A@$CLQʊLDJP9AåJ9KjʷvsoҵE.ݿNbJ쥗~8x[1ah6 e 赍׻ʮ/hݹvu2Wr+dSտ3o!NSEakY?< MG@y@.o[] )skMdô% ʤGMRԾ.sOx05tc N̊ ^z8lkzfk9\_JR/|p/@@ 믚wjuKߌ^<+7NJ4&Mז4).Nx2wĩG:OSѽ;ʫ П~koxxC]>3QSxuKu@{k] ܾ?VΦ*i֍VN:j=IFсs:+! ޫMiXZJWx(3QPkՅ}Fbb3ޠʖdaC`iji $#@(hӭWS8N@(ZO`HBŦW8dqfv Unn4Z)1F@ĵZ5CBݹ؎ײwUh4[p@ RYrtH8%TkÉĕ0? 8P>r`{Roc*Y|o$=.Efh !cAޅюoI ]6mt[L}84q J`8!;jizlGj'$I,r9ҨoMXB(f.OZAYJAĒOJs:0#FK\"$T<j^M5 T`D,JBL2[PmFT cyyt l^CZCJ`$ (cɢ詵<^[-OY HZE8PJG ͪ!қT ="[1M\d"W[W#1`$ews$Th*REh]ze}ctzz0@5&(A٢hl&BU5k,zU1ZHk4 t(uqXQ"6=B* ZfҊl)!6q fsV Zo7\_/YxܖRYq>."ٜߡ|ǟ8_<_j4d2olw$K/;o$A$QĉM-[ýoT*qP?ywiCN@ +Bk)W#Lp/k?~Ȥ2Q:518TFPBTBtZT,C.Mzyqy>]{Lx1`@F]JU]\\=]]mn#t2 "E4`0`񶽯̼(JAJPEDG̬()T)h -(` iej$YljjFU_O1ƘPFcRЊxpfB[[Oy]lTߕ8_z^6#{?B5R+O~v77^czXŝ_xw.uh+\Y*D_ryyoYdk'_>u(ޚZz9o%dzᙎ|XYͥ0{m%Y:[T\8{ _r9#7nd.1GA{J+-Q*w7Ts(E9>qҩo|ޥ2bDd^v|L,ufWάo>sd/-wu{}*lcvn?s6#o m%su\_N/ˣvXn..y74n..P[ZGOyDc'/ I pU5k1 TާO=7T:+u{eݞ2Sg^x0P[GN#VK͍F$%j;W.Ttz{i=OT" VA67?x]_~՗a!>G4hm,~唁R6aUʘX[/QVUZ uaW^:5e&dtA7zCR2+*a }`7Kx_J-C^i$S@?y0q'a `qN Q#6뿢ruxhq10Bp;zn!qޚM=V9ag06 ^} 0@ FkBc$2}O>]l)G0-p$Kۍq4>1bftl*OHL&&&,VcQ[J_~;6-^';ow_hVѱg_9߻.o;Vc'N7<'~|謬5'޶WNv~|`0djdlcWe[o khyris5zɹIv|O6_yqi~& kS/}/ʞ_ BP0,>E9"c`z|8XJLP8T t"AW}CؙZ 1`{eo:"C 4M~Z"'[>|XBT-Ӊx`GLʺV]8B5$9jmBTV:L"n 7l6IF pc253{@99p숚؜_dmSkyH}]]-Uq3'&Jiq=ԓVV 6;pEQ:k39Snh8pnVo[H=!#Og߻;h=bOR}mO=D,#F8[90U~&ǭKgVPXr{7.$yE3r\ϊ&7.zf+?س2(/]I[7t+'fǦd|G68l7o칅 ۶]3]q؍޿pLCKC1O]ފH {԰:qt&9%fJ3Asad3ר%7ׯ8+Fn6[\kAg^s|f7jvߞͷo57~72Ɗ2o߸?q-PU 4ᵔkqgd16jc,!+wVPf'g޾X2G.ƽ{~ֹvHk^]g/NNMbՍFb^x-(s16<20 @?X+IH\e8[,JΝ'J&UUMszp{Gw'7V_%!LJ%(N_92<;0]Z_nvzEZݔQ6.%wb&1i8.(lֶwv2̠*ܶʹ_YeMlMMWbs)_DZ"╻X+HXottb2**ZLbIZk}7fX2$Ab2ު}ZV5hLX腫=ph1ȗ$eJ -ua-m{n  [ZJ/DG[{G]M}scy3skx?r& T׸vJ]Ꟈj!߶lsw$.\{ĩgvYUJL}Z_LR`0 -Her~?,YAnTKnw9VpEaXHA "7_ \˹v%f*9 r|kx'zoĕZrqY>'Pn:E;<^ݎF1N5 @y3R9'3!pƘy;c 裣O*(IRaAcja((1EQ׏W/h[c !d Hf1%suQQ"#DArZ-(t&Z!T~/MîFX%oKM0F,IոnNNխWOA zSB&P(}9@# !X7BIp0fJJ&馥,I@gB}BA)8D#,I"!݋ (JDV,0F@j3 tg*c|J9WR`LLFCXTU cDPDQBYUUuL&\4\ "1ArAsUE}0"ҽQ&J"jZXf D}cuP>)9&`0PJ5M6},&Q}PA!pN! 41 ]sCcR!!"11TB!MU0qc('jsc@DG2i$l9Ø K$ !,˒dZBVw ?fMl6GB!Cq]GYC (g (""Ƙ=y"6jB=#@L&3T*2!a~&@,"Rj*TQUB0B(PT>F2(("c1FiVfr "@(A1JeYE˜R8a #+style: * Mug /Mug/ is a toy/demo user-interface for =mu=. It is not installable, you'll need to run it from its source directory. Mug comes in two flavors: - =mug= (in toys/mug), old simple UI, only adding dependency to GTK+ - =mug2= (in toys/mug2), the new UI, which requires GTK+, Webkit and a recent GLib. The plan for =mug= is to be a testing ground for the widget-code which will slowly evolve into a full-featured UI. #+html: =mug2= supports: - HTML email - attachments (including in-place opening, drag & drop to desktop) - bookmarks (see the =mu-bookmarks= man page, the UI will load these in the left pane) - view source #+html: