elog-3.1.3-1/0000755000175000017500000000000013117320751011620 5ustar rogerrogerelog-3.1.3-1/COPYING0000755000175000017500000004307112757305354012676 0ustar rogerroger GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) 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 this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: 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 convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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 Library General Public License instead of this License. elog-3.1.3-1/logbooks/0000755000175000017500000000000013076344742013451 5ustar rogerrogerelog-3.1.3-1/logbooks/demo/0000755000175000017500000000000013076344742014375 5ustar rogerrogerelog-3.1.3-1/logbooks/demo/2001/0000755000175000017500000000000013076344742014757 5ustar rogerrogerelog-3.1.3-1/logbooks/demo/2001/011108a.log0000644000175000017500000000060412556103750016347 0ustar rogerroger$@MID@$: 1 Date: Thu Nov 08 18:37:57 2001 Author: Stefan Ritt Type: Routine Category: General Subject: Welcome Attachment: Encoding: ELCode ======================================== [B]Congratulations for installing ELOG sucessfully! [/B] This is a demo entry to ensure the elogd server is working correctly. Click [I]"New"[/I] to add new pages and [I]"Delete"[/I] to delete this page. elog-3.1.3-1/ssl/0000755000175000017500000000000013076344742012433 5ustar rogerrogerelog-3.1.3-1/ssl/server.crt0000644000175000017500000000366012556103751014453 0ustar rogerroger-----BEGIN CERTIFICATE----- MIIFgDCCA2gCCQD/iDJhJt1QLDANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMC RVUxEjAQBgNVBAgTCVNvbWVTdGF0ZTERMA8GA1UEBxMIU29tZUNpdHkxGDAWBgNV BAoTD1NvbWVPcmFuaXphdGlvbjEdMBsGA1UECxMUU29tZU9yZ2FuaXphdGlvblVu aXQxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0wODAyMTUxMTQ0MDRaFw0xODAyMTIx MTQ0MDRaMIGBMQswCQYDVQQGEwJFVTESMBAGA1UECBMJU29tZVN0YXRlMREwDwYD VQQHEwhTb21lQ2l0eTEYMBYGA1UEChMPU29tZU9yYW5pemF0aW9uMR0wGwYDVQQL ExRTb21lT3JnYW5pemF0aW9uVW5pdDESMBAGA1UEAxMJbG9jYWxob3N0MIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6/zUTd+jsNd/S/cPkHSlGmygFlKU m53/HgLf/iqxAI0ZfKD8t5YKh2i7bZayRVPoTWhP58F3gndc/e2fpr5xwwivLm6l 2ZD33RKRPNdpoodUtDS9P2bNE0YLpnh2R/KywrygDLn1927A/Rubu2IIO3NHKqIe qD73YfRpGAIN8DQLYQIl4DR5+FgN1qVfS721O5yao9Y8sx4WM4tru3DAljFWmeYn yxvRupWvAVtDu2wJpY5KfHWpO4H0AqNXAlL42P4EzyVzsdoMnyx125p3kMzHZXyI jwZNbgwt7qrrBFeFclO1LV4r7ivdNI8Fa4GwnXpwJ/Zb0nMhckMpaX0jUzgq2BKq SdBgFB17NMeEAxQC7ZN+mavQ30yKhS2M8KqGG2UvScj4L6T2VsV/Ez1X7zpqqHr8 92d+SWD30HbkkczpLaW9zeagKQevRsbw7o9dwdfhtbtRJM148/OeKd27R2AxwFnr vWsHTNFFBfgToyLnAKFcrBvqVsyNtTrphDpJpZpLbFLZ/BnB25VRy28wlFz60wYj VfqfGAXtSqXzxAFUjXYUbCiU0yLbpE4A1Uew9+bv51v6Bv2qR0DWyNMCRJhFsJpt TyhepATTDpcJwYqYZTnIAPV+U+os3fu7JcdbSVMJrqpiZMT/B/0O2iKPyaQj1sIg sVNk5jtab+TXjNMCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEADhmaIkK4YzzBOND8 mpEGcsNLDDjlGTf0r11Q5epKBA5hDVbP//Z/mK/dVxo8FaEkmAvvvgidB3zEaP3Y hcrmD9/CT2tDi8HbRgW8gUkAghokZ8arVZGi/PjZXDHHxcTC+tPrngeD55cK4xp/ NVCg96cdXiEsDWvAoFAO36S2NY3EjMA+XrHvOeBADd73jCbrqkohRyo7W66oZ3yx WMFKCbsVlosq1U+fsP0Tql0x/ysD5UcenK7wRRutj3L8zBbIhLpeLZVEwFRQLZoI R2drRbOqJHyA67/691pJK6TwDEiqBeU+MBwECyz+kFvsmob2ut16Jo+kcc8k3dr0 t6RP6WM/MfbwwnaqJFu5iiiQ8Ap+3WiKNi+DDkIjDRRe8580gTtlTqPNmkR/iuVz hTiq2Ahr7R8Vj/eYLso7J/ViBzHAI7vCM9JlKrkYL6GqSsoC24WgzclH73aRusW6 +KXvum3uSvDa+70AprivkGu3FAeBOYUn6XyvfaxnTLrCHKymbEL25SR/u934HG/J Wug8G5cI8p/Yj2jQeM+phhEhvt86rYOUt5hPzvAmffw03pDu7whmMwaWg5R3X00L 3hRWrdpfVXvyrygm8bOChsJm6ti4WuZXwmyog+JOJPRGQsJL+M+TbaEDc18ehbtZ x1oa55iEDL+F09/EZua+2IdQWeo= -----END CERTIFICATE----- elog-3.1.3-1/ssl/server.key0000644000175000017500000000625712556103751014460 0ustar rogerroger-----BEGIN RSA PRIVATE KEY----- MIIJKgIBAAKCAgEA6/zUTd+jsNd/S/cPkHSlGmygFlKUm53/HgLf/iqxAI0ZfKD8 t5YKh2i7bZayRVPoTWhP58F3gndc/e2fpr5xwwivLm6l2ZD33RKRPNdpoodUtDS9 P2bNE0YLpnh2R/KywrygDLn1927A/Rubu2IIO3NHKqIeqD73YfRpGAIN8DQLYQIl 4DR5+FgN1qVfS721O5yao9Y8sx4WM4tru3DAljFWmeYnyxvRupWvAVtDu2wJpY5K fHWpO4H0AqNXAlL42P4EzyVzsdoMnyx125p3kMzHZXyIjwZNbgwt7qrrBFeFclO1 LV4r7ivdNI8Fa4GwnXpwJ/Zb0nMhckMpaX0jUzgq2BKqSdBgFB17NMeEAxQC7ZN+ mavQ30yKhS2M8KqGG2UvScj4L6T2VsV/Ez1X7zpqqHr892d+SWD30HbkkczpLaW9 zeagKQevRsbw7o9dwdfhtbtRJM148/OeKd27R2AxwFnrvWsHTNFFBfgToyLnAKFc rBvqVsyNtTrphDpJpZpLbFLZ/BnB25VRy28wlFz60wYjVfqfGAXtSqXzxAFUjXYU bCiU0yLbpE4A1Uew9+bv51v6Bv2qR0DWyNMCRJhFsJptTyhepATTDpcJwYqYZTnI APV+U+os3fu7JcdbSVMJrqpiZMT/B/0O2iKPyaQj1sIgsVNk5jtab+TXjNMCAwEA AQKCAgEAiCwvcfQ/p5biPLqGv6ufbd0Kj3b9i8Ki6PVtvWNzGcdR0qmuyl0JHFPh +32+xuSBItGzch8gUCeCgIBy5Kv2Rz2moJt2llHhxS6moj4HFaMTzJy2K3Stl5aO uHPAhQnV/ysLC+zusuW09RjOR1VtPKjrgv1jxV/4Lgzz68ebtWTCwCqWIocFbrmY 2qg4kiMZBhn63dmM0yrkUr/73YBVkRVNTKcThU87OXiQMK8yKV9RPbga4TCgA9ec Lz2DwrKQJ96Edee9l/4j1GG6bIr4vZR85uHyht1zJrJeet8jmDDMr0kAPfqeTOQA 1AhHb165xFVXjKfLGmgW/7Ejn1x3Wb1Vc4krQPOJ48GEITEM2q2seYfPPE72sIqm PplVCSnKgf+MHGt7Nxh/LsR83yEEfC3OkKlmjPiyh3Nh3hAf80DBdsM/ml39nZlh 4W0dhRPrj+ebL+5nE0b7TUWBFFsJ5FGs+bFCxSiZ7ETFDG5GVdRaRGLm85iQ+QKM R5/BaFOxT2cMlaLo/I9Ps+ygjwcDoKoiWeWpaPyl+cy0P7y0euWi5Si604Sp/HY7 2VNE63icXRcLg4oMRe5txUogbl/ZzzvhNHEMr/t/tRwKt9AHCzKz4NgIX5SVASwl ByT/UalQN/4yZwcnPbzaMnS+Lfr4pKCsW3qXSS/hetbR0+j8FMECggEBAPow2WIn fiGdQCJ93Ffi2rtRvUd0Htm1cIPopJb+RV3Cx59IdImEsZ4lG7Kl/CXqoG+GXWVg caYsEDxws+e8fAZpuQzy1lEo1ptOQnM3a9XVuXjYlmBhL5/1kwLFvo3TCtvKBJpf XwZEQaWbqiI5rKk7JmU8FvCaQZ81ReB39wwyChRsfJsnBx4bnCMbKtQcEIa6ANzM zt6cuty+2EBwl5x0n3YUOxL0fhQCF8hWPPNFyK843y1xWDGdR4jccpAK3bRazaS7 Gn4aAB/Zw0glWRHa0LiWovjC3hHdNIThVOFfnvBvfY9qffiv9riYYG/e7328JRFz embcMebfzefw1bMCggEBAPF3ji1auftirELosaEGnPJPYCeyh8auK6fXtMrtnS4S lBpJ8BdpkHtpjFYT6ZhHdxv/iYF0NvbE8aa1cgRGTSGP9LJrYpyKUCKi3VQMyXnY zo8eTOjaFMAFM4oaalmeoTxjz7dr/pHTVPBydQrl+lRGuAHmqPPoaV17IkvnD8n7 PHNQ3kT1pXXZf2iLGUtWpB2AYQfM037Ssn3qCeWfZCIB8NUJArlZPtiR/7eJA3rL t/3P1MxDYJyuI/8uRocJ5vhipjxzD2Xn1zAtPLHV2i3/pWpS/lsKSQ6s2txXilfM a2f+sH7nYMohKWoYui0Jmi041AZtPqyBDNFwPJptHGECggEBALsZINfoVMjqgfXJ Tahju9NtkzEGQ7PnndbneX55dIJjkUkqtAvLMdDkS0xUGLZ6ObRtNRcFAriUJygr 2D4qlE1vbrWiPGfBNIw8ADGNdpUZGXSn8/zrcOQcj+3410HUW0B2WO7g930GOlmQ ibDClL6Rv2rhfv47PWvEORdeX1+9ZBBV+PFGRGpdl2wheuvYePtnyhFVEDcl2hAO oEXAbYDRLhbtlifcsxRfZE2rQDVlMGjpDO084AlqZv6QbRQPLDEd3r+cNHmtE/69 qO4hD/TsQD5qWEHT6KJPXGEq6YEJMyD3H1zK9J2vacAuI6BFha/vrYDZbOXTMXZI VAFp90UCggEAN4c10+o8QLoVQSG/z3b02qg0RYzBuofNFED/m3b/IVw2YMFGDCxD hI3mLtlPa6xrkK24EmQGS8NIivzI5oDANgqjy+/z8mnAOebYHP7/yhx4i+L87sNP 6ESJ8KVYdZmMHJYba2PTW/eMXesETWhLo9vLbux7R/PXXx706R2OvPUglqgvJQ6U aUCGEyWBwJiBDyO5rJCGxnBviZMriXmZDlUB7Auxa6xBTo5sjulcOn3kpuKHXh3U dtN5BbK/l0hIrDALaHVRQi8JhlOw1HiIPvG+OXAye/8mg9BuWCNtdflVbcndDqHo 2TK9El/zWG4P3XxnDb9LnWHj77nuxdh3oQKCAQEAn4rGCH6JK2S4vwv3ZvoXfbuT Kro1+8YZsevewfOzGh3Cc07ZCycKMgfTrHCiWWmt3ePEhsey2jKf/xE8tZmbfqHY qILvIKtmJXS9eo8nvEPIy69GGC4RAxj5RoNyVHSP2PiwoRgK6QukWquLPSHXTW9b VUygZQUh1swccx+C4AVafY+imFhwyisOxPmrqvzI9Dw7LpjMyZUU9ghMQbxwVEBc K76tTWjn4mxuAvWsY7kMEmRngZfaQwi6z+a6icKV4bCJycsnyBXmsgmHqwwIKDlX K8co9hGjzu3H73UOMgOv+1kgOaPkNFMloTyV48Y7drL/UWg0rqKqFytLjXQwMA== -----END RSA PRIVATE KEY----- elog-3.1.3-1/man/0000755000175000017500000000000013076344742012405 5ustar rogerrogerelog-3.1.3-1/man/elogd.80000755000175000017500000000564112556103750013573 0ustar rogerroger.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH ELOGD 8 "January 15, 2003" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME elogd \- web server program for Electronic Logbook ELOG .SH SYNOPSIS .sp \fBelog\fR [ \fB-C \fIurl\fB\fR ] [ \fB-c \fIfile\fB\fR ] [ \fB-D\fR ] [ \fB-d \fIdirectory\fB\fR ] [ \fB-f \fIfile\fB\fR ] [ \fB-k\fR ] [ \fB-l \fIlogbook\fB\fR ] [ \fB-M\fR ] [ \fB-m\fR ] [ \fB-n \fIhostname\fB\fR ] [ \fB-p \fIport\fB\fR ] [ \fB-S\fR ] [ \fB-s \fIdirectory\fB\fR ] [ \fB-v\fR ] [ \fB-x\fR ] .SH DESCRIPTION .PP The Electronic Logbook .B ELOG can be used to store and retrieve messages through a Web interface. Depending on the configuration, the .B ELOG system can host one or more logbooks which are stored in separate sections on the server. .B elogd is the daemon program serving as a simple web server for the .B ELOG system. .SH OPTIONS \fBelogd\fP accepts the following options: .TP .BI -C " url" clone remote elogd configuration .TP .BI -c " file" specify configuration file .TP .BR -D become a daemon .TP .BI -d " directory" specify logbook root directory .TP .BI -f " file" specify PID file .TP .BI -M synchronize with removing deleted entries .TP .BI -m synchronize logbook(s) with remote server .TP .BI -n " host" host where .B elogd will run .TP .BR -k do not use keep\-alive .TP .BI -l " logbook" specify logbook for `\-r' and `\-w' commands .TP .BI -p " port" port where .B elogd will run .TP .BR -S be silent .TP .BI -s " directory" specify resource directory (eg. themes, icons) .TP .BR -v debugging output .TP .BR -x enable execution of shell commands (unless compiled with this feature) .SH SEE ALSO .BR elog (1). .PP The full documentation for .B ELOG is maintained as an html manual. You can reach it from the package document directory. .SH WARNINGS .B elogd is run under a separate user/group .I elog , by default. You might change it via .I Usr and .I Grp options in configuration file. The configuration and stylesheet files should be readable by that user. .PP Passwords are transferred over the network in plain text and therefore, are not secure. So if passwords are used .B elogd should be started with the SSL option in the configuration file for secure connections. .SH AUTHOR Written by Stefan Ritt . .PP This manual page was written by Recai Oktas , for the Debian GNU/Linux system (but may be used by others). elog-3.1.3-1/man/elog.10000755000175000017500000000622412556103750013416 0ustar rogerroger.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH ELOG 1 "January 15, 2002" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME elog \- utility program for Electronic Logbook ELOG .SH SYNOPSIS .sp \fBelog\fR \fB-a \fIattribute=value\fB\fR [ \fB-e \fIid\fB\fR ] [ \fB-f \fIfile...\fB\fR ] \fB-h \fIhost\fB\fR [ \fB-l \fIlogbook\fB\fR ] [ \fB-m \fItextfile\fB\fR ] [ \fB-p \fIport\fB\fR ] [ \fB-r \fIid\fB\fR ] [ \fB-s \fIsubdir\fB\fR ] [ \fB-u \fIusername\fB \fIpassword\fB\fR ] [ \fB-v\fR ] [ \fB-w \fIpassword\fB\fR ] \fImessage\fB\fR [ \fB-x\fR ] .SH DESCRIPTION .PP The Electronic Logbook .B ELOG can be used to store and retrieve messages through a Web interface. Depending on the configuration, the .B ELOG system can host one or more logbooks which are stored in separate sections on the server. .B elog is the standalone client program which can be used from the command line to interact with the .B ELOG daemon (elogd). The .B elog message can either be submitted on the command line or piped through `stdin' or in a file with the `\-m' flag. For the former case, message with blanks must be enclosed in quotes. Multiple attributes can be specified as well as several attachments. The password must match the write password defined on the server. If user level access is defined on the server (via the `Password file' option), a valid user name / password pair must be specified with the `\-u' option. .PP The .B elog program makes it possible to submit logbook entries automatically by the system or from scripts. In some shift logbooks this feature is used to enter alarm messages automatically into the logbook. .SH OPTIONS \fBelog\fP accepts the following options: .TP .BI -a " attribute=value" set attribute to value .TP .BI -e " id" edit existing message of id .TP .BI -f " file..." attach files while submitting .TP .BI -h " host" host where .B elogd is running .TP .BI -l " logbook" name of logbook or experiment .TP .BI -m " textfile" submit message from textfile .TP .BI -p " port" port where .B elogd is running .TP .BI -r " id" reply to existing message of id .TP .BI -s " dir" directory where logbooks reside .TP .BI -u " username password" username and password .TP .BR \-v verbose output .TP .BI -w " password" write password defined on server .TP .BR \-x suppress email notification .SH SEE ALSO .BR elogd (8). .PP The full documentation for .B ELOG is maintained as an html manual. You can reach it from the package document directory. .SH AUTHOR Written by Stefan Ritt . .PP This manual page was written by Recai Oktas , for the Debian GNU/Linux system (but may be used by others). elog-3.1.3-1/man/elconv.10000755000175000017500000000332512556103750013755 0ustar rogerroger.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH ELCONV 1 "January 15, 2003" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME elconv \- conversion program for ELOG messages .SH SYNOPSIS .sp \fBelconv\fR [ \fB-v\fR ] .SH DESCRIPTION .PP The Electronic Logbook .B ELOG can be used to store and retrieve messages through a Web interface. Depending on the configuration, the .B ELOG system can host one or more logbooks which are stored in separate sections on the server. .B elconv is a support tool which converts the .B ELOG messages in old format to the current format. Basically you simply run .B elconv in the directory where all the `*.log' files live. If you have several directories with log files, you run it once in each directory. .SH OPTIONS \fBelconv\fP accepts the following option: .TP .BR \-v verbose output .SH SEE ALSO .BR elog (1), .BR elogd (8). .PP The full documentation for .B ELOG is maintained as an html manual. You can reach it from the package document directory. .SH AUTHOR Written by Stefan Ritt . .PP This manual page was written by Recai Oktas , for the Debian GNU/Linux system (but may be used by others). elog-3.1.3-1/README0000644000175000017500000000057012556103750012506 0ustar rogerrogerWelcome to the ELOG package =========================== For general documentation, see the file "index.html" in the "doc" subdirectory, for installation instructions, see "adminguide.html" in the same directory, or consult the web page http://midas.psi.ch/elog Please send all bug reports by electronic mail to: Happy ELOG'ing! Stefan Ritt Jan. 2006 elog-3.1.3-1/doc/0000755000175000017500000000000013076344742012377 5ustar rogerrogerelog-3.1.3-1/doc/live_bookmarks.png0000644000175000017500000114534712556103750016125 0ustar rogerrogerPNG  IHDR%T_iCCPICC ProfileXPOg/G:r>‘s9,s$I*( I` "" N}WWoS}3 K),,F @pHT'g48K Ӷ2}Я/]Ymt^ޑ@Vd &s#{ED50D'H3b?\[F̏""|yb<}:kCCGYӏYF,88Y:I$}ߍB?2,܎E.r 5G=$e?occert_ 5ݗ_ٗG^/L2)Z~w?sڟgHZ|" e#Yo>`_xFm!:Hz{ ̀><@ |Ga |GnhX|_69"xC<%xdecuq1 3N~^EX?c$QZn}1~жg  00Vw! dPZٛK`+~GXp) d\P.+:pt'c` ̂E7AAx QHR4 } w # ( ʆ 2 ݅:0/6 18aD$L 3|aIX:V kuž`p&8.W-px</[Q,|B<q*aD#!! ͈GQb Cr E*Hc/2CV cE7 ńD)PNa9T ՁF-6h4+ZDQEMt;zޢRpSPP8SP$SQܠx@1BDCIK)@BiIEOAyrrrCĨc0L 1fbyXk?6 [`ߩDt\ҩ*:^Pp8"N 猋¥pqp[xj jcj/D"f44444 4y4 44DZ]Z1"ڻtx:i:K`st7ҽGO_HxO Uc"A!!#=cc}Y&8ɘ))iiYٛ,s & ;7K*K-6+>k k65[,%l /9`"69pqlprqrq^|ȹĥkϭϝG'`D&;ɼ3|>%>.5~n~s#/(6DGi;AAcji!PPsappLD^OHdP& /Z":,S +`0H#Q_Y2K[򧔼TU)izidV/2"22E2eq-D.MOw*(*D((,++++N(1(Y)SQF*('*SRIU\5P;5A5oj $2Y  wRYM&I\s^OKBkI[X;@G)&M]ݣzp=CT}z}{BWk ;FFYFƜƞUk&&GMRښΛEM/O[XXܱƖ,g­ڬQVEommtmlo~ӱ˰rqpqrtsv= ygNlNN-hg 獃s.Ȼ<w+k}77[;%aQWﲟ_ߪ{AAwCCCrƅ͆熯EFTDB"[hSs11E1[ qtq!q}"g F͓˻ɏΟ-0+h1B±"bś%^%#.\漜vyԿt̰Xwu%۫W)]`Hح nsQbU հ.7nj)eMuuo7jPjih,n76Ckw̶8 5ժ&Vyp>''otuvv.tuM=<#GM<1x[GSw{z6~Էv`ms/Y+}ڰx-fJ߻vb 49F > JpNS 78#_ hVwE#Ϩek9&G6-c4jXEH\HnyoB:o=G"LZFK~} 6'>*PItQ9y-EE>Fb6Zb:Lz]M5wƳ&fY4X޲a]es]mdžN ]nuuwo!uzyzg($bjq>"=z2se'^(#>E@ÒK͗+P_h^d_|RҤ+MW{zdM[5qgnW74467?pX佩i_?d|$XUgOg}6G~<{".?fB#on$’0ǩNN9=6#877;BDNB E ꊛJ]tMU5 JUn7ªnf*_{{uûƵ]\+C=b&]N%>𤲻l>D?qP~Hglv˸ۤ )4iso.lBmK}f?J#ĚgA}(vleK;oۣ;u?47c#EϹpj<||?t!aaAQ8$\ZmU8tHTEQOZmK}AcHCANn^~i$80_SW3;sc K+Ika-=ۑ3A*!ЮwM-Ҏ'̋›ƇɗÏO  Q 3 p JΊ)?0sx1X"&8e2D΢oN>?ՕR{LTR9|42糦'/LLț̟,8^hxdRr[]bUM)qKFVN^~^NVŻmypwwru?XycӠޘg}g / ?O89yE)iWf=$/澽yUd?}1X7MuSaK6q.'nj(rMy'O`%U` `:&+ `RWD.<)-` 5p \S&̀u  RJ C`fZ\߭YFx-\]ECJk. Հ@(X((^RjRVcX705#y SHtTt9nG%x&,S)"K+͂+{1!:g)96w%=Ih #!cB‚DD4QE~RJX2ͲrAf 2\JxejZUf5VI2N)R6IM+Vkuo 6/[:%;7\~Lbqn?SʕVpIs-qs'hh&hmҟ34F򙥙֧fg;cpqsEp pO _x9+BMI"f̢ b5 R-2޲Zrr;3 LPH\F>}VkO]WEEAw.SM37%m(me\;ʭvEY[,ׂ~?ݻS!x'Wmoz4},fTLy7Kŏk#C[ۯw9_; `v{8\ #8ΈB4E!LPEOh}t zžӊPQ=`{NmFFV =5C) &,b,}!lx6voz0.!<2F_ʿ<`32:>U#[ϑPthyHRSR~L%՝WOB\.USEK.=* -2~-ҹJ͞u-m;+;S=zzK" 5O8>2mw!~R*(׮o;wbvQ|N nNr1 @GПos -D\'U5߿qśPgmpt pHYs   IDATx]wU?s},,m" Qg5*D(],XP@X l{g~a#&0g)g3s3gr-P۲^+c cIol.4"E)RH"E{~#'4á;6l+6f'Gw)RH"E):Q`emū7-*3W!ٓF@KK Bc/#?cL4J)KP!R {^\%m[u:32Y2MӐpyF,\.'p~xQNXU+ _W+GFjoѳOtXc'b8qFdK~2X\6KB2U0Y«;C7dʶm%ضk\!P+ _W+햰c5+~}RJ|afT0e UV}y I:_H ^5&*4*l3j'(#3X,L#| ZV _W+ qǩg9v"XLFML H ݟLXm&9"!]%!2 0M )d B8Ȥ_W+ _i0A!D5Сm|{C{v1z:`Ty PmWC=7I}KJnj T"m56ܾ{sс}hy=:.9)ppqą$/JD# y!K23X,;_~!2.;MȀQ <#0_W+ ¿s~Kd*f_A,̄TƠ&G^|IXsM}fei6ii#Κ{<&iTXTP)9;![ c`Iyeq玟٧gDJn?N ~l<dAb^.y`cu]17>nvQnfH+!#HrZWߴ''cenY)[]u=6?. w뮾ga׿+l=1鹃Sӝ 5   o>thݛolg1asH`_{bw$'ǧBUnXcW4j+]Uӷ^ cӘ8Z\VqoQkĨAPѸeo|bMNL Nl9z`C8X>B` CI$* 5qꎫ:|MevT'GyN庐 _ma 9E'ĺ)H ÔRB? +Z{cޛ_K;9I6qX%ۨ\ٶ#L _4~epa[>g4Y6YȘ  yyXmkVy|* vm[qF} 4:9AMZ~٪_jSNj}4x;M\0/X0O!iPZ{'2ẏ elѲ,?gy0/aPRl\Nb888  p՜}_t)vߵ&t֡U7c%}Ň~iWҘusz 'Eť6fe Np{W+s hPyw1 Sh]i1 WxTo#5 vg75DG8dF߿8%pg vDcFo'ڝ'mg1Y>_0"Ϋiadd1̃\Յ Py܄ M!!xJ5(GvQ򗺮#ȧ_?M|6kF@ƿx*MӮO~?|ǜ˾Y33R}-BQW_W<0V s/z5՚ N7~>_/zQH'!nqN1EX^š.-ngY z}_msizD biYIJPU  :Dت[Mx+1l Y*K/cY^M%{v\u8-qzG䡬W+#ZYM[:p@7QF4L*ĻZ̴|7A*+6<)g)W+ ,ǀ}`Y1hpGFpB@@Ӂ n_Pu;]g刎Ҝnb8YujTc8ϻ Qt1nOҝNd;4YLdLy ׺"pc60^V /O,WM%{< /ÅT:.ӶtY&3p ']y( gm=F4|5-%m0VL2: w,c+ *|A<1#5[pRM1ft3>mF{4ֽUԾҐ\5D^p$abJi0.e>=M(AE,>LAhk, On&]%4&Rͅ:_O4555UUյM5iCŸ1V6^+_fMˊ0i'#Mt(W+'2zF1SO Bmb}ZBDE3Ҍfl*-mNL9if:A"DXY^-OLM̲|- V~mn^<^XZVP2u7 ?m+v[I#0L8k d*酴=] Dd1kQmsBMK#C!\31t__8pc!X˔&ǟHV?y"! 8ޕ_dƓں&|'wVSUUWWUL _՚4Of__VVIrR45j#ͭƲ_)9gW_WSfܶ@GRci\R&ҲC{vN۸3b-# U=SV Ue%~wRlROESYu]+2;{rr2W;I-G @=4gq#7$oQC.WiJjm}bEX<$ِAk^̏?2/?Y=y DFIɿ ,Uì:{->LpϞ~t*^Ƭ8>SڄwLT潺+'$¿=2ũW_W/˫]*gƢ !57o+޵R{JEFrsaV9PM ":+>u`RI#Z!RJ u%и5,!.[rnn`eFVeq / !`q <|u9-6?48t/3G'ے4_oD%$48?WW6z=:9\#T5P͌&-> HH 6׷+2: T7[.WDDtDk)W+',ize mO>i\.4QU >/ bYA/a6M ^>0pm; p|DyiZ[EӀ0NETdS/5bb6oH8q!'3vDqhru_j`' ;_i Z$0 fY 5 w38b',z~|>6*hy0[syޖJ-<ٱ^Mӂ~z@aےM q_5)x%| !=@c 8şWnlLfMhjqH䱥W+OX5Xe Pſܖek 5  wy+q93>*l70"bB~?"3$-E¢ e$sRHaĄL@^D%Ǚ \CGrcRQ'&N}4cGW+ _Y}GwA)EQD= Q%wdI 0[`۶Gr]`k/@@?~h82TDVjTrZҵpmAȖ\jy;'SP"be .8Vp\B[G&n "8Bd#S+ _W+' (w{G\R0,5B}m۶m@'@DT݈dx02X ^#0rɘp [ʠ~*g! 4i"( !D PJ+g.ZQJ5EKC#2n#/PWyt^ ^/O"B`KFdauX n_W+]O@3hBjBm2 \b."cc%&Êt#c3yPa2a TA[xlg {byFr%BmMFO:t !#<Hi2+6o@ay Kyp~j %f4#pt҄Άjo5lJ*C&q!ʣ!8*c„j}Bư88T:"e2!TC-EAsBz[8T8a + _B ZĊyfXEKL]I8 &½@Ė<~7!lK& 7LZ|!+x-Li!CJd5)|j#;G.ARvo2,L [$-lQiR<+U@2r1dyĉPir\.Wzz_@}}se¹$#$2Iz&"hCG4f 2V ʚx&0qsqBE$JInP _W+2x/w~4[ZZ<;~`0+E 40 _WWW`0t:B/(N%N~)>u]/,,t\\WOdFpyE"p(\H >,M GnE=ɥVr9rIb^iY鴄ο?9 _W+ =%s~5" $)&S+ _U:6̺99S'O@ z=cF0d=1h¼ȳDBEJ\4k5-VDXEI6P[K 70 8{RyK ` '*y0((vL̅r"c/=&#MW+ Wo0J)N4kl`4Be@&Ftս[5H7~˲|~]0FDMxMf|%` @  Lb!RT <4ɹbbOd9 IRPiDNLP2.uAK%) *N].0 bp3Orf@d?g2a-  _Ww V I*Y]C5eJ@ !mݻP进,k/ .)la٤e78I倸J&pGQᤥ.bcLa3墄J"Q^/`&X&%:V"c?N.IPDr^(TQ+ _U*1Ig@OV}фvaDђÈM kjo,˲,Y =N-1'*|[DSd3%!u%,L"ŵ'(, 4;<ׄ)AY J*lw8KgL: Ҟ 琋0]~ɿqJhŢW+ ¿ 7@Tc l1!B<χZ5'$5}yM~.r,=~䔫>z-/ʛ/OK?Ή}KUB:J.SN';|Cg 5s/ANIQLyv["jh" eFL\Y"KJJҹLLl۠LAʤIyFA]81ikGY,c* _?? 7M  m1ǯde@ f[{e`*P ߷Sb!8Wi]X>|dͽ8}|ě.;/ V?t a˞mܶTrd i)VW>9.yrrYhr(7AiCEo0qm`\.dqrP`2[jo q)H;4DƓ,@YßK ]YTHrS$g>Dl"eYK _Wrطo/{^{muuu%T0"p@D']os'1`pYNlH7^c?yeaZk]g{O\Hf$nա_JTġÑb'0'M\NF8W-mMA\pqB/v.| 1 IDAT2 DlWYx/8 K1DNF-pܪ&`:3A _W+!UUUW]u{W\\5}#oO>aP"S']ŸVR|\JQxےs\S/r\wosia>@.|>N`̽m+U9^|(U 7pii:qmASOQte  \,h큵5h Y(xi|_,R3AA(Qq<yml&> "(lKVyAw([ .Z _WrMlٲI& -r,z֡9FCvxD9( iq@ˤw N$`ee@'y"0s$&.cb[1M>,r8ap84 5M0y|577;r( eB𶈴Mt]0l)>/g $#? @n撅sxMx dز,n?A! AYk9P͚W?eAX[41A8Dxٶ _WCkjj&L`x.1aA%F&]3AD|.iN<ƌW۞@va`?];{o_LO\|fhZƇ}w*@ `- iobsbܹ=8-KJJpzCDm۲,] OTÁ2B4 o~&HvBhaFU%2$Q{9Nϫ& Z9T<!DTڂ& ϹEO9i/^ W+ xZ˵Ê&osM0CoI|VU2hU aؾ5цeY,`wZ,˕3x7͜Տ?viP OuND\=~&BW|n6Pj~C|޽- zĹ)C!P6qp$A?R&-L|OٳD>zDĵ5)VR<&]OnpTs-g$"SX0)ö2 -DŮ,CQ0s11CghbGkk8iLC}>mt _W+ .`޲s"7S_z H#@cㅎ!U _%m \J|&9͝u> b%`Q?m=`"#=zɫ>4 I;\ĩM.2TEWym>4 f2|2z^\P<—˖l lX2~ . kD˟5 q\ePX.hF'9e!µk,T[egFCa/B RR _W+ q{28iqL X3Qt&gPfQ;ት4ԇ SھFBtCK}#k'.F J) "F>Bp`ؾ};a p|6~ID7.81)EPcR(=[1M[ jMij- y>mP&%~JqC-9dsi ;mdnK7aM6hM [AĮ O0DA@ƟwV?ڔAز _Wwǂ ^oT1Q(%`BH€h2Bl;4@S[zuSScM ᳑%&]nA|;KR~mۖeX---N u>W1q l0 ıT&|o:(n#S$W3>-r:| rr K40 .[8ü"^8bK?(۸Q!s.yARCW+ WocMӪuބ4Ϡ;Ċ C)-n_7DZҵ|rķ=@BiT$". B͖y^5Nrl@1ʥ%"oi +/[˲~wA,F%+(4 J)a6Ҹx!&y#ßyO ) _Iqj2e֭#F1b NHqCUV}G999Cu-LX-0tJ"^(mz<ܙbK.e(\lıHD`P{ NW-NqF)P\BI("7qC!Q.؉Y5ale"b\D_nS(* _ jv ӧOEw޽r"< D4MeԦn|҈fY1+ha9m ehU5M,(olh4Mrq9˷- , 8$}4Mc14qMu@@?-.BS0eXô, ^ ͖\5tQf(sFmٺ6FF4MӨM1f[1"hnPJy.hR]Xd35ݶmέmkM5^,( _dX477\N:iڴi פii?<!n9&dvss4MS48)RH"E'c/޸ܬ E'iORH"E)RUUU]͆"E]@m744t5)RH"E3dC"E)RH"E!zw5)RH"E):fA w "n. KGS_W+ſ_5 yuwRcTVVVUU577@DDDbbbRRR||:~X!m3W~..ſ_W+ſ_`}ʽ{deeEDD>~GIIIϢg&גkIߵZRw-)]Km<Ɲ;wsqqq1E4Mӌݻwmm={6lлw﨨΋=V6"גkIߵZRw-)]KCwqqqK8WH`0X[[sέ[~4hGZm۶vi'O|}I9uuuEEEk֬0`@rrra|uG1cqDFd nc;vFc7au׎SǫKבe/忑ZN賤H"E"##/^xaْ?<&&o2 Ŝ"E]Eqqq٧~zqq￿x'FDD9r$_Wvǃ_1O?=rvCƘi6m=z/YYY+V`0hu *. @vb FFZt

='~՚^D)!aѫW?|~8m4I:_%S#UUUGݻ|g}6SdddZZڡCRSS)""350Κ2/5BKC\24S淭r(sYvh!j]mBօWHYkT^C<>%to) E) R0,*//OIIjvGG l^nݠA9~zʶ@7 ?𴖞Qß}ԓBt6P0$V"(:fTXX駟@NNɓ5c dznݺ1cƄ'!DDDt<y睦ivŹINXQQgaYkkkO>CKKOO_f1) FP0`PNew{>z/L,T[[2F|m-Z6WZ)RF=zgyfhѢ3f}={DEEem+Ou}w77_mS`#S䊎}Go6ݸqE]t熤t8sΡCӻwNʴmEusswܱyf_-N/ 8~덍\m{W(+Ngdddcc v>}wRi٭ CFUl! i S39&eǥU#'NaaֿfQ& `uYSNZ*a >ik+,;1i" *~GLr8"ܑN0vMQ1d_(o@}MEDNkCfN?ezGq#r:dוnjշ͍Y[_/ /;f@CSmhnMՙ146غ3mJugT[M '6긆OѱR+++HNNUWYYxM6UWW'$$ 2dԩ+W|@`s̑)~ " EtÎA7EAPWoNm6]߬z TXX>mڴcʕ+?w}O>} Ip8&O`yyZQQ]R Gy#dSfddt ]瞫t:i;iiiu}:NG ̠UPD4~͟eŃXUYZ}o*ޥu%bW|[^棜<5/s^ق; ~}"aB~z>![mo1|82 Y~}r?f|/LF݃aC/wu۬~W5͹槞}G{yB/zx ÿpſyv?/c|gqGz kH[ݽjOxۇ^z2BWeۢ>9G߻G38ofv|~Q^})]Gޢo}i%! jo)P@xE])Ux5t@ :ۈ鿙8Bѳ9#76ED)عsgzz:_dddRJ5M;5<䓔SN9$?????{h9ql%(=01RG_ 懊Ӵڨ8G뚒ɮol=ns H =5?!{ZhTfL:U~w߽뮻³dgg}bbb:bo0^>~%3-O6xĚ-omRj4x8·0vonbqT*2&*@pnDkR& ߾6̽qzŁW,׌hpj{'(}G@ GQ_z3Aɷޞ6=>#_\^aŢ叼pKSrXNi;eшm}g59m+P:QezKru,_u1Z]}rn54Ylre2Rx[I,aiĶ|/s.zZҋ?s_8Tpo.XrU`9@:`ɋc@s֭ > H(e{%Dz%2Ase̿rt:Hz[]^&)fa8}"" ΄ӯ:_zl||f gd@ʐ0/=J/Yzr :)^17%=ńe>crG)+hӦM@1 ek׮u]Aaye3fٝ[UU3$%%͝;W_}5??\c+**r|wq]ϬM}ےenzn }%Ĵmn =Mq^GSgW5{$z-uecd0 ce,QTRRƍ_7޿Y!}ݲe76n:hCpx?Ϸ~aBr=?e[}ڵ~ʸkgd97i?-ß_z?}vio{Go`k8gc-˞[g,7pK-h([{r/Y2O˖:oo`N{ټ^<߾]?wjqUK~rG/6{?{%>g_t͟2Ƽq Ϙ㳅EjtSscX>7W|S6T߸^{w!ds3ΙurbkS4^ej}9;|097|sي])5*Wۏ o]5қ;7>֞ѣW/[3z3C|k&Hu\7xKn \oyN(%ɘ IDATM=&e/YQ 0|ܑ3o1E߭N[j!2%juiؼ%2YDʗZ6+hcHos6κ4{O]00Csf]4vyx7B!}BTeO7y R@5=yƌnZˇ4n@] U 79B-LlHɛ0c(_-^7p?^0Sbe]7>, DrPrD]%8#ּ {FKG;+Ɂ_mnSmZmzTN߾deciB r>># K6-Oy:e6 \ƚAk{$,X_?~Μ9]+BE J9<}N?i:yٛ5@dpH@DO~%z齢)']~t5ǩ-W^Vh3_|ł "##nwiN>Cs9묳䯝/5^_p8|[.)))!!!**vgI\i0Mz͚555594 ٔQʨtLtɈ!G M6,_+e6m-r5 ;6/=sks]ފ&fSG&+@M?76A ";)/c2Mg>f̦,f_??W^ٌg>4gYџ_}_߮-gƿ -_]i;?^9{ƼщΠ?^5{Ƽ1L~Q0lʨcnv[`f[jvug>ZӅ;oǯz^qi'td鮫a]N<{'z4X(.L<\N{(ʨdFگ1ʒN(7 0w @ükt暈o~Z4ݶ_¿~.7<1?0*16zkzv7ǙqZ4^̀Àv<M.[aMy^^dW_fʨS&͜1oLƀ^SfhƦe˶9o)Ex[@`GKD{ܙ獎t=2#M6m Sď[Ʀ@%k7}\ OҴ㵍P{oEϭeNtgjnh'~H9ҹs/ho W. N꺫fM#4Tysg}s>c [}q*,p^_?a-}ܤݷhݾ V%3Θ1pɪ%̞7O"`o~_Juu [F&ۛ|bٖ ^w(W;lKƸ͝}`KyS]C*@g)^wݬ Yh[Xޖ Λ~^ ~oEmc+s`:+ɾf 6Y]_^Q>!E߮nо۶|ydiv]Ɇ_E:ɓ'SJ#""<4B4pxåK:Np8̴-'~?K_s~vqeczeF]<inYf Tghhs7n\qƅ(=;j!Ov;VQ{/'x&=mf ( 34F  Lw/^://,Q~ gZA%~\y+E˂2aH [d_̦ љ fC87leќ uS( V0-c}2 F RFE)[>"Xk<~}Ι}Q|F p5 p2 @=jYOGv+MEm?ƒ)ߟJQ7kߚUi/kFƎӔ6#F'~Ě90kFS@qOٯɎp@XQ DUwm[G^ٯ.oQ]mZ9xĊΫdl8.Lͪq27t Ϩ0!PS􈩝CL9y̡ OU}DEtHӺ̩ SNwZB___㊋t\e]$Ir„ -bYVR%%%5QooF EyyӦaʲ0^myVj@4 >㿾 5ھ}a\AɁʛo)&OֵIzeT X/+e@dkAd]-B[ Tuf[e m $ qqq>hrru֭[u…D < X5 8S(`Wqڤ66{å=<>رc8ƥye'{Yׇd.k垽?Gq֮} NF,˜V7Tm9xp@˪_VP71!(`3`3}vX3@?ʜvҗ[l믿feeʪ駟3v˗/{zz|m{"Qs1e?Cs p9ਜƦs-GWшUvď1}qyzzڝ#'8/\VV#666** "q9"]j85#?1ki<;i̳mj3Umx<^WUrjI9]~q9-rT Xq8 3Vċ^[NuM~Q?np-CħWx-kkUTV^p65F7V]h9O(pğ&*(?DVX'/<㷝gLyv> 9Y4o=EtCSA8t*s7jDXxiOWJf3okK\b=Et( *.\\@cl+9⳿16`.ʯdãaHʫGӮlSzS+oru=@M_kr6};X &@X/Ӄ69@s9lN1<#lIi/*8qKZ-KC:xkR֣]箵7g2/N^s8K+=}8FUT(\߻1-aL9Won*K 2wSpMn>MRS_bYt*mqC֣CmW5iVD目J/ ;ӥ3@8Y<t8ƝyS*9W^z9\m(_իW^u7.38] !13{$_9ѡY/BBDYoJf]wwBOoaŐWgU=͗l wb)AA~Gܠȗ^s`x\q M&͏}]GVyP wŔ&A % EiK{G,; P& xl󎷟;7W$ HڼĿ1=-|-|xl(dl8q?o :2W¢ T}EZ67_y9{܃?\S>GlIܸYCPrfےmz{0J ΤٕtԸQ {wC4Pu;F;L uښGNqTʦz8(]VgqaJcڸma1>.m~9smw  , u `="lJ/X%iDk%`U+j,߆,@ j}ZN'Ni{'nb;Я[eݟa@U)_~yAF?io?~$ǻYRɑ=E9E#䬜ekM׃Gf>ͻ[:MIQ)_|@gŗ#}9Y{2lӦMCu{}Q%dX Mi9X$^`yn@>ߺ݃bԃvWP`BESƤgrS^r8ddye}=hJ.]fIoh[I, IDATG֖b0rpS VeLE7VY+(5YDΞ1-\ ^7-=|¬YgBqkaNg=p6qM&.;͞+S1Li8ijoL̙tFz;edSeGw12oo)DC`ɓ۷gYh4 V}||d2{YײԦP^^~С/jZOOϸD E|Mvӧ|497~?"%Xρ":_#&=v|/2ڣқj6Up` *(G=# 8Eĭ\rK+}ɩӽ ֮hUwcA݉%AUUq%%%۷qVf4klȑ#Glu DFF;vl۶m#FB]# ?Qnn 9L2LOQUHx+s<|Q0OA =|zv=q$Md2Ym۶mjڵ.>ǥ.S_9rܹ//E9q\eef(Jܸq#>>ƆNST KKu麬H 'D+Z[T[q4`cZ[u01"A5X~Ux^B~}9+;;rNͯqz㝒79 扫y=pNXpfQuކ{{%KH=T*AtŞآE srrAP7iQ:ln.^{nqaRyP'a5Z)CSyer‡{46%z3m&*P{g*JӉn-s"OgoPRRRZZ ]Fqgz9qJS IuklȋsDNJ|M2H7^1]^3KHHHH5JJJVXX9Adtww|088/OBٳ`0ű,[~f͚7ĉ~nm:((HeffU܆3gh m%Lx;i^I+ρqTkxul\8 8 ԞpL,M B\ &FऽJ]!!!!!qoQVVaǵ.۷o_PPqF??hڛHHX֊-Zۊ_0,,ܹs|||\n@}V5H|\.oٲ\.#޲yqHjhn#g}{x[w'RRRk׮r)RՄIe7n\v---mРAP.hqDEnm"ZxtsDg J?ԏϸJ:K~Ѷ|9]侪{P<ɢ{A<;wd^^^2$I ,n m?fC, &qF|[hw%!!!!$33sEEE,VUU3>ؼy#GnFno߾2eDǭٌl̙7~:hD{M/^ơ}%fĞbׯq6 y`o-*5{ҲFWyxAh^1>aÆ5۷Ka'"Å)Oŵ]Q3[ޠiRثAkM+Fܤz xM!aϞ= ܇[LsSSUlܕp7|=a_ PUU\PP+^ LT*ٳgDBr8\hjӦ t#qζ~ϻ:P{{8 qࣅ앳g=&KJ_SMot>QYdGO6ߒ[+/$agX0 ҜܺҍYq6mڴ0qS)sޞ79cډ)_殼#wumxEǿ_|f2]nT?1G͐g-z{O$MУg]effjZwwwGcGNxǔN1_2-pwϒn~lyuұOhWua 3)'*kGoe˛8Aor!y҂mkd BfffIHHHHH&* IEQMy~̙f̘A$jo5!j!IlyZ;*+1Z,6'xH8{!=ŏ夋¤,ő @6k&o%CyбWbGۯ1=/~^f\l0Yk>rjqV{p;~kɍ.Z ycqsœ8ԭz㇏eiB I?=p x'Nx>߯H=q݂[t|4pwbceX5laKp|u\2*T_98hq vڼiBUWN]oǛ"^Ó:r#-$;<7o@QzHl{HNM^s8!u B|+N^k 2gzX*5DU jx*S-?X\4ʋh'gHRԗ&~hP(sל*z>Nܽ1!ْ!!!!!qWZ+޹sgqgϞN7<$Nx;);:e# _ S.w4ް28ah`Oق x8R&#rxV\s [m=ᝒ?.H 0{x Z<wf(1Y 8IaHޓ^ѳ̐s yvӟdJӷx^ gN񖣛3-Y4v&=1p42.l?=zHh5zWJdKXElJT:x@2%}B`}߮A@(ԛlPybk3j'OmS}J'?I¥C^" 27Nl-/,] WuU&:/]oXRҥpfKK~0k8{^߮k4?: C֪:TӒرٳS/^|[L&+++_$$$$$$nq 7FwuӧO/+++..޲e˕+W\V>!.$)`JnqLᝆ e_-K|.GF޻y#MR^ښ|8ޘ1i.nn 6{BABN(9us)Sn:eC?'pP$jGξ?fv Qjn=4oڮ?6ްYggNhs-@e:˹E\N}IM9s"J|=#;( i_/1Dk}Z4`ؠ48eXPߍћ-CXfPK Fk$A IB"pTeECŞ˒^QHhҟdʮg8y7= ҍ$=7ڟo\_4p@7n7n+**޽v/]Jnsnw⁣ue\RdVk=d5*o l/n*3|}jͶo/.{)8ޅp?p2BٌͪLF2F2E;ɚ0fx%>@F#ߔa_~ "y@uY<^u^G¬q&I3Ke< bOoj%`O*K6ǀTXdM&O?D?Wn;q d8VFk?hbdt-pͩ. |;?tR,UoǏ/+{.dZ\ykWw(`!JƍO]<#ܖ森ĻV_E<o \) $g9mQid&#QK>=۽G-l6Ng^ptu qSpIQ wmo/<;< HڍLm#WT4wX\Κ*g fHo`9f^ wMD!8!!!!!!qpߐ'Ov~ɓ'Om"ٔ?* @@YH9"ZrrnzRw 7ho1 K` -=P*7oJT-5d݊ȟ6o޲g='͛㬏5VmX̊q87Ϟ*6&Sn tݏ`̋*mpeOZPwsWz~Nc33APrT9U͚ X63#S39'FN罓F@kfN JG4 VL ?N[0`ƂF2--FБNy׀=rS⥚squ@7 PآC@^[{s XBc:xuy]KGNco rpq`6%Lnv7\$t!ލ ?͟YzӈTjHpL f˰k3RcDsT/?4kV~.CTjի= 7F Xfsa߹YMvז(IZ OJ2VYk4AlMxj%?۶{V^Y뻌J/9f;ۣ&Wڿc;9؂_.7'sPge]((f+\L46JIHHHHH6r9M,zzz_qもhi(x'Nxg҅օonZ>oQz2L_" ` r{Q9dΉ?^9wg n?Aok*|Mhfhz./'WoC֍y =D N:u `q5ʓ澌DϫdJoqs#4,,8MX8(ܔ2T6PY27ʊ V# ؋Z@ˊu׮ TC܌zYHs7b80rh`֔N02e)^˭:NArT/ IF Huhs_¨n{P[(4,;8̋&R}? rxXrȽ;G4#0kC޺g[D|c~yVr/Za2;Vyz #(nn*zg Ȑ-G&/,Ƃ륎 R˿Q. 0CI7旸Q*Kii*SB"{ `BKy8,&|czKquCoX'/uHSF(TibJ0y)l-w/`e==hV<gї^ Nࣦ^WS O4F_kl(,6˙R?,ShK gQ/T4bV??#FUh ۷oGJJJ<==F˗)"jnݺMN:ձvVƪ*V A$ʋtWbq&D/l$a(+m܏b es?7Ԭ ,n*9f##c++yokʩZ_-d a,.,o{#uWk A@(~uh($%#Ƴ,_# <[k#OHXikdmtZzbNY]|DgW(+.PBMl{CixeuW|HLNNfkMy?>E9k) a A#@R @9=Ǧ  ddIIx  뚲tYQ (JJJŒD9pI%%%^^^ ØL& Ľf *i裏6ftݺAQ2eB.V>jMT'uN#4uF.zE׹JYoy. g(*)M'Lxq6֑q@#o8\# ٿ AFHZ Q%7k Yθ'k]}5ۅaÆ$)w޽{_peY1ȤL&S*4Mב+ AP( hx.zdSZRʛBC,* Ee$ )x5L:5|̻opB!&N?ZTT$:V%|6ؚ(z4 ="!!!!!Dju 'ɑSIHHq(ͽtRaadbДοU*UPPP\\\DDFi&!!!!qqHSvTh,yyy 0@Pd2˘" pgXFcqq-,pD7$$e***4ؐq+H$IiY*@doHH8Vl6GDDDGGԵ=Ľ˲999JR܃BՓ8DlOOIS7bHHlZ$::oJBBBBBΓ !!il?!!qqA`&,,LzJHHHH ~HUa${CBCJ%T*V8&ejr]r8t/O^ s9xw G7*a?޿>~߰4:vǿ~"‹RՋOo rHЗm\>׺i%Iy)/\AOv{IHHHHH4 İ(_^{#`]00mJJ UE/ktj>u%߮Ӫ+,eICq:G}Þ B잲 $! FE5{S/^4yUmFZzvO/1?ݍT(d׸Sz+Ϩؿ]&[Mx^wm=c1KגA~rmΨ^;*_[t뵧zLP6ZʲJPF?y7?kgܛO= g.;@Xse~{N4z >kqߵH(hmp{-=x^1Y;3 4h[Yumrhgl"\pa V\dDo5pbm$6{`$6j%8-wyF{VSՔWUUimYVS-h5ZMT[ZQYJ>j\rE{H{Os2‚\V@Ǘ$i;u^"6Aڳ/,?Wznߗ?muƫY;)`i/w۰8Wtgn'Μ 6{ڃVMsQ3񴏿J>*n1.y^2 V9X\}Rsb:1u Sf:q8h/9WGdO@9Gt e-hũ{w=zhH>0iԄVEy7O}>B֩sn~aSiO>Yzmakg.^2PԷM6˟,}X G)Zvvo{ZFW%`fo/Ssn.}oguUf o@bT5ulԱՂvn:`MX4oX'H2}@~9ޅm#fݟ1lcgy}閙j|=3g[ 0#"#; Kj451yWZ;#>:1ڪLC4ȦAСDkg=rkI'hL~=AilEK h%oµT~;ߗ}ȖMBTi^?Wr^x̺?a+֌1e[fezhϕl 6⋇ɮ!K6Zq=ы7-C+8*{znlUeW 9KQgmm뵔޴lCxgexmw?gS;X6^a֋>mS"ť&@F+X}P~qφM6 1 6J.>?oV`X-0,#pa>A Юy_.ԚaٺJ feabRW&H pK[DzfSwqmSsOPҞAzHSovK5YeY믔 v;٬![vw>I9k R[{7[ėkK~KFv R:Dt5'N8gF?qħ'>=17}(5@8?pFٔ|4i)^ɻݹ >iN?(d5z!L̥l3P_w:?CtXyw@]wXT~g ,4Qk4KQP Ē=D-h{o 4D 6@EE@.l"DKv{3Νsg9)b8-X<5FH۷).+P5Zm_?kPkwv;r9 ,c O=0,I.|yf 4%27P ?NdyћҤw#Q}Ҟ) 0jAAd]ヌop2N qtr`hSql; ٫kӼB33Ϋϸx'd\94ԕS0 :(d)xurlم̄! J)JUR۬C橋@m/5L #\Z@E0ѫ]!>aUA3設Nq nNk˸4Uv",^ +˰}|-2Ir %BPclxK@~2$_R? v 2Um9d ̈́dʞ] 9r"vVeɟF&wߚWW b Wevbm3 ɹȕlNcA){7FV]! >kdP? ry>;ɹ1k_B&,BxR, *u_&, B #=%!M~9܋zPȽѯXaqǗBzUN@gˮ }GLv2C&"Trj^|ڮ>*S󆷓]6k@h+.Q!n>sl&FK[7߭Nd.gJ9~Omm(_8fsWb7Oo߾x 7 kȐIہ"/XlԖ9{g>eoWĦo:|5S3u;4?d̈&"8#.nbۭqm:fo62s7~?hO{t{3v3y>Ӿ9&}UYS0/ԥVebQb_5ZӻLr u9ΣIAO}c'zԷE;u#7~;9ơ!mɹUQ  ?{1|J_u InGk/)݊JG$F~kϾ!%eSM=3I"ՄwVF](~;tfyMƂvHTQ$MB$dľ#d3pP;v;Rzqjq;J((t }zCԕE%~|]Cʒ<J l0PcjRTfD*U<7Q$SԖJ(+I?QJ3~/{px/+yФlκh8뺿g/I*d7Kr :6RJUIo)s```WHu#C,^r,2*I eZFF%Hr6yU?D\6I*u>˽g"Q,ۂDRTT$ RdTj pcZ޼׃]ftnŲwMv|0K,jtx놹Y[H&u0uU\2!]f\8I."6\*M"hlIo9@A[ es3&m|wgF E.-ֶ̜J\G蠙&pGÛ/!ji_C[Yu :Ūƿ =NSG?eyuT̜\f.RB)= !.<6' G!c(sB& Aש$+aBb _]9}pϿK^]vCD Jwj_rZ Bʿ@MlmCO"l0r|ZrU-*rFr2˖ՑBKm!WL R kzUaP=E!1۪v%4D_z iH9rK֓NƋv Wb*vZPC!'U!||=ΕU#W}~ng3di؉vDEUĀmc!2_x.SD@q3*ofq^{W_?t`>g笮*@5H|+8QsS)xvT,@؈`O͑FS[Ӓ7bşHɨtaB*R$4J[۠^"},]?#*Zm<ٽnє#v^Ϗ}.öO׋ &lF_ޤY }&Ʊ3m\\mwts֐R na"qrb&t%{V i4.nй<+CnjsrĪkqxE ( f5tl|1kSsqI>.^0h#= -8hv Thϧ?n䤉cK2k5MފL^ .@${W}%lUێҎwcS%,9 ._yM3($wҋkol``jץY-c˺"H˒@ ۴k sHd'S&oj\/RȺ|e-¹UACIʋSB qK9ԆĥRG. ݷunN& @3w{x#s&vYq2^%QGoow $?4PX,,.HhgQ)Vb^ 9uߠ4B$ܼE&R= 2@rP1&b`lOz&TͰ(p(EQW#[kS c}J?ɨMn.zٚAE==$<ӽTKDUPHvCp0m>@+) ψj?lz{7Z 52r9#;l݃@s!=byl9i@)FVg*9sRヒ1&~?)tY37lM,Bh*ōz{Nƽú$Bo\%H{oV J)ȥkn>릮o'~b[.Enz?`:({GT\w|!%bYlo]*}Ǽ q6`(bͨ\|*-54Fab]`g*!/7ړ[jgC Y,FZjgCVzfܵ'qjgέ[^^z^Fܭ ٨F5tQΧ aN=0p իky.v05;NNA})={ܼٓS w\J/y*>55%[9bbJW[t-ٳ'nLƱrl]OR9~I @dՈ¯>˗3fM?? '>{i?T c}}n/5\gR$v҈S2L[`qQ:C.&c%BuU/Q7*uw3kv}#n9@CTQ.ьH/Πte n_7ݡ0J饎N=Yҩ '8[F\'\{Yҹo]P4Sb٠;q$̆U֍֨,JQk~HXY6.ꗸ+GWtRQqhփs*e.{ݻwۆ=^ٗq; ?*(hm(aP_rPrF5Qw)|i8y݇7n;i;58qrGQPVT,vcGgPa~r(ʔnriذi!qϊ@) ΅N* kݣKػqrj9mmS^fRל'jSÆM[|!+t:w|ޠ6?sx;kp QǍ6'J@oyy;S|6lkhܴr1h}J>ӵ j`[y=&lzm@J2:eߏ{`x Iku=k3-}W/|}eԃ߷XSWek^btdr/Nc]vkfj"8bqW8ov7&Zn"DV&fD]u?fP_GOi<[7m M_DPmbҽ}bWtnZs.R"}>6AƛžN\~{Zoyݝ6k˔+[F [٩D /s59B:K|Kj 21nbՄ=04h)İ}?mڰn6}'~u*i#4|Z?`r/u6u=CDnƫˤtfAmx>sm[66nRAu#ávǾ?hnW[eP夈VtnZAko]{[YV 0z m1@TkfΊV.Ƙ̙ZWUU:V^&nnxݨSN:|H-!iW&O>>9?,5ZY G%mU [ ȻVJq ңn0vo~hG|y5=+06vIm{O̊NeRԚd9fi^B* )H8ɏsr1i}fAU9FL$ʱ!ErxNUKZ%eX- (Ov^]]2 ArxbעYtkE'h]”J_GN_m]lc hwZrm}P6:9Sg;͚eRB)3m.t;00kX bUnk.O }LTADƮŘѹsؖ1_t߽aViF;4pѹoz`/QkC Je6Nn;VW?,4xCM$ RM8P4ؿIOvd̹lx`Ֆ<-OѺNIe>l*ڵ/PybڲSAC[̧?-hjG^SAm8%ϾiQ+Y>/u}yr Sdzyfըs{yߘO,Q~CAsIѭKޓ?MĪv:Zp7ԗŽ4\յT!*D)frebgI6jע?T-::9L1GbGk (,M:5.ё&sm&P2d/4U$ EYfiҒ6xQ~֣ŇЌiv,w2{0 _j<nQd-@<5kcs8<߼k^RC:)*Cߙ0Z^:.rzF5QjGzh 얮֦R`ڬC%7ǃC-f}۽u+F(Vڤd ksribAԬ-l)?w=pg_h*Wq'6:q5#97~vf,{O50sPf//8IܹYDrFstu\h*:sPkzΧ梐"iѵ.]eu($7=PqvױuX^[vfEA/j(}o\Щ UU#{Ǔxgk.:F;"ȋk5sPRz֘w9*h L2h^BKyըF5QjT8TŻ2y["El :{®V#z a]Ȯ4%\ F&gx  ou'F`\裏kXVa(_M ,M*M;1w3ß8Rsψ MHO1i=yRNwcr,@Bd]#U"bOͧQIEh: QQ0fsVl9(/?H(F"p|7yHRU:Y(p0p4m ۧx#%@VuDCj4p."Lf?s2`j([3G !edV<-ըF5Qw: n58Tjqw3`dy65&`o2p-7oA=?ϱBڪZkDo6Jlp)A0&׍t ̒"O \.O:X+TZ.L109=#&Irye*@WC~ZZp̰4(Y+kR&P{JI#jR._sT1~˛7sw=.f #FL#jk0XF] >0l> ^<}Sl?)j744''''PLF)r ըƇC }$<ȆN-m}7K>@((A-}2N1H0ӺIY՝{ Meq}QxSBxa]0»L/i@YQkjE36gت?Q5.'(;^@H.y.?zNW19s ,L("4PzMgZ~bSDEEk *~*C}ԀZgRL|u/iJEՁj"@طU3fYY׎>ݚ8X(xܫɃ\i$/ ZI?㏚}<H@ym%xraP(QW@Yi,>*٧ġߚ2!4Áuu=vejxvXX+k  e?S:^P8!oszQMv@^:9]֐<J_jʊ;'C Í:|WԴ]SgY Ce,{ܧ-tvb_ٕP3mlTaU|2Vn<~V*~V]݈^~ϥwAEU~p'UY YtBq|ZFBFo3OW ,-^nj3#n޾]ax?/ٶxCQ*Za``Ȩl:=V2u{KBԲ2 =OyfY+`EߝpM`SB;Lͥ #xRf~IAuEװdZQgM" `fPGHIJ%\2F ~!u캘h~Escen͠~h :|*w-rnD-[zO,~k =:F.1F|hדQb%3%gh V3uA Ŭk=]E,vp`&:xQ$\=ȍl; TwFO@3vz9bM G.nj: ga(b4Uq EBգ256yIt8hd6e@ˇd?LiUfvĎP~! >Nl6vnze, iBFLPCce!`*@3">*BUUBq1y 1b{@ {('MZ۽~Jdjb"9:%-PTI{A0r6O%LLL ^l+R*t&V 0c!{N%}V}y6=^0ע7م,8t0zvdMO7 ɊZ1jv ,/Cv*`<~۝jT ->*r8###c_ȡeM$yq^@qH;^JOŭ=adrBg@me7&Y&r"qe?@Rkq{ƍZRPG֡sJ`mmmίG]yPCd*c^eϷ&br u(7kc>Zvn; 7-=S Je[N!*ihU0r8Ael_KUI.Ǵ!*IerNa[U:٥}-*&RK[Ym߮*xOL.a1WV@ x}P= N,?i.eD˙*_(v8QF{U90;Lԥ\_&i$׭esAlXS_U*Mt˂̻ Rqm4a9;,F}/ s!qhQiW%:0Q~k&ZW#͗E̜ov{x@5=]vA |>x_~Tҳnew6߹qpT8:ũQQUgƎFRRmcUT%v6~{SS::t Q0إ X;NOQ NlX0vvp6J8fJ3aaf*AY'(UP!D}rP7@eNsoupw|z7RzbdJ,r=x=Wݢ$,T?2)O-8尗WH~|Bm'JCN+)˵gAI9ũ!^^K]hf&ÝQcD}]~jBOX{UP#GAB3Ug$X&&')]?)4}\yc5 B[X #QJΝ ^xU&OUάR{qzK]j>/7OJu}+B33!!K d\ m&OܓH)=z Im]qXc|RWP(t'Ďͻ733!u A|Wײs8RF)nfPR٫kӼB33!qq%;IUyr?qxqO.jU|w ٪ꚍ*;af HiPh&$jEG]׍BH/L SW}xc_7" dS]=J?uj>?$[6a{2u,M[#^^!W &OOiϷTE),ԧZv __h| k R)t ~V_7hYӖ}}憕ltOS7Sj?(XoID}LRTJYQD*PR¢6A-<E  erTa=ϟp65$~|.N|aY69ZEA遅)#FM_6'Ij{?@姖|:vYs)|awyn٣NA?Ӛ_N)k1;kL:>oX/qXۚlaHl;#lb*TŭhӬo֮F(l3@\4sS~Y,-ʈ~`j۲>ڭM)-z~3%,]sKV]֪߷ɾ c~0,Y[r8D rы7{蕱LF+;4G42ɺ|g\ᰟE4ƶL8Rj,#JeG&|!/]"#V8U"kuď\LC6+ LNh0i[n[YãmYB]~%RQ~:gs/((6ߎG/^ڽ攂3Ԓ|4d{>[ѶϜeo[q׳_kaE Qp^_e]0M q`u7HLܾT"퍓Q-] ĔR}{|ڶ1u;ϝ|x4: *ȶnJ@F|9u?y%,s٨}JE+b[z$ad-l ov1>UJQ_ybֹfጿx51QtY?d/7CqGبIm;K(x)H%͸VdHX{3\]!@!(;qbTu IRTҸ]dY7qTynj+gj@)S>45ըx=XoY2ߝߠi? C #?2L @!7x,@s1%|F()a!F4u%|UH ;d>!Wqm{KQVqu%#v&Ms}dc<;3D!k{k5h!S^\sx{o7o(XJa8%s]TwMe.|ۅG~jN) 8ub"gHn^YܪwotkԨu71lkKd,eeEy}1 T6n`sӈo6p/~G*PC@M؂*$Rڧp|'P:xL/`)b$5Q%E X!*vL )e1e6,K([/,KY B.9,U㱔*%(, @RskS,+oJ%KY<}'cLYt@T* DP=+'VrR{#ZB& TinSBxLF7}SJ .S—{WPE# \\k* "" K) &V.hb/hieha*zpup?f~/}/n8s<<,Ǣț ՞ZBgU1>ZM%/OF03hXGD? W@%}vlAЄo?/>+ZYr>T@BZl/j \,?`I˿%bX gQ1nukjNP鎪j xWlb&`OZ#KB$@KpԹ;;5QFNJO1Bb_|v^0mɴ%v棖"wbB t޵Z]:t0Sg.L)%(.KRDi;`8[[3ՠ5T^l|:MZ6!%Hl/>]$8ULUO-ln^EH&u^eDg;WCMlxrj͕kj "11NuËӤ$5v`}>Ԧ q!Wm& K?MS<`I+V"?S(-TZi1wsZ8uoY7GVߍǤ*'& WH,@`Ī\NsJfh,k%%E* FmAZPoǚ+Y ::R9ڎMT55Ff96aQ::PԳ__W/c)jޯG?kA[cфX߿\{`p\[JPW R R &$u@xXAEE^ʲ%?}=;! ĺ]cِv4ߏ/}*`(!0-@vwj_X'mUQ&  TLwd& [όCB' IDAT%kU3:ؖ)@[ h%1" 3#b\l'@8&dc\Q !A7M\=8R KZR"7Sr UM+/7+H? =ur45UOxy"q96h _UAHџع!v~LIإ܀`g1_$|s_lR=` Y;7gUohnS2sΜkwo_8ΜۀiK{_9iK s $(~!taq킬rI5/JhfiIް]R|y'R1w'9ۏ_+.H+Η\00SYY5#$˗bZ[9q`,;#&}`ZB@Ee'Ņsv̩x^lfd7eb+&fSs|B nhנ եzx?*XzT?>ŒUWCj]p%; 0q! uj?K8ȏb+TD؂3ݹkx@ujE$(a!k;Z5kNY?<:h8 9 n-Ț`F?w1! ZXgfULp 2#Zqǿّs`~׉//ڽ_1[3f@C9/LB#j7w%w R(g~ËiRMI=(*i4BHbCh^ MM!ՄH"'w DԴp~?pb)2q{y|M;vlڴf:}DV!+9/(1,>dHe: ر~+Z0͕×ou;kȉ)lO> z?rXDz2@&GzXXr&Ѭ'qVCzn67Ѐ+{@Y`vy yܼԻXZU5UPxfIEpq`)(Me㽵0;iJhF$%Iړ<>߶¸C\myǜءqrOr찄I(pes_E(WXխ&~G/md_N{w`nh#ʅ7[f ;7jѽgqFI^О(jA nր{LڬAK) &,bwYKpv7u Gq9*Rt蚻GM@5:^[\h,'Y@/N6($ڃ՞"SQ_w\/WKl?ν⿣KU)csU5Ʉoop1#{m|RRG+_6,=o 䥢>Y n.M~/s!OZr<<-b݀NqCc=r^嘡}#;g_oCrpr9H"?MN.WhZ;T^~o y[+ J\I$b2X"EP*ZJT_Q6uZ׋OPB fܾDGJ(ٹgGg\_D4 %'+DbG}L]->_ vru: R)2y*evtc;aE>B. vhw,=S:Ӷ3x0r2L]- X.WPpJ5 ç2-'u}jZFzuցyQJ<] 'Nmflo4Iz˶uT;7@%yU%&-VE+k|}%b\b%W<1[$q+S8eLkqžɳQԒ)}i^/5gF}ю!?~|on/NN\MQѓuEf]4G6iPɪSZKR521~aWK=5y; y !NG3 A "F,VIQ5%Ы %{-qt^ ZDÍ.ϯG_3n@+ B]]&~T؃TC:yUM:7􍗀-ɪӜ?oM Tdo7g; TV(ܺ_wnLP'C$m/|E3~?s̩сn (wY#v̚2B iܸp'r?o]W}࿷2BrNUNKL掿z( 8rͱH.}T=g(6MZ̮*Oe_qxP{wxA6iQ1E%!}wm2%˾Z%O-n i#qCzkhط(?ݐ/"dۼhQ/~GwұW>ܿ ϴ OG)/1O4 C(A,]Q^ Zj=JQܪ &'un=|)'~c@w҃O:;uGHˀeۆ%F3L0=-E]M,EaHٰҹ| kPV)*k(17Q &N۶3 1ǫ_-B֮%_Z~^zSGig%xNJ L^b^a!BPY~׿a\bd7P5M @^]٩m3cN))fN]l@ЀC)Q(J%Sf0jI(J赎BD{ EBZ7ꨶ1&jp%8N+.ҀJjhU fɃ-*$y2^W9̍ qn|A3*ʬ@FIэҢ/H$^jX}+S)n@رo(aO{ SW~w(kx6*y۷/:z]x}D !o,IsC7ZrN64pш^,~w 䕍{+]O>tR33qU4muЀ4?곻M MrxPZ;OhCnxHAsPQSuѝ^@`z28A`0 d2Nɤ8=8,p}er^\^k&{{ `@ jC(QrcϜ)ɿ,Mk3g"wvaa7())ݴ+ٙ%9]-U Hއק o%j>Rr0_݄%?ݺݵ]-mgاPV*rO-(TػYfn`5_#{$..yؠ1b>Y~z+O ?s`{@MYoW˖o_y=w6Ȍ{P&)3[WK 8 W!v5P7~M i#[-nKNؿ#_dӾHYu?cx#BU^Vf{3/}ް-=]]tA3xu4C'>#2go;݅Qã<,[;ã=:EN\r|BCNab 0SqAsZE~&"దG<~?=Egػvi\#Рl4 h%5#" C-HD[4 }z5%uE1J>4@X2^xeA`9Mc!08tAs-lCgX4d_h2i84n\Tj v4-5=T&ghѥ[ZED-5dzԜM;8Nj旇z5i׽%rV'31pߵ4PW)G;ph0-lEF{>Bf^|–Ş"Hwl&P?7srr^,ß}WPj{1{߃k- b@D)n۬ɠ.YNm%Qkbչc[+Q  ?Rl<\:zuEZvpec;3azܞvڀ4 h@=){K):#^A}u9esMeLeбBK8Zo,8N.g!!|Bf g9cˁ@.jU:ޏkhT_ggu-˛\4,Ĥ89Ix^,G3 Nͫ˂aM)7_`& 6i2y#7ty?J5,ɞggM"xZI3nQ*Q !MoZ隴6zb?Qn0{5Wl__ X^8KQQoODթ,}{-+mZ? :†Ac8}آݮ Lu`År{j'dN۹3ዂC)۱h{ :{[ ҷ@6i.AA|[6-zݾklw1pi~*SpOiVR PV!3u;FMs_OܸRGBuJ7s—\5Z[pֽ2}֖@swރZ[UPT̪Lei@Ѐ4oҬl Uj GSXe`7MicN&qo^ -Qt\f8MNҀ I4}@@XAcYK$'B/B,ބA327C >/ǩ$SN5;Kn O%RJKd^"s2[ZYXtfo\y櫝{YQ2ùiy1`왜,G6Txę0x9ZJ{: y/ϽIcG S8Y;:n'+ 3 s&~%[0x |V XJ{P#ԄKzz֊WZw8q{:c*9wX2Lh8\)E4-~օ ;u8OlDYvd*0oSy3UC_"i.ܸ7ȮI^Ď޳fpr5UUPȴ4M{z}3_/>P.?7@5%ϋ}o^1^byd5odK5ll, ~0cL/bֆ/Zhh$NؒJVmTKOү?\%n%@eM:<,n`ˌ6$D3ۊ>A#֟r<ڎ(5)+n3W(Q߱%3lҲ)fAC.-7k*GQjC'~T\9E4{wm^\3=n񹗜˸Os*{{9ʊX+о|kn렍 pּ_fܕ(ҊvSVͯ0:#7viܥec령4>ws4viʥecKLx*7_2sd[{+:wAIZunc7bHm]yd]Z6o?6+X:k実aec'w}4Zڭut5ܱxk_*u}KͶo; &ʏYY6Z`gGܽ{7rLLqo^oRtmm /uԭ>q L T\'VmZpT<يJ@d$EaaؠNnwuZI!M<]*Bd`習_7fw1{4~ʊk5 yX$\jLU PR>p!}q{+OA,cb<1u_BI_v($ipҼK#O>3!!QԤ5[}*XTUUٴ|a`aI/%A]uN@#c\Z؂K>M$͂O IDAT9 wW3ϸi_eŭqɻg.hRUUIĝD  ؍i"=(L|Yd"۟ü[y2on6#fQ R^l! Nָ$^^Խ*IX`)'. ֡TqKR=Mz?tMޘ}WMpu6eo! DkN =׷,6-,npxۢ_DN1not::hmY5+Žvm\|XaZ*a?%#6Mz#/iѠ`4hF^QC2ίABӴ3/va G؞i!-6" 48FB(0CCHҎh#KKJFM7;G'%4x]_\#ZMP;p\;jcY_!~fit <2ݝWɪM'g| ,,@wv䝞X3CM[ ߚ yâˊ+=ʞ7o%Ei@(=yMPU]u& yO+1PSg9|+p^5CKKg d@u5q 5|wnۧnu,-1E2o~Ci7:0\gqћܼvVjb0'P-pC&"ouXwsDYkxc=2@jkM5@L=ƘI/?¥^邂4L\Ĩ+H>thbx6:N'q3 ΅D~} ]Ǿ?]ckϺRX(?=3dL) Ϭ_\׀?[#-N?뺑iqw|$ы &8鯇{:O4S%Mu(Jo9Yw%}=HF#fOIή#o>$:b?NAI򷻊@' eo*a#0zsV|saDBϺƮXوKwu}DgDkw$?JNjC4jl*ƈ(=BPU?0 "|tı,'uEZ%D"sZB?@KMCKDp-Wt 0i{ 5e_LC~w'R- |U*q;R&H#%jA@MWY|<9}clHB`אk"(,s%3(mV3wp#:8xo0ylȊ6"J0(/} XJW-?(N)1Q+R\8{ `fMʐ~cF~cGq9[0}E8 2GHQ^v_``09Cy"hNa-@V—KGY#w|әK(?|!`1(!*)=$, #֋^,£LĂzO kRTZ nnNFg2j^+ᰥK j9aS{vT_-N{ ~̂sGNyh=/-P077Df"3F]*^s~v9[r&>|._(&ȪjT{Db emknb]_0A ȅKKGXX;ғVWKl ښ~~ܙ|/>h;G c͹|Go(V{n>%o{crjks=0a4-c [RJ+k*AH<}1}`*'+KGVO c>i-ڹ \p Z9ƺ-󊜻5v\qS杰 C;)8{A#h)`,6XᲒFtAc۔gNY-u~gcmkc݌BaJH>pqŸ `P\:G࿟$ QUTm-O$~cV<>2~՘*KP*_VQ'wZb|B9-U_4rA3)P.>yW!D6*7M &L~4EoPy s鱫l6 jCd-.3-*U4)V ́ht)cĈCxøzZl7FK4mO v'r9' M A34mڭcCV7Aӂ}.wޮOF2 fn(*qgN|(N34C֫J"ALFWю]V~ T1 &L n`!}꼴Kr~%WO%g=`[JɭӐҧJ+!qi=`d@&/^r,l7Ԉ\F0>a,Z%[ Fa‡W'J..ؚmok3SL#.`ݷY,K3!$3.pW,䟝6G)V9kT䩝Jm=]M1qMӊw;$nMЂmڱhh{W9ىC?MI[^GLm>T1s %.?=_V QcO~AySm5MQFfF5_xfXΚ~6THfLw )6"{hk;{ٺCy+g01I#_VAq?\~by\@@󡭐T ^v%ah3l@[->4% c::F `X ` @0R04hf@ӌdr8\>\PNR`ros2 6Ђž]Fwb55䜜J1^e,T rrN^"s,G3 @+^;=Dr^3l !1@yҬժGlh *<;ƒO/zy44K|i@sWQqP+S S|{nn*(o: E31cgyAj.E,)n5Ap=7;M{R_%ھU^o-7o)QJ |5A%w;v`EOC޿(smSF{cۣesk̡[@-Je @)#6Pk5ܪiߪە5fZԭoRfތsmL˥Q)'ǸY#뾦-tD_2|gMdZ=}fB ΎKFN>埴mzń=~mڰ;E_1Yw.2,a{>" r(U,P΃cݬ+%/:|4Rph܅)TZ;jCެv)~\>c Wvjg ޹ջ Yϸ @[Iy=}-3Wϧw}NҭeKPs< gaq!&LKU2i]0OK&z{d*\ג&dlgoLXࣁG!OӖkgBb5uL4u3IbMXSUpz/?W",>zk_K(ά9ps s)N0@ϟY2rV<KT{f7\߼&G"{:lݕog"{է2 ܡqVp]8GF\ψKN$;uЭCZ6E@i|iC|&NJ͍RU9!1czgA#ҚEčι6F;$uƂakg7bIywUO&Z7V3n&nBz @4",=NϪh * )ƢBt-AQ1gyW$49qr>SM;;Ip4 >,8mFi>, 2YN.䟒 _>\mI0RqJ&i%%m9\kV"ahN:@ m 8N& ͱUZFӯ9~pL㉋]7~vbiLz5jyz%5=vl%ݡ+2Lq;F >;sDf0Yߕ Y}NdO" TD"JQR}:_rxL=5,Y80 r s?C B ii x(z9`X[956UywG,;;%SmLns᭳΀k' ܱdfP'FBթFRczڼ- p6cbUR]U*A}`@rS2w%-rϙ]HAD|澡HMzGvޓ8zϨ5ڏO{oTUM1&8XSAdp(4P6(@KQ-<0wHc3u<p1@@?( = 9Ғ$.fZ*}BHR vy+vv34} QHrGY_7PVô[Zxu L lqD[۵*T_=ݷr~Ik ~1T6I}.‰082hΆI'Gx%8j@jaD-<C w{Miwbό` -~\a|g3Gwq.>;} G> mtm)!+dž$\D!.vrZiB[z%@QM춋'Bۊ e|qq)SFZj E !յ8QTh6㪇' *7P|`mOhN"dNƧZ[e]WWJyɰӵ>߬]aI MѴ= kjB$s,`F ~>}p fq2Nf/qUqz .) h?J~ٝLӱ+GQ5oPT#џ-;у2eyԪY=ԇl U ss!jEdc͌C4z7{g?3=aAx=-7(f|ݥtq/\a3v\:xK&"sA%/^7ҨLDbwDr&fP7ve{K-iSڌV>dygz'{>-6M\,N9zK;HJZI~f\IpbdkB)qØKzcͅ*(BJ PDe ĈiBj)C8m6mHh Pp2AW?(z%`ڞ *@Ƞ B5%ǀ 7i}Y^]Ds]v7G34A&/9}Tn"/t⫯Рo4lx;ּ\qёIvoCF6'ϗ=KxK` hݹ`;:Z86呟s*IGأ{')':+  wzr*ĭ7:Kw ^ -HzkFGpLAqLaRw|RA]ZA{{<8Pa'FOLL|N0#L/w&P>,.7g#DiaȪ~]AQI'Z,SL``ꗰo{uR}^P:Z>m_7lO8@  4YݦP1?y R1_|PSW15s-<2>fALԲsPj&,m$ecL <}5~UJwǘ?W݈d[i- &ܔ 'mO_F Y@y;sUj|W Dm%>@-C+TaTxVtW؃JE3%E%E]_g*HNbTڕaΗ÷OIQDZ4L6%Aʾե'ݸT7%{7X8zJRр'dMl$IFҾVJW EO|xkq~~3lʠ;w<Ճ~56OΊ5liJ5:(mµ{7X685^I.ţѷ#} ~/"۵)ԏs7^$gAV'>\Tq/~HGQgZ5:-L g?9<{G7 ;]q#o:A'N|^r6&4m'Wx0@{a~n-/At'sbwԱy LDƘw4.ji,&!91lN|Ҷkk=.fxZ{ŒXwT)Q\8KD.-J+~w}A۶"$u3;8M5Nf˧~ob6m')^&)(`ͻg,utJ@1_QwIo,zpdߑ. X KJqa#$'|K&2 IDATd(gcDs˞ ?ѭT"'NʶⷫrO}$fKVws݂6e~|+k ypOi%OQޑwdy\!ZF7-L>%PY_J `yk /<<ri?S*y-+"c}=_}A*_U>nǏ{<<<< L"I*Hv}ڛ'ۦ)Bo:_VSNkKtޖo1@f^d >h"wr/!unQb9" +><<Yb8xxxxx[мXNT?(:xxKТp~ȡV/YG<<<<<( ==1 . E؊y>#F 7xx>z_:xxxxx_ݚPߞ|1)ΑS+)ݻu-g.冕gY@"o mH$Rc Ew׷?6e3mբJ1Ƕ]kWe^<>g3nmX|=~V).|ϰ7S9m-0 \ɲfJEB2RP(ׄU*!)K$ȐgVv)D T+*rȴqe7X!<>]K':xx=aRE`ggYFܶ L +Sʴ[{{{{{dXm@Ʈ=-mlmml$̈Fsc aCvLN]$E|`rSWXt!䉿VXpN(M@Sf7i6_d_; `w13 "vޓNnB.`ʲ+/lv؏BP4ΌPun)/Ɩ^~}ժ69wyuE?y;36[E(~b!+7h'Yhrslrn޾Miƞ6!f-3 ҕNh҆yLԳ`¸Ѳt,GI<<ڐr!$)SH 2F&̾CH5[}ro:|ܹC-.|H'Ԡ:>p>WMomG&z~ `}pH;~i}N1R !wȣfV5ˢM3 Ģ)5ʫ{7h:};2$f%ϘYDq0seEo;gE=B\2o}I>F ]X{Hl]Q%eZ*yʼZjyl $?&zI> (p;uEKGF^~fugv]קwԟЫÍ̑N*OqKM ɭinS&0ёsQYrXP7C}ָн6jj ;HrкzNk`__kU3k[_ͤ毷͸p,ϻRxĉ 7nܘ>Sɳ'2LdW6ぁ'\\\:'NdTڬ+T(0@ۚD!TL h;x{ͩ(ꏤ޵$`X,,Fi(=/Oo#N:G-eBW'ufxHHyS~! Bzn۷FԱ<",yXV Bs@%$䃒Л,ƺJ#R&ZEX69y/h*}:WyK\b[Z4Xߐ_H}0vMi `v6xpaJn%,0enn1͸1,p6F+ƽZ>W 7[D@WF\-{_I*@㏱ Ŵqe7Xw7U`{o01 lSR9C&BFolDؼOSWyW0❝"QT9oj9(mFͫ@vo^9tL`׶vT#2o}hS7g:^~u#-X9t4 ˠG꿆 ˪պjz]N~th% Ф}D,;p&b 7~x6kwufSgo4^܍'jvh8^\ZUR|TRB>u7@M[=Va2ֵLhy7 c-M3ǶPG.jQ5܍<8OӶCkL[dՃ0:?#c3ޫ+**պļCldߟj_jq oGGԣe׀{ Hf3&]f\t|[Mй?3 V=Mq$ǜ A-,?׎vux M䒯fe}zPfNdPJ&D]<./Z$:߱CǦwS3-=|ƞD"8 $݊ *ΪI?sefY u#G mG^snpCK G!$Ԫn߶eKw HI/et28v^JcFrw1N:g׸6^nﮉܽ˨߸*ZJgj%c62[JqBL,v}plA! v 7F:J9F;>kwd{y9 {vw{_~ayi=K7>vY2+2.~W_>ilТr(3APfvv2}g=KUغz%4"M;?{cť];_aJWI?`zkBu 1k~Qt&AK"Ttk:d_-nX99OL[H4i1By±?-3>]YS{6ť1&Eyf-H/0.({qW龣WZn3M[(7[AMC13^9]RK;ґh+ \&㲖iP{ d%Etq!p Z3 Hzz bfO"Xd՗ZJ՘?ͥŐ ~ ުVUx!;E?R =}H}*3QaRaԥ^-v̙6V;yoƧlp|R!GK!@ ِ=?]?Hw2xM8|;*E6MT@=gQ"kΜ7+h$ZC+ު*RU `j?ydg.p&3>jl8"+;2HĈ ~T ɋF5gSJiG{P$4? ȳJ [ZmBH)_F%vWL~5Zhimb^ ) Yd,p?o?/\XQWܚh+ ]Rvj:[JH( ᑔTxѸ-ٔ z7RJuo yw_h1m|cV RzI1xG:3l>sD? ti[W{#`lh[Km]۪@m#ؗ<>v߳yk <q/(s^$գfq>)†|ޅU|f)K-o/ϐfKOM=lc 4h=Upf4EHUDkǖ}buK#625G>V=]L=&tv- .J#nj_7lM:dzǰy{m]'ocj* 6_΋3;Z•L k+]5(⧷=- /(.߂!3g}O- T~..'ny{Q40ͪ'_jǖ1w9Ԩ G|s>[ge$x`U7i,#h@FuX|B]j $D$B.5,84Ɂ-mn^,4}]Um2HdlZO5U9yfPW cc$GVrt6u6Q%U} <3OH-c'VhXY0D޼>QNVQSuВC}-aEm dVD9ZuM(nꊅ Ӿ]s 7's#;UkV`*'$6^2Va Zvh V FɏL:~)@RuJ ϝ_T9tyr|y L|udkU'Gpe:yfwA+M`rGU#8 Oi>O=f}~Oi5"al.*UvInMFn36 Fn9Qf&/Px-+xW_Y2,xT m3F ܹCw@.{vl4`YZЩvmZ:qOr+M<{CeqtdY^p@@d;thYD }Y2S^yڰmHOAbDM9Xe>#4\ .^~4a_ҡq"ۡwwVMAHG%}.&qoq.I-#k1&yS{ťCSuN IDATJ\kU` oGTJ ?)##5p5M>xoWR(n/ u_rZ;T;es~ub] %7R㳦pu` yF2`o2^3HDnSs!0۽D_MfQ$"a`QϠ) M֢ɉ'Ƹv,ϻPT...li {}LV&"zx(" SA+Pz8|:auBHjCV JRAgUׯzZ5Q40UԐaHXi qrQ PZL`gɏofg/bTofKܖYw JON~|!l B  ?IԜo{[Wvl(spV۹Qv'ϣ09#O+@S:[ 8Niک}!c}F7</XIbr. ΃v0a:4{򺃷SR6syeRF9ҥ5}\ILIy$,|RRS[Yw8)%%1|XX׺3qL8vҶE^Yh)7{KKn\sL8(5VcHJgknM{<@שg& Tޏv+1lV_iM ;u'5]nLU־6bs{VJj8G=YS:佚uא }8 >_t҄)ٓI ְ{c,P}CkGjǏ2("L EWBj#z׻ ~ԝ& [ LC7v9|;`M8,`rA|O1Ucc¥'n 5B܋S%fi C.DWI]Ck._m'vZjUJ/&ܚi'<:_t+O|ڙ 8z˗.]NJ+IM4-.? q>qh׏6}i+\clZ>y|61ehM8])oF9b ]_|r ,K*#,M(gVsmLu!Εt\M9WpZ F9YζΝsE4Mo4~e|"J4A9}Z[& ^?%7(.6ڄ!lOV/ڠAS,kb@SkČ9TH`+boa^6RMQ*<|%tlzyªXfFʧb3 _n8ue'}{Ncv?7B~30#m-_Fzzd^qŝ ḵ$[o"W\;:sY OjU|Li7u\mw6]7kaX3g CTF|nU;)inؽL FNb?o8K9d$Vj&ĆW4([(=+E /nЈ{$ѻFf^0Hq~,߮dݥƞ#96`8Ͽ{/>}zO1xFrrMs(\ ˀ sXSYrQV&BAlnQ*8^vX5w<9+7l{$\a;Y6AB@*dzYn`An H feü ^ Rh5R62ѯ#<jΞb^I:V/)'5/\c)NoBxErm\(sQ0d-Ur&'LRJ e1dɆY"Ɩ}?A{"/,C }%P aڪ];giZ^e|ԋ7< $RMѾ<~g@5A+XV̳R:?8;شF9,tdwFy XFiͷWqC 6l&6*O, ͯZ6U0;}m:p8cy9ҡU<<<<<<Go9% ϧǣO;uz\ 0P)rS'lwiӦMP`C`7ʽ;c@>e|ꏭ4L'[ϧEo?GcbMݺ27#H3, )Nd}IUۻcT$zp>Y>eڙX~%dcW QQ_ټ#kLOER.WDfWr94"C`]i T(ҢJ\ {KYEFV%O +YP DjbEZ egұ<<<:<ѩF.UvpYTΙ=-mlmml$̈x%(;Klma!KPa6WDײq)gcT$X7~+f7?-f%^;D!ff>zB-zϔiMUZl9]t;GP(XVp|gF(txTGOyvhR;?HE,mV cʫ.ۙ٢YJQlicoooooo)tZ.Řc^bUm:9xA,2+'/+'/KPʳs_&r*%#uiްo=Bn7Y~ƃn7"_dVohZЉ$LrӮi];Vd` ؊8`$@OtA bAW4_{WZwC՛o]188t5qKhhhp~P|DOC/PğGOrj%{ɾ{OwnsZgv]33^9PW17#٫QR[ vI~t@>=ޠ#87e:0lPnxbd>88l܃gk>Oт}6 EjO?~vir$Е=k&z7*.ϔcy>cB)E9ƮR#ڡTM B@5(@s 6sEQ{׮a"!NWA:]Y@vr9l/:3#tA=AG idۅU*!)Y)7r,B(`]IXV H2~1wV<`X#gR,HȸwŨ%ɮ?U yҘS`݌1n]ctȘ(U#'}6lQ;x.,˼}/?vϘf=S @:2CE&nEbJC -|U3d~_g .v]IrfTNxUNν⍡* v?XNYGhD:)X91Z]˂njy+IِslZ?׵玃;2!qړyri!{}wO$)<P閧|džR P"fPFB@Pk"?@(2w(u!F$bo|jJ((ܴEbKK R8I("X8XPԱCN3<>4vo9}~FU>Ggzl|塙={-}0P(v\~e)xo[81nс^.b]})kԖv@~k=vq_ _*ض;Ciz@QX *R@gw-O@g lu,^|S"-)]X:xx[p=ɔPJR h+ TJV]ldU|fmj oTqlGZT#5)=سtKc&iRIO?Mk:ߋܱk#;zRV uwwթOnٹ]g{M۾]cX ^'_Zsp3~My8'%&\Dz(PD8!xQ,{c:C /vLqM7}W 1M]`V7eYejS5CaD|)ERP) l\ 0tTqqn51Wt/?W3xJ~wz2z.)rGG/nn5 =Sټ<&fΘSuMjkҲӣcy>&Oݲ[V !_"!ק~){[eYJBϟ?}4%%ŋZV|mL)U%wRsd f'OғRJ׬]oZ@??v|lW!O}/_fee)7oT*nixFy J,) elxxKS rߠj&3(T_N#{רsjT*ZX<@09pn>1/cGչd{Ց<:CEjEDAcM,̮ HQPJi'{h!Yy;*ZD[DI=9IV$ d_j#pukUӜJy .9@/*Žs|x]r?2 <5Y\Gtk,5p*/mj56w%p*2ݽn,1&Py#Xœ-{oq5?zNG-Z \Rɧg<t/y"`QOk$gH,MA||!U*B@ʼỷWG(R ;!-罠A-BѰųxa!ly}۝r+@5jYB-]9dxSeCw=ӷ DwgmqAYtՠj!?,Jiuˮc{ vo$,JV q2*5$'qK,a,7;WЯksϬ.Ԧɕ!O9V)0U}[S Y03e40/HP41 Tm#=Gbq.In6]cK_Y=nή=ptoҮ#WY6iU*Ó\} ]0B cW^ewq) Ԡ@rzzIDڶll7m;uK 1.^eJ0mYM*WS:61p %~ias/5 +:~]2߂W7f݇_u_LE-9:> x@ BrZnAo01z`.)"%3#=܈u;|6 9z,xpx]Š=fN}M=i]W]6+Y: ô[~7mz|[OҴq(bǸ O`~l׿[\@KԢWMfU,ajHM_Vu3whWS·xC Zgǁ^[Z1.|6w23⬎5}p#W7+n0yŚCJ=m?͙Лn1ȹ!aH۩W8kA"$\.RX3Ԁ4\ yBmJu?wMK2̓1=szO3k z]Vo2qwX_o 6E-DA{L@Ȣ6 +}XLYY]אڛD%Dgc*lGa sH;"Yf/"jX1,r&-{1%^j&!ڟ&`uڊ ې 7? -Mv vqb֞bk5ҡ}Fk-& i4 Xluo&ĩ+:RbEP0l.SY7w  \]۷Tz8פֿ_Wmr]w@S$@"AQPD"֍ZZw8օn\uW+ZjquTh[A5@0xyK~7_w﹟{#9s5 ^bcJ˝~n:fƒ oFL=]5~Z% {"ze㮟U?hUO@YD!n]aȴQ tl@^T 6/n+)_@#p]%s`߰蜤;? (eSJ^Ou:^:a cݣG7.nmx˜Z#DNhnG͘>ɢpP+''_T3 \)^JTi@Vx}yJbN zQ) $Ri4 \.f((RfiFߜs}Q ܟ #ѹ2J%Baak5 \ F@"WTJDH|CHR0R%MFe2I;n˛=bרM%Ѩ8u3*J@%FbF"W|7Fd$Ri!4J4o8FT1ҏ05VDPfeIP5*eJBV􎌔w7bhJ -o1Q>zCKDfz6V?{J%D"HDbH,D쿄ÇL_3 p\xF: T3#rͫC"4e5eII*??[?wnT~۾'dZqXs~rFUOmV]N>H+;դ\6wiKCzg0oʝƍ^{Cl>wWnr~# Νxݽ:^Y*iɻ 7dqA)Z-[ࠣzXj>( ݁\B)8:9:PJ̎XNpvXh0BUB2f#4"䀾QS(|9\pJl8nqY!0BBa)$ "Fb@Ց7sodͮ5.sIr-5a%HR?CR$3 .y RE4X+U0-lBSI@y61< c(5.߀Vk)rR %{p6֫3l~>KWy+rAV;*2}7#9>?pxEwz iՑw?/6sȾS)( ]Q۵%"m:}鮡ҙYjHD{f`)<=nuLy |Joʂ;iZ-?c7 2-fx4kOVLLm~юLLQ~9&/3M.e,q\q2Zu9%Ѕ^eVjYȔ;]tԨ Bll_ |qFV2 Ab b [0BZ J}}~cܒWrWv6'atΛMҥ!aĿʣ@F-sm #mv/HZ#+ĩrVX߽f9@lL~>ǿgoRhhB uga"9jz?tg!$ly:2;#$քd2׉+믜rO ZC[F &7i'Q#* ?+|J\LEZܰ [yHK5*sXݒ9h؅V6:4(Ci$9K!a]>c kL#rSwXtBh1@1gh=A-I!V(LYU{8s P)yܢV ^)R=Жm[ak3g:'|1`}Mie,+Jsl.sj<^ԮĻf9j/A*1P1|0%C6䐰JpDbĚ iOy)d V0p973٭wYJN-vS[ 6` 72ґp[񢄢sW P[I=\]Ca =ٯ@w$^2c/ǿX>̧ߺlS(ID^e ?WyL}A' ⓮Af{6K;-x؊< ;ވ.4S{ݟi оm*\-=1rDWlhS}6ֽ ԼJ |g([sgTYv >|QNފݢf!n [ίEP&tU᢮Er&3U"7v|<'leeg;F Q/7!pQ^_xd~PP29tIe$Y.DU*LgK׬-}ъ>=ʏh`]4}pzqablqzﺥO\ }"PĔI\nyJ;Xl>/OouBtF'kxZDЉ3_4\Y_VX߇Y+->~a}'2]Ұ-7 ,kt{^ ._%PΒJ(`8olΩ F80L fcZR$d}S 2`.ż!Lܚ/nC Om|3Eȑݩ̷OuVO]ڴM^a weɛ=ݍ-g'>0z -ڨiB\uBأޖu,UV/_6M!,0HjZij" ֦ >Ⱓϡ$Y;G.g칤twϽ5sA?}^IxtuƦRQUK/<jsvHu6t!ɯe/(S}a cb.lfǗ1kk}⶿:Ϯ9ta\9T@MCoJ׉evl)FQۃ.}2t ks@co4smdNފ5k 星QMSIm\>QF?}ճR[b(2Ж#a̫TZK3Tq֭m7 k&Wd, l$|I/%p j$ q{xx9z;;qM گy/rx53dQz BLVƈ#"cݘn)N,Lr|16ÁeRi3& Ao0o i(ºX$jhf [@)OS_K S o1TGE!_`rorz8OBB?74x%l$á|;V VGv 5ԴZNWHȮ|eL|+~ ްzhX d9{?n`6kDŰ(IŒGe5 mn7DR r//4$ן6j*v f$)i40/W[ {ʺH%~"}U`gХ~KghO@ӝ I"- N@aӇAVJ쵐 Zsٲ]Ěj̭`W5W8pyB>l 唯OP+CnQ~~Q,j1s{*ü;3;/;R&bs ?Y6)ªȯ*9nאo_zD x2sE]]#c 6`ٴs@^P o/ZWY+\j䨏|dYA} !?=9VKFv\vt'@I$շZbO_ȦZu q9Hm^sDl 3 ~_Y Yët[")WH2tꅕo1w^~΅ON5; $8?ʨa>&LVim0剿}iuc[S' 6UOu92jllwlf7WaU+[>= 80OuVkc'N(޸$'ULwbt6룆nC0qd3{3S7`|6Sed`*l6z֍X/nXIsYQ-{Zp6` 6_ǫ>tex~\D}*gr=׫2)lD$+$sNZe5ۮZ J7Nu\{u]gaSqRk^㏌GD\K($/2^e2ߪTEdGW$@ݦק.'J4"9lUs5~zP$%wrm CU{U,c/C1?]M=d~_9 7[Wd~N!k~{5U+贀Ci$M=.cT\\Ȭ} *S Ux[1ԼAt9S7WeiLyA ^4E5\Y/L{ AZ3DrI""~JR\5 ͎OIGݙ"Щ|]J,c`e#f1ykIJ 13ޅq a99sP ڌ!F i",~崹5p`f2 X,kfa:hqK9J).fa)( b&lPj"x=^  ~'I=@W+MV/~o)Yo IDATV pe_?Eg{A~׼ZeՒE0u\#"QcB |$] ḪuҰsOč "Tvln AS̘TCOk^g9Ê{Glע`3>j]jA5x3 ,ބ@EO.4qމaRŖ濼?QKudav~-Ij7Ui!o͸ ) +UyͰ&orm"QyYx?k)*kaŸv,5V3>e 0|qy@vIy8~ЖuD/CCRJtLN:{Yq~o^_2@\#}58"p76>~iQ{k4ڛ8 \ 9"W)UjF"r1ۆQf)@*W8$AL(%e7,NX%<z~&V7EYt7u2"dȭPJ-/|2gRc6 ˂y?:F*|?q8Tq]Fh:h2EoK:ߪ"FT@pPRܭ$[ T*N)FTi܊=RA"w+dkL&IHO|3X/Cے .+ o2E-1;rw)c˰5Hٝ5geN*#"7(ک 5iI҃9/k%e]u\F⮒@k/WοۍRM 2\bl_T.2k[R tp94Rq-R25j̤'Ti)]>TKk~cXKcr+p74c4D'RFR&)RȾJ^9'.|Tؼ_zᘞVkO7m>Eذq}dnbvC_H7hrsڰ~QoBZSc:ސdteFl٨6?HYyn/sðsŶ@nhlLm5YY|Ɣbݻ.kO670ͽڪLÜ/~WײagPjΕ6S^n[XwdqP :k@arnt7\ٷ^q~osn8{HmOL-u>_mVRv~&~V}Ϸmr{~cWgw-(Sb~Yq?>ʸԭ-_$āoC* JP>OA@10 )Lx 9R6:jlFhRU3Ϥ-=2pD!Y]V $J 8_%pKN#ΎԙJ'?uI,KԲ4* tfiRlI %OTH {Cgl)S$ =,+M Q$N*%Z>a5",3)|BZ A {L#)f?;ZQ$yZ>BI+[9*݋=f8W@Thws6UӚ(?[艻P!ɁRjH&6Rӹ(Th< ӊ]NZ~`89=/*ms{>?g[px_ uVM{fS*?B( rALY>Fi ?'d>"~bXiR;+ڙ[炋YĪ_y&UYR??7+g~+7oO|C-I '=HHhiNZh@9Pb O^qd/(w[Vԑ!}AVަkR{;WƆ P_Pu0W2vVUKOLrkJ9>+OjJoNL҆n~߷ӫ넍.*/e\yaj[Ŗ>c,w@?5mt(^¿ky_+z=Gv5SMJOAr{ )abCj}BZJ@lq H{cy-gc_y=cu7W?wko{+J6 ,l7Y"*>9Y$ֿ yM~ Cp$G^LW *T:dcV+^?$yA2w~Գny/,i65 ψ(C&B^d@"lȀRگG˙UâNifBzϚ[!L8܇GOҺW8Q Wa}]t R3 {"G`g@?ٵL>X›0&$n-X>ֳ׾fw*/D0:sڙi= EӞipޚV!BZd4HP g>>k5lX#~zȐMH @90ܖ7ժ_3J$vbJ_9`^pf!7Fj?8vWew*<ۿl(l^R bM l;zT[z^fv¾s:g]׃CUo >3f|7)M;4P҆V%$僷?tZZRpo*ᗨ!ۏF"k? ocl`a6kG%mu:V;|<({NF<d`^ D+a}Ozclzʾ5AAY}ׅe"A3tz" @OK$Qp#tc̢R;G*>`ޭ/$~c95uhp8yXj 2l{巹4_^AJοyG̟^"$&e{/h 1s*/TT *}Oz*ֆ `_"^`#D1?p}lAt:_Npx葱*G}5:jZF8;XףpK@ux64 )߭F1gFueպvEx+nǬv܋0`H{_F)ewͫUx]yɋ딭[j lQ _};O=r$q![Lwӛb#deM^mRTK:Jr,p7 >x[smd7|@o2y;Q^S ~S5QZȗ$ tפ/+-Oq>JU|:ܻRRR# ߾K^ yR6(U~W{:ex'➼q7 Y֏c]x]7Q{;I|Vʠ2|q?f ~߬ f>9gq hÀڰ~WkU-mKgD˨ԫ@|爅f6f#mU% bㆈ3l-^]OCG ) .ٓN"EG͊=͕ ޼l0Grv<ݠ ]I͸w+&nUʛ\ǦݜneQ];UYWɿnƵr r'I Z`2S..PB(GM&t!1!Ɓu:QHNOO_RJbX,D"y_Bڛ?6 |qz^O:N1:F1g/F™0Fy&]t{r2:12)V~vS XWMdJoIӺ6|uYOu6 ]۪,B(SQ| "28z7!C7pvg͇n\m"6HG.ZNE3ҷUOK(2 .tGn(=z7!ї|(3=xc~ӷ ӠU7DM^{ _P&_R*OpFt5xn:7v>z0KCU#ru)cx߻ؘ<7g,ϥ T;e]gx;IռHNr0v!$x祘 mlfIm wz K9x4>>l9-3UH-QvSEÞ8 ZF? 94'?e%ؗr$VEH(=Q[q&]9Ͼ Z,j*@aXf+tO_GA,lonؿY02dF-*_^=DC[{c7 OSY:~aQ+kR8_B xv짱 4"\.`/pwM3R60[ r/!X"bX,"Eb/_l}{P6` b܂b78RvB}RpT~$N$b `GeJ" F6m8.U1-)ٶFش|YKںo*5X\`>ym?{)lbK{{+)k'Օ84<(Tsh^}N,nyt"Ts8K+! i٨SVlf T*SݚN]tf偠b` J>/k㻥v04j;H.V$ ԫaD3 ǗѤ'nzZ T\(2Xd'&n\ASٲrZsR~36=V`BFMģfg))|.]%`y@@ )g $>ŢPl:oIO{77tȃy‘4\“)ljcָ?k~v.bbxQ@BlU%PQq O!\jcG}i-+4) t X.q(Ek%sOۅ$ohNka.SDDyjQ)aܰ3N?">xRXɧGy| ƻI%.v]rW;TV9$rr*mF@·i] zE_sgLUunFXQKmm>ӭl. 6PW z=Hn8f'r__{,g@ ~sE T@snnR/7t៯ݥG0[rkG'=VSӏ W¼~[l vRڨMk>(Ff&% 3nqh,N;$ Q_t Tk ٽ¼2#{Jꁟ昑DfXRe].ŕr0դMd[ _b7v_ u=»MF.f\tI; ~pi :.AEG3.S D>ʲ7oC>سƞ-nEfPUX]lx/ ~<ճ*qoGl @yE)(oTЉ-8!.&%ZZLWpk4<ݷU-)$9xՃKG4Y L:{voٺu\0x1/,eU:jGHk]>s_]e~׍7SOPհvlW=:,Ԥ[f I(Gg}R:ۿ޺~6>+R6Ӱk _ߨob~ժ3׬obZE@';H7#_S Kl9x(@(1]m 1l/ ~|NG_{o+P{@W@[m`@ZRJʷcy~t~uFԍgD94iN;_kr񺔖*SHlTf o#䴯Cl|#ξwIbg l%Mڻvq eK"('ߨ @mSpdjS=DR~=U_@UA;{ f]Uzi]Dmc*/77A!7^gN2 r˾w%Rj\JP-G֩̍ITK^lGajLo 7/6eFS"kBq2NvQ([y ~K,wR!;

m^) FRʾ\ SO%p9{vcۤ!B+G5]{vxfފ&".[+2#PͯI㯄/)4z@/9U×̕Fx#tIk~X]{^W {uPB0g3<׭**>U_/?o(H`X`C#Ra ^zpi䤉ݲY9kXΛ5mismG0 E,JR3*+U*Uj-صOL !DTꈝL.qSQHr /W02779!TCg^]o<eu{0JH RDK8ߎRJ7* 8sZQe+uv24yRN*wJLR)4ѰջQOߌ&_"dg9˥ʝ%mSޕeY2\52aTJL¿5Pr ?=ϡ%j2cd6 f^mR+w-=`>=nZߐk{~]nv"6dWopk]J_}oQSOA٠=4/.޸qY<|۴4sj ~1|;ԃ7\\WC˼5uq@뼷͜~ʣYm+4t=N ߒ 40nɐOYpJы9w>seF8~t{]8ū{7/ 8>h]+GDZ @ cn)j5 Eꅜszҭ-Ф"`pqV֚|ykκikh=||+K!H>p|_ܤ7.f8atcB%冔꣋7]kVz@{cn)j50w-%1A=;]Vo_ЩNڵl~  s6~Y^9nt6ebrqpMhiEKYy19>ļ91Kq y&Fsֿj7q &p,&3P)# ۖb1 I͞"k9pOZ h4S/ĸ4+ƽSdYu j|# &R$3ֹ$chw| x.j\l+p->Ʊi]#J9¤MdlL2"^53c(ʄLPB?|, M]3]^|hz@ӫcGG;֮eKJ=Vz=K16ZwA=B1${d `W;0YkWvo{8eYE!jXұ1ƒnmծxģikSƧ<񚌻c/RϤM;o&U^ݼ/<&̖Ńf1?aH|"s\d2Ic&p3)<ƞ~$TDgMZ9{|+q orЦ7U2V]APB [@)'չu :#Z;a]EL/[|z9gaԾĘ=8鷒RYksλ~-{^̄rkwqDw-K쎞\K{; Ǘ=!J:F8(O5m^;kyNMRPuƁ{5qČ\3smM{û6aĢ&nr޿ @ȕ?+;8׸[}U_nlxl5cnM{Ev]^Pu5ˋ/o?شzg}K1Y|@s3]`-zl^yܡ}4Wyޮڶ^u1>5E<-,P˫zi@k?d3#9x5^8;hIӺ;,)S(yn2c_% gڏ+^Eẘ?k a`dҖ_%3=h) ߃BƎ w+.,LnlIkSv*SHf9. ;4ѽzAOJW ؒ8;hIuzy=4DO_}< ?*+߶\®3X[ٕf&xY&d^mRÚ^U*FsYg=)1{YOu(|3S~sF#EI53ĭJQ5˜yu–y@PS6` 65jlWuxT7/'^?.g{̵yfPC3Uϯ]}(дS0/1@>˺k7*m'MP30WD5bϧ{7Aֲj8[^Փ{|C}ˋT_8dT)e,b"`5ФS ՛9Urno_̔o@Nͼ^؜脦_6т* @IH>-֭\5[3<yn,?/GU? bq+mY 9"XLj@?7]jmC[?y ,rFpg 0.}]й AAA4Ϥ (J LM^DD 04s~LaZlPxlnPfRԸb5זpy#&ID(ֈ]1ӬY>P *rbV#s!B--<Ւ +ڕxVRyΤ =/^Ͻ%B'>tiHH2#w 35 9 j$c;8d,)4*dM":lb y9lmu-t K(O9bϬYhZg0yTfY7=S8 RQ2Q~pv2+&ك[zVw2^O=SYlKaq+Y換AEa̭b9E).e! A"h s%Vhx Y2 1[81mP̈́.QEؓe21&9P3+ Y-<b~Z 6,%3+_0^ l+VM>~{g}1<8/fsa\5UwM%!]v-v^]&,P̎oJ: =n]TA- 3c^/޵smTIbg c.\uܡ#-VŻ PqH];c\36@>B H9zK"0O-A Eؾ6 ܚRR)*Kr)[Fuc7_SX_1c5[*bؿgw4OW\Ȧco}3X_gD*rEsWDpoMW@2xO s0M׬QEFsJ6jgnf!W_g:[fkc<lj,Lld»8d5blj1d` a{`YX{Ow꿎Y9)6Ev>v~g9vv]ġ3 E5c#v|,ZTj䏑MRcև80f>տl}}CFoOwpqFpH7 m/OČI6p˜*z",>r$GΙۣjj̖kkz F ߛ<,uFOZϜ޸lW+<=}U >QAy1Ɔ"%~sM W!T*-9 QRpӔVL5sęā%bAFlfmL>]w\.r)(`%QT@`(Ăb {E%XDŎR$6rǾ약wp3<3*P%R˖M <ㅿ\ty¤4[xkW 㞳41&k[| \t?t@Q5ob~:вX&K_VJ]YکW-Kgf)F4TЅZU, ,Y {(&O,"ZRl1*{ʯ@@CjlވuESՎK'JnYí)!5H054MC5) FcXAWedOۊTAg`>  j+:% ^R+-)͕J,E\ =5|J h0xKH@3/7J )Z8eɿħöwٳ>^{zw0R(/xU@IʊrsK - íɔFޙUz~}EB)%4 )|3 MT"7p94U_6eUWZXʯ)zPJLF05 U̙J$}WQ@Ѣj}tHz:CGː  RsU_".,R!koAdW4i *9a*/@(౦AJ Zf$@62mUѴTTUojܘdCwnj-ueR;hnVU٩aō#1UJ޹/{Ќ '_PnPqXSD*B˪,-?ϧ@ RX=`!&D_/¿Q%8Bj) k ʍ ^ĢM}zuWSJM쮷1dT%}ƭt@_0K~ ɏZ!OƔRa7xjWa} Gc^O19Eu~zy^t^})9̫$cLj/C&w%/OLqe)c#ksw~̑=^īQ˪UZoTOcdbQƪSԕ dzkӠT6 AUB #;XRI_ǫY~5L6$@peG*"=QJK2 tpXwVI^5cCUkw\>koTA՝Un p8x-|ˎ0܂nƍafCN*;xE'g+Zws[w)HI~631Ŵ{A)á \ SyECiavՍLWcʾWzws]vܾ`dF(9 ܞ0oROLrSh:Svߡ?<+y6隑[Э*'0 hcEw*nF]unR'¥ Xyz{UtcNtpV~]҇Qq!}>/MeVOnWZN_oPmG[J^~#ֈ'+jzuTS\TQJ 1d_GNWrQ5!-(Q! 0Bt)|Es`+ZoUej oW *eXƌ6z;E;XV_V>}A$]2Jz圜/^~۷EEEbRT&Q%+**lh>dgyiZJ4F+ U$E*] +}_&+//QrD.1->tmW]ؤw̙ugIIu?4EW;^fLH kր G/L4[-<38;oo9օ43gxvPJ/pFTQsg7`W]Vvëc/R37^:Ԯh×8#+Ӻs57\,Wь0ӌ] QiAOun @6aǃ@8s26Ę˒yoG7K bJo %\n(\2,X[>(J&Ú}G9W]sxq;,H >;1qubSIe@+z}ڹֻ۷M7>Xr.ԴAAIac7|׸9 3. ]2޺!eKӇݑu_G:UK/^îo«S/w|s%ܬS 7WZ 2oyкS`.6}hORSW n͎M4fu11[x{/7)XANJj*U#}V.>\qфl05y!&(d: t{hˬCCoui2e#5Bi]rT"T.f}@Ǝپ7}k ^st|Юs#Zt{U5Yo'EpZ>Eߖi͇*ƥnAaΎ&U'vTFu'wkCZrkl{z Td?}! r8}8kA|%1SܮMn,(0+&bSQcVCH_:Xp8F$p8######a3ϢqcoUX[߮W>a/FO EuHRDdftfh Rr% WbIhybxwL"qaǶ2i͇lºiowǥikS&ĸ;w # G#~ѦSS?-tig1M]1eg=;ֲv/e u6sSϦf4s u$Yc<4d1?nLm-1v\m'y5G+! ofSc;tww[OfIaQf_̚4)803MxbݹG\2+2Ѯ_7$!nlؖ]1`ffş u!9m;p3pң]+zϮvKoۥ`F 9eē@c0M@PO_.߲bq驇6l As?DNWwgW1}|?/w9st bY Og#ƅ'^epvP-^*޽"X\L6. JYIРZBi2zNGPcE~AršbUS1W'z6kQ(ytWJ(4(nU}020LV2X~(Өø/* ygHǥqc$Ȉ7*)-%&&H]יCi5$sNkf 2ӧ  4!\vm` 5N]z1XM{Ƅh;KF}[x,1NXٛO?VeOӉ%*J>>fD>Q/A jY:34@ȵIMVhAӄRh֍S{[F ?bR 񩵫 =3) #-]4h @M K i Z9bPn]%>ebN\N4D2"*4tr^Vas6UTs%c=.BKr2%'jN ݫy~)I zo9) [p#P\NIҎK4Wݐ@RB[ n#?ќnI|4U̜]*yt6S-8MrŐd_ձjBT׀}Z(iՔ\ftj腇r^_{0MÉ3:ip3G&@Z&3T շYiʡcL{aݢ{VZtc$r-1 ..-zYs88Wlj &{L SxpۜNj ;yɸmՓ ?JcCvS_RYSɽ ْ1gQ)e D. N's.~ȸaUPߞ=[bRvT'tGĺ=Ĝcc"`07 `قsonϣ6$cEܶv_R2鳀:mm9lԨ'ήS p^4҄,%C9:Θ}3~ߟNJd:ǷenXs̬kXgժeXG+J3'&?)49u?Ye 2}  =1RE@vl[Ŏϫs+V)V'tP4/jl,jQ";}ȷyˑJ,ۏrgy&>j*tE<=ɹH>,i| BΘAӴm\#1+׎äu?Y}ۙ\7Vl5W-M4R5\-'h@:\l)l>駳;`G JMSCzB'?4q֕CpIne-hc߭s;Msljْ-oZ}11_bs?zT߫[7 :>AMd7 f̏bZxÜ&T2iE *yjr8ʱdQ$U[W7>ZF) TEP_֛V1(&l?=݈t0r?0OkbcWQd^wIn>{+a)B^/[1-!On.l22R4ngZVɳblǾhpAa B:?hyDŽ?dPC/(A6kW68^#-Y?ˣO7yu|)Yضe y`+oU{ͭ<(w+b*ʺWIf&̸u`\.Oj-D_lhNE̗Ҷ`%wRn>e?ݶ* w\zYc(h^^D9G6THec> aU1GeTnufoHrުyu[~?yay=*EGwyMV-zpb-Ei+5&2AAr¥+=j.|'"a= qY$ ĥa3;WmT JV&Bn+}Hyw6 铫{{ɩչᇏ!(](~brG;/{Xݿ0;E %e yI.1ND%G^YJW(79ۘMՊ3\ݍKp|LI73Z$mh/cX&K@wsBĬ Y&iQD=7rTT/2vF1G Ŕ4д{pON {*ANsqA΃:A$$z 4lOX.6Q&zoJeAQ[lqv0:9[64Bwiϋ +x2Vp,*L tV M.S|lLy4qmo҂'wrAxtk\OӄzJM\}Nguh >"$A.z6ѥwFnkFZ.},;E_x1ř́1 A?x$lo\EO_FH$$WkBY]l"m[/h@]{7RX3og__ы:[u q7T"SϞfc2ȿG,aѣ2Uth,;c46Ƚ=@ڡz?52@LpzT~?c.vS/<4lUCwW*ut m\aKX@]}R!ۻ*7fz6gP%?F\`~1mG80` (/Y3 z$jHbgߞoo+hI~NoMZO+? |"8e`}dMHu]~nnD`B㘰{op/iTQqz'<{լ}ǹ!D/Q{m>S ^O}QԅDoM-(}u(SdҶWfH>'d_C?| aQ-u=ZuќL*,*xtVllZv)rJy/E%yΞM{,%jJ^ Կ5 ,C-sѳRg޺UȲR^KJ/T R _vf*ztUQ *(qKI2tm9vsg((e=UPrtJo.ѣM5r^<:(l}{K|;е)[9Cy> P<%ݻ:iws *.3`C5NN@( Р ЀowMn]1Zԟ:ߗ#.=iZæyJ26u_} &W Dq%e挘݇/.I:5Mh:{\7)lѕe,H¸;[qh nMo~;w7e:Y1ޜ 4hwu|2tb$e}>y^k2]'m囗O4ÔF+G(Yw.%$;pܹ3n)m5ʓ9@:lmBƷ h[ihѼ6vuՇnp;MSZǧq.=U/s|讻״"cG\}0@zҺ=cgn$5%xzv.3x>IXC%MSYen9VjҌe)#Mk^8W2Y8m;LS'-OYt3f$$ .>s3#''= '|+k ^q537IFި2M,m[8qWOrﳿ5+s>Inƙ)3z)ɫ[ǺՙEAw$]=`pcJ"f\rqiRVU~~nފ=i-룶ױd|m$^~]Jy¼:>R\L?=N@(`[DUC{3799xz w[W-7U^϶9u#73 SU4;;meO_,0`6` yt@C?V$ʡ1^?ϩ ٫w_y hy݊6-{;I'@ؓ3{>`pOJ3@! qHYֵ KÊwFCUOs:n١N)td,Y?B>me?LqIIKi]=z'eLF inmY^6EYEV":%3.A{LpѭT0V-ḁ_$n f@71}??D| P{ IDAT͐|q+f= U\Fh;>I)pBdN ~[wrY93 Ah`&50`GmlH>{3χث=țchдɨN +P`5` 1d[ڌ}2%}rC ȤР ?k2l*%|0& ta}w!˻ˀ#6hՙ{@,IFV󊹳m61@SȎi64c @ގYwxqͅ\@H0{C(+ෞg_@Pyq:6xD%]ArO` oZSNߗiXt N"\A1WsྍtjМ]QWG~ٲs=8\+՞>&0׫9x:f})Joeڞ]KZګM:nٵq; ر`S:f> Z8a׊nwpoĀbC6<໠3=g- {'* "Vˁ urArMX.hoF(\\3TPhX3&Y"Q >ܷ18t0o,f }%3@c5 oz{b}:)@ГK &݉؏WM%4Adf$kjr8d7E5awln;(jLN9~K~.q-w\x4S?tSƪ11/?_s& {H%+~Rxw;l\$o+JGt^ư\4rCLem 6Gҗk=#l?\Q˜n Eڬtҿl{t6c]b2Gm+)Z{މwX'8fAv!;ƚ,z?MF2fjKGq2ގsw] YZ/s2δ =rA~. TRQb)A.eyb'c]&- `Tֵ}.H0|6Tv\? RCⶌoJkϺ366QY[6iۭ6EЍ GSɁQSC:;en{=/;kϋ5/; sq1&NG3;;@iv@@Hܖ JNg/l_z4!' :#J }[FmH6Ͱ[+,9{gD6zN|:1 x0=v,hkZWޱ?1_p1Qg Mcm N3_^QQAg:< "> \ަl@Mc1YQ:n 6Sj+V49$*)-,Lc#{m{ FFzy}E)=4M+)('lₗeyGWܑcǤ飝htVl9g?i8* S%ޕQ4ͱVGy3@R4!rY mt*huxAM$2I(WMSw&eLRB0%HLp9ff.5~ՂD,@+|&JR($XBqM<(q|@1BTbPB%T#N.b;sAwL\(_ :kNaR XLQ)j晒%g %Ҳ ';J\X(=A:߾~vBu %BF$C\XHjz$b E|R{}:$/_P&u,-<OkOF%$BrYu,?IB U pwQ|zaXԃ] 7x7ro>ZF͛y Y z'! "Ź]my΍[^_rwi_ yc:[p9WQfn̔Lj[ 73KtU@)s_aֱNx0lj("0_ؠ 7LHu!*>J^ =S^``s k?*}Sc(25]h"A%IfhFf6Էa-!ۙ٤l;ٚ j1Y ]<~ltmVk=qt_ *knVp~Sy6Ҿb'iI/^|{Rd>:1^o& #`9[]?;QU!k;Bwӹ]*O8"u@&޳ ~@PKR-h1߲ .zi|!TGJK3a33"~GnzobEJV8?W٭c56ŎMa>^@/lP޸P)NZ>6ݸ6$To'yB!O}. 'zU$E'R(x|PG"$IZ͈R,cɔ:EE:M_P L3R7ek] *FܧA(TC\C4%ZwG |eMg2a0e|q%5yAMyLyh=fneEYQQ@j](r=I._@zǼn]>Ob<+&aլ7DZig[ Wl(a|q;zG }~mk)trvR|ƚ :DL¼8#4.}W*S(k2&RV^o2,[{Z۽ 7%^K7^TD2PDS;qw y4]:mmT߰{_ 6kwf7nJEJTx ,WUj[*nP8~VXELnX&Xi߅'-:!L*\uK*#3Fp^|Ȼ|wfT#eV+e)/,үl|MPm 0b <;A88` |[lœr)t2Rr+UMy^֩ґ+gN^)/ΊpY~Stn˕J]}$>[8LX@v.W N4;ArBv@QѸ7VUG%NK~zpnD- ).P.f)W&pJ(ʚ?=KZ) e $_A$~_?{j4|-vZVdz~ A8Kĩ4?>CIcTmE( v͸뷪*lbnO+XOۥO@OZQپ_$2U/Pz~[QҊsJEQp!ۀd͐?vˡiFi\ׁ߷7յ1f22A=,EU`SI[BoiZzJTnO3! "hiP-G-\%)ܨRHy߅AVJPqAiN+9b SBJ aŦ{Ii}58ji|),zd}]0 j+Ze4 \1-[>"y#/\@$20k*r<%Eq#ev=:x`kaw.vOȏ ِ4}ݣ$sSYiȨdjj9Lu„on;41U1w熪¢(7H'$\trH+uKQ&7"|| bn=r޷`LZ^:K/vZc$}|3+OF\ `)Ne+7m x]+KTX-yq&ڳCo3 p#·GLmܹG~SV8=C+fH=tۧ9?S ! eiDVBeM7JSR{d!(o!R`TU;PgU[~Mľ*#&rM1Y> XZ}( UZe{se RZQaSTPa?5 0/\6}!bϸͺ^,>1< }}^+=NsE8MPs\ߴppxq9Op#]{7>'? 8W=ֳS)2iW:\D/)>;;Y^x|E6?(*!A-LHrЮvVA@`|unF _y.NTD. 436vq2 LPKLP SC׋C&Q].9$yu#wgX㌅z&U/乗ͬW87M q7'DZzy~˄1G3<85㥌!bC;V% c郘䋷G0{],k됓S%KpaԈ;rVjJgH1i1w޸u<F\JQ\.ӺBY}gFJNhZQ= &VQTD 4F꺊VjiW'LWM2Kkɻ0&-7Iإ2 t[a8AToi0Bt̕&zxCeGk`**s wPI~*-+| su0|++3զԴ2e|9itlk.{4Db -L?CP)dyϋ)pVBӿ4CiAaDNV7 KTآWǫ9FT.05C^TP"sӘ03RMf]R?4M寞Wi<ϟxx,f*}GjWRiD& T:5:;UҬAA:`}cEieZҢPtȖVVZbEʲ,l,,[H&fn)DU hao`_( QTijZ_G kX*45RMy(}xSf׹P")Kp-)ZpBJpx<s*wth MW{Lj[hOB4nry\^AQ*MJdmiФu0+^ N/Zɱ ;H>3>ʝz_Vf–40g ,&5zVnQy>4Sm ѵi&h%#uk*&h-|a(  0 ljU5|[ Q/sl 1%cw{?mb~9,sXOUكluQ]tOL7E5a ѿ̀՛~3|u) (e^={Tq~ `ӯxͱSk sXwCׯŷӋd:U>pIo_vj.%Ϻt)W^RJ\7{>M\u7Fsc%Y9`֏l7]>id!6_<ʹݔˆƆ1իن+~}Tn@ڗ5׸pzX N 6K}TRlwkpSS71pUv1Ѣ_ނ(ρUhEEe`>굄?PAۿzaXqM4ACrmiU{1_QWT~6Anv:͂idW&`J(6T4c3|RW$Q*'@Ĭ}c>W [xD"4З@85d-nMswq;99DM:c>P|jל;ֲZh]Nu&$%i]=K2$9?pQĐ;-{ xK` 4rd2/нhii9ȡKtEŧBQjH|c6ľ(h܍ ZMlSLZзgHW%;#4 P +r)T*J"ΠiS٨(姏#)Ji(ҮI}<4AujPE$SVѪeb\(VRu質Tkl8w#VciݫE0i*"  |I'.TN pU ML[*# Ew8}¸q|p9}NkRTGA} ~҈'@: F5|Oo; \AQ {(*۷x/^ '/C( 1&9\҈$9$cĜgtEeueړr}Wj6&V.˹4'ٲyqv>JR)2_v=fu4'.wu5ufu;JjSKZr\y.UT$n{AUFNro'.IrIni2=|1$M=XϏ8|3^9Ȃ~StsD nJ?L3HYLOJ|cqgxxXvOs in|os4s_E=We3y$$i4͗ e{{ѵٽ .FŊأbC%`ǂ{EkFbĆAwn}ɗ}~W;̙33sg;X(tE<:uM)=[Mz#%8غ27/1"֛rBqoae!.^fD6u/!O'YªHX&̓DBH( "4rWhpځ4dzJb)M\'[Yy.HUj87#[䙼MX3(EiR*h׎h!Zu"^jRQ EIIU,RO~}"1EŃ, Ң95lbל0^یH&gˎ5s.?i7tyv|W(K~;XErDerZbIRխDYL V}LBUvؖ`;ռrBiAGˎثS Fb,!!ԹQmFxed`he]3qJ&qu㞋?]c\[*oMBcЖ#ojF02?|+S6O6`=:c%ۛM wrWkkC\̿_najtk vܜشMjl2Eqsۨv|R{ž#:yw3{R[)!'$&tqYEyɢM +˴ٷNgM $lN(}t|:UR&[[ūcЍ%ױr}@χH_]9vujeۼ\b̰ U1eBM EAr@$ys{TY P̈=}?=ߡ#'pvyW@&ח#~m {HdjXRrXbjlJ8##{nfs&U "GA}\MMsvCB~2B@ pŚWwb/A{%@ $&a#6mΖ~6;٤Oy9q3=;N\+z񼲄X۩W E {psWvUJ/\1+"֫M=Gǝ΀#m XYu}ӻ6ʽx91tppFm#ѧ>:4`S;~?uiP;`;"fjw[q 84\El~mA!=e1bڱE%! QxeƉLv - !{~~+'QC]ڄv ޡͮ98]sRwWN%gX*AA~ݦgl Ս&k/FDG39},fW gGAʙVCXbeَ.i@˂2BF(2BP  ")fq>L)*A #`Yս~V)d2oR!í*%Ew6dm yW|: !;SΌi.\4j9 Dd7"z~x!csjgs%Dr&YE|029>cw?N?n@G q7fxj51s|4jy (6bW]J N\,h.rP)/{dj !!~wM$ûyzȳEQW ?=uurBN-mW/j{.JJ]?9>w-(ҕW[g }J>_)}sh{[S`Xߩ h9֕R 碌,S#c~ uڹO}߻| beON#xv;3k|tդK\[zW[vZ`3*|oB'xxvQB/Av7{&߉2%0/UhF6&ȊX3rdNwOHR> ǮntDc;Qa}ڢǧ#2h,^5Qe~ޮY،s+ z`+ Wy0˷)yU55PuL"62o6ipiC1@֒iWܙA&5-=$ph;hϘ1y1 !w/.Rk\(\vsnM}mUf Np_B@rd=?/ gU2B!Sk%_e'Gn}:׶IɼlFM7Ui.?b7K^LqĢN;B(P2ƹ@u* @APh/Fc=br+9sO'ѡWG @vP :}|7!SyxN*RFEֵ}m HZy @@Z"UЉJ|RY)G,qe"X5$۟@ j߁q҄elz\0`@wxZ. x=V+|IJ ,?ǔwr#2nVΪϩM _)uZH5[t7HJy#;'eVhOD͡4u CTZ E4KUg)=ȫ)wWz-7v]W &{d6l\wmׯsM[X;1$;# 㜥`e[@A~;p>3pCاs HT*9ˌ_qQJ cZP()o `Y\*nvs &l!1䧸H`xE>xx/RT1&´a hR=C,VxsXxV⋪ @c;lX8a-~ÒJ@ΏBAPښ{͋[Ę[m;N8`ifQa\< ɣқ( =1F e8L?Bo D1n V=*KФ_ 8wٗᲽO3jFj }މ K UiDyH҉W%Fn~$|r8jS[4Vs&C:7xU_]gup騇\BBVv" 0"׵+$CRm;LM?īiw?wӲ$>Nzq;nwk ~JjKz+L Z" ĺ6n )S ah( t*zu1R>Xjrc/y!&XmKY'zG[ς!*V ъ,'"Y k.~v 7CJ Osvn WkJXҫFYKSpDe kp.(e9q>R@VFbj`lӏ)(iy Lg,Q%3-"I 72 b^;,w9l-@ 6KFa&1-e[/n\_[g2ŰZpVNPd])U{|Xq<{%B<\{QIpIY]Jۈx.`:"/mZץjէشz8hfTߌ\.c[Q( $$JC]Vb(1IԨ!m@vt{&Vcz2zZ) MXsv'@<Y2š {6wP~~LB>_B*7~5zZx')I~h|NRs\9V.wGO/mk(x#$4i VZA剄1UP $K9{#ia5+رk9jItǔ .Aγovɘf@k>E^>6䍵;Xys}nhɲ`I삶cep]/7A\s&|dO'jf<jAU'Vl٭ngR𔘕 ^ Rg 8ׯbN%`a|ЄLT&P! cJύa~버Z;2Nɛ()To<#dJ;YYY]ㅗu5鋬G)\8xBi@*'Op3ЛT@ N< <^@j6oE-2-mԚrPy!iֵ4qi/^Ӿ47Jl KT(vj^!K(]SwYO2|Uwh:ߎ"c2da0{ iZ0 /^ zgW#^<ʺv݊bѸVWrj¨mr^?7 Y;mmLY mmI"iխ4A5e JKLN0>}8kL2etȑ̬?^tZ\w2ߡޔm@BZV֓qN0FʂV˞""L1 <3zΊoS2ڊQO|8M Ps; {)E^Cl@4 H9Wo1p-Tg^35jѴy>Al}<39q+ҢK[߫7~W^\ ׯ}u}6NƯ:"7;JנKsWB29oU#^0S= Vk_zQ֋gJzQTW#֟NH<^\W4Ƥ]f\zŃs:LuoaG85DmԖJmlllRi~㽁HIΥd>JIܲjr>Qoop!!Ứ}]s2L,slrWS6=LNmgT26iG1@!X:s ٝ#FS^Pw`xŭ2zEX*zƯܯg'5L0a_kl29q`,hO? uV(>Ps%coPÎ}:9Ll͹-%,l{y4o 4sv<?qs`ߨl%*j7b MO6?oM}K*m/v!3|\N~@uXB`=gtWKZz'z%vnX^^bҏȽ)`M)w!FT cš,Ԯ^Ǿc'Z1+h,ҰjE0(Q Cz-%.>YQhszoJhU¶]iЋP}/jV$RkR3 ǴNUk41!=h}iemVOw#KuXw"p[թ6?5?J5hszoJuoU]gY޺M{v,u͜׬n;}~W X94ikhwzuuUhgPbڍ\BSxwl5ޡH @ɆEZ7kGJ lm#p ` 7 @iM}&ܞoG{M}|5v 6Q}RF6bˊܽJ)`c}w4ۼMWϸjMt-Ӯf/-?`l ͍=O/XϦJ!]1>\~Φߜ(=^jnψ]M9.6jTjhUmCnIanEh=<={gƉ[OOqcJXgm7boúvZ{_~i>!+C; q욵w9W67m(4̣#NUk4wV<?:8v<R]Z:`|?pĄ ytD󛷝hAD^?c&ip];jUl%['pGbUR6-P1e,8_˦. qt?ƌJ*JRrYFuǖаAs>Ju8{! _.,Hz>>,WƂK$Ɯbu Q'tV.X"freK%\:677T*)ߋF2bq=BU_YkT̪9+ gA HV }M O4ae[Iϊ<%#K !PԶVև#7RӣZo}6BU\9$6RHf|ָ_^o|"\90b"gT :O Rcѥg4wGXtnAlAca վNߖ[m=>u+b@J.lQi?->u?\C>&sa5@"YޝWmZ6O49 |umU?maƛSGݹ3>~ bIeo #LC%Z8?ԽEIkdE'6/]haOqsyU;p/QTjNZ wмBsjR\fyi5(@@~aM{RL[e{FE&^^~y %qN^Jm=:LY_4%0cJLn{b@4(t&J!mĦ4 JƉ$50zJ WJ3O7rrr)J5Z+T0/R BXR hI }F,~Teb`$R= Gov bY8oʅAU[nRU!- BȽ}xrB鬘)VTQ60$2g ?1HcrhL'sR&#!|CĊIh8"lq{ ,޷ͻE_OSܐU{VB[ۥeiP[%q2mart1^as,& o1@?;|˃YCȐ 3V\1 Gg.]XrSΛv'as.ORx5fm[Y:wk뎿?cgT r,j >q%~ٷlDvvT{C5G)b5sӜŞ IDAT榮 꼕)c`>uvCSEU<$[pD6Z%h_ ´2ofqi{Q>Eax~K?waC`g.a__2m]ط磂;/۾O{?ptKݏ)^Ce,$S(ca!c79c7%ukGo)p@ K(9IQ`5'a:#_zgٮgj?k~G?9ڷ+DqmxG1ߙtæj_JUc}&nXkzO]=ssQ>(mkԚ Md-sDmVjcfSG_ l5L,˯M*Ԭpط|nK`Ȧ91svYd I*IJպ&|r JP̖)_S]?:*YA>oѥ'Ѫ}s44oפ/tøg]0qכ-l+.zW6KpS>6V+|%cyWkB=~n}^lnZ=t] }B!oEU2MZF>?rTvjNb Zba6E?B\2ň.5~TeYTn&WS&ۊu9Xs)  ʵmq5%Pɤ&|nZnj>5j|YT*PhzSe(n-jEU:q bbrU@^(J\_U:ʊd%0e9u ÆƚY1mѴWM1Hkh}GCIM;a',yJ Q.ٷN k!e߄=|YV <|8uL^u,DGM=%ȹ`hX N9*,u4lgaj26Z/X뢬zx@hK3Dԯ4–DN#%zv N~ ]X]Q^)kc;LYz]zTNT YЋXc?GVğ!1G,k85DL{(H>=~U=-`{J ;y窻0/ y~.cŸv]<06_ou7hФ_??0[IQ|#Z挵*2 ݝ>(~ue2:'aԻ,Rrz:PRm0G]CUv1ῄ}l{<iBT#^%IJ^.b\{#o!oG׍'F~)@y"RHw~-5ͥ52N8p:7 Uj ]Z4 !~nˉ@YKs7 2t$H'Z<σMjh6^!DPvR~gۚ)=uBM+necYˠ~KNϘ`_rEqBҺZS1ɖ[敬-X2, \> /l+}ʞy8{1X, _}ϔa>6nپf֨YJn!ryˉJZ%䢤"\Cߎ %t6*-z~m**[[ \kh];U`UFͪUl+ZdRW /H#d=D۳ggD_1诫Aٰ zo`*xsH ?zntO:H+TJBf07O#{k*}#N_wnY6`(+l>s7n.Miq9[12ޛ씓/>U8qb޽41lTrQ"q=M;Ou.J-&c[CԢ=h]JGWwq!g~qIb+(J5He14v6;7,ښP6Yӵ@5i I'׷ٜ!Uk3oADbUGWXHd_?vy` $n3|Azë~)0wz-B}o۰h˕bݞֹ d^AhH w{Dlh#6|hfWYY(g^A;qn$jaGn2dL0iLRF͚R%(ee5'D>o?S!uKg}Kn8HSWPdU:bO{+b5l|C9(X%0XdOtt 3{9[f7rkClXh;:ډyuKs.<;\LtcʋR*J_2oH- ju^qCސI~qwy=!9wA')`HV|خN}"׎$mlxO ֍4'd^.AČ Zg]sðeTqxZMۇg1?=+Cl(&,˂a+\ Gjq&jO_ 'i֢KױlV=uʮز`Z$YD@]N y7?Xj̹ԥ}=P>Zty'ʳH,{DLa A"ЌPk aoJ/ruc/p%E^ṧd^b?-8Ϯn$.ji#F :Q$} gis ?> 1ѹhQldw?`eT]"j>9̂^ DBH(4;Q@"̷\7# =grMtmn!gpd@$a+0ɯ FsN(4W07Y-p}Ѽ3<fh|VO{,@ѫ3-%ba=7^r?<ƽ #f"͆I<}JX݌ 6aq |Q] Yɉ4/`NZ-N 9f<EE V2l6EEv1 ۠ tG|P#j-wn=#6E&GD(ģci2͙XZGsuRCU'o[ψE:6rbk24"~*WV32?riΖ<9_0[=bq)2z'F$ZO^scEUޣ# = 1C@$_.>פJH)V,h?M?_+W1bUc|2et?,3gsR<'U3{/HyW$hW‹bC Xq$1jPtG3;=^Ƃ=sT̀A>(H(M~oPlH~oZm0o a ;x|c~*Jq{Cu!/;77'''''Gf0z;Qg}&x.\/Ȱ^PbƒIӮ̟EljEBRxlJmP,އ$OQb ؄oY-#-5ChϥpײТz.{xlGyvPس>AnX:|]@7א#)Ɉs@"$$Et̃ _8|qE.mfv (gv|.#W5/yzO6Ň !z_ifsԄWUz/MspC ݴثW C@_XVŹכ Aၭңw z4 ܾP D4X{ۏ^%xE L,xp*Z9J Xzy-`]  +o=\E91îwkW#b3K9Cܯp^ KXkg{!SvjPۃӋv3[,eL5ǥ;b'ؒ3Grxx|էt,X'l5;nz75&)ap:\Ggмᡗ. ődXHAG1b0rxͪ Ry*36}uoo'̚:?mG4)+~>)a{ǁ ɜ^El@\bGa~2{rvK׌hm xtk\C4j4Z-Rw\'48e`(K]yJ;⒒Nn1I\lABBXm4%˂U xszHf. lf$6$V~V1Y89Sn+|'-pX/m67`2ώ?frPU@G'87{IR*J(eAii67k#U$((-Z5!ӏ6d6*K,wCv*ݗ%@wvA R&aR+8=$e#G _6C3 zm; N5!8BLPs+Wj˹G FbU5\((#dz@[ ^ug}W UμS +enkv9V_ uZtlzb-G8aWQՅ$+Q{f6::6au~2@w ͚K0sg-&z5薣_Xnmw}n}ݎ*2L4xRa']jYck|G-qT)E6=:q4&r#Ztq'nJRˬّS޹kwx:eHfb7=yiY78,~/:1՗'j! Du 0l;.\;Ըg]~2iP=Ǐ|e }3:W1kVHrS' r-R*8Lyy {q@۳'@M'9`A$(D)(MBVZߊIVPDQ̀9>c仍m؏g6vxz1ZM繡сsɺ? jzNzPӤ*_>iWUlq,u3ǬIztD̃z)$r-wTݿCD&:L0A &|6?-_}J(i;/>)bDgo((nKR^z_Mb> UJ );PMU`D Uh(:(XS%2``nĊ)m @-@Wu2p7wE"uv :DNq@nn1ϮĖETû2#}UJҦ\/acDF%5+n fsSdk[K٫W9pD_{i w;(MWN3?M3—ѾyuRfZ+ +֐Q:^TeUY.+aH]pr^ A|έ7o%95[m"@Q w~ztCԣq7vпwj;Q^<'.a:cǴQW8-Wt?NGQ6F5R3b7g=7?h, Z,>Wz:ժJ!/)MTXXB `.0 `U~{'(pȓ+x @m(rt7!(ag|+?z+@?R L\U| )V'%IcH^o^Y|wEO۴<0|L3;I 3h$ IDAT^nWkS@u9]*+*֨{fHޣ餾1W3%ڥ6U-VD>յ~+ZL2gIi^q>.DX݌@Q( ~Ma]6RyMЮ)+2B|nYWki|wɑu:L|*jyO:y[;sm= b[:mژnˮ-hOR(}o~TȅhAg$t&ٶM+lq¾vb+tXg+HfuMת]Tb]L)b| ='8QUx)YʤJNa4W-4!L:7E o,۪tB\g_G%~97S"(eN !@]m OSyiލ޺f#gg[ ~B*Z2bݰ_7tpO-{\-,8hVPo= z[a|q&|j]P?SZ RR U]YY3=(--)h?KkIb&~0LHR TY&yB3N,%IvIMWr_b޿roT]"5{~؝PPha5lbRö@\l"3abmͦSS-7&%i$x##RQ*JRgM"(/ [>&dv.j Ս&*jwY[^w2԰ܾ%ݕ2OT66֭N L[ԁNA8J!@}3̺EZn!m:.8ի&;4.>sWDHjWYbբ뚤^j\FHu#&]n-΀҂Γ>/76"9KgYIXx>E6+ 1AඥRcUȒ}t}v54 ?T窑穇ZR@6 +0dIMZKyA g> ;Vj`˔mIxз~+NY13 SOUGSPͯ >SR E9/+&X*R >C.?; ۃ<4Ml4QԈ!`+bo(56 $j,Xb_;r 1?݁XR~>̛77f @xx` X,K$/],8昔ſ=ANq`Ѿ6՚@n1ʊᬐO[ߵ;2QDR<, j;0<m"T ^RP> ymPI@D7" Im{ :چ'{`RsAy%zwnLQ'Wh D"Wx_ q zf?__zqb*VbUJUbUJbUOr_|#mr%RفugZd9"(O d~HMk(*bk4 %)!v}U|:`qQKvkz\ {ִbB$Qwo})C38a~RWEl܁!ÈPH`0o/05|{سkffSJw~ftpvu{hdka1 f(bbBQ?(C׾q 2$i#qusP(ocvn6o #i-6=3}u>!iAxGK.|$9gQ3ihkB2hB{ذ{-8s7>}]PJDcU*B;za:Fnʮ(љem]%% KD•F,kpV @D2"au<{B*[F?]$^Y\52ӷAbvYwl-)̵)Y5=G<}G65zRQ  mȊ~{i_H `K[le H+0b=L M͜և&34Pu@::fӑNժ90+,?bo}Y% >40ϤU#_NHS?oثO7pv@庂_7R{a"7r .8>}]fx;=f܊1?$)/:b VhL-zc6kù{cӊnf/a=&eҶR bF6.kŸZx1x藫j4zgv9*iBhb`{85@9ͪk.VTV^ M@::vNժ9 &ܻt%bF@_~1]3a0T9%q }Y֕ة#G+zh/#uv4;P=GvrG)C&!Ӣq;T+ѻňRBHV't' &3ɔ-b*-TE(T7MGQB%Rgns/)`K@ A'Y6< lmb (%үݷHrrN] \~BЄk-讥fao)(@@yؘ$|ؤ@Kغwg!\"%97\ KAwn4\إBnޜЧ89YЄ묚&g~ ;{xP֬HrjE"M3Ϩe}BaFL3~y&/R.}&!UeO 9:dhD&/! R9Cᘳnۺu!iw׿'Gߺ}ԪmIePzyt~=;JAM6]gqt8Z67]ų{}8xl<29@,-W\ff68XU ܁Z$.;kofuT,Bf(xͪVlBjLKz? :3~evd[?b^kMnN4^Lu*ZqMNNFLpZ}JSC-S3~ѭeyG}7Q@Q!q;&uN@mMio|>29ҿ+^4;f{ɑa{ެ3! (O]u-M ncyM<:>)!NJqNS6u`/¤ۏ'tB ԽSbLGy9,IPف")P^x6.,+4]}[Zچ8b_|]Kl8ݻ3<S'E^}VTGE#=´J嵶֜ʮ&pӠR:"@ױPǔ.Ռ,q%7:ubH;%FN2y_򒘤g $R-O;U[Wީ{V㖍#>1#tN&+Hݖ=o6o`ZQ/Z yLiN|B܄څ3̙?uʏR`Iy,^RwXZ ЎUKH7, B2RTdc02w:VU,adJ}*ra+3*0 YHRavJ3Tv(Q/eA'.ūX}ɮduZl?t ZfaŶeeuV2#Je$r.XZa0V&*r%\ fV)W(H+rB.gHuՄy]T&+nBVPD!eUxr4֘F&i:@!ST*a$Jk, УC)v^Ti}Żo)(b,lw4) rRdo5/EEE7l,%߲?LL"dАvZcitcXk J(#f$eٌTf_F.G)Ue#T8Ա=U%K?3i$2{:RM-ea9J@f` 7c!WRL^e BB)c!Q:uDZq̞.tSCCJMRAc(`]@((LfN2֎խcѵ2 If<UiuErByhIPK!TOj# &(< x晌ztL0AR Ճ}ؚ7ZƢgshhQLf8}5.=1m+Ʌq*#_(|1zfI̻UԨ{/J} Cgn3X<ߒgyډPIJۦ=d1Hn8K[ϸeRc-Q}&G)ΨiqZEQR]h~Y%9vҙ>f¹}+LJl:dc|ȲCnU=ܡ}N?U}uKs 3u7hIzkY^|y(iЦ/?VKu쓒 'bߪcղMj=rN5JA%~kc찞,v90*Ǟy4lդcC$L۽xfl:*a Aϣ U 3VQW_]UJIJ e³Kpޢ)JyXS\VByı;  'Neb'k1o޷c& d FLo9$ٯ(g,^$?6ߺg|6?{cvo>8g-?D !(/|ө?qZV]Ob»Ao @5}Aʠa:Ը#E hwMԒQ`'ZepAXtDuvQc foZb28>%S1-7bon׺-̀KMǫIG={}JPk%bv}VEW˫R~ѶAEuc!N6+~l^_U9kUߪZCΝŧhf)X;~cu;5Eei=g^|5W K^:Pn枕kb,ˏ+zI!#m>V\o󛟽u9M}w=}?i ˹Kf+̾΁΄ BSZAph粍8q ρͫ_ ('{>~BSIxD2|ikv ~kՅ!XɈv~ՊC&'9PJ b[%ՃCj[}iNp[]lV[`Bn_ap錤4ᵩWοV589Uhծ㾨zMA M;yp+/Vyd6h?~-h O\%Aj[¼r}Ύ)֗ y7k&A bj|*{pΆSMN$Cv\4 BjFMI(%5%GoM(#~aXc+`$e`o9YTB&D{{?Zj噕S~Zqic٣m6Vb2"-<u+£k'Y'(RbPo:wNo"ajĒ5))o!Au!8xچ׷?~o\z_f\vp~&DbX?qWOH5P:JܿaQXЕ;y/l7*aj_prbżeMs@%TR?ziU2;Zvg2Qs0V >veAI[ʹZU6cjpTԄU~+ _懬4> }4 SGoEaFt^SCb zC~8xxꡓ7sر rdߘw9ToP[ 뙜*pl۞[,OSN EUCf nm{զ12\ܦM}{ƍ9rڴGBWCYc]!>M^큋/J l}>tmڃ.0mIw- cXз ֦@I=jh@& @PgQ2|Ue3ېAC:2UcbӤjk$Ǣ{mL N~EzBf>$8_Ա ;%|1gf? ̢|YKݩih}ZBZ qUoX1&uǔH@^Ve߇_!te[-k]+twWƩ#]+׭3_m]ߒvٓ ZVnaYxan|N&Ƌ*Pdpˎx({0aDbܧ9cn.67[Xt=F{,c N,r/?ݗJ}o,Mo/zz>fG~_qK2ѳO FVtơ5u)X[Ki(˭SYV\ζЫ2SHyJNɛk-,.!F~ԩzTgX4l>]ͺZ?z/Pf.J$Ͽ5P^ÐK 콝e; IDAT_qq~<By kLóƴfi-)UpNfS6~m&(Uݳw֥n^)(b,*圚z.,jB%PVմ81# ,`ȹ^(-Z1n̮s& ¤ADUl1\~,,+T,/Y0l-qT3iU+ܹ>9H[ۜLy>$6Vv27#Nۄ2jƵr9 LfkÞg26-ō'?#+X0$SQHld+;yoOfgCQ, )Uˤ%/P̽BHB/LHRVnwwnbxe9͹< :Ѭ˄՝΁a(p?cWF{30]sΆݫI- mx0;!p%!0#X<ܥ!&\ {&}V>07F^rw|g>s4Wçv$n)ܴ`EhPD;4Mjl?UMwO:]1KwѦo65ܯ@"i5גk[@,1R(̎eK i^sẅ_G?7b4kX2k!G #l.k+4W;r>nZ@5kL#mE+gmy'WJXh mElƔ ~W;^27)lv|E'E奓"֧gA&|Xiovn!2YeF*""9FZwi׿kRf.,"fDhqqIQ-.O]:=t~wvNCGТGc ׃X8Y7qav҈ -(P.s[O&ڼN{YBC˲ cXƬR [e0"1l%eIB 9T+|JH$X"ǹeEHl%&3>))b7_cJ"YJs6LK2_nްiɪM/gLYA]:Ńe!JP%qju[! u $cRPtwuPADu 5ZO8}kMh^"X39WNKMo3/7i{2Blc dd2O{v+{#_M(aF<;g^}F(k6 Jh7cl^\o9{Imso6`?3v &13 1c#qfPLK7k-Fb-Qu'qs Rp Xނ=QٺS,|P8Mi 8SysjwBoL \t0'_2 0d<;),a/bz}*UmnQ;[55SuÕ0fmnyj\Rq;rny^*uydž^v\GPIn9R:ǪapUWbyO. I$bFՆs;oF,̓.ʍ3qތX"1o4|wH8/ȬS(s RwJP.7A˳q57yZw/-7:>+O=4?eL1+u*} {7L(ڬK'r% rU bYUm7: qܖ@)ֲNY&]Za^N}:n(׸iG:qзrٽ8ؠ˸km1Ia.c{fm9tY䦦+˓b3tvrQ;n3r=8ӺI{i< u%=شMῠWuuԚ1} (RT;/eݍ_#.: Flf:]v| J ƑBG/yhf={o\^YlfzFFοy6S\˃L6ތ5]kI>)Hu٧pKߠ/zH` #2,Y9cLyritnKztyt /(P=HMګ_%#c\f?&%~jmX|YԶtTWݜe_e\˴yuۏrۜvdCyeHl/ĞW@@^=$-ce~np,\~yr_'<}O=ƂTtV!{`B}9qM{Hv^^djɜt^i?ǤF!DԌK/ =<޴q뎦^73n%7kي'w}rOA7f' X6]9rozj|o~5[ ׭5}M/D-ѳ-MKI1 -h{S/erT^ΫvkctB m7.ßzO  FmA%L0A v]LgA5HgtU LMlvUY-*Rbը/U 8O})r {ھS[w@oKH[q #uDRq)]vD @-k!i( x>u,GF_no3ggTBmӈ:MfJnٓw^:3oW͔KIMuLkÜEIn>S_@/{a25h :1x}nOl*^l쌦zss\5UiKol9%e &|p0JCeIM"JJ ~dϦa䬤}<<4 [__t_5hūؾ뾪JP_H3x$[9pǩ}칧Z5t̔8v.V3Ϻ.v!*~\8ueYe]}&^}I7d.(}!ã0O8:G[']~C&>AhSwe}zi0ѷ_}%u%ۀgEz͎V􋝷g/cϋ[٣}Om^?퀆Zg/Avrpo}׍lF:H=x$.6ܳ4e%>Mecmf4Lߴa &R>Y(μG_-l@](.O4<5|oԀ\ cjQ:9fLna'] W{cuhƌc7X[EgmOGamA-:%}}&}(fp8 \{7۴w_[}8.Ty#u կSft6!} (=sT E6@d 7=<8bSmz@a@'ޔ\ #=Ė\g,_O 5V[ǔR c9.?T44L|a  Z psŠŠ@1 <7cB,@~T,vGAClw@U%vf}%A,*ݡԊ !6'7AbYt1Bа(t7_[)z^\['Kii:λlԜMeA Fx{&+ MHX"..gY QI y9$Q;7/*@ PY"*jvz\< Rϯ-[6b1ޘpF)(ϗ-rШ& j.hjHJɻf f fڗik}LC`o.L[Ay(..VT_Jjf7!n{)@r?'MxA)XU.*wfUc&.o| w%\)a+5O5)x5^FqyKAOҢ|~Fu;,##]Vcc?%`LL;))\^(p"p3K*ժʄ@4#Ҧf:_Uq$b^N_qtKz|.TSh\8m⋆4NO)Mk}`ӨFDqa[9Q>u-&RzK /M;Zwi5ìTfM˯n󁔮U .G!]%]]'R6pV8j/O&ĥryL:; s@/HlkRt-կTN\{Lߜ{nT2:v5._ÑG_z٘ު!e #bv2YWa^E2ưp\oQ-*`|2u2JNuM];j}UcH(N ǶPL<ת\M[n{P̌{&?tu70,jp`I6aXG{|+phQTXZ(TPԡ9׈8ڵ3m_i-wGo\iGy[ScX;Y Q H^oDZ^b &k(uƛ6⌍-Zt{(e @qS~+q;kYH={YH ܽ{Ks:=|(5'8x(w/؞\+po`늷QYvM] AG;G澶m v"+˖TԨv))._E~`%X@Ns/x0ᣂX/\:=bU=`҅^e6DI 36՘` &p(u,,c9JďKSW$g|,~{ز?^Q Y*'[syʙ+Naw_tk`5rQdL޿=i%.yMet/3kR.y:8j]n@ % H:G(K?^P>ڽ!F%R2qM.KUL0LO=T9c̝o~\%_z/z.lڢ2΋\Trpn01W\` [QS҆0g6tV^.TRgN pqkTl]kŕdh͉c7p,!6q!nE/gv?=.?gj2(X%!BRHn\+r].+ٕb %VXe%RLsi.o/||9999 FyfӮMf2m̮P2p/XjkV lz`f!HgߴQSkZbCuw{tj>"26,WĎ-E? Tm Lgܴe1|wp4>?`Iì42١hB!'F<"UF[y}ӱU=hmvQ֯apH8.?xِY#<U._ QDm̃$R@JQ!z!//_@ʜR/CnFݠڏ2VB %PB %h;>r]Ә@ѓGE};jHJ6n ۶ ȓ[cgOa\qxt `0@ow;r^g\u\`l8UI~Emsc2,wa*5#2$='_˲ӟ{@g #MU䂡aE3Liz?c | aLE$s {EZ^Pp&ΪL_Yr0-39d·`_5zwqEpyBnΨKNvMSӯ'$'0tXF1>'#^dDȈ KN'2۫ķJx{)T֖& WTT2z!#22XϞ4AB4uů3 r%CT"3^G6D &BAQ@2,vҮz}Dr@48ځga|c%,/+@?ߖc  9%Ueޕ> ]ܐD|a5G;>@=0aX?͙Rt"|DmcРӯ6[Ne޴?9lV# ~ ϡzbL9Yi٦IԄb8O1o+py;Wjo]]1j'u6/k#)M) ~JRc4flmG wKg Z+ 'd& BQ"lըO-*#RAgþzVJ*EaSƱ?R#ES1Bߕ^%(?74ɼc_wyxpW/tr:3[~̋]+K4`0K(p9e$4i10:w޽r=vwH5`[M*A:֢)S&]k޼vs0@UDf̌TUw{uҴlsI$;x\SvͷdcOי࢟DŽ....&.Jl*+ LPאP1#M:c,zuF7! E9e҈ 6&L-FLrSP}&Fuh+H&|ӡN6M# WqZk+AtMztu~r:ijuTiHrʗD %(y/Y3E*m'1<|?sȌ'xbR/吼8n$ҩ1VZ9PoFG䄶lᝎk.N}9#]Cj9F}9Aihz*]>jEډ+3nM[w;'&S.Hc]:dB13luP\EvbjUG2NYkY`W/N2Plv>|FH  uYr@Qqr!^5a~iu1jjpu&P 9/zIyȗtF”hDS96Wabϼ̬fZ!wt E#F٣[{~j5KY<߲2gj@/Ibs|Wyt㖤:6}gAWSӪ9I^#gc>,[Pe|aYZw1 zxW-™q]"#BM>xbιO O_}T4ywF,BK)Ts[sz.tnyqk^;f:%;lPQ͗KAËm.L'J(7@AWo^\~=gaݙi{UԈlmI]zI}ufm:qsSKa[5 ︐SU|{wQs+y2ſ0`>6 &RE|*8E A hi\%>Clw77&[nr.F=<M(d xCbvJ,_=T'%VOs!@#it(\ĤisfM59uՊ!F@g1itTՒ'B Th(F1mؿfH/>s-Ex`|ԕ)Hu4L.H-L3d&Se*z{ I4`}I̞=vR_u|{./rR'W3 RRr)wPko(8jJ{.xhh}шw^I2HV9r8_6P Lvn^ШÚOm+l'Q6^@yR/ӉJ POEc {HTݍ9O^ Fî"x?\o@zآItԧS0 ]Ӯ2vx*qu=^4`o=[YWvt]۷ɜuk 441rEɧw.YUɾyՌw ٩X(}K~z]<sv熜Y ]L̑p*:3hge}vSE/ LJ,j_ T4}|ncMt0㵸Ӌg`j 6Pp 03"T^PsZelf䡔beKI`Ŗ% **t*I*$I#i4AN9$iETnjYPN_9>?D*c.qdl !BNI@CZ::}JN(O9jIt:]| eW/8:J\mk߯H3(POw' lQ'h'2Hc~>ܼ~xm&IWV,(v9*V8ΏI94~UsP(q3`tkK38و=.&}T*@[8&xv+8ym劗Y?GL G_u<#n+n:gE$I[Ք%T/b1f%KWs3^6t% Գ',CrOn2`0Hx.V IDAT{hIz忥xT0 -/?f&:_ Ag?`ߵ2U;}RPrdRqG"g&y6r$$zc࢝ןMCb7Y8H)Y8ѣQX.7)BN |RL_gơ!wPP7h\l YFma# zS}vp2ݾͧ >S{J ?()3֏3{5ݾIk6&\492kジ5g  5"[Z ~oE6Jƞ@NmDkh[U]O i0 yr='VXNz[ikЋ @s'VYVpC1" $[! p-uuԾD32ֈ> nuA’_5Z>Y=Ed(38xĺ2YRilc xڤA,2Lfl3 FgvBk)WάQv@QO&X~^'jgƨ>Eϯ~T k j#e TpE!)PTE#_zƵQ= HMfGD3zzaML-: i!}>|-|\ŋ_nn/%%D`IR^^7~љ!.jfPFPU/RR7Ơmt`cil#h)|{LLj mVxkYPUSJ0GSD܅C(p/П!vCO}\ɹEW+IL?P}!i4 LM&M]bэҲzn?0cׂVDdؘ-똼w֑oa[ph2*cEQ(96sK/\OtbO6)zp:5ĸAW ;|))dE(*ܶ`zouX M4H=߃R~. U{‰5T 5YW,Npc4ҧ R|Kw*wFݓHOVҊKI]KJRNX>uwssx$r8RL@MWd֙ؤgo.6'nԔ& Bz0[v${-Wހ+W9r>f6Q3v9(U/SRWMdM@u?1|)X7d s@7Ȁpzv؛׃g F% ZIvx# Uj4KokSV̖M96PcŦGos YIJc!?#7hd.t59o(2Wԭbs[=&QUQo O&Eoy~EuLKAW$>YuAZm\v#/Db[p`d9wvv!=o֑;O ݑtW .LAfLN?{yvc'34s=ƨ5"-u8֑=}p; _)>hѡ͓k  t#Oy @rKfTu qqkv-֔BƇƴ?wz΋/>]AxTMl3Srfm?m;/f ]2${L+P5BZ/64(J⁄rD⢉>%BzWxvM%Yq!M ֈFɤǕI [cmW,T^sq%q.54"!wm"ǭ'#"7-[*O/ BeܶMiMӴW 8,f'ըX\ёm .9vrH2fm3p O3"AEU>~`9v`AcRoPtj| ijLE>Ns|UT&M]6N ~̤iܪjИL5{Gh&f{?Nhm찘gɇO|>աS{ aPdm0 ~H BkAAQ{Z 9B(PZ lvWfV;\rB @QTmZ]Hܲx]iEQ:&@6b~} %Ro]. ca{hp]M^OzSngf(CC_,# ĉE4ш@FEu ^: 021*3-1MuYz,jPT{wq=UIk% {?]pN^\T)D (pG79 E‘'M^eC$Bj|%J/ݜ؋Zw "FC=g66W̞5Cg%LՔ钵m]Dv0_$P"_AZ W˔ Rf=I eQ#EÃM54$UōXQڶ3Rp, vq0sŖ=O>>HLu|8ٴW[V ̎VOY8*㾋pjkP+{t{Zs <<]@u¢2?͇@; ;%{E({.+z7$'7U]uEs$RN tkgPЩ+*޾PwdTEC2MSx^5ڦbݦ@c܂$Zm siѯKSEcDc"yՠ:ȧH!ϯv X%mF (4um/kYy`ҕ]n (j`Y PTM&<'sӔjٚϯr,P5rSCF-xǬyy; 8$c阁t:>HVМ.nbDSmcT|T5e` :rr#*+]דzC&qfMBIMBȀc+F P*]]JN{eM 8ڛ;*@yHbŎuJVŠC PĈn팡gv[`춃]׹D*05>A!tl|-V`hgmڎ{eǏ¿ˋ?H!au͚bwtq"B< wrL Xz^3a9DWne&nifp= e4W@Q?JO&=-ko4֍:Ə Cym:k >t|T3֤&dQ&8t0x`Y5.F-I3Z d^1NIh>~)p&=mđ &9|Ꚕ?R~b:xaÒb6"{/sT~ŗg 6I/ؒ+BO'cEKG%UP5 D!vtVe]L0n '[*l4 iR{܈qsg|gGufՕYʮPqހn)tEЦ,MtuOvؒ6NCACb64eޣj2C)Y&7 HZ2W Q@Z/.(E=:eu6%.}rS>^u֓^~_`UA@dbW2 X((K]IȿA3LFC, E20DfQNqWl{=JPaMR|JwGbѽCP=JF": ׎e~L;',I,iRKSR : IB={wS\,0q-[/%Ѽ27p¿v[B3HV:p:h7E-$ c!X `& (wFlLUrc)RoH|$$@^r:/E }0qOQn}O,HL-+H(0D FDO@S_v#Em7N~˂ӊxns~l ųO5G݂G>l:EeƊ!Hdntá~T˯?.,MXl,ӗ u%d|}6ċdu[Iښc<;&5nu#4` 4@`Ar$&ۿJh8ژQ{fc){W W7o4rĦD=-i͆e>,ŋ\XQl6%;t1/$dd&wַݜv>1%&1r㡮"!,PTdG@Mфn;{bzRVCQF6#0ݑ Qn[{Ƚ*[Ç,\LR @ZM^I{ 忸(QHW-J޺dm#O 1ЪDR,m8j ڥVW]ܕ7/p%37c٦+0 5-I=_-|%`ߣr/:]+5B!d|\?ZBl!5LN8J?%C.*=}oUgb;M"B}z{W+rSB DӞEI_ bP?hx@J|eP@\/YVB %!Pf'OE4RB䤒=@cjo_qro3H22cBɨ9I|y?oJ4u`vhFH.B`%@io(?/19u-(/PQ+%oQ4S׺='+_ n==ynHXiۧ?-J|(m %꣎?~G^C%G :Sj ԀUԠVNPB %/ PBPAOF3s>y2d"0dLW5j9dL~Kr44U=}Vkd_x*lk\6/l 7ce[rP W]t_C^!>K!%۴ICœH)8'jNAQ x|SXQne;)LNC|7((>tWO=gRT+AOb_—hm55ۭdg-rT.KhN瀅t͏ZK@k췸5H/:_\D.h8.%N :ڬF}' BNe_^viyZ\.-^nmhMUU"sbª=Qߺ3˽H|i? VeYq*Vc4`+Ne(@kWTk M&U@":Hh*jUN!xy&t%^ ?G!gNiJi)gUȁl(L辊!K-P\ķ) zQLw¡+Qh:-N_-Pr =-"+W+35hW񅠓i2bp~2KQ3T{߽oY3 cgqnϋ +_×@TKS(ςqnNz9o)aijMz^@SV3m[Sw`B+VU$CU 2"=̯G~F$Ikp#ѡ%U. JzJg+Jh,#Sr< DܸW%Rg|u=H01;_Ђ< RΗ)@{N*H:P@"7!v.7  H4 I~`>Tb'VՒQh:͒R-kч-p OK)_O%༎Ku/sg--=}'OL$g: OcBoopwϞtϓKzJ`whē>'=N$X_Z˥kW'P_yhn ^OB@?Ͳē>'}NX8ashVW>}'}O.9PDtȸ P_ TT4UF'UHFh*=5TM#)|:v.+{V+uEh{u |:k c&e24(8mC %IH:D5Pg3,h/~:SM15Q@UKRkjR;ٛ[}#`\NT~4e%l2-$&7=Ҟd?Z^x bjݣ3^(NDϸNW_+`(!ۯԬW*sT% 9I35Zkد,>/a`]yDkmK*Kq #O2Wi"?Aol[z}bJ%.٫]Hf*^?E]S]zKXĆQʑKlj>v^P>O|H+s7NFQ)~ڛoͫOB7IRQB.Y3j<-$ & K0~C Oyڶ̴u4Y'kb,A _:4U+.$:):SmՃ-S*T3z;fTi<>cϜT$_TD^?$G>P--st)2ֲ>VLՒ*#.=_&^Fi@߼f/N VЪ: l fE2 fUS)*X jAmsEtWnySq*ڞJivB2laƎ`VWr˿0>0=3i.SM}r6L#e2%Pnp(!co!SPOP%YzZz{;]u?a0|W5ץDEOAt, û?=<GjY@Ͼ1潖uj+SpK-92rٝjmoގYDUkdhJLCnC wa+_@9cf?|>54uuU(F(]-T;3FI,j|~5cU%P(*(OgOŀNʇK_CO[\8XFWwU-{!6_]ڟ}V/$ƏӲo> 25epv& <(IsԬs9_u` όN]wt5}ئke0 LM\NjD|AiZv==Gͽ2aUN߻fhB8.n4Q:Z꘲oџKՅ::86=\Zŕ- g -&8 rFҩμbv[: ^bE(*ʦKCCfUln;?")ԐavG='NNL2ZVDR@=wĭ?u%NelRYgK2LV԰ٷ(0ECO2gGρ qǟo=*G.5n4{3yj}}SWO)b4Nۥ?(i]7]Tp%i<=Izf`hۯ3p5^6p3O nZ6ܺcot2l]zʺv*O޾q:}.8>O8Yu>|-րߧaZT㏀9G}gUsBU} ]SIpVo?z\@HkиAQ5a{z,< fޘ[&8G;ltao~ F?V551#FB UBUSk;̴QbTqnܗ\@gs9lE &18t_Ȋ9{TX{(U7 ܃efqk5DPS_K}?{xkjL[fVs;?qCx#+ΪW5k2׽J 7LҠi;YԨ! j=]q\~xMZUy-,0 |zO1tTNCƥI#m-uaiS9w `lonc7e7Nf~$0h j,Qgaj'lIm_ct+ڏhCQt ;Xiا&A<+VӐ~B$ &7anQcs^|d8?pokT;R*/Q5N XydDi=.8@H @ Z}y u 7mv^a? pyUAVbۏQXp7}SmazD.;,_ecne+??h33G@b] ~ N_'nj`豂y琠 _KfK-9QzO;v\W=]f{xޟ Z/dfM`>{U}a!~ ̝O.3J"?aE6 z-SQ;|§(䮸6;(r=P̖xLXvi\>jb])h" $ (xbEFb;`EųϳWN{Y+8%hY)R+~\䲻o>dw̛Uc:J|FC a|@gޛ]~ 4m>״@j c<{eNUl5#z3BQ8{%Xs4_|>rge#/fYVǧq"Q_g =2kȡ(fvh+&}IУ^2Z024;âXR:YA"@0sv  0M8*= |/.Ǵ+_R?p-|@|j/W3N;8Ĕ.B-gN2 zQҬX?{ K \6h ) hcuO¼Kb/ꐉB"%E]~ї H/D#~v>I 16[{.cGYۚ%UViHl0)CCV*Eg@wY$U-7AYmuݖ)G<L먋8;3޾LuBqt[X7yʇ.V/EwƠQ fQ @F;܁(UO(L:ȳBRzV 0*L T\ [A꯱iE@j+ v'ם/.D%ާGCgK]OYBX4JNE̊"v EGⴄ@NjBǔ0 1P@W!+8_E|64ܲǘė%  >NdNk/#is> *ylju/)qQmFC3^~OV^!`DΜp*όr@5}ۙj40iz <wv&>؁;GWwݙ[ wcf\?E`tr#SuMޞxttvŧ)@ė[&XL@sdYaإ0b`ä ǡA:'r71p$|X{r3>֒G$`ȺN!儮`cvBp,CTˌaD ]}@TLc] b&_e;\O؇Ci )Ϫth)$vu9:/7mQ]* X\٘dXɪSOqNgo4z#qb4e#jzvoNvEYGn5XtX޽zOS(#Uo9s%^)פ1I%Ѿ|Fmx`?1R|bHýx!j`}.ΆS-&|B_pv;sԗ`'џ?­}sh:CVIU}L"\=( ߾Qq1A VǙ܄ԹͦsRwnJ7V꾥}k3ZtbWFt9PFw*MwW:ynoI|0ueWfIiI pk҆ 5!_ HzB i IZS 06*k MwWS6]]8u] hRִ ]fwfIxm.=hҟhdn5~.-AK= ƫ1Ms2R&Ɍ k|f$6[_ q ~ E$jL @\ E h[0y0Q$Xkmƕ$^ XMϵJN&hvXۉgD#.פ/;B]H-W{rR 7kW9dWV;D7[k /q!< GǶ0=%JzV\g$Ge4ͻwiXǦ1k|Y}HT" _];r‹BaCkgtHt\S7;NݩϗF>A3Ykȣ\{*Mvuøίs8w.K[fEZƫ[5m~:^]|1_CjlORroP$>,x_άߧYZkn :N[؅v/Ԝ6lXZt%WdST㇆l O2u;(bPy!vj0.ۿ7Ìn32Qf;H/Ό<˲cKJI9x-z֣֔%10 ={M9yŎt xdbrbsńZ2A.=ʱǤOXq" zy2lFJmz(1SFo(';}o8Ԁ/jPdMG ?xܙAX80 AVJJ:>۹F1_$;Fxb-I/23?`cUE~)Kzt&%ڭWvihcQ g] 5'ܰo"c.8tZ .\f{ IDATs90}Ҝ ËNؕm&ȡY|qEE|"d'ӳf=\0?pnP@&NɭN_?]Li~*G]lq9{199q'cWtͰ-~[?E{sآa $+3ٿ%geL΄,1mC;:mdsoz4̣GR',?}pbyx2lf2MmG'i-r,ѳKNzY.SܖnL:`Y^b,2_3j#2QfVcS^I/I R4Bv!~W,viGkƍݜ.W=q|F)kUN=8FTٙ^#p.Gy- #[]R|wsz2\+J|Bcc EW+](܎CWT`}|ࠂJ7ݰQ~^!01͍!NM }X#sMVc_ ebQ&}p`uU|ΩޱPREoc@N%V] ]q^p&qwNnoD6U [E3<Ԉf8IHOvg`*&\T]f Uׅح.sB|;M75"0w+kȶ~թKmAL)qqݸ%|01 wb>cA}\R{ ,8z6~FP o7VDxܒ+0vg)EB%Vn=7c=GF|Y gAC3$*D0gFv&mtJTmqljbY .,лCv!7nz:em^8Ic$;6ݻtVmKJ|çv \?jE .gRW~ x*Hi(f]ЉuYWv e ~ 8>: ۘK{A(~}SRU<ɸ되_1,l1P&/EϩY-t &pF.ÁaC@a.!nj}tUz&`y4߾¬O:ZN6uX @]!RJVxzK/m (DʜHj(u_'> v0)1}8#(XX{ؿ.MV?"WlV:{{#4):]UO̹P5\at!?+)?xm~wcaӥI\?袝ߊoҰӐ'Q1kVۤ)=Ƭ-;wkmIxzQN6lb0 3L1S,+G8 ۦ﫳msBf6g,LҰe!!NϧA- i6׬bB(6VCqss)ϧ)6[;uPHe i]{hPL`s,$iEQ+/0~j}hS1?W .6Zm0LxC[+gZ ACqaM2)BPL46j Goχ'l3Dg_jgaoA7* Z[;WQz/!ʌK{J@vEx,;+Э,N%sb`@/1!iWM~HrU+Xyoާتw6AÓH$]{)jgm"⚛Jj3gcnX.M>˫x*xeŦkCbʚ3([ \C)E <3Rn[qQۈW^rV')tvpxfơ/PF)I˺ @_@}=IJOԧDi/ }lV,w)D?CepƗ;NIwAl,_Nt7;o1 ׫qn_omq٫00:2(xz⢢"ߢ"ZLڃ{/i]7۳SY45]3/Fk%ۦ_}{%ś\#!=C9衇60Ёuԡ044444400%J7 =_#+~" g5wC>wDPEPnkDWoy3 eR'VՕUM]%zCFX!`!=C?X^~x*=!lXDS ~fY*GP/YZ(.!t>E\NWw0k`-a"{! VQaz衇z @1Sũ_w#u2[֛ޕy4 &[S˷ EҟhDbXB^&d17)d)'&_S:~Rޝ{Ye;wS R:8wY^ dK~0-o](ښ(gv^&S=/F9 .ij:2*j&~/#A" DGG>/8=|xײSK?bzѹGSX fQ@@~f4<U\ҡ+ eM]?~h,E׶Dayv8tݣr]9k0J> 5z”5kh2 4CR:#C#q衇zs(w7ߤ/$ěeG}*Hg/ TLC;4&{$yO{*5}=8Ԝx8}kX Մx8)Gy{d@aL2tdJ 7_\6 >%/tZ 7/>>[Zdĝ&ޤuOeд2[53ĺoBV=-K TK[ 1EM܉7!1)dZYѨEbT+)gurj#k=sAN:=Z*P^^MR)I E82*^jWT(ΨvC=CPx,d|W[}r=E\ U{xج]ܱ< ~ΫcwY3&B䤸6^mV㰶O;YB6бϏnk9i2[L9 ֶvs@ԅ'"%VQt<˳}Yj-Wk y z<ٶG-t\6nJYm1R"s:%׾a+.gϢ#ֲ5?jPD+Kʓ;9ݻv)>'W%dZ`ϘFFl##6nmЦ%9*~٦be֓WxKH(MИ|<9#^?mK/0 /q}ݐcrZY}yCAĖ ÝeV.MԼ$ys?0de59`ң-Ys-[M {ۢS}okȥGJjĕ #} wå*|V#yMHK)MY^h(!NFX8xbqh~z0?,o}{vl[B = kF%΢|ǮBXz"`pqv)0VNBH<{@qo zyfX鴹ٗ_Yv r7Mz诬Qګ"ַ}[C|:> n5oouN F]҂J4p/)óS! 6>˰:W*a= Q~ђ?o'\<|u({ ǓrD$0]EJcɺH~G6l&!~nOY5"F -jc[0h ö&Fte:VQkc7iW2`}-my'֮*Z  )Ӵ֘<HDn1}މWDcI*rB.Y)ٲ4^0_orz1vG'yK2TV IUCRj(L9 =Vvi'df1ombc+9_XU'0 #ߜUuAlyS!Dڽo@bt@S\,@>|\b B\=Ǎa3>́u AQAl >Y|2wq%!?YIZ,[}ை" lE\B"BQ ( :zf#r ڨ76u64;P\(624i1 apwd/Sݤgx B)/49!i2LuP  y:!CҐBz ݋mFekQNޱmrfđM) jJ}/E<\8l>'7 ,9}Wk߉PT857OgZQ$U-h(MiGulS´޷ij'd4za=S)79@T4'V/+UKԛ_KZSAQOvnѐ9TK='9?̄6.'-?O)kԵE 'O?iڎ͐rc!utöŷ3DF<ՍgyDWzkW c3CY_ڹ) ^m@lO1<I^6:>|$4 fA=00a@(H/բ{ &y%QjkZ-k }F8?:-].- uQO^ƦHv8M!eQhSԸ}wt48'}F퀉2UQ^}0[! Y)}TǝtA^^:j)1on^q]d^\k1}{ciBZzueQ=lϹB@|qh/m_̝+YaP)JHq(=e`24 YERtP콡[2P m}Vq.lJlՎkYRGvGtg|X6ϋ [LMy (?~{HK#$S]8Uejܑ2=iX~ޙmM>DI;ډe&~ R(TII,B5wP v< Èa|Lu-F6qo9C0 o⢸[:#cd4zya-^910Z}?Os{wWYE_Eq{|PvoȸSa`43"A('=w`%Rs%,B|[V,T} u&$C{FLJg܍y3g7:á:+)k&'2taqX`]`_Rc7})=9jpJĊYe̒ z~?0x-:ϐS}=݈᱈ӎkY/NxZ ּ4!{GQFssiI~!,.йDŢiE]G5! ~qK3x݃索j[?EJɠCEqۊajZch 1kЩBiq-SZ#yfR!d#7+,P2)^Ič{tYQ,sܻ+!5(4'dz<qR\ٯ <}{J24_# >kFEr?DUwRw/Y]zw7LP@'ͪ6~S)np*up7T͒ вc߳+~0A;4moML,sxr >cYOLJS3]u=\́v#ǵ^?FjCj皧ʺ 7mZ':$z0.2lDv7JQf6QJ݆ӿZ^ pK IDATBsJE;`qcbPr׊EA%ЉGĄM26 JPM5L6\m|L>z(NYOԶ^*?^G2kH`kh5B5Y5ۮ6lGn26V}k6Ŵz^ ݳ߶~Rm.Y6@urBބơ+FF1U,UFOQ\$>ɛ ƈC  e'7yUm9IkC,6DIAe( q(Tf0 ?!v?`ߴ9M wlNlFt"Ȫg`C7U%jh[M4C_n ֲ2BvyV"H-Fn0)}뛆UV73 >k&ioJV}z#fӔi~mo}Ufnw8xs[rV.~mES׭,%c)]N2IaZ;zL\I^ӽ2)OP)udWCF qV/ ya^bۄnkUIv :[SU}Yfz^vJĦL%+7p[m"'kURo3Grk96˗]RFJĸ~{_jq/v\U7|tZ$јL!yqvc{K@ȗbWħdONSW2?$oDCՈKVojscZƚ֎[SNWno8yvlj[=hsmM37˙䨪8ٕzܽdao[kL&<٨]ƺ[TK2wS8=SZ:zۭ] W7"}º$٢ʼn67Ӫt/UuXR!ERro~^wO6ga)y U,u4L-e]C^y L#xlC$o2vOl`˜ F5@72Ъ:95j t]J2R+x*(N Kn)P^CBغZ~y*)U^<*s6T^jeemFi1+[Uk2Y 2Pz+C1!_xɼ{Tfjb(**Rc% bbBaSڳ! 0LjF- ۜB(Y5 !75B K(.~dbqjZgd2m !P(obEW i"qьm,|`i*rБsz갈S-F 2ͷ4MHDlkg/¼+b/ >avnFF3y'3!x)Nϝܷgp `P 6SU|4㴨7S@mbϭT>{6~R6{YdD Gq8͋(U,~v.A9abTGf Do3S[Y r]=̕?LƩ}ZFD||9Нat_UJ. BtdDXeuK"o ;۽OP #/#)\su=&UD41S\L =2|cD8T0WDV.buC*YO#}cW^qalV Yzx+gczT3 !!_ϩ5F5YҮ`I=J6C݌$Lr\6D =J"d**eiZ8ԩ*>agkv 馭;(YF8U.4ΡgTHƤT6#:3ce)}C7.xe:O,_2)mFZ*j<6%]N{F3vNZ3lWw~ֹACѕ=eԭ!ل+)Qz8&1!&O͡ ":Vnpsڄ4f*IXuBnk\ۡg؃j<;1\)ivT+OFIV%2'+kh𰥳c:3i9'$j4P8%?4U=l[Jo- >GE=*nG-h0{?Uxc ~}6v$?:W ~q.嶯 8:YtTfaZ+95@DΌje]s^܆_(6ef{uQjȻc'+}ZT ;ŏCێmlN5C 5BirmLn`zf9;H 7E %~&ſݤf0;vPk[qV+C&4rBKlzJ`ͪDDF`P!%a;'9`hݦ ZgIe pF hR.%Ml"R`;I,I6JV(Cݺ4SajWt#y&_ኑR6ٕ[ sR4`ogJdP,UjI>Utg{^m{l1jURUuW>WA6jҫMJu ffVlB]Uj3{pSl#6Hʚu[a_=6ׇSWDT85 :Z]C(%Kf ܬͩ< 5]z#liB l nބfVH^ !]eZ t)aju-ge;+Њݍħ^- <~fjHR2kѢ{[ubN#Qym#GOQcbWh+u79gCZZN^ =~(|x*=УָU"fgPO~괐1u0xtۙGʐ)1̔ɟC fHc33_ܺpxo gFtE˲hȢ ^^,dp~aӳ^>:ogbcyϠĔs&:taMN]Jz5/ ի[Y S[|\T:Ԭ5!mqy5qS/23'ٓ%20qN%ܿ7gߞ={'[V>~[>TEJ(g_D5[<:WkA=7-f`=>YzGI3c  "]f^F'V]8:a"6_%&rwncDBt$4$0jSrD'm03:/N/ 5gyrEw3/q!A^t6αQ3>cr%O6CvikzQr76Oܵuzpj֛atw!w II7yVtŶ\l"ŕ/Yg+_ݚ5:HŽZR*-#n]۹Ӥ[ق hp>GZZ۶3/Qa5>Lʌ];YuP]t}XmItUְq6]Wu۰m\c1m}(#qeY><>󳲭4xnN{t&eE _8*|1yC, 4Mlj* |b+Sf]Fj4*gy)i 4MqK- 4)B: _ MTJuh!/$XZX=uퟃ~}]|T?xCH$-%A3LpʹVJPl6hړjZrXM9rt+B?7!vINm_ĺͧ##]9k-km5 ƈ;Mi]a 2)tw҉}{,r8P`s-chz=߁CE ,/pjj8{o-{=z衇CpC=(/(m՛l&֮a[\mz衇z| C=C=C zC=50LE7C=CwEum?vaWwiQPAVŊ`lQ!$bJb#&QcFF4bDcƮ.{?n߂{3g9{gΝso9ӿF4S9S9T~+'1{C9S9o"ʽ?EA69S9S9S9?BF9ӿKL`7qgwEGq@FU 1."y}8xV/[d n&v,[wX3],ѺʩʩToS9D)"H4Qd@)=p𘳢g a=l `5ec 1,r$*Ce5TU#M-57䠵b\!BXmg/N}jש_=FFOk[SA:%ߏA gز-3 ,ƣvV}v҆CԶRCX?O+E7KJH8)HIMؼIJb;5_Jr2BR[\)y$W0>Im5F׃E[VYKX֌O)6>Cg iUMQ]OѾfUJ2SY)r*'{6,|ӭ !o'SIDĚ줹Ms% !$")[>&d?E(&[_.el%7.b[#&uK2Cjȋ(qM/p7efR!V_JP14n]:4OQ_PO1״S'* e,d^[;ɱiscZʑ0M$q5H"# )] V-CX D#J\zO1 IDATywrXmQ+Sf~h֐5х%_҃T{U-.VK'L`an>`Cf }EZPsქc~@6ăTHksX# }@InmRJ/'7 9+a--DJӆ*koh-^N_;nC`ނxwOZUGAiM Ͼ<3\8WĞ WyEV-H^#˩wo4k$fߛ /Lp";W'L/VbLҁ|y$BU'<;O(2=KEѼKwo9.hk*(㸿ر O80Gj"e[-2 =sJG®2M% bل-:0ky䌴ʷ_,,CZ -Z{buW-9eCx̆(.*xG N DF#J). _2' `.\}Ϧs&)X%JYBTe d/d:>JI@BɹϿU( CFِQS(]"ZyΎ1$-[= ' PGȝ aٗW͟HEy.$6ĕ~4!"AD$-5} R}f\k#5;AY`:(;2v5YS&Ad-c 8p%XMMj-3\tc@u64=ؼ(uI2NZvgrkWpU~:)~KSDž}{IER^VNo߽!=8r9W{~f6ضR Y/;&Q I/ ٘^%lB"2uMr]Z*B"2g/'Q6 Iu܅S $a ߾&9'Y2Mb8GLMBp2D$j}*B"D\pb]Ncڇ=r"/,!B"He/<ؔ0j !cN&ٰ'Go. xS9SxrebΒ& IGb4kvfWq1䞋I(Ne͝.΍8`ٻ"fl16;ji>&}oSo ArS&fjJaf>..[xt98888>q@f jr Bz;))p{lLaLO3ca6S3'jF3r!}z|QFLQݙJ-80]8zJHHzeE)wS#LM*rm1by*sDN.|HNJ3az @A)&bda-h4R UcUl<(+ mi2L\GʷLͬ8{icf3f^odTYLX>5W=/ԨYG4 xü2oEgf*6n4rmy31&X}9tF(XH/3AI7 żkʆys)o F`a L|%bh`儴J$WW`ןYecL۩qAҊ.2q W:,YJȞ݌_65W{UCVL[J/r~3x  {A_nj|orO@x YtzZB_|ϊy6{6cdj⥓Fr!qq[ί8sdm|}.XUOZK[ez,/Y}dbxsBm-CvO_~ -p!iqhmW<=iytlxP9񫺫Ipc#BЙ6^q{hOvZUwuͬ9k7\)tM߻dzO 6Tl})93z㏬|xL 14]Cu"ҴCUK݉s⟨oR/y>Fs)%kz~ɬC0͈=yd)?؇/W?UtG%ᲆ*1 -N^ &uh֝'V WrjΧ3o>vy:\ w U g^|hZJJ{:-K<)ls$~7fE7 p؆JL,4HW1a 3VcޭJ[(f63pa \ \$sd2G! LL"0+.F(փc3NP\gd٩{-8(Bxe0}ή]睽|Cҹ/hC=Ć[tn]0ܚu١GQHÌ?3BprRVh˜r\w|#4绘fs.-ة9:̸c3[rBYe´?N2gL㝏fj6??#\!+cJAvnjGZ0w)Zt^^?}RYѩ gw} SUλ& JiΡerRn1q鉜;̸exw'6yJ)-K؟guPBYaG RJiӟM&W ~WMf5Cwx_͔Nn;o(ǿ TN]b0r_+>j$swՌNj%溎)sf*UEy+/LRj]طEa l{YciյKys -9:PsaΧ]/=tIrLt΁:ox8:RPC3R?} @i/gl jU%a-j7w280W260>Z3R媔u嚙_շB;)/9eKq+\1kѬ(=Hoomؒ #؜]w#vui>ㆎo̻ 给_AE{ezWcHnޜSvko"'DN|ƺ fY澘}W<;I]2' ݗvc>"r]FCJ1ӫ/pn p +[[:9͘N{tú`Qu:[raFz'R-'> e顋Gaeݱ&L֮O^~0;4% 2m/?ہPN_ &=ð#1s-M$_39j+=ǘ9[' ž hIޅǂW,ܻ^@HK[~?i[93{}ZXlub:z~- },N{=w`HFGVy q n]ANwoɯgvӾ7wo;_03{aO: XMVKOvjjbF ]On۽Qa@AgJPZ]!hx7}Q8riljj6qؽ/*ߧw8 |_mۧC@- ..+xoX !lZnNG\Z߼p'>`lFǎ?2*j ыL f~ɽLLi#;eXHam&GP4Sr.`Hsg_^]5R=+ix"th ؐr %r%2mݝrRa.1 :-3AXVh)L)#ے . ؚ!_=qt\P#MRG42|-&wLA{1&fYtSjF{z.пq¼azLz`veN9xhʹU?V| 3ͩ_wH -LgYY]V;hԡgN;oвCe(ӣi},htYKE>b3̟ػ״vI#}U? ޫ֥C2dSfvҲe1f;lRG=ޒ~|]]־Z/r9 >#YpadzUY#}# }+)(F6=}qۦ]/ja?>j5ɇ\?ӜEnjZNc&2fnruԨ4}qkڒ~l]Vּ5}V;~hE-KK' xOH2bhi 81&S ֶ6VDX:|2Ƶ!Șѡ V-AWu nfSݿ[:Y_^o0>>q)>heBE_2+S[/_5Q1iZ*4keBE+N3 opƱogQa[%$,7+k#i^tpzZv*׭b+ljJfL&j4)7edeO31-XܪJ2;mJѤS>OߍFwTTgq(<F~9KONJڎ&90[||7/-Μo8gU/mTx#=Z6bY~Ȧr+ڭ_MϲnVwCakf>Tu讎1٤gN,$>,KϘޫmd^ jβs?z<;+iZ3L~L;uA(r?»?j ?_W)S6ǪMZW`42JB E~67 I4w$8@8k4=ZVaz< ZF 9a#й;{({ÖhOMLkz^@ eZBV?^ :\'ZT|qF{"Zb-&rM^f4lh0<D3%-v{ywa,, 7ޱwMUp jv^1 xa1r~X9ed36LGRl:\O.\=[o8rXЫԱ1RX404)cvE>; S9_\Uk6ƒY1)1a} c--U^i4*zz3Ȃj6춊Oc*mdy=+|^^UX.ҟTq2= gۿqrVyl5\ۻiri#axfKRtv5Vs05w3qSC: )~Gds>EMh|O>e,ء:%41S)@a.5f9r ^Y0]m3dȓ+ )T$m(|jǧe;~5fȱ]5C%~޺kSWd#MNY&L5u7k>L6It`R+37[M*0`\&wp`"$=*T &'(Pj S^[er pȶ(P#X Bޟ8@R m̀Cиε>UO;_>ӳʔ~@zÝci邞d!}Wdn8XLF.bi u`x&Oޠ!GAX/' gQ|4)`s4l6S=Q:0dh;΁N-5Rӗ]/aC1_'}LPA`Mas4Ҭi8+u'ЎY?X<ЇT P' *N ''e(H_/ òC<|p2 WEFF7i{p4|n@odP§YD;z6LH%caՙ3 ^ zp ԕz0ZpRlʆ{2 |@Xm"EIN*!//|~_5S!˗ׁ=#C{7w1^Iy6DSp7eQ !'E3}%was4, ^=eFW~{ X`YLl*~43 M09Ni4w"'DNd/C ; p m]~lWŠZ yCvQϽyP{~ak"oyxR(bvSFPN?og`Vu 1>eK21XbIm|aLhSpD=g(ɿqܖ{7<_H]kv嚀G? ^͌w3az_m.ϑ7صi9:@M‚syvL@-!Xi9:/'̃CuMoWӣ B[;(]ws~}ecek&waiy%> w@&-P^*O \]a8%cR+t;Dhdu[kХŮO}\xը5r (~bISI-{U:Z[ +|*? IDAT k6{+T2:q@|[Sn CxF),Vm`bÑ֪V 8_ݡZiQI9'.! cq l6py}-ǧ9E{Gj.W1!L`!82ዤ)GIKK;x$9xyIV|FDq<V~>9L?Aڅ^B c6hi:9~`icEsWCGxM $%% \ g."BVZ Kh&9r0-mZ]RH6u 9LukKvo?&hYmSk&$=T;xl]@>53(`1~ʼn76'v&'CRSOݵKm!kE%oi1,$pKIٗ/eޮO-ÆMYG}DJ@ܔPPdblWB|YVoG$GX}$w\$6W fTN F7:^]+hJ$dOIQփgOFlzO4g>uc>iWYp?G{Yߵp'"{o.{&UMY/p Օ!}">x0{S.iI8}Аו!ve>ߑrg>vp=̆^o_ߦN@$ P_={b/2gpBc/Wn]e h\!2r]Ub|^*OO;Au§/cV'WX rjdr9 g3=p۔@5v}븬#5x(׫ݳ5afԯS0}{L ;'5LϦy"|uqG(+`fө6(~Ɯ3i7{J԰M`ؙ[/ %U;?J9h_73c}l4BhlE8}~ܖxa8W7(k WJX 3]oQ9n}6 zJVT8 *"!6fڛ 6f2Ɏ45;Ux(1O&ATgwZp&Ӭ[P\=K@8Ey)!r7!)#E$'fzkK2ʷ{09ҳс.ٝq+Po. ,:Ew_P,kzT]0mf qpVlʾVA5vCIDBTJmBÀ=^@R*9KŌk+'Riee@)q{C3oh09n%D}'%?j;μ̍(cwer:@fݺJTPN?oxԧ5lnvO3_dJwweg5tע|=*wx_ƂOr ڿqm>TK%e<^o @y%S?P6\_X0NJpouʸ#iUJLZw.ʱטk{c P([gUVL\YBx20l>dJ\Rٺw|XVY!i&پU\ـ1MZxxZzLN$Sy;4ZhpKe :,1 D؀ mϨe9%Ğ#9RJDKh5fî8z+ǒPFPËqS7jur %@Iolr8Oܒ'lJimolglɃ(:4v'~%oTp?rIh-gNքՅ325聴~~0p6 < B{Ϝ51=s׉ƋҝGevr*8TCw5nDNz"(߻f57<掙xZ<+CMU9>A 1+ z}_iщO&\0 bl.+hk ؑ5!=y3|E2m֙A5O$rh7mt|)AN?,87,no윧6}*1=:8<rf2 D# EΣ3Cڳ_N3ߞi*;ClX,I[U&{''fL.f9T(J׏ָҗ͊.w2 U#r{ ~RU UР˂ggLؖ!ܖG9)QR?vtmągB|1SТ34K;ʂI2OW"x7já!O 86p"$DR"8`ǁ]]Tx(MVEDHillbzv=ޟ.Wd5 Vv Vuƒ[T7vZi#?^QScN9F~[ .;vc g[ܸ sG(Xau?T!"íOvDR suRb︰alpjy_'9?[|\^N:Ni齢|.7Lb)|1= `xtjDӿMH=?EVGNAs$S8vzZe]dBV0(La+.M֎jJ//[ˊvacQswbHb2dCȜԥ+SzzyL6ޚlIpRȬTV'*Nئo~׼Ff9WV$J6@LF)^ v0_Sg%` 2W];)vajo3u U{Q !ޞ>@!, K ]m.S׽K =.r&R.2Qg9Y`.Zt:AMS:h7{Ӛkͫ|@܋ɡk˅)ݲNc] 5vٖQD.*!!8;xs`Lm^fTRƧ.*)ؕ Q7]:{?0E;xōSoXVf#&<4H-@:ڻtب?8kKʊp0 ! DFN\V!1򬻦&;2݅~2@qq"»S7N1/ܲMWOY-~EB" B{̺qxKқh֌~];G^A."#V^@0ݞ8[6E#4d`Z]yM1/иl՞3PU.L&YٗPR\l֨|Sl:3!J˾;MiHY@&SK'+a=Pd&2 \5!pф-t;y.m/Z O!UڄOԼ]ex>)!~9]J3 ^MBb߾ 7Iq}stּoln{w]Ya;k්{V=O . W.hǯiaxX/;Y:i&YT0njV[gʁwL_ίfw{l >=&%q[*μCComH6$,Rer~7r4q3ƯX(srGg׭=dMޖqVv9,?| Ȧ;H[ߔ?I&݅Wz wrsz-GӸ׈y郃|_!%d6JL{o:`5}dVpcJq>O4^}"1n] 2JR&ΤPђp t/ %2BRT^LbSP˗&BR_`hL0Ar#~,z2" ."Zȯ{$SXE&`BU &EiT%^ Tlѭᬢ= /4UeWח5T GJiw֥=ЅV"}MJ%0_tXґEI,rBrm.7]iU0' :d2)ܪIez%de҉+L1W^; /_nn [O3=9ܫG7Jcn-mwebL+"ss#+LDEE A@?֙E$&˗-g? lY?I۟;fMOQ&sֿy,Q d?"uN{bT58v)<Yh>fZRb6q`CH:FqӖoKaPB|8dT1U8rs8L})ܸqqB妐\ݸn++ܤ-( UE*QJ,DhnI U[*h$21sVްVVDpd"U1@^Ye;!J.k6,[2eMՌS,lu) 7󿋴]"Qp[oIetO֬Xpvyi.T (KY\ne. U3@Y;=A8k!l!]1l1[!*@XXUTt\DR16_L*R"vD7 lK"ޜ6"ECUI" 6,'Z䊫"RgF[? SR5I .N'-&ϲTRKG K1/xՕi,lCK`SX'ndDzXf͔,$Bw P1s lc,+%`PlQՃG7E2q3A0SJͼCK{8xWл49 87^nD0;g8GYޑlgC/80C Xsx K~ `P`KQPcQU^rqۀDQ!XƃF3܅ڻX02p'(%vJ֐0( (4Qj񥶐hpPMDP"-qf(JtdKq(riy8l@p"ߠ066^тDPܒ㍂ vl#% 6kgDgS[,Hr?SMҜA "BRI4-$-6,8sySw6q}Om{On 4:slfΎ;epkWmYuԎi`Je=m +W YK\!=]s5"%+Bd7%>j`2=&[V S]L±r#n dh3n[C۟z.6*?xđKջj_{~3#'7 %|ܱB^f~IԵK 9ƥc?'|w0$aVg>cl 7شž IDAT҆<@U;0ZrN6XI%.ލb]ek넅b?.<b~@K/7&{әfhb 9ҏ>T=+.{nxWmV^ mXVg@{77;Fl$TGT@|`(CTfCr p3qE=1W{hmuayr+<JX,ZA=W#q.?6\|spLe7ޡ6]vܱCE 1T"D.N9YiEnQZdȒ}) *p-,OI"uu9xCTbԝBWppc۽⌍ƌ0YhOM Z @t@JM&F#<_K#DVfr@-`25k(1I"E`Gr؅tPTTR2$4Y,6!&  ~?.%:T x̨/?1ƕr}mcOFkR ]^1+I ܶ2MطE.7 kLwH̩̋: w7(AkCUYuH*vH: !#NqxeEBlW\/Thq _Yu@l J [)uBiT'iMI)IY%":a_]!!"E;1`vaԵ]E{J,b2FTH1lx[S aTD\|AŚ@oM<%157yM&O,F"(c6$w*bʞXMN L$G9naʻfȎ'Wo[]jsqS- 6y/ W76FxhVaD3xϛ9ދm-1\-LvrEgvfe`:%Pf];"0;6 iʵ[2n6ߓ;KLrz񦱦vުyʎ} cR+Mؒob|{Fݹ!Ӯر̱0fʯ#M0ܻw)>7k; iuvrd8Q"0eb®v$∋ ފ¶Ia@ɥFibQ6$ 76v57X⯒ ̦h"b‹C$L]Kۻj3sa,&(mK@u @S-q/:RVy5X&1o/Z bz3ObG쌮p YC=)n*VZ {Sjӌ$r&,oIS9 ^ קZ*-t൶غQ`D=k6{ȿS&;f`z_R#N_'[= =__k sͮ\1!/r`KžѤ]ڢt)3<{ʀU{t`'d.@ q/iZ圦c܉'%|Qǟ*];h#-z !t)0ʽ M+iP/c}oicv:su_9s]ZOkkB&4;eaU/aMiϋm#ʔQL\lKF(Kc2[<(4  {xynNa=}!GL#őu@|iܘPDd:,(+ QR 9)Y["W>U+2[ݶhSޏ$=>r' V 3!G"(TrEF,SZI\:f.>ʡw MӖ !i4G/}fwzZsx˱*պqIG&oh56=#n\‘i 3~3eЫ.k j7faN̒k+vR5{-1CM l&n;wAAS4+5覚k;7-f~e/:GI&E2?+eֶTBk@$|?($ _9WK Z/Y<z{]qC6R9"7~HmQ,ٮȣ{.oj<^Ha홳G]q _$Ab#Wq=VB+!CiuYPz@Jкŏy/Lȳ)+I!g{tVgϭrnB@7G5jujgYVk]7bM!A GKDXYgU[I[$e<3JILфlN0} 0KA|%~JgWQk)"Ua-~b ~ ^y]`f?̓ +?:KfKvR4)ܕ!0?a))l#SfغIlFc* ܖH#Ā<BѳGy8W7/2*6ѨƼ"VW0^;~S7ŇnԵntj9r\iѠ(la-b=ZN$Oon3yٕ'|G ƭ8e_m؀6̏/}ŏC?\'T"65TmL[BuRlD<>y=t9v,E2]Ѵ1hk [jLh*l%LU{5">JS@ҝ@sv>+kWk=`?ȻΫ'ZT}Q@@'}tȻR#Wwm] '>aLn~ XkJզ`E p+`nY,77k}:IŋK`èIrRkb#  ?tޟݣ2q֗Bz,egA:^Wo1mAʣ ]Uz})Ƒd.h5M\emϿvy.vݣʟLԿP edb5kky)8(-&rUBThI 2 =9&!N6nfrV^'* Nd?ZWW%8/6ٿ@#VFX'|j%lgA~HUZgZ%BO,{%&9a8X ܘeY0 |L4ܑ;b"-fNXDۋ !Ly p Z­^ F;,0Z t DYg],[HSC~(azq>ePB#֘/_a[DŽ?m4 ;l;k6CQ [NJB34P/.6ѪEvt$ nU LYBn#Y["m0gW2P* aŇSB`3 cԳ!Аy-f FlmˌإѵByshd;Ny߾^mypj_r(Y ޻zςu*\ qrVqbR9q͜Aĉ "B`fE^tn=:eF[zË|Y?^3@i|7m~Nj''_=)ٱoMR}'`2899;\–'AQz22[6 jNpp쑜c˧89*__y];tS8nZY?FrV\'s.~AJ۵U;Rc3gs6k6Sr-%5ߩs_=fy^'TpuQtm;>rr1ܮmfI1+GR93Lw `:0o;rPѥb 3XX!`tJ Jb7~@Ʈ[|sxпm7_\Aj!d]RQNsY/3="o>rPe'Ar`f{'RJvmy0p_ 1'8xgn|},B ܼq1߉nGY'f᯻uw-]\*j|c>VT! (e  υ(59{")]}f ar9uU>cD!:A-Zl *oj[G&0p{o|GԄOw<6. U sWXB?9DF'Z2?HS7._^yn'0^ԃN,6: -yFEv飛2cIwhͳ?=(:Έp‘MaG]BfG/rk{k!V ~bmcߩSۦeM3j6SچGgHoMjq\-/8bpL5lc'^[cV$Lwq:tR)5Q{Gd[g6!:0uy}[ o2$tn]"> ( jӇҕZkN1?{^`@)}~Ǵ_--u:zsMXBVGLKюzŬ^<5TˣM`u[LON??$eSR:ie}5>?gjʦlvoԕ-fӰ:M\q̝.cpڶ WK\ڃcd9[g^8/1&)-o^F 8 jJQ#hQ|a*;ui)7MX[vZڠ@ h` * 6?eXJjsA֎*ھQg^:ֶެiʢF="z̦ŭ|Wsn恽xu37>.p*~Ԛ T~_k 3}6U)?=@otYboO-I;7[_0߾xP x pAγ]Mv7|(*rY2M5+lKwNC#ky.QlA35)kE.L5.MR閕@izlH[ ;۳Z/'zfvX9dw:2BE[bk)ްsF,w/'2m@:Nuv`@)܄%FՆۄK7~Un]kSWLR'oxw"G1nXTB1-JP2fs~MN,|&nnm֤m=+NYo&jB ɾ -T0hvUUݮOD ?y{}ү9d-gf_\JHY?>kRSBěfwqG%ґAN Z:Z? %'9*76%[@]xFRL|}>Ȼ&[˜\d0l1ˡUi#[f`˃cћgotpxф񣇇 ; ?_:8}בMso~ߟ_{^QF?ﶨCϞ2Q.j~ȸ1Ǐokߧu0W?p{PWۮ¯;m&̌7\zZEi9xgQǴ?֨CϞ:)1M sϬcO&u'KdNBK7~˾‡M 3~Gq~ps_3- <[U]RMӃƷ,x]@&}~z{V0/%>Α.rO?Q|̷w>d)~ԥ%p4W0Xr[IwyCIpz (UJ;s5@P @Z4WNC IDATqQi$gCjFRGJϲz9R(QWeR K7j@ +eхo6D QK~cH <FB0CS}_B?|Ey9Mݺ:j^Y--2>qZ~{O)W$"c;"ߊ~)`-&DA) Z ]dt@л*@.dk%=Z1]ܓX8Rku8dmExu}=ZFPJƐM#iS,N|iP B#Ra99}qFSU5C2k4-cNN'S)ys{dq'mOQ`)`*4ɃYf ;romJA-.|I0M 5싋? ]cG;wP6ݝ QW "  5+?Ws;gVD SN+ z%Wʺ?o!r*|w~+[r(FƎmDF.8 ;l5!B Ҭnrǂxn肋O/r(C;ut1bh/=ؘq>;Hq>صl#!Bdpk;jA DEM3`ą^币]̋t#scCX耍{7m @vft@ky]8d8KYg$e]p9bDľv/d`e."ddg\0b8-;ҁ} p7 |kVmeHJ}do "v0-;ƿ ՝goE)IBC27[q_vٲyq8sљ}m cORDPaY&gC!Bb`8ol%~6?#f}Cd%\Ξ\aÒ5{&-e]v PRTⲡiFci1n\BRkD5[ob2JJDU)Eiɫ#*J? PR^Vyf +)i&!!M'$!̈<r5Ƿ`V`#ٿkNzBP 5@`C_/l=ۃ7\Y"BHRJ2u[v M:OP6 l=xڱm7ާ1O{8y5G}^luLeAƖgR ǎ;x0СA4 f57y4Z[l RFQ @nlӽ}(q ~,q;NLl_ܹ?rPC,8w Ч9~h4eƩO"nWu-W.U_ިվe=!<-mA_^sԥ1!2揈1'_]qLcL\0_M:GX63)Șq^on+1x[ 5p0ոB5{ֵs\rAK'Ƅ͂)bȰOOԪ*e_L`MMuwsԫKwx'miJS%mH~f-F OSRMKS՗Z %N%qKm=},ڌ}_]呱Yvka_6xx&e*f{^‚wB,5>>Kf,b3Ͽ.aYV"^]gĠikN4emΏb $$s|j%. N;P/^l~*&WMU&顷xn6;g^h&&KuXޏ碽fn_<`P돮 JMFR؎vf7μc~o'12j lCg)+O;-)1v*BgsluїxFk7XwnZaP3(Ԓu$Hq0P&/{ -)\@ߥ Ʒe= 7L)A0B*S_E)r7A㗿릒)3X*|-bq'"뀖)D0Z$!3'{PPPPnA!6*B&G)t4WoJT|{4bWe+Yshv:JHtdtBffO]7hvӳz֦ԙ2խ{PHfU)3?3vB6!49TҨAݻ Fψu>Y2 ߄w]wt*;@'}mH98_֯U=O8ey[񟋍i@*3+rt})KMI?|7>{guz8` (j˰,'dS#a̭]>N7)(oHɒ[OmfG78zjߨA(hJI> mbF+=U(;y~6Ao]ȣ}=Jr&+}oRت7=8:R#(&Sކɞ4Ș?y"37j@smy:aH"e-TI ë_DoJGxo={{WN~-Wd]Nnsgw-h9$.s)c ~p/sz/UhIʔy_zJ|>o e8On1i?pՇv _EEAA ){/mgh1Ҵ^3]CL*dqgW89=Vkgt_-ޢwRz.g#1S.=uCE˅gP0 WzR(BbJ)7?ǏZ2Uo_`JXۓl8e{ n{lr=Y+ &Spt+On>)Cب+VO)m X4Pvpgx3ߝnx.qIG\. #;5J+b[}pgLhY`g-b Ġ",4T]vy͕ڑ]gy[I <8/ЮeV~I93G젙m*%问W6ѷjG}z) ú7toBxlW6/9Xº muS-`p\P})){vdy5`6rbD*ka~ kk)SEEݟJʒ5΂jMPm|6j_6~d\ht]UJ.jpkցpO38˫s_Dd4]w}9]WJRz7Y=3@pܦfv >耚FbΌn)i_,zq>}6zߟ7NWB9G!'w5y #v3L=7.MF:}a܃I\>4m֐~j07ުZK? oٸi,*{y\٨~!D?<NXSLҴC:fܶCi$]ߩ0/GotLQ}F]IӖڶm4j\ud!+gMdF:0T@|v#sn{nAȭ}JF]K{nt-3o TbQ!\5( yNԩ9 +~^P@dJO]RÁ<*[U*nW@ުlƴtnkMx҆aH"?U o2*rwހ״>T&j5Mռm`n!"w>6QA8vn765x[z WhsJ *M:sUq޹~+v6[5k~꡸+7':?(e`5$i~#97]#kp:vtvH2GK7?3o e'95?lQCq͓?Դa݆>}LT @#>`]Sӆl0yhQtm:~t@˺u1"A>twu;la rE)!@&uޟtH}K('*7~[$#^Y0xx§z-<ܷ * r?} S{JC5 WUs,A '$cО { Z\ oĐu@[G]㺆b4btD0hkåI+kXSz`_KKx?1܋ϼlPK e|yҚ띺XkV)?|Y wPtu:zog9("Ieݽuۈ.[56?M٤^6q3 ټ£v n@[r6>ˁ`zUy~uDLݎYve{mu+h`*17^ )>R8_`bYJf;5(!A hfl} -ae'N?:[R2|=SV[-fDÓ)99`t B I~VSwV*xn+2qUkat:S7BiTY泌]G1[ ƈbkL|er#b0n:!q Z& pmax?)Z3D~ԢŌM`Ȑhi5dqA#*RuͳsZ˓ .b!0Li[]cQidE D?zK1zn1A 'ڦ#-9yhpvƦW fц[隍-Յ- ?Ψk>h,GA<0L=Q%]E&MʥFuӁ4XAyx-cz GthzZnf(^s T?Pkz-&Y,󜣖W3AoY^˽`(iV7赧0;'e4iVT&X[7dS!aO٠/8sN3&4Ҳ>SⶬnrJvɢI7L$NJC9O?0xkWy`3W :=^o\*̉l] - f݁zmoGHfznk>itm ^HÄm\{aBz@:Z $ 0[u |m׮ [9@̆d@햞H;uV^+ۖu J~P@z/%»R@_V%ѝO ]k[#>> xz!,ۼaΥ {?FdmؿK c{6^CT^c $%J7-Mv}:8LxtL kl]ȍ{{b5mo%RdS?9,&ٴc >Yf$ZE@/}\[  @`X6'nݴe-[͉[iTtp\7#$_ 2@N@RKi&ww7-Qu/F4Nww:xt 8cQpnH<3ѦD! :w7wwE!ѺUu39vjx"o,-dWkt:!Pݖ܀ PлA@:^80zww7`tnnnn]쾃`x! *h9\FQ55Dahtz7, IDAT7=RH^GpIAh!^7gzNzw2ew)df!ky!Fz77wwQꩰFM "Rk77NX+gMTv-nnnzaӹɋ +t[ht:N+c,sss++28LfE| ?[FW(^[UyQ|Cvo*%HZm d:G }Gyږ^K}lJy{$kwڐRe`ە+>HSCVPU}H9k"x ȒSAe^6 nE?wnuu H޲mN[:vДX@MM5[}'-.3ǻ.=z`Ħrh+v>4Һuz=@nN msQ}y5TL_DI.dS! {E]2L1V`Irx[{M(d0aM8/-kFnk=: jD'|;d bWt{ǯ^u}ڎnsf-T B򌁈+yG,m%U#,P*r(%Pu]2d]VP8Ԇ]2jRe!#FCJvd]$}Ta 8,{'RIJЙ")(.肂 l!ܭb-=9L]Q%r$vTA>9J b+n(OjM#F+G-ĞA&y4\^n@DkvOr`~}$sۢϩo+BZ(?)HwO}YطQwQgTb" U*f M 8ptsdY \="F@ѳGy8Wi7gYAXǀmTUY/`"QiG5g?eC>0^;~S7Mvkz%%\0eU")s<" RZC'iWT\ Nkd)H [:#eCkvv RQJ8ԲVxVBN._ rF([yJIT6ѵ&^EŋK`èIrRC-Cȟ/jF#n5+;Ds$*;zV' #P{8 ,,T5+ɯZ˾=bao:-dһ5U"fMURFOGMޒPYR|\l\*H{Tvw$^*RzMlڱ}DL*wJ6h:85[{?<2z]NMUzmQJ0$6 \,/ysZ~(KŴU:C,?(pvd3(*NErE!HFɡ,yZʣrT6IKOIN(ulC6Sb^F(sVUDqViMʡ6WP(9.uWVsY ,Xg^ rCThL091!N+G.36-@GnLW\5_;k$킬n n.I?t&_sK&+3nR l?2J;QJy8D>i(/X%ȁݳWYHl˩M+۾(7C(W&YJ*|X@BQpSS~Z:;rI UJWS 볗7m(GprA ;ߋeSGҥ eZPP7'qqk![en,!}F0f5Db -b{툘7uֱ"˻3 B -Beosd&sЈC;n$"tߙ "q@ƞqòWwnm9kn=J҆Fiם| ~4-fw?"Ɯ|uŭ31qg^”5\d؆΋]ci̤Cxgݷn];n]ۖ"u/u~Șe0ڿt/lվe=!<-L%poKXZ;F b"MhBY!S^˃~yo nӿt<ѡ G6wq ]y+~34m+uȯI/,߱ӔH"H$wQUs`gAq\0JEp%rPS4}O*|O4A-DI Q*|J)(C00e4z{̽|w9W>{6RA1J?|")u o&R@Kn)C,^]+pnrA)v3VTcLjV#Drs!q`.2I)xY*ӈM,kE1ώЄv FRwS><48Ik^v~I8<2؀&.>ݻW7*|}ƽ5 6[vi? zNbB䠔C ٛG"zw;vJOO h-P16 ]p!cV&>29/y GExt g.[Fkm]X|qе/&p ` 3hgvܳ{ys5}Qw#=G?XyybPhˡY?9@qyx*)z(aioj@qَ=Ppؚ*pH,e!>f;  >P;8gl-nV۶ / ?-ih4ª)Խ+dޕ >C\wWzCa}mI  Y>AgNLMx;Ѷ&glrPG:%diW31q9BY&넆TQrz‡[g%e^7m_tArяd/2 jz\>u^7mU0Oy:PY. 27W&ux4uR @v84߾qx]q#c͖ӱpePY%֡Jl:s2uҲ#_?ŸzO9Uj Q=@S[Ռsזlz}}{Ps(Ýtou_=Րɯ 1 V@04)3UB}m&o"JVɊTDNT>B~3[<R*,rUa.d%RZ+L‘STT?|(JEn좔a"\ 8[͵B/\VބƫPA9.lTūNIʨ* П9M8َTb<D*pajJ"=s@SleYa*-Y( sD,@jDb+V6I@A,a'BFIaFKl`CL6Ğ)t%6ڢDשHhaA:qCJʒ=MY+4hpo:t?/=od`r$"6c$s O,BLV6:sGy>xuc-6aAZ4 .ܡ;n°,@`aH4xu` Ͽ=; $rѻ'_hw1" \>Tg64)c.1^rq /O8mh?gSm6lRtjY׷sv !UNM0ovm/m?PŲyro]ۓja9o|Wk?$m[iPCÀ`5hРOm  bXΥ4!+wC0{p?lA1{2}îGQjS{P\y((=ȁ slܮ˞䙝oxJ35$(!?#_ec)`#dCLՀGuuO\nIsPBڶ 4hgohT2`^Xm{ml҈7r(xۭVTCvv%[v4TN s>F=m9.E.!>[ k]_y\|zqB^xO}P,x'DG!@4+V.}j1d8L۸'7πZ>7ɿr/. ɏo}Uzb4{x2h465fťrY-;L^ )iРA ^h VXoK0or?{_R/Z̿=9d'mlHBjv_|qdD<߿vv{ѣo󰎴S4hРA?|C?+!@@:x(,Pﶿ4ŔcB)r ~ßo-F[ Ż]1s" ^^<)=`׷M˖>8]m )j3ڭ~hZ8iX_zk޺wO>O>E"Ŧ{/!W}a @#Aܣ*{i쿽8 5̑˳AzA 4/CנO TQC5*77/YSa˲j;ERJ  S = (E}(}Gh зT?gB:ʋ3^3f'+*/glV  j;=pzO5<†rQSz]4 4hРZAß;?;p?RЇ;zJ+@?w}ؠF{Є 2[m*m{>qM=WK _LzxbՖw`/F舓ٮ:2uPV4hРA3s/JAI)ouZ?>Wlzi䬑; ~Vh7.Y"Ac|rMAAqÝ h9pC캧Øj}A8eH[,xXybҨ)n=_]}LN=ŧ7.j}gt}>:Qjy3$m+rE2p "+OkjB=6?0PGH7RlB8Bc> -BG7~WMEy'6u7Eiݭ3 ڿzwe_r6Թ4hРohpVZuƻ=9o_u)c2<_5ܠsݸNM 2m#LuLRӱaS!m9=:4h R75Xx V'J' 7p7]Y~ғ#=]}v ѱnu\xۯ=݆>qZ?fhtȐՂa( N"X炞zzN~#(`" bG) {iTH,<:J)@GRJ)y l!}ga7J!pG!KeX!ȅ;',=Sj8;E u,?m;/V4שU%rJ>ԙdKe[$DgyQtJ4h'o3:DD'Sj|t.;$ܶ׎tDc"rRg37b:[)ZeܴliIy__#!+s4FbQSp&]@w_Wmd/šn:'ƅz/ݚ XX_[? Z=zm颳Ͼ9x&_wÎiM>=[IFRJkkiMMmmmmMMmMM^k5W=| D@oq (^oݸe@ Z{2+ώ1m%gϔwad$G;vwR BjtrLZ΍;VlӢN),A߸ݬ+XKJv5d#I - :*H% ԪE \ǠT$Q{Pc*8 ީh : t}Fr(ϧ|jZ:tqҺFZ}yWYy=ܠ78}{_]sZWs!oT'9_0';{ddq*]-_ss곤PULжNԿ CㆶpRf=zhpw/{yޅK7%eP;.)sG1꘵#.?ԊK#TkѺuV[8^(;e7j֤4٠R4jȝKP^\Kd]RPfZjժmb~lӾ][ Gp#//^M)eGew<5k戧TnUj-r>hX@u,6ڮ~VlAH}MUMI8jHňH7ĽT`ƒTRUEUTt"H*:8FuA?V>w%L ^4zqo,:@͹4vo3g!Q؋ UX \HK@%D| Om)=7)Z.d 5o?>3~}fBYB9؋w-]EH!Q(.1 @LCEz9n*R8j.!QgC7XNL=m3!.Q}(q̥F*r=EHτջg~#q &(BaBzF}|")?339`JZ~h59cQ~(q!Q='ް:ei5MH¶bܣd7N^zldKn$N^z(7UɥT r- :!hLh]8ɥ3:zounnol.J}n;_z|ȑ/W5G=cҔCR_ [PPkPzV`sFUS M!Q‡ZugReUJʫ*o|Ǒq7\bki-Pw(%>~Lp\P*ѽ0ăK8 arT'G|ڬb7e.iJLj)}p'uO7?d fB Eo%/8@(KJ+؊.@ 4؟J`K5d& ZuTR]yնIq7) r;:rMoXrc#r6;al0?cA0 t@#裞Y'|]?1Κ~'XW-y00Ϙ>8˨(k;sdLO^Ѷrk@\)"ؖ1d[`$}3y Ex81n;&%?9/'.q9܌ALNzxaLƍ7*i鉧3"fMP 5*L;x_>HNYx:`!')Iޤ6vg&$Eq-$ }q^o'R/)"kֹуGZ1}[`b ee\9wFixYf^sq^N\-sG,5k>b2 zɆSw]*~tM/;~XR+o'RD|Vva -S0JVf:0}Pwhd@J&Κ+2hi #"5Gvݼg⳶? FoVCy[<V,m}k]O a2 f`_[.O7ᑿxedOe)Ao3ukYx0Ysj`<#tk;t^A(t](^ eܸUcqhIcƂ3u@2g(psFw^ܒ nz$6=yA /?~sv0:̟f=d;&ɗ*N:t| p:ydMD[a=FLbBx)YzμWoGgm399m-HXlǗp1&y>y 't|wU6%-3}`o9pYs]5( ;O 듹yybgC`_pqū1c9Xh::gzҖ?Y&8C3#t1RScss~:~Bmu%/`TX B)u̒勱 էn3Ry1M4QPbNPJ)'嫭qQ (D1%B$t5 @)>b _-.frsPQVj5PAOP /,dm$=)G>K郂A@!T֓d1&ٙR8ّ*2unpO2 !]v|W!/K̩jP"͑l6@2lm 0wh7`(Iـ6憤GO GNsjTIz|'a> l,jk@EO3Z.e3jLtR F@XGnO@[-# SyjgyZ+]` 3f>|X0##dRĔM<})1t^Cwg 0tXyBJ_Eu-#(Hʱrn&O-A(u4ڍ<2OO}k,OǎA}^Z;U&bȽU?+.0/! Y,<Ԛd:nnuBbݼ4$N}AMM"PtC1 @˥u A殾<7 'Hܹ^ReC ݚv;Yt&`jpKKٴ^2FImtdY%ը)rb-M\dž!nmZn_zota3q驂J)7Y%AC<;<#ٌ~~6udF=yVT~Yq?*Jvް'ϊ]sN IDATljjtbdXa/{{qNv,G H@GC {@0\`s97&p~j]K;1xG?sEo03|2KA,u۷aqt0rA1$CdꢺD#4d?ɖr,?閵{OU<FQW@4Dzq*F$r| ^,+0TP=Jr&H=Ŋd\#O8HےFRV }JKG=s>4hF\zؤͯf[tF]ni%~nMXcsuz/ -;A޳_zds2ȶxhi9QLv?.~J&srs{v<=)OL\Ψq{vWtswgbl@F+a{QK 瓃b}.944}&Z)I1c;gk(}|Fyb.FzvW\,)9壿ar|٦.M =Ɍ2FD4c}4Ɂ!7E^0ux[8Ec%ߠO1$m-K{zޡѯwԍH'U.\t惍oڿoFc㾾f&2\S _}~ˏB%[5tC3n:TŘU5{Ջs~SŲ(g=ϬN`$Hk}z{<?qBwaaN qvBįu09<|V%Wc@ RT`cLWy8as;y]$-&C*v$j&ZV9)4$uH n% ϟ%d:4c.H:YЅ:My4e@@ic%eXc]H>YႼ!r 9A O[:wN]bwԡb6:j<→ JmzV~^L!AۚRj$7AfWNlZ˛%5-}cr;;/RZhVF:J4ձ~VYjLޞ<4n)i"0ѫE6x99;owj|rI畎j[e.S=$;PO'weGud iZ/J8h؎y<2KOmWg<,RSAȊ}%!N8MQzH%wɚl*sZ\̇EĘ0J4C&9Ty1)eJ(;_̪-&C 86Q2U b7L(H:BDg P)MsHV 2z"Y.^'.xzv$c7؊3ioj7VPdrn2q/ %+"h7aRiLnWU&2WtF?H`bE/}dil%Ԋk_*NBzdȝETTO!CON;UY^-A֗clTA9o]5H(%< dd#M7(%b\T !Mf\Wq`^j %A aEPU,65&^uAJ71"kq֓us='>ٝSzw/k0͔/;m#N__G~ЙF ޙ`Ϻ +|V[ +T&?H=BH'g=iץ*zo%||l߇5jýѫ խ|=wP#M?嫟?4(l'{_r3ŤE.xcFJozizo_ּk=QaHd9C+)Lw&냟;xܡXOg jwsxq9'A!Fh:S$_9EY1Kwi9*k.R[YmnA]CV8Q…! 鰆p 숊t%YE\!P0p8c8^{7D4@F L$U)%4Bۭw½s;ׯ_yfrrX5vC =(d2|F{j {,s0ۿeR?ID|GtV 3353ɉp˕8vxzUDmrIH"2ل>@;o띎O)k NE4d+Jm 7%7Ⱦ!;rcB>.iyi Yo[!*(ھK6IK^`2ڐ[i=Tq,?E^D l Bt˅oȓFSzjņ1£4=ּpZ@zI<:+[pno/_U W|̓ޥ‘-P }(p\<ZAQo8}{GC)RK/x}B}G.H`ˁ\^W9R5Gb~<,0태_=:Ej"Zkn܂̃ ˧b[qђ̤UT< V$s3dBPOupB#M9y $ EHAY^Nb[a92C !(T23 נAz8~-Ǝ}224ztx08_ %Ӂs~jhvgdE~Nc(kp'ԀڪC/7 ŕ9]A/` RCㆶvrZgqp =7GD2BʇZRv'R'j)'1:2ZPO $FiUmSR \8 '/Jx*(@KnX6!1LqV@|VѰZJAȘSz{QJԷ%lZy%eY5q @ϥ 5nx7GQ6m &bL{HM"H7eSr]/3 0G@RtN]+)KY׌": VJ^Cj]kH?* N:RIpdp>Q 4(h[l!Q]y'&x׆BIHPn5Լ̬cmLH©*3v$Q̍ݐy(BzN>pAju48x@ѹo3_~>}c-'~X:t>!s-h ޲gr(BbsZ /-Yw=ޡt_(8qxx+} K^GH!Qs,7'[pZ?L^a撨g_t`  䞿 !BZÉV]ص9v7qaWJkX:ymx]Χ5;("ǹ%uFdJK%U PeZlBxTBTT&G;ZX|`׆fEH ?V+J{񮥫rs?NSw/y-˕_^)`<~ 쪶@<+6Ĉq07<R[wЇ䩴N&UGB|iz|zOhxPJ'R1LQPzAk2uiЮug7b#\2Y™*ipԲ4何c^{oQ/ށ2M<( [zm,jEm-) gޅA,IGW3!!!QdgPi: Ie S+??)L0QL|LDK!Ś/iuKM 4||Gm>NM+UXR6n[qrqOÀй3uo&ѺzRM;i鉧3"fMPkW_Y6"eSu@ʑr?J5/bkH?ڏ] (?cs ^Q [#L@fRڠU[G%EߕWm+uh[q+~811]홙i/W~sKRvˎ^٫G\yrb"W)cu\>/fC1``^(|ذF5N`ƅvb[I׸ֿU)c7[aI R.0G1VMJjםBF5]_I{}'QeWVN3}V?rᝃ9s2'E/{Ǧ?:ujp)U;7M<蓕}Peg2>q \70]kk'YO ,Ykcsˮ~_ z3e_Ef&0[̉8=E:bz`o_]u–w .KDW:FIW?@=(::$"""b-!!E]՜޸q_ݼyih}kNް X2-!\oJ 4O)w Hbǂ;4j^;P J TW4_*M S+b&"PΖN6#b!] J BKB d2|X4hA6(d\X+ȼ/uv|ѦIiРAC ٳ2GÃ:^0%H9Xf!Yrr/gv0?42͙`naСåÒW-8|!ks+kwsNus(lTh/< w4 F{0N0b$c0[ nGJwh[cg9'c;K=8 Iy%(. -_Ў7br^Һ{AWvfn>owW-Эy#Y4y̱+sj.{tJH(Ýa/|!>el9FEeL~RLsݖ|̌.0c8>'ߪGP~dwn_- G`x#x }N<~:ROӿA\pxT_FG+&Ksuԃgn߭]o3>-SF=tl{5h4>\,߸q͜Ifo.ݺUiXsԓҌU嗋-Y|` AQ3GR:)h{]#{vQE叙Y >ТY-Zn^MP 2\J\$YQ@u>Էm?c 韹X >ǟ|>*?@dIuߏoˀ텲RXl6’|B_ C[û(%E3(Z@<ŒREI睡;p}ؤ#pB4~4_֏5ݟ|"~oL8W38Y/dJ~^^S`.MB@uPvY8zjgt;PStDiJîQ'_ζ鱏w\r ذ&"=9)Q $~<pʕ%OCM&qЀp2>|ː1/}|ɍL@)>3=z$g K 6e(6}& "sӽTa~L8|N:Pb11/?F/Ȣ*6KNBF[ 빷+*1Z6`*hz*S 0RˤMQ +`{ uYIhQD(wҠA {U ȸ8Tc~(;Z3>xvXO56ZFL\|oBl:Wq Zȗ7h3i JFۡE,c*˼H}h˷Ǿ41ã3Ns#s17PHSt()q9s?ܵn ss1۪`78wz.j3: Wy4_W{^7tExQU?QŴ‡[g%e^7m_(b Y9^oa3;ez.8K L׏jӌn =R6?߲ gXrLHH#jUު [{s ɝ8^!UVrUUS=MUL`@1OmHt# VVWSl"B۬pCc{Օ|SUUSY'鹏*s|#w~&9bjBhg-62'V-9yboEu?,v]oj&?Q 6N_@9&3Ww*_LQ* **K֮lei3D\8AT10z: 1K2 =yl'95aq$ů=ÝfTU nl#gg9R \_q%XwM` wJ!6 ps!(p3V,o ]2Z&uؠB;Dہn|$VOgn9`%MfZaaSW;M^w+1G0ri*@؜LJ,eԋ۸CF <<(hܟ\uo\|ͭp/l&gEm3f?+М3?豻;|!ՄxؔӦ,Y5c+D4ZLWS6Lpwf#} |7<:bc[t`B^u4C /L"!dRQR,NtAj)"-}ӗY:PNxcIk3Uz$`|Fl~$N 6N:Q Lc Ig]}S@w68vumX| EVB-3wIJԳ8D4ޙf͜eɖ"ÚR7;"Ha̚eNbgCMRZ&$殶Y9D`0[gnJͮ5j)%bK9DlS/*͙ rasꚅ[߾-^cwA׆N`ndSf,ې9mrEzm^$145%r//PWoώ%w+2UӫkۤnW{|.~{5u {g&\]0[[(Y&TȠWkrmmkuR=e:ऀuc* j=/8*sFu0^o0:Q:iޑ DvV`}Ĝ氖h:zֈ;iZZ{0vv5INi3b n^{_\ڶUNm: ӞdQڧoPVbYfQYN;3b[`lAt J;N@/DZR)qDq˲ ˲,k3y%:Oص; @7oWvKd.yz۝bN։D{vsO CuV0(|۵Dk.Zy.Xe^2p y# IF2ѐa qx:sNm `gHY^NC:d㳘sh}mym;}D4`;B';fvj;M&c%^QȎy1x1Vpxo9{CNgx[Dx\u"jG;;sJۓӁ2CmGv u3pF;/\SDE>qE2uǦğ7Cܯqq\R.u^^r0&F A˛B0(mi9֦ zA7Fh0y7F鷍uG;2 q,DZ,J$DI$]`/z.A=cyˏa.RD_~F#o0F#C/:' : 0OقeguXx+J$cD[ډ.I oDbnsy,˲<<8yFo7.oD#^!_@%6?eF#%,,`44a@e42OfRğݕV+{ "O%^8. }* Ee;)gn8ot;t&}ƌ̥M'UU[˻!o?@Ӊ+O=ƠX\z€{z]C!rX$kKF,DQ}f0, XN~_9s3 QwcOC<0\9Yz@Oa;%GW$P{'}ztO"Z ګznD/'X=.aF[D v~'5sy8| B9U<~]Kv57o: 3WNۥ\9]Z#}%cz^[^;+ Cr'>?8n+vpKS/VYuXoX_׉n7*?T"*meü.s* <:';VtMmhJw㯀|yy_3͞k7:5K.gt W 'YF-2iTcYT7F9O솿tݙK=4Y$xdƿOvO%ԉxOSwTMNUב޴2I HE} zK:}3^$:qZ0݋FlZ1 ˉOvV_C^7,ݝ]=Xil+e3NX{VtylWsIIN%Wxy{*I0 ^R?RCPC E4:4aC?+k0h+X M>Jb&&6]R7}{NeEhu}qinNo^ZHPIOδJ}{f'J8\1Wۮ &mܧRK$\il¡-rwС Nͯ;+pWO^pv\+H{;. UE0 `Zp .`mg~7elbX淶9ƫ>9q's^9z^{CI$C3,L|ZkEm ~1IwAb^ܞ}g, x{ _X0icƯ7@mJE7m͏Hc7n];ya@„5J ŇL^n 'ZJ Y09F*jmOpfߞ}LͻE,\*\2'jVN*5^ #夭fohѕg`RNIQ5_+dr5G㋊J+vAⲲ }R%&=eSQ3KJu_$MwI[E- Xe&v eo}ejx%$ӒΜݻRN*a7oX1FUak8*Ƌgo̿`I%,ȥT*' o75.=q.N}ml5[?χl>gZY^m%9)l7'6hZj,@(9_PoeF)'m90{V\Zw,fߧrĒ +qv?Jq!gy6qCUC[~:y\< C&)Q\|Yw e蓹uU@sgRr` vֈ[4J*S2>8şU~/ĄʴdWht-M9@| s/-Ml#h 4Ymu sr *37?p*+v^(.M_U8,lèUՕo?yh)NgtEKvoy6i^Ѫ♳Q; /Rٚsoלk)]]jZawA~9+9%-UL~7T3_v$g.;^ O1JјU;׍䒼)kL/b|8_/M4n57[lI鞯V>ta1͚§[^ɋ~yIܯǎdߘJ݅d^x8^kUis6_zIǿZHJN?6q/}Iʡ̷6t۬i_DԹ~uut /]<^G GUq(~{'ؿ_m,ؽ:ѣwx_G+I# ]4|oFM?^MYQv`} ׯxkOG]c zڤRuzqݫjv*'69wL7Y$NX[}88u״^؜~%mV+1B{yaZl/`ӭi<ռ7 WX[D|s}_kf}L+NdT$Y"ї9CA"$}6a|ǒ5ͪ^}+Cnic?:`w. =MpU!@ȜI]u뿼N|SoV/1hީ|^ײkgc.JzqڑW6);~$xVW?Ry;;@6wRΝYņ~T*1ROR ^xlCa҆$"6 FFI\VZr~JN/Yp>Sn=}^tN3Ǵ$md`ת5*݁s' OA7ћGnò?wiJ0BGLmoq}.Oɺ1)g5n,bV*pj:,C#=0Čc\K1b ZBJX3>bG<co8b+d`=-W [,MՕ[:ʱoW~%>zH nI8zes5?=[N9ʫ9AX,'u|!3yb+]\9psR]XsS8Jx;v?nmTnns}de1n@Y#cGkk:J L~h<X"qέ>èy5>RX˲ X˺S+XNc_Y7DuÈTR1V}8VHòsMgUXˮ8RG ȇl*>=~@᪫}+u*u*_POmzױVNωvp5O=;?g.E盏X`Irqc832paY[4hpFT*`>?{}82fz8UbZ<> \%P]0ai&"MQѹ:X) +V @"R#6> 8z`[:ۼLG@ ކ_/M4eD YG|L1{Fܭp1aNř$T9PF pIxM&Ngos` IDATz=bc:摳~w:dV{ .L2K "lޑ #h[ք=.x^#V^s!%wiKk0V5e־s՗ۢf?7V|EHZV]B{2@0`;Xo@r,eYp8x,ymk'_ {x>hl 1-JN9ޮ}J7ڻFܷa{YW9k.s,3bDZ@oX`hd97S,䌥O2rpacYIF  z:#gzۇdu=z6e"m; B#{sńGd~Ԉ s\9bJ۶^yl%gcMY>~8#ĵӈordO {Pʷ'(_'VUQ9՘*+)W92VP %np`d98OHyҚ%w\K!JW1RwdsʱiSzY}#8w 3b0pF}cٳe>3.y犁pHO4rYjǂeMkIytu; yv}v>v⎣";[a>w'%$978]m6dnV||z wt @k:iZϭAygt~։{P}AaQZNSPۨ ޶*.+Xˆvu7-Ӊؕha+z^<2tՔ7:B߉2; iO?SSY]9U{kF*Wm^ǶwW\9EN [)7tt?[\9VJ :#$nWZ{>8\g:/+3ަj?`?|BEaϕ!/FKe8Z‹%SdTx.X)IF/LJ[A~ԹQ; OMV ڋCy\Ys 1Xi/`A;N?<9ǦG><9r^h?_Uura꾈2`y?PSuܢ~7`eZDX|۹b0ZW9=űp,~̳>l/`Xkch*SE!`uNN38eA@ooS95$˂p h>s?·t'z':LHQ_(o̩٬y\6`Y#q,0k8΀3l#y0]ҘjvHt=x%1੉F΂{s\,q`jquAw]%Z-[_CǦ-g=ѩrr,YbVˊY{󗧖QǃW85 =+r?^30>(Za'. q4Zuc~w/)əPROUWkoR1%?^кWPӷW\TC9Qpv3UP8}{7:"7f`Ɋ/m5OĀ:Wm,Rj,RW4wo!p萐 ^M!>~^\U M]8(P!!cGk'7 +5kK)l<2,GMu{+ĵ*L*wgjOߖ~vfc- U<|4n Pe`6D}$T_UެP\ƮY ߐ(K8^qQ.;l̋֨|K{ v`,+L(yo3(\ȶKk+/čX!æ&-GMsc'&ij, 0Fff-sZ~!!0'>|sӓic9a1Oյ28Ycg`@0؋EK }_K0@pZtn `8iUD 4:>3'ooձ&=px'3iA|O8Xp^,=›{+5~[7PB0m5^5,Cxsf]޷ p)fFnr^= ssX:`J{1T1`pi ϲ orS߿4i֐z%n٧_ X՞J~#Fr|}j iyu &"ozs0:8}M r8VWdնsL܃ tVneXVt'/Oϯ[9N9~P[JR}0tfi&[لѱ-5߬ۚ]H3 Xʿs,pkZ Z,:7^fxfƐ^6Ŭ~7[[&r630,B-k%r_:l';=ww| %ſ4V4kܹAS^^3RwwAa3Qi%7-b1s!!}A\m>b(~Oi{*7B*H@IH`R.>d 8BvܤRh' 6=dɪA[-We24[|0^N_2e|@9!>&x-%z'#kt % hyV];nrV9c?e us lZG`r$pƬaYN%_#w A)Hֵ={&sqA 6o,N  Gr~;QyPxڢ fNY~ߒA@9YS7,90Ptq+^^|n\YVVMy3G<-tkJg_{%߷z m~ òG [K^c1΄޾kְǖ?@fotu^L eqN2n0+;ǿ{cA+n/wi^IT0jYF:&,]r On^m?u c\-ʳQ@^ƫFn5$}ͦ2Ǟ޷j; eOЌ_~m$[Kf=ŒLX5[oWz@z,X9<~&m^9B0X%U>L"/|Qso}?Ycs['=6u2kTem<'k Ѝu!FɐC' Vŧ^\3~8n>}|cYOw i0K%+ceͰ"Y7nQdq?tڢ2bcp*gzT|{:(l~X#tMIvQ}z[]&j@VK^aqg, iudnAC3Xv}w{lYgA}<ٸandA)Sr3t|m)ms |VsMpR8j mRe3 CC]3d%Ս{5udޞ j^ΝStѡV |voA;ڊw^-^S`L uM-tnkmhT9/_w ju\!92ydi[[D33Wݡm _љ_7hPxy:SժsC]^"r0&R3аp^I/9lKJDC]CS <=!۹T;Ů`^C{iߧ?*3P45ۧsNkN&IV&HsӮixH9Y0M.^V߸A/8DxSȀ!ivÇ꺆xYՍ:[/nEԾ$@1_^|'D7^|Q ?_QNuܚi;_ӤJoJ ThYlˡꗞrK;j]Wn=w'^VmZah/΋h;TlYvg@V?dr_LjDonCkD"5k(Q%@ÑwD|Kv/[W.W/ڟ̶ٝ/<8v(d89n57xcѭW \|ʌ8/_2gk̝&Bkq'Saۗ,넶r}ݮ ^w߻ݗDn.qRE_gAף?~ie3+aډW*wuL.|կo5KD =#mW+, #0@~~_43\=]UfGWˏM C[Z%zMڝN[3}NgKܻP1>kykM5יG}=~;fړx}ckѧ7ꧠ%Zke:Y*z[Y&YReORR2*5|Os b9{ۜS?~}/'ƥy]ƭW t'G~ngʿ=ͺ:9a=lȇ}xΨ_n h=3uoB&'o9RUO&9d?=I:fbo>A];Ağr0n |o3OQ˾PRiby텡J?hQ"q^+1mUp . g"zj ~iܱw_=9`80f鰭\= iuʉ}\ cƿc@AAD7 NK۸B4:4aC?+k0+ '*7F7k6&2L83zi)4Ha&<6Un?.-,Yp"&0"3ӷ̒c&a747&L|$AAH~/PX_{Ko1Nu!KU>Ay^mߑKS&b>ќ>~y9s5mS,9UV^%+Ree‰`ȏGزiS)9PR"l'MӞ5;mt _PqzkOAA碫 e*8/vWav{KϘ8?҉}B(^T]<7~&+\I\R_@,ՠ_BqYP G{9^OX0%2>o m{ CuJB>uT9>_M1t7 y5ZޟyP-7;KqQpruk*-UWVȠUz/gIUm ś#  ?7]^;)㗮bBw#I?LQΝ(=CAAA]6{<]_AAA]AAA8   z 7  )         z 7  )         z 7  )   調Q0v3e+F%W^^   {tBqoLW>_g_N@;뺨AAAtu|C'88p~!;r)TNjc&|Tg5ZPרc&zWڪџfg2L8$h WW"9N{Lp#:AAЭ7.&\ezfS%/dn dLTLQU-AusIV%Ϝ=oaɵ\{ҕ)i9-3ǼVN  !:]Ze]V'힯ƕ- u-ÀjsР]yބ)KϪ2uQ1rRZ-T/1  FzT:@9G8AK`O)7)y @䏮{1d{`]cyIAAAt#==\'a滻l}&&^vZ]Z%ϩ֬zP#iU_~^=Vn/AAAto_k}ƽWmNDc#7{e+En7aJFE-   bpCnC* ˆޞA5P=wo6BRH7`F^ uTɦ׶讇*,;kݻCx 3W!%^Ӥڱ|Y|}͡E9-S]=H:,HO$M`yfWt]vAΧ|`E&"$۾k-ۓ@ *D2 ÄF_v69;WVNI[eҐg@Bht9)a,RmlȎͶy X3.2.59:aQUimͦpAJvykR#CO')ɄڼFOgfTxrjBttj4;a0̨|5P|%sDW%L`25u aMm̤ [5gWU;"aBc WѰ79aQq |dɏ$ O=Pe5lmzoPyF먧";<:[mtVuᩇO  O[%kg9qVx5d[EuO,!=5.agXDm Ȟ\,NY@{ 9::!Y$2a] E%:y] 2A罩&qv[b}UrdtpIh[2wgi]F&F e Ц8SP*W+ sS,L[I,VN)*4)oP/"x(ٶI)QXAuB 0LCѸ/"5q|h01լFƆh( TT|q#)QB]ɝRuzsKXyع|Ö/כּ&';ʜѣ6-F_ȭLrkTa@|a=WF*U#jbߡ*/\(.|IJ2ה)%3JRH*j Q)E`2y)-XT *-(>@|YXX*- @DRNQ^FRi!2&OIZd4<7eyNS$HSFZhReH-5;KS5U٧I2%^W@XRIeenJ&KB$f{6 "$)@LN\ (r sӢ|S 4Uee^F R$:QxDU:ӳ$r;fG|N 4< HS5JBI i"UTdnGWJ)eKѢ@QX"(Nme%)Q@DNIuxrb0QT^񅼮< @¤H)Uf*,I24xuΪI7ا$ UnM&+H+̉%*,̈ a2*WTS1Rf"q% \Ue^Z4Uu6ǭՐ3",(`ya}SQ* :R`jNj`¤0@E)!20CHżx 益$<ZIUZ¬n8w.4['DWM(ӅSTp5M ?.7vZ% Zv ͙ [|SFI%, 党J tq4;T:o(Jx^WS "*$%²LaIyr[JʤAJ!S/8%)$6R4E56$)C( )i6%iaP;K#S D#Sc!b'p!<ԳPEQۏH]"S "ŊWX[ف~=vύLeH5;r/ /OT koin@JQEm9`Ql{ lg Sc -oX ~rMW] K,&Nw< mDA 7*EnB_-rmp_ܹA.g~~lS+ܺ>oVK􆪪ms2pwvԵWؼπg'` lCSg°Y'xby2]^SN/Gr{ؠ_c-__pZy^x6:e9'syΚ}G6.EL`_/v7"45_XqQxAO긣Y6nb7v)]y뚕/g_/;2w @aG;_67Fw&jomdneеqk9kOd/v~Ҙtјh`Qmڢɒ\PPKPHTʋ:l07qVv} =r֖{q@/?y2HF[M?n)B~a֋M+ -mb;ϧP%\zh`"B֛i>6]'=+mzx.X :䙑s;&`9ћ?}mV k7Өؕ\A[mުP4d<].jgUJ 7Q-[/ 0`,ܟj5в7iN/zo{o w^]H#)\z[ݦ}{5Ѵap+x*m/~CšڼU_D>wO,WwDn\:PRq赨@-[/ 0`>ts|'Z 0` 0 b0` 0`a  0`  0` |À 0`kca 0`10` 0`Zo0` 0```7 0` 0V0 0` X+  0`  0` |À 4B>/F0`?4Ԟo䧻*rWдe ?MQޙMR`V/Eu?7:sҲ'(k9o58в7 ɩUQiQ!;=9-ƢFmEu 1 sScz mzz *ǫamSnE3nS*XJ@~ڴltLܺ ܺhhn]znLVW{WKOF:OWgb2yi,%IX\5433U+ㄙӔjh~DQJ9J wQ+tŐTK#\i,ۆ4*SȼTeq3eޜ1L]0tn{=2_ #&wdz^5?79e2$Ƽ&=04yYF,nlb- ņuǝm/L_7/DqkoȜ7~LVX @CvmqQFal~q JPoUx5(HвYJмLWH%,I%G(<9Geո6X^S wCz+@\q|ec)IzXnpE_Ih@(t eiy x^!_bHK4m<}Y,y| xsylbý/ط/.^{_~ĉ'/,xo:rտWnxdt0?v#Wu~㻯̎<{Ϟc_ߪ(k$LJzP~aatGwh22/ ??5 WGuL/h 1wvlmaazw$C:uvSEz k[thjœ508 S{Mh]] ?x慅!$pO+#2;-N|=:俻9sewBydw;^9D S};zptw-UXu+vMr5lw%ّ^P)1)g?hp 2#{յkyXv-j2Ë ~\R[Uw${C.SϰT{lN&Mmdf}o[Ԍ'z:d)%zd We q;;6CQTG70?|xDQ18лW%LuzI?.xܓMD@~k`tj%6T+?630(0A:ʱPjV$FN :ό9Sb{:KnVÑ-J\G"q\֟"4AGߴQV[TX353ZRkOɹqoEQ]\Ʌ ȵQ;2`ACVuNL =&.XY%>M>Ty*oo.$gNSu$ VBvj;ME2>Np?'qΖzBh4D"6q'JF GZXmq<3 Xv_(pŅ]I$p^wxd2L/8ш .^X x<0pZb esa;`I6`dpe$o,x*t0V01^GIRqFb\be#N3[R}K&C. c.A KZ%JF!ƭ{8qZbW.D%K*&0#K%#v(˔κbUʏA GG$uYhЮ`4 a ?)I??J\Ia@{ԭi .h'G &3ɀ+=V EB~g\4QUӱҢ$I&U$`Ӥy IDATB9^FKmFe,,`,gLxY6_Q'оHLN1i@4rYxjݫY!ƀaEYx)%I 7ouu&Q1-G4@;"![UʝjFiY'NFp5UrQ6pg0a@\OؗGtp y)z?M*d?p(I/|&6󘹿?tI|VA&;]ӈb F|4}.@3̦NORBNg#zD$Im[R#D_{eiAB-r2t* x2N dIT=~v?pa\V c T1ZN/ql8KKg]E/ρK眰8`-N+`a+)VLc8BUR&PO+pTl˻c=Mkv傸i@]mjoPP޴ki?d_Xؙ/g~~lS+ܺ>oVJ{r~j^S,v^vԖ~׽NzK3lyhJTc۟1J5-I㈂rbOeMVLBˎ.@ɒqGso.Yvкg$P:|yNy;½"Vڲ^̑׵RCX:7e}"p6DT/2)Ju\|QSSo&)''a]6r$KlF 8(j~@@Ⓒ`QD,ߦbC[iK']F$-԰H*lms2pwvM:_ Poz P&V(j˩U'qjߤg^X+CocGi2!g:yo|u?Ʀ-MŞ rdgӒZ:`vln{;0ROYBQdVɈ+jK1]Q˝y!P5!o*=(U6 ]H=-Ь>)YkӘ ~%MW]]]\Uۑ. bwluF 7E;Xzb-;`_ɽ {nPz|P%S#gd9Q~r.L(Cͥn冽KB:`?r`CS[5M7Xo\~쾺B7`c zJ4ݫR溽FDx!iOXFTؽEܺkie17G_U-r#{Oi}^&swOPO&ڜRnX7~3@L~AyfnsC}oZJ4w&ה/@|N\|Ŝ7͢MK]\f[T<#}<+p<9eKvzd;8P Ib.?+DǥFLaq1 ʼn" B&E>OCKMW@\^x4t*⺷*bx5X߆ yYZu$[rjxv+>:DNwi!qoU67Qx(5c,½ԄcS'sWjYqg$totaéMd+ [O6߃੓qV/۷mikZu~#" vM6͎;6+翹{m֍{n=ɊŸ;e~0T׻w||sy9O{豩\>;zDPvpLq X3ޛi>6]'=.wڸ/·s޾:K۶L#}l:C%Nr:[ ޹itN~ s`?w_ M?l8;r;rrNJ7EW6γ؀<6jZ"# G=mkÅo;Kd.p ^M+ -mb;ϧT=PjhUeumhNzĉsf7Oi x(-Rжf :|Y;"q{wfsq3{O}VlwmXpl1o ֏nH`nGvNjsUG֭{1>/dgoBY#VD?ǁ܈oml4I?V5 鉡?tMK!<~;m@3`O (UT#&g27?RX#sJ"#;x /||HL<|4 |ko,$ŀ+c}:EK|t0` oGy 陙\n汯O[|lf! wߝW(@zq[.#㷕،$IfyxUZR@`-Kys2ҬL v +Ui: |6:K3t\n%Jƃ ӬL2$#5;7 zx0_,#I{AG+<I͒+_r+FTOC#!vx :e$E1 \L2"OZbN+&ƭ+ `,V+FMb=f!%Jeeb$K1Θx^rW4Y.q^e ,*%Q*uճA$)z $-YXځeo:%a +"J^B 1sqi`*dO.KVJi0ʛ .WUy} xJ2uB~]X K%:!".syr)S zFr^bR/;niἇĔK54;2}]nJ!R CWѕk}j;_#HR5IIu5ed~?+He6KTHŐ3`O0 $4bjS$k2}\ZF^O㥂Et*RN%*e5ʋ*$Ttd,+IKzq)|t<$I:<@ß%W ]$ܳp!`%`\\uO'+SKRV KR<5SbKsA$IxfMB8yUiҩT*MH|RT_/pt:]\O.ř KOQJ%@` .|tn1~tr}YgG4M& .>R`fר/OXx5j*Uٜ%ULU.{t]_MȻHYO_Zym7V!05I[S$I`l>쩹g*B`nA|4-y;4ӫ|pk'1ŀĜvc`Y.Dlz??=~sp ^RI$e=~ڢՇzZA}?J1Wwkԣ#e ֔/#N쯿dz V0 󣏎vס0zoWQNkGmn|7jd`ɘAoyn]ýkLl㯺>MXxJsB/rOڜՇzZ7 0`/|S[ 0`3ӊZS/Wk R}Eu -TbA f=`50Uwv5Zc!;;95[SrJ^&&r06u NUՕj 7EW-X%LeZrhEy+Z,k tPR;VGa~j]Z#oweYW 2 ,({(qmbF~XQc٭n+UtaP{D,1=Ԛo,OdeY-`QkEUxWw>@Kj~ܢ5嵥ax :t}+d&x~Xuc%vDe%9|'ԭ; H>8蠰E7kkEp+ v"J)CS{_>q2 Ƞ+;K* }JEs#|Tm`Y^BWe.;[qY;]j76ٳ}ߞ {s\(Mπ EQ@w{AQ;4EO#==ށn(S\@bwxF 1;H ۏ8AͩoODz]}SA ?y?.ձ01u 5f=^z֘K O'g y0?,FK.0:Xbh2_3.X< \h?@~lg`l!x̮&s.:==ٞ`wMQ54>9ԧT!1gV8fzLcs(c.\Z{EQlaaz@n545,xܓMD@~k`t6??Uxdz,4Hwip|.13(=:>2AQ5r<<1 M-3uʑ'L]Õuώt {'uטּl`*jd7.V:dc(:G oTc1il o r=#y,D{*zh}j4[=u *KjuǢJjTBQm>lkvDbQɀ+=Vc޿P$U~0+lē1'.` 'Cf~8b揤10꒙_,dsӰ?:1bM#>r]:`,%yuр9ǣ3`mvbl=C𹔲0;cIg`Kfc eU5϶iGDDaɰ&g},P,<ў2t<_9D|6H$`hWLvB(-J Xjox6_81Ri6?-z&rY\’$maI-O%cmNɒr\J| QaD99X*tH|I[wѰpe$A|H$ c%.L##v!\}ж0wEtYJu((M~Eр ٠#ijgO.|d%IE av/-@VDB(K\Dzg$qʝԒz,,`,W-K[:̀|6ʱ& L _$&3>uр$I"nb0A$bၬhшӌ!ȹ IDATT3uuEr&j_ZW21U] :fs  < ATcֈ5 ؉ҕlMjCu{Vg! f h/quؒ%ڽykKK0ߠ#$I92{b3hWF/ɝVvW%0Q^D!v2U}JJ_MXGDaioN!."KdžC$|&τYKMfl*CKZ `J!7'' l(I86|IêP3u u,3|.]J&קj@lL響W屍5bx;UBBoRC=;HV,H}A{GZ(dno?UVT Y'ɿg;ĆuxmдFC+[Lڮ?x-<,V! zjn lX+J ۂg$V}XQ9e'S(+[ǶPTvbؖuw[fCSUm,r$/==?cuہrMTs& ~r MW] |j):MyEe`r@w^c|MvpܨݺZyߟcSN ·bτa9svoj?Pbz~sc,ӹGB5;rhh @>(Ob_w:iZ'#@b^>pM^nƀjк# ,YrG3 SZvtJʒ^nZ tjr3ef@2%c/|}V olӂz-E@(Ok;3e>,q{PoM} 20vY>v͋hfzQyE@ZaEKq_a|>/ J*l8؞[x]uf^)|) ظIOt;2a̡O~>zG㯾5(tzǮ{"N|xZݝklY'QN?t<}#c,;n|ߦkf"-sqzs7#7y.=n?֭{1>/dg6]'=.`mw-;m6ݏnn?[d+;ߌ&@n􌕅io pW `Tx0m~ [TpMs[45KBu=Udy_~dzmmc;~|sm!m>aPhk=8}ɗdX 2D' WuKXۮ/e),L?pdJ"`G gzGB.}GNajjZAfN>\ԨI%ЦwMV`I[m[o?T.=u"X} Z׸8~α V+ѹ\~n} vԠnano`llbѽ nWBr&oAU5}z+Mu(K{o#alSCCk5vNޤZq yXw1F*Z0xqj _d 7S/ꔰF~5Wp^ߪ ijҸv]KΦt!/NK'royQ7Se٭PnK6BMqmv~+Rb:RNX3Y>c2p䥘Vtac74mas pJ zsIJRjozl$IiW c[hΌć=))L+Y` Q)p^̠%C AGaVOTTQN~5l(oǃŴ]Y%3~[D&f*LD̀Ö.αĉTV 13y-A*dOg-큸ऒpT,QW[Q5<-Y*ȶ$eb[k\Dep걔봧 Y "LryBNeSi[,#j1kfQXSI{T˝VI ,Ir]M>HG2\:JzHuW}\2)b.mTp\AUD#rLuܑhBY!5Բ#1[&BtucHN(\׃ Up! ŠU`%nB&Gj/W{grո3WBkB:p\}"mbMҷFNTVoR-=;H*RDlRc,Ǣ%x@o|Y9{6JywgL l=6V(i~R0𶆑/b gGDlV`ši))d6M:I ,+ٟjк@趷˫oG\)ϴ Fu7͆;.$Z {4Mw0ex}=p扞Un $_D>wO,Wk} F{NRK?:BoA 0` zZv}mcpZ}Z sS oIz(bqW{xljĒ.#7=HQRTLC_y DDLEykt͸w֬^Ӄ*nAP,%JU׀ a7h?}wʃwjQn#F#x :t}fW9 Zwߩ(~QDZ{SV5wE>7B4(@CMi=J0`j7]ys&S_" ?50:(rSߨ؀g0:#@whR~t679d(:g*gG{}EQT׈옻X{H_w{DQTG[sw' %'3gsOϠw(;9Q,N$Ι8[zgr(,L6{+5QEx'nޮW>Sϰ5;cS=,Ԋѭo)oh24c={ቁ*o2=slIH6B߯'g_y>wRm0(UÏ>}0 w Z~j/YJr!MM4ISS!q bBw'B.+~N_(65p ϭls;UǼ6@@nE ܻI {C8 T-{H]6@$45 }Nv_Ar;EE@tsKPYp`[ezDA { N)t:>`w#c^_:Y5๰w`,^V)DbSaP/ebDlz p ¾i 0FYܡ`HEԞ.DDtVn< xZF|d s6ЈG3wƜ䂁iܨ^oqt:ᄄ FsM5-Tc--|y FQ5/ ièVDDDfE6} 85~FG^ u gsUTK.~^9Mwr:F`v<>^&)ْ[ŭꊍ@|E)}\./ѸfH(C͖Tu@U Bcy} BՆ._ǀ\Ɇ[%UUk3 ni߶^ޝ{{T/o>גY5W}BX +Un3 v4dKa2飼ZU;bQwylOӯ?:[wQյ}6Z̅XQ]>SDzH[T@nj \) E]8ǒRВfx|9ֆQ]Tțfq F_ܴў6r]SLAI*Re{÷0]vӡL}[xT^Ls:+e!XW{mP/3_S:m YP> \)G~V%Ҩ=,L2Q4>׵x?u˟\>ʊv {'l޽K'wPQ|1mm@eˬ!-_&+_™ZO:ʲR8g߬!z*ˊ^) Ĺ2oNaksQ?ڊg!f,bZxq47<9KϽ$V@TxRoub}U%,)q,ӄ/oRK,st%]U!eٷF TMPTUYQѱ_޲$sZණ3j}w"ί)<|,oqP^Lpg+x6p5UEmleK?jP2mS|S v@aiU༩R'Hlk*I7ZMs'ݣ{ }]2#GnTսwHTյlq{) }SB#?zg2Ȧ<5 J/7!>Zӯ?'nW`Ϣ)s %Og>ꓖf*),5cÓ@q(>k =t:;3 s8N"7W^/b'g'7VEXGu| k#yM_=='쾯6u5ݣ@F-E.>f׏ZvcꊞLTxdxwYhIx[9֕j^y 0Oәɾ@a{LmN8zrFXأ𭩆dSTe0C79̍.S̭ӅA[Dr\2*tߠ/l겱@'gM[m*[iLJ\NAOK?(Fuߎ 潺̌}GJC[ Yh>aӯr{}mNC`&4MAxȞ)^ Iڽg7gE?'XjrIDAT|{TM4%ɷI+<-,_\Ӕ!@i6 ʀ74M w,\89ؼWHS.%Sb!Nߘi+U>Y7.'1iA'@buMӴ7Wprw,b1>Fu1b`HZ\3,s GbKmp\Bcm,}.þ\M670%Edɵ -󲿠7P9 p5l0s+A>M:oD6`k0 ^1[&ōmSѴ-; i^4Q%QTM#ҿ2j-Kޥ'""Z/^LIE4MUdEwΡ)}DmsKvTX,(%'X\^z~[C:d"Hʉx<*,G}.'_rbiRZ*'%\@ O2]4M|6kL$zUYεbץ$KftK)+9֚lTW\ r Q2,g0|6|DX:CRp0;iL]-ˆ%J=U raKd:,}Ck0w/"D<|N}i**Nm"" 󍥔/ͤ;k%G< \etѨQ[`^_GJF<.ߠsC̽TaZ+ewȭ#G>/-UW c?Lk;|pם[j>150?k-PގÁYv-.ɪpmk:ࣙ}"""nkg_:AG """""֧""""""@ojaADDDDDDDDDDZojaADDDDDDDDDDZojaADDDDDFfHS3+/7~y|:a4q2O ž3X%UT6P4 EbSSSQi]=Zqo=o@V/bDDDDDTY6^+nZiӦv'0RRw t?>̎* >3|fx8oN?rOgRS>:< =3^r㧦d9?AیAZyGjsIcV-tDDDDDƛk呑S[;߰ Ip=ߟC}[jƧo=u7U-O'zl sI!cps/=v> MMn< tu|*߰@'04LfUHDDDDDe*+9($Iu*%G6C6t|ƙ5q9i[h ٵe}3b^?ѿgi!ׇG]-j e 58s۰6Duic*wVM7lgϞ={VӒ'OkA6t/Nt7W [94n ظQ! |ުoq 8<]_%.r)+L2<=*v4 Gb|6{:M,+J2 *B|[@Uhhp;~2Q~(5 f!K(~;3 ;bBQY4%b+w{ Gch5U7M8sf|xO(~+oAzgєՊOw9$i'_IwUO1Z䅧^w՞C^]R_=1$I}8ӚʉXNtidU Uuu `^ͽ*\U/vfP@^Glאָ?=^߆_ +,+,}͋gg^lw_(*+DlU }ܔ7o?ɹL6IMz{{?ToY7lt|tjMίZU6oNGҙZ}}(i^i-vs8mu";7g8o^vOϷ 󦾱ilNC<$ J} nkg_:AOodlmZ_f2Ϥg*kM4uɦYSm]L:{!'""""KKaZ\`iX_>e1Xev65,a'Zp>DDDDDZojaADDDDDeMܝޏDDDDDSo,] >fN;o`+\^{ y1 ؜x⿣ gp8ڇO^@ 6/6'7:|Nxy?E=dg"8_§?;;^`?᚛qA ﰡ MubTDDso7_7\Do0{ d6'؆N?$^ d_0 HK9u ؈:̿'cpU5ZLg^1F3f̽v|ZK;t 0=h؀Gp|Y8w?#FL^Eb]ZnD˖.=n7z{ >Hf`&x'qoN? L9 ܉'P@?=-& Dpd+~1 |B= _#\aܽ wJh{fO>+ao BQ֍,?l.A~fъV, 1[>?އ ×H?>f1G;`݊ 9Pod:`rQ1}- #6dSxHD`N_(ǃxa4]J3rĈbbADtiS߁++_AKk_y= @-ZNwWj~M@&`?kt (_F^ezwUU i`8̵h /ډQ_44`9=W&FO%b3n.NOMMSqlKjj`^nmV+j0TSrJss~K;' -486=Uk~oOVLhhvv\ǘvĐyԤOhwh/gG,z}!v`mWyx6 4FK(IU.i1xZxrmO{b3x{Mad7TnU}Ȑى6ݭ Ь16.ŌM̙fЫKȬoHpOx ,?BhVhNlˏ֑ʱі̻ᬦٍǺ╠Ҙղ̷͜гдꚊ긭ŝлГҳ!,0C0oȸA#:(ŋ3jȱǏ CII:r\%˗.cœ)&͛6sܩ'ϟ>yѣH*]J2%F9n܈:ԫRÕ+A=2fB,"|Ҫ]˶۷pʝKݻx˷߿ LÈ+&2ٲh,̹ϠCMӆ]1A:0 sͻ Nȓ+_μУKN=Ak}࿋O~ϫO~{ǟĔ>(U4vn%X FQ`^ra()F8ыHь0(c8ި<> ?)DKNNF RVIVfZv^ &5DV B' pFg ޶$xԞ|矀*蠄j衈&:hCIB"$)ohЁ3VQ 9*뫴Z묶뮷*lkkf ꨳ͚->+mZK-vׂmkf&?+?[A4 n" pnЁ)ÁY?HPQ64Wկ;1ƿZq'q) .L23 1<4s" 2A1K+=O;-RC=u.+_{]s f}&!QčH>ܻpMx+H?˜RᡔJ(x ?@ h 'D})yH@]xa#S&1bI xr$} (GIR*07(X+xJŅq'(A < hi ȑHbG?(1gx%L%cov:!YkNQ9 vv^1x?.y=xӛ:= vĠfIjʊZ% *!=эyRL!8*+0r /^2|$:"y Gd>@shȣ󌪍lLc>ʊ}Ԡ:6(e-9 'pQgLcC5hPY3Za_yNy%w]FiUax8ƈ=|6F>M`mlya14γ>πMBЈN hNw~EFa.1iW Pqb P0 S*|qFp~(k %ӝQܧ t+Ө 6c@-C=elcva@?y7~~0Ap-fWM\sj[3ug|Ix911;>qKd j@gN8ƑpG+sCRnD+}n^(~U(qp s G8R|# nGo/ܒ{ev?w{^{(vss/݀/@ŋ | zc[^}-/<ϻQbWVz޿l> ?|;§/S>s?/~i- BЇaG6-T:sixek )@dG7f'MAvkvFw *zxwxXz:qzu7I$8za%؁fh$x&#qwwlm H3?X({TV8W(|XZ؅Dž_RZ(fgXhj؆_oȆqpXrhtx;h7&/e1?@lp|0lfU8- / E?xvҔvAT 1UO.0UzhIfFƊƋ((kׂR`zT8GP5Hk(H`ShxhehenԍS%zDفY ِ y iI(ّ "9$Y&y(9z|s, ttJ? 88(i(!@)9Ā_m6(az9@'"\I:cw_9yT`l1?[IƘHƊ ktGo]Iw~4`piY阆8/a_Yu{ yxҨɚ oٚYށy))ٛY+97&DuY]Htٜ c^> @)(@U5_LkB%1W IAA@ I>PicAٟNXm4 B`Zmi ɟz>IJz9"$"z;J6  jNApo5:О Jc* ))\ڐae]j_ɦirt:vZxzFP6` ;`+2EاF0gR 0 p(F:i߉EyDuZꔻvRѪfʛo:^J`zpJ ٫oJ Iimmʫn*:jjКY݊ߊ9Zz㚮⺮檮ʮ  1{3zگ{CT@ ۰K iEut$_Ê 6$[&{()*۲.02;-f/6{0c;0$PRFVy}OW}zWS ~V |}ǵwY|X}i|a8hm}s8|x(zvKw{TK~|~۷P{JcFTBJE ktb1& Q5TXѹW[{+뺲[{&ۻ kRx[{Û̻k ϫ[{ыӛܻR9{WqL k%9#%ZS{+tlK l  <l <|,ۿ# %L&*(l,L0 3|2l*59@AB\D+JLNPRm͆|ӗMK=Ϟ]JR]ڡ]NXW<M mЫխLֹ۽]l}bo]ܺc Pעc}/0 !& ft//mٍ؟?ͤ=ΙMMF8ީlN=AUϳ]W]YմZ]ʷ >֦ N˼ĭnN}PܠPi07+U #EE-LJ11;=:zMӜ}x< AԊ ߈S [}XZֲиWnbdmᰜkr.0vܟaeP'0|mlٖ c"ݎ.ތY]+l0ǒ ي=K~K-aЏ. ۾  b=u0 0 0-n蓾ޛ Ήg ߮蜾-}\.Kǜ]Lم@n C= >i n]~`Q}\Nɽ>N ƞ$. a@ ̅aP 9}z4#</>ӬNNJ؉q~ȸRX_\eȧ}N^ikix ^~R @ @-^ 2o}/8lM^Oo|?D^^N\?_/oo^t?m s/uop.<~odXڐ0 | *N@"҃>>,@A_Ƌ.4H1H&܈AQvfJ5K1eL3c4H(͓ 9xTP8>uZs~Lk^uJSXz1׫`պZl݊}[m[cɢkWn]yX`… ˛`Hbd4a+b8NhM:AOޔxPC2!64=;%l]+p 7ngˢyHrϝu3kj?tYvTO-yZݳŻ}{·7-~~,˯ k KAP08msة'Yd )3D#5|L(8j.FvnH vqِ8|cH)f;ij-##;(2=Ĥi=2‹c6ۤO.kN: Aͺ=E4~F"I Ė_衧e!B4aeͨF nґ6,ҭEZir0oq3, 3ؤdUIr")Y\mSn)%;oLk䯼58PN9 0\w/B l^E7/F91``Jy1`cOQU q")gB4zz $VU7fn㒼Cs}Lmx&*ERgZzso]ZAŅZ@ɕ9]^xlWP :|U$ņUn!`dVP̭6<(b1mm 51y$?ȳ"Y}NI#o6@Ln.$p>Ǎ\ױ>P@Oٷ6kz6О-$C|zQŖYnAM"|iMӈ|m|_<[.qkZ2_:2p>eDtO!փ: Mĵxwf%(  OcSмg<& yA9qt`CRQ-nF>YLo=U׸W= q?hS֔'#6e!TcZ}µ1Fi҈A)k$>X(1w%crBC8s ECl|8WET{EDæ[HJsBr9JSU9Na!9[Je\~vmSOwŪQ-aC wavZb6̤^mL-ԧ=q^pϰZ&-ih d&?tW_!GF'\taT:[%+47Y@`G ZцUhSub^x!d{ e D=H Z J2uJWlmժn ;NԶ4?zT'1]Coc;m3'}xFv;k8@WKO]xY7==Ǯ,0%>}TC-fU*[6jMe.Q+^Lc :9=On] ,3r/:Ӥ;,n3Д\`WUCkwHkyLЇw<;_QiGѯ%bq EPX6qyc @S,gWrsoF֎#e~0!d|8'ڋ?L{?agDӰ~4_G?NyLCڌ'>*_ `гtxpTӨp>1ʊl  )}`5)P; z IS@9 ~Іgpgk8ߑ%;ƪ XfX17K2 JC2ҬGk> |p /;ІL|X=D1X+IІDR ņ' `c(bAG2`{ GZt9p, r% $dh8+1< spjp΢K-xl KY8YP7OK5=8p:(􇹼CѿmˁPmU~o^*}ȇmH]avۧ~;A}txJPO|JtPҜw#؈4&eő,b\6Lt6/e.$P(8)C38*ppnEggONvxS|whANDYPԆhP [-<"OԃN0P) :;TUlKAR} u4mWK}ծ ֧ZoAVeeuV# A٤Oln5TuH;!w`p{hbȯxv_[ʲɲoȂ6YHqo`; ӀU%pJ{C~P)`Sxsx|t =XkLauhi YDlEt@YeDqلYG45+GmtZ6hh UZ;dեIw"UxN05|h E،/8}^-Oݝu4[XYMk TTĨ4AP(1,;X``_WXfhRc@KUpW0av( $=(0}؃.73sx=*u)%H$a5CmS/p}Hhٽ |FN!{bm RA@^-i]aTFϙ gkч*XF%K130+ڶt VMUsAN@eS_Tvm5`f8tU-UWn֡M`]`(Oiɰ֥~p[n-Oxh[DN1]ńcX{?Ib8f blchU@ &a( at:Ȃ1hdG<'\%`$ P)+h]ZHsxJr]<% &

' 6> ;6 ~Pyn+X=Ppx Duup{㧬JE|PǪEAi6#/ˤɹpU&p>@8~U"|NVQ왓#B21eـ|DuHk`4ψ^Qe}k1ODg(s[:qm(vWP]`>nfl&K ql>x+:ȃ-o1>| 2'v. ~81h g&peI@x6W]Vnkuˆ"`_}q \ 0a@s\gdžbd\ք)7-V-mjy~(-.Sg(^A~3mrl'E5,87MG x &0o? UFXvTGG$BKt4J.VoAMqЍ#FLP8BToI TR%LU g3ܹI&MTd$~uj]NCѬ7FԡUDĆll뺯 ^'Ω;LV=yϞE++Wtڴ %JH3QȡV6{Ŋkf5UKW]۹mglenҳ=bƌ,0 פT6oϻs>͕6nȡE֣CWP3mխ^Gg:7`?ΰ> M?3T4M3I ?b ta!8be8!  !tQVD=qQeQEA'$JBbIfO5N/{É)bK&Kk\PmSXEҒ1o)13MN#AU4Tc3/C>[Yģ.&Yq!9j]y9FXk_F2ܢ4C|>諢mp=&֚=j-x=逰Z'y²MO{5I\[9iAȹ?83'cMzdY N>L`LPSM+ЌN";ko=,tI24mXt@d#SE% 3,/sALϨT jX2.Regl\ Ьh WEjXq[a6'?rB{(] CFy K$ `3ju d` grLU",F>5-)H! Т fȝ:a~uk?y`Ȃ kXbC3>3i( `O~B #$+soc-H5rAE07eSpl3[К Py@E#D/$2cGRۓ lmPJ > Mm=GùICۖCȵKP cM3VwН`bρ&Gx m0tRPI @[ QHD91 p+jpO(яQ wH'9 _8ӝfo{RK%!+GpH#,{)^RՕTUR~U  Z]:K>skkaY9]['`A͸#?p`Ch b}eI>X!P<6=Vc W %G>qv t#@A {C@Bs փaP$AZ)=2_9iJ¤8)m+׽2Q%R]ҏ3 G*{) %Y~WoLU _Ө[u8AJ+{I(t@Ӏ}*)78}ba v˅[b_v 'ʸ*SL㈟;+ʞt&% G¢A8CF]nQr\{-rk![V g<E:Ε:>ⷓ-e|ˣh{ '%†5&sC3M34TL0Qwj\g4W}T[ا1F#PqlV {$UJ9?#3$^3^(':qБ"aF69pРHڞa_'tFtN'uVu^'vfvnoF 260yy'zz'{{'|Ƨ|'}֧}'~~'''98[jU_@%$0h8 wN(V^(fn(B Lr:h_;! !aad_1E"L@v()D4MUT6?|p~b`^:JV鈲"%)4BNdJ)6d"$**n'8M5j{Di4p4A^ib`b:FVw4`4""6($*idՒZ pƁfn))Vs$d-HWZ\!&[ݪ?@"9 ++*g6 UX>ޠ?¥A`|bL]ZGܥƋ)jKϜAcpFNfbC l Е,+2iòāJDxAb\+Wre"ƚiedBnƒlh(b?P*PdO߭C $?DBA4H<ߦDgI0&gF{ -U0i{TDJDDNupKVvmȾl-ޠeO&Jn߰+FD>AH_@n6o>oJ gNo.gHnoz/@Hx/g>rC8M.nі\)Jt\X+;ʙi`tp+j?loVIp>--vP-#*p)* ?V40/f/B/pV7s/ps">qFDG$AT.:AfJD^踆,(Cxf?u50?(0C&X@#2` Ԩ9*"#+0$pi8Z@p !  ɨ/J/)/+)?o*oo+2.2Ҳ,212:fs-/r)3O3tZETot?t gj pi1=+{K&(idfGP2[P2#P`We0"(V:zZφr'aCV@6ߞګ"C6zVŪ.z*t2|?yyR?LAŞP8CS{3·ss;Kbk:|7RWgff=&?ÿ_?@Wo ?, 0(1p1^ԘF;xQ 'QTeK/aƔ9fM7qԹgO?S^Ѣ$A$QN2?T"55SR81 !4Um[oƕ;n]wջo_xqǑ'WysϡG>zuױg׾ۨY?|yѧW}{Ǘ?~}׿ Pǻ"i!P ) 1P 9A QI,QLQYl 0ςqQy R!1FoS"R)+r9.Q1O%G4T6t9嬳;ēN==A-CEPE MEmI%K#ŔRM-M?QE-SCESDr/ KlVU]y_ Va-cMVemgVikV%ZL$A~MWumwWywn]504Prm3NXnᇣLgp%,sAYI.dyE2q 9Fiq]q_$]gl6jhVguq~ZۭBtF׳u[haƻo^6ohn;ޔ}]F\fhwYͅXν֚l.s6W1'lؼW6w]ءwS'c.V2c[vPĮt[s5An|D*MH7\tZ$[`mZ#AhL&FJt@ Q h bJ/T&'Ei'U4:ŽV|Kf6R>߫ @Вh1[$1CA )@=vJR΁`U6Rj0ͧN-Y:լuy4 TrB-A%%8Hгm+UTU.G֐z!*(wIZݪ7+UyVjlK6-F1֫[qԠq9cGAw ^ׄ+^UtͶ}w{7/l;֓=\"9wS^q˳5>.O7}N~?Ljp$tA77Ęy=˜5sʃKbLQo]*i:d"x'*xe,X,^ f9' ^)gJ^,{*oNv6^WLH͔" HXˊܴ;2ѐNs>]hHҫ[H8X:wo)^ laczmj1/[Ͷ~|tιr9٫F8 &ݫ?m#$}e12sЀd7?#]f`;pqo]ApXO[j37=TˢAN"ΰ} `7{"<חgߓ?wvW;6jcgSn_V}_E=_[ڿe)pWUɫ.fj|ԱKSSU2$16tT6w-v%ցjyʽmvg`;+-z8`um/^}_}GS--LvP)]5Vh;{Cuy=:;,ӎoIܨ16C 72ApC? 3m25moW͉5*,\?ƝFGƟ^ i"ogiki ,iB(RJI^ Cb\,DH JhHi͈įLHȆJɅ zjH 1tMlhr%(lvаEXPc-rhfx%~0WPXhxЭR PIt%)0Ej Q‰~&I XJg.ba1fHajp uoj0 %QKgfnQK,^fDq1ffEE/$w14YQqm&;r2 r gB9!!!2"%r")"-"12#5r#9#=#A2$Er$I$M$Q2%Ur%Y%rN`d&i&m&q2'uH !DQq'r(((&1())2*'{Bfni*r+++_d)e,ɲ,,ђ*5)-2.r.D,.2/R/Br/s0 0. 31s1JF~R!1)2-211s393=3BSBҖ3M4Q3535Y5]-C4#-5m6q,W37y7}S)878&w8s9SEbB0k9:9c:s;A;s2s<ɳ<<3=_; (S=3>}<>1sI“)ARBBE5TD^>(@@R@uA#A++/tA9tH3@)?GA'$AMBtB%4@!tEQ@[E)?E-F[tFoG7tEyG#tBsFY4HYTGyG;IAKBHoETTBBqKDKKk4KL4K7JkTJŔLKהDMsIH>t4MLTOUDG!N_M4PPOMO4B G 5QFORJTME^tOtP7Q5IRI-UI5LCUR%UU1uVONgS:EUPwDUE5QT;M5U/LsuR]L-MeVuGvs@tFcJ{DTK%UHHQm[RBd@]uJ\uZCt?s9^.8_ +mu`va3a!6bq$_Vb-b$`1vc9&eVIa;vdID4dQ6esu\t`FSDFM_fUa55Nt]_Q14Gp(6g9`CUOMhUX_Ltjf]vNu^^Uk)g\ukVQb!d8ahuJgtK]tTwBMaJfZASwplbKiJ\'5o͵\{[Vq4ZZf9j?T cw6shgt[5XUYoE7vI^}YesWGwVVmCvYqiUnGuWjuYOqi1LCxVvtw7ZU[UfeAcTH}tGU^+Wsw|{T]P6kqj{IW2K_$m'mjMiWd xu6;5i)bO1>x3=8Sq|ŵ}E"Gw\WqUawzTwSWv]7E1_zi>7Omj㖉kk5lk7ŷrVFW}NK7yEZaC7t!gowLuWQWNǴxc5sq8zxø8IwQmXy۷||so~vNS7wouuMux#Bx8 5[/Ͷn9oyUiYwU؁׊٘DU[Dٙo5yKyyX-O/љ9UyyGqSY9xmų3ysSz S'ZӘU=u Y-ٞ8yG6u9H,Z7d92 :؋77n'76H;7j{U}5lM؛㕖W;+1S ׎[7֓6|רGy[:bw]i7YrmOXuGۚ~٣˘Zx]٭yX]W:{خSw:vzyz;q9 WjTnjmx}8E}oeX~y?e|{U'1ӡ7UUVYӝe[{R;-;(`:)[7;;'{+;Hλ);Zd广9{ H:q|dE!|§f)GKE>URރRa>c>u}k>2]>꥞y'}~>>י*]^&>~1>~2G ~>~? ?!?%?w=`1]3k==I8K_Y=SG\]_ :m;93_/_}[`E|W[‘?ý$I3_ù$VUEnVX(\K<_aVDqGg%q?yqЀ! !4Y1ĉ+Z1ƍ;z2ȑ$K<2ʕ,[| 3E4&~<C2=4ҥL:} 5RyՁ?*Pرd˚=6ڵ.)'O\*aX| 8byw.K8ɔ+[,mչV73"/Q̤K>:5a6]ЏG;ݼE ךg9/̛;=2];^=ܻ/nvƗ'/mנC?ۿ?`H`` .zAW4xGa^H`C9a (8b*b.c2Hc6ވc:c>dBIdF' 5@ 2HIVVyB(e\~ebIfffjɦn 'oeb҉zfaڙ{g'"l2ڨ}BiiJz)j1xjj kJkފkk lKl,dA> mhN,.6-V zKnRn玻nNnR+foB{oKp|.*L0?\7q",0vl@;elog-3.1.3-1/doc/userguide.html0000755000175000017500000005526612556103750015274 0ustar rogerroger ELOG User's Guide

How to get the most from your ELOG server

 A Quick Intro 

ELOG is part of a family of applications known as weblogs. Their general purpose is :

  1. to make it easy for people to put information online in a chronological fashion, in the form of short, time-stamped text messages ("entries") with optional HTML markup for presentation, and optional file attachments (images, archives, etc.)

  2. to make it easy for other people to access this information through a Web interface, browse entries, search, download files, and optionally add, update, delete or comment on entries.

ELOG is a remarkable implementation of a weblog in at least two respects :

  • its simplicity of use : you don't need to be a seasoned server operator and/or an experimented database administrator to run ELOG ; one executable file (under Unix or Windows), a simple configuration text file, and it works. No Web server or relational database required. It is also easy to translate the interface to the appropriate language for your users.

  • its versatility : through its single configuration file, ELOG can be made to display an infinity of variants of the weblog concept. There are options for what to display, how to display it, what commands are available and to whom, access control, etc. Moreover, a single server can host several weblogs, and each weblog can be totally different from the rest.

    This is actually a problem when writing a User's Guide, because ELOG servers, and individual weblogs on one server, can vary wildly in appearance and functionality... This guide only attempts to cover the main concepts of importance for ELOG users, describing the default "out-of-the-box" setup and how that behaviour may have been modified by the server administrator.


 What Words Mean Here 

Just to be clear, some definitions of terms that will be used throughout the guide :

  • ELOG server : the machine on which the ELOG server is run. Its operating system (Windows/Unix/Linux) and status (server/desktop) are not important, and of course it will probably do many other things besides.

  • ELOG administrator : the person who has the authority to modify the ELOG configuration file on the server. May be an actual system administrator, a normal user of a server, or just the owner of a Windows PC.

  • logbook : a weblog made available by the ELOG server. There may be many distinct such logbooks on one server.

  • entry : the individual piece of information in a logbook. Can be as basic as a text message with a time-stamp, or carry much more information : attributes (see below), HTML markup, links, attached files...


 Accessing an ELOG server and its logbook(s) 

To access a logbook, point your Web browser at the appropriate URL. The default for a local Elog is http://localhost:8080/logbookname. Logbook files are stored in directory logbookname which is a sub-directory of the logbook root directory, defined by the administrator. See the administrator guide on how to create a new logbook.

If several logbooks are defined on the server, the entry page may be a list of all logbooks, with their descriptions, number of entries, and links to enter the logbook you want to use.

Alternatively, you may be taken directly to a specific logbook. By default you will see a list of entries, but the administrator may have defined a different "default view" for the logbook, like the list of the day's entries, or directly display the last entry, etc. (depending on what is most convenient for that logbook's purpose).

Each entry in a logbook is identified by an unique ID, which is last part of the URL when that message is displayed. This ID might be used to create a bookmark in a browser pointing directly to a specific entry.

There are four ways through which access to a logbook may be controlled: it may be open for all to read ; it may require a common "read" password for all users ; it may require each user to have an individual user account (login name) and password ; finally, access may be granted or not depending on the address of the workstation you are using.


 Viewing information in ELOG 

There are two main viewing modes in a logbook :

  • the "entry" view : this is when only one entry is displayed on screen (like the latest entry when you first enter a logbook, or if you click on one in a list). Here are the various parts of the display :

    • if there are several logbooks on the ELOG server you will see a row of "tabs" at the top with the names of all the logbooks. These are link that allow to switch quickly between logbooks (this may be disabled).

    • below is a title bar with the name of the current logbook at the left, and the ELOG logo at the right. If you are logged in, there will be a "Logged in as <username>" reminder in between.

    • next is the "menu bar" : on the left is a series of links or buttons for ELOG commands available to you. These are explored in the sections below (Note: different users may see different menus). On the right is a "VCR-like" set of buttons for browsing, also explained later (this may be disabled).

    • after these comes the actual entry information. It always starts with the entry time-stamp, and may be followed by up to twenty "attributes". These are like fields in a database and have been defined specifically for the current logbook. Each attribute has a checkbox besides it, explained below (this may be disabled).

    • the full-width box below holds the textual content (message) of the entry. This can be plain-text or HTML code. Note that for some special applications (say, a photo album or an event log) the attributes and/or the attached files may be enough information, so this field may not always be present.

    • last and optionally, one or more attached files (that were uploaded to the server when the entry was created) are offered as clickable links for download or viewing, along with the file name and size. If these are images they may be displayed directly on the page.

      At the bottom of every page is a common "footer" for the logbook. By default this is just a link to the ELOG home page in Switzerland, but may be customized locally (typically to provide a navigation bar and links for integration with other Web sites).

  • the "search result" views : these are basically lists of entries, resulting either from a "Search" command or from shortcuts such as "Last X days" and "Last X entries" commands (more on this below). This mode has many options, including :

    • a "summary" view : one entry per row in a table. Some attributes may not be displayed. If the entry text is displayed (or its first few lines), it goes into the rightmost column. Attachments are not displayed.

    • a "classical weblog" view : entries appear beneath one another, with attributes on one line and the text (and attachments, if present) below. Images may be displayed or just linked to.

    • entries may appear most recent first, or in reverse.

    • menus on list views are different from the entry view menu. By default they only have two or three commands, but they may have been customized by the administrator to add more.

      All these lists have a number to the left of each listed entry, that is a link to the corresponding entry view.


 Browsing around and finding things 

There are several interesting ways to peruse the information in a logbook :

  • weblogs are often used for applications where chronology (time) is relevant, so a very common approach is to see "what happened last". In ELOG there are two commands for this. They are actually shortcuts for searches, to display the last day's (24 hrs) entries, or the last 10 entries (regardless of age). Note that the menus on the "search result" views of these commands are a bit special : they have the same command that created them, but with the search "interval" doubled. From the "last day" list you can get the "last 2 days" list, from that one the "last 4 days", etc., and similarly for "last 10", "last 20", etc., making it easy to quickly go back in time.

  • another useful method, very specific to ELOG, is "filtered browsing" - again, shortcuts for specific searches. On the entry view, the "VCR" buttons normally let you see the previous, next, first or last entry in the logbook. However, if on the current entry you check one (or more) of the checkboxes in front of the attributes, only entries having the same value for the checked attribute(s) will be displayed by the browse buttons. Thus you can quickly flip through all the entries you submitted yourself, or of a certain type/category, depending on what attributes have been defined.

  • for custom searches there is the query form given by the "Find" command. This lets you look for entries between two dates, with particular values for any attribute, or containing specific text. If you fill in several fields, only entries that meet ALL criteria will be selected. Possible options include sort order and summary view for results, printer-friendly formatting, displaying attachments or not, and searching through all logbooks on the ELOG server (if applicable).


 Adding stuff to a logbook 

If you have "write access" to a logbook (by one of the same four methods as for read access), then you may use the "New", "Edit", "Reply" and "Delete" commands.

For the quality of the information committed to the logbook, you need understand and use these as well as possible. Here are some of the important features for each commmand :

  • New :
    • you will not be able to save your entry if all attributes marked with a red star (*) are not filled in.

    • some attributes may be pre-filled from system variables (like your user name). Pre-filled attributes may be still editable or read-only (like the entry creation date).

    • attributes may be text fields (limited to 100 characters), list-boxes (max. 100 values), or check-boxes. There is also a special type of attribute where several values are listed on a line with check-boxes, and you can check as many values as needed.

    • a nice touch : URLs in attributes (http://..., ftp://..., mailto:...) are automatically converted to links.

    • in addition to the above URLs, one can enter a tag elog:<id> which references another logbook entry. The tag elog:<logbook>/<id> references a message in another logbook on the same server. The tag elog:<id>/<n> references attachment number n in a logbook entry. To reference an attachment in the current message, one uses elog:/<n>. An anchor inside an entry can be referenced with elog:<id>#<anchor>.

    • the Text multi-line field, if present, may be pre-filled with a template if entries need to have a common, consistent format across the logbook (especially for HTML). There may also be a comment inserted before it to explain local rules and conventions, upload rules, etc.

    • check the "Submit as HTML" box if the entry contains HTML markup.

    • a logbook may be configured to send a notification e-mail to various recipients each time an entry is submitted. This may be the default behaviour, and you should check "Suppress notification" if it is not wanted. Or it may be checked by default, and you need to explicitely uncheck it to send the mail. Then again, you may not have a choice... (note that notifiation recipients may or may not be disclosed).

    • if the logbook allows attachments, there will be a number of fields with "Browse" buttons at the bottom of the form. Use these to pick one or more files on your local computer, they will be uploaded to the ELOG server as you submit the form. IMPORTANT : there is an upper limit on the size of individual attached files. By default it is about 1 MB but can be changed by the administrator.

  • Edit :
    • normally the Edit form will have all the values of the existing entry in its fields for modification. However, sometimes you may see fields that have been blanked if this makes sense for a particular logbook application (e.g. a "Last modified by" field).

    • the "Submit as new entry" checkbox only appears on Edit forms. If it is unchecked, the modified entry keeps its original creation time-stamp. If it is checked, the modified entry becomes the latest in the logbook, as if it had just been created. Again, it is possible that this is checked by default, or disabled altogether on some logbooks.

    • managing attachments through this form is easy. If all you want to change is the attributes or text, don't touch the fields at the bottom and the original attachments will be preserved. If you want to add an additional attachment, use an empty field. If you want to update an existing file, use the "Browse" button below that file's name to specify the new one. Lastly, if you want to delete an attachment without upoading a new one in its place, you must type the magic word "<delete>" in the field below its name.

  • Reply :
    • this command creates a new entry, but with the current entry's text "quoted" (with '>') in the compose form, much like when replying to e-mail.

    • the new entry has a special "In reply to" attribute with a link to the original entry ; the latter also acquires a "Reply" attribute with a link to the new entry. Unfortunately these links cannot be trusted in the present ELOG storage system, and the whole scheme gets somewhat confusing when there are several replies.

  • Delete :
    • nothing much to say about this one, except that there is no "Recycle bin" or whatever : once you have confirmed the deletion of an entry, it's gone for good, so be careful ! (same holds for the replacement or deletion of an attached file).


 Misc. tips & tricks, things to be aware of... 

  • you can link directly to a specific entry by its URL, using the message ID (from another entry or an external Web page). It is also possible to link to a search result this way: use the "Search" form to compose a query that will result in exactly what you want (either a single entry or a list of entries). Copy the URL for that result page from your browser, and use that as the target for your link.

  • right now you cannot search entries for attachments by their file name.

  • right now attributes that consist of just a checkbox ("boolean") can only be searched by "checked" state in the "Search" form. However, if you start from an entry where that attribute is unchecked, you can use "filtered browsing" to flip through all other entries where it is also unchecked.

  • as mentioned above, the "Reply" command only provides a basic comment/chat facility - a full-blown discussion board is not ELOG's purpose. If a logbook has a very specific purpose and format (picture gallery, event log, file library etc.) it might be a good idea to disable that command there and move all chat/comments/discussions to a separate, dedicated logbook to avoid "visual pollution".

  • it is important to understand that currently the ELOG server application is "single-process" and "non-streaming". In normal terms this means that :

    • only one request is processed at any one time by the server.

    • uploading or downloading an attachement file is a single request, and causes the entire file to be loaded in server memory while the request is being processed.

      This is not normally a problem for the sort of short, text-mode entries ELOG is designed to support. However, if a user starts to upload or download a large attachment file (or image) over a slow link, all other users on that ELOG server will have to wait for that transfert to finish before they can access any logbook on that server. This is why there is a low limit on the size of attachments, and why ELOG should not be used to distribute large files under intensive multi-user conditions.

  • It is possible to use bookmarks to pre-populate various attributes when submitting an ELOG entry. This can be useful if the same person often creates similar entries from the same PC. For example, with a bookmark of the form:

    http://your.host/your_logbook/?cmd=New&pauthor=joe&ptype=Info
    
    ...a new entry is created, with the "author" field pre-populated with "joe" and the "Info" value preselected for the "type" field. The same is possible for any attribute defined in the logbook (note the leading "p"). Thus you can define a set of bookmarks for various types of logbook entries.

  • In addition to submission of logbook entries through the Web interface, the standalone "client" program elog can be used. The parameters are:

      elog -h <hostname> [-p port] [-d subdir]
                                    Location where elogd is running
           -l logbook               Name of logbook
           -s                       Use SSL for communication
           [-v]                     For verbose output
           [-w password]            Write password defined on server
           [-u username password]   User name and password
           [-f <attachment>]        Up to 50 attachments
           -a <attribute>=<value>   Up to 50 attributes
           [-r <id>]                Reply to existing message
           [-q]                     Quote original text on reply
           [-e <id>]                Edit existing message
           [-x]                     Suppress email notification
           [-n 0|1|2]               Encoding: 0:ELcode,1:plain,2:HTML
           -m <textfile>] | <text>
      
    Arguments with blanks must be enclosed in quotes. The elog message can either be submitted on the command line, piped in like
    "cat text | elog -h ... -l ... -a ..."
    or in a file with the -m flag. Multiple attributes and attachments can be supplied. If attributes with multiple possible values are defined in a logbook (via the "MOptions" keyword), they can be separated with a "|", like -a "<attribute>=<value1> | <value2>". The message text can be supplied directly at the command line or submitted from a file with the -m flag.

    The elog program makes it possible to submit logbook entries automatically by the system or from scripts. In some shift logbooks this feature is used to enter alarm messages automatically into the logbook.


 RSS Feed

RSS (RDF Site Summrary or Really Simple Syntication) is a web feed format to publish frequently new or updated ELOG entries. This is a bit like the email notifications present in ELOG, but the RSS system does not go through an email reader, but through a dedicated RSS reader. This helps to seperate ELOG updates form other email or spam. An RSS "channel" can be subscribed to, so one gets notified whenever a new or updated entry exists. One can either use a dedicated RSS reader or aggregator, or use the RSS functionality of a web browser, such as Firefox or Google Reader.

To obtain the RSS feed, one simply has to request the file elog.rdf from a logbook. For the ELOG forum, one can enter the URL

     
https://midas.psi.ch/elogs/Forum/elog.rdf

The browser then offers the possiblity to subscribe to that logbook:

In case of "Live Bookmarks" in Firefox, new logbook entries automatically appear in the bookmark list:

>

Standalone RSS reader can also notify the user of new entries with dialog boxes and sounds. For a list of availabel RSS aggregators, see here.


elog-3.1.3-1/doc/faq.html0000755000175000017500000005222412556103750014036 0ustar rogerroger ELOG FAQ
  ELOG FAQ  

Frequently Asked Questions about usage and configuration
Please check also the ELOG Forum.

  1. How does one configure elog to display the last message by default
  2. Are there any plans to implement a MySQL back end?
  3. Can I run the ELOG daemon from inside Apache or any other Web server?
  4. I can access my logbook without any password, isn't that a security problem?
  5. I want a bookmark pointing to the last page where an attribute has a certain value
  6. I want a logbook with public read access (no password), but restricted write access
  7. I have many loogbooks with password files, so if I add a user or want to change a password I have to do this for all logbooks which is painful.
  8. How can I configure ELOG such that it displays something else than the message list by default?
  9. I want to have additional commands specific to my lookbook
  10. How does one configure elog to disable editing of existing messages? I want a logbook where one can enter messages but not change them afterwards.
  11. How can I track various revisions of a message using the "edit" command?
  12. How can I enter a date which is different from current one?
  13. I cannot pass the login page, it's always redisplayed even if I put in the right password?
  14. How can I change an attribute for an entire thread? We have an attribute "open problem/fixed" which should be changed for the whole thread if that problem has been fixed.
  15. Can I use RSS feeds with password protected logbooks?
  16. How can I mark a whole thread open or closed?
  17. Does elog have a spell checker?
  18. Why are entries with large attachments submitted so slowly?
  19. The elgod daemon crashes from time to time, what can I do?
  20. How can I create an ELOG entry automatically from a script?
  21. I want to notify different people for different things, how do I set up this?

  1. How does one configure elog to display the last message by default

    One can use the Start page option for that. The entry:

    Start page = 0?cmd=last

    shows the last message entry by default. To have the described behaviour for all logbooks, the above statements can be placed in the [global] section.

  2. Are there any plans to implement a MySQL back end?

    No. The idea behind ELOG is that it is a simple to use, simple to install application. Many people use ELOG under Windows, and they even don't know what MySQL means. Other people like the flat file database format, because it's simple, easily accessible from other programs, and it's easy to backup certain days or months of the database (since the filenames contain the date). Since ELOG should be independent of any other package, some "switchable" backend between native ELOG format and MySQL would be needed, which is lots of work and not planned right now.

    However, there are several contributions from other people who wrote scripts to put ELOG entries into a MySQL database. One is available at http://midas.psi.ch/elogs/Forum/387.

  3. Can I run the ELOG daemon from inside Apache or any other Web server?

    No. The ELOG daemon was designed as a standalone server and it will stay like that in the future. The reason for that is that elogd should not rely on any other software. This is for example important for many people running elogd under Windows, and they have no clue how to install Apache for Windows. The installation and maintenance for elogd therefore becomes much simpler. To run elogd in parallel to an Apache server on port 80, use Apache as a proxy, following the instruction on the installation page ("Running elogd under Apache").

  4. I can access my logbook without any password, isn't that a security problem?

    By default, no password is used in ELOG. This can be useful for public directories etc. that anybody should be able to read. To add password security, read the documentation under Access control. The recommended setup is password file security with guest access.

    Note that passwords are transferred over the network in plain text and therefore not secure. If this is a problem, a secure network connection should be used.

  5. I want a bookmark pointing to the last page where an attribute has a certain value

    Use the URL:

    http://<your.host>/<logbook>/?cmd=Last&<attribute>=<value>

    This executes the "Last" command using a filter with <attribute>=<value>. The following command displays the same page, but also locks the attribute (checks the box next to <attribute>) so that browsing (next, previous, first, last) only shows pages with that attribute value.

    http://<your.host>/<logbook>/?cmd=Last&<attribute>=<value>&l<attribute>=1

    Note the "l" before the second attribute, as in "lAuthor=1".

  6. I want a logbook with public read access (no password), but restricted write access

    In an old version of the FAQ it has been stated here that one has to use two logbooks pointing to the same data directory. From Version 2.0.6 on, this can be accomplished much easier by the usage of the "Guest menu command". Use a logbook with user level access (password file), and add menu lists like in the following example to the configuration file:

    Menu commands = New, Edit, Reply, Find, Last 10, Change password, Logout, Help
    Guest menu commands = Find, Last 10, Login, Help

    If users access the logbook without supplying a user name, they are treated like "guests" and see the "Guest menu commands", with which one cannot submit or edit logbook entries. If one hits the "login" button, a user can login with a user name/password and sees the normal menu commands, with which one can submit new logbook entries.

    An optional self registration is possible by specifying

    Self register = 1

    in the configuration file. New users can then create their own accounts.

  7. I have many loogbooks with password files, so if I add a user or want to change a password I have to do this for all logbooks which is painful.

    You can have several logbooks point to the same password file. So if you change a user or password in that file, it becomes automatically available in all logbooks which use that file.

  8. How can I configure ELOG such that it displays something else than the message list by default?

    There is a simple trick. You use the "Start page" option in the elogd file to redirect the start page to something else. Here are some examples:

    ?npp=5                                  for the last 5 messages
    ?last=7                                 show last 7 days (week)
    ?cmd=New                                show the new message entry form
    ?cmd=Find                               Show the "find" page
    ?cmd=Search&<attrib>=<value>            for a search with <attrib>=<value>
    
    The various URLs can be copied from the browser's address bar when doint various things there.

  9. I want to have additional commands specific to my lookbook

    New commands can be added for example with the "Bottom text = bottom.html" option. To display all messages from last week and month of with "category = info", one can put following HTML code in bottom.html:

    <center>
    <a href="?last=7&Category=Info">Info from last week</a> |
    <a href="?last=31&Category=Info">Info from last month</a> |
    </center>
    

    Note that the parameters "last=7&Category=Info" applies a filter on the display. You can learn how to make these filters by looking at the URL in your browser when you submit a find command with certain options.

  10. How does one configure elog to disable editing of existing messages? I want a logbook where one can enter messages but not change them afterwards.

    This works with the option Menu commands. By default, the menu commands Back, New, Edit, Delete, Reply, Find, Config, Help are dispalyed and allowed. To avoid editing (and deleting) of existing messages, one removes the two commands and puts following statement into elogd.cfg:

    Menu commands = Back, New, Reply, Find, Config, Help

    This prohibits the execution of the commands "Edit" and "Delete".

  11. How can I track various revisions of a message using the "edit" command?

    There are two ways:

    • Add an attribute which keeps the revision dates and names as follows:
      Attributes = Author, ..., Revisions
      Locked attributes = Revisions
      Subst on Edit Revisions = $Revisions<br>$date by $long_name
      
      The "Revisions" attribute cannot be modified manually (since it's locked). On each edit, the date and the current author is appedned to the previous revisions. The "<br>" puts a line break between the entries.

    • Create a thread for each entry. In addition of having the date and author of different revisions, the message content is kept. To revise and entry, one hits "Reply" instead of "Edit" (one can disable the "Edit" command for example). If one puts following option into the configuration file:

      Reply string = ""
      
      then the reply contains the original message without the usual "> " at the beginning of each line. One can then edit the message and submit it. In the threaded message list display, one sees then the different revisions as a message thread.

  12. How can I enter a date which is different from current one?

    Usually, the current date/time is recorded when you add a new entry. It might be, however, that one wants to enter "old" entries, or some entries with a date in the future (like a to-do list with a due date). To do that, on can add a new attribute (let's call it Record date, to be different from the pre-defined Date:

    Attributes = Author, ..., Record date
    Type Record date = date
    Preset Record date = $date
    Date format = %Y %m %d
    List Display = Record date, Author, ...
    Start page = ?rsort=Record date

    The Preset Record date statement sets the record date to the current date, but this can then of course edited during the message entry. The List Display and Start page statements show the record date as the first column in the summary display and also sort by that. Note the Date format showing first year, then month and day. This is necessary since sorting is done only lexically. Please note that the List Display was renamed recently. Prior to version 2.3.10, it was called Display Search.

  13. I cannot pass the login page, it's always redisplayed even if I put in the right password?

    This can happen if you change the login policy, for example move the Password file = entry in the configuration file from a logbook section to the [global] section or back. In that case some old cookies could be stored in your browser, which confuse the system. Please delete your cookies in the browser to resolve this problem. Read your browser documentation on how to do that.

  14. How can I change an attribute for an entire thread? We have an attribute "open problem/fixed" which should be changed for the whole thread if that problem has been fixed.

    This is a typical request of a bug-tracking set-up. Someone enters a request, opening a new thread. The expert(s) reply to the the request, and after a while, the request gets satisfied or the problem gets fixed. If an attribute like "status", having the options "open" and "fixed" could get changed for the whole thread, on could very easily search for all "open" problems.

    Since this functionality is not implemented, an alternative strategy is recommended: Implement two (or more) logbooks. The first logbook has open issues, the second one has fixed ones. When an entry changes state, it simply has to be copied to the second logbook. This can be done by defining the menu command "move" in the config file, like:

    Menu commands = Back, New, Edit, Delete, Reply, Find, Move to, Config, Help

    Note the additional Move to. This solution is even more elegant than having attributes changed in whole threads, since one has two separate logbooks, and can treat the second one more like an archive, make separate back-ups, or deleting some entries after some time, while keeping the open issues untouched.

  15. Can I use RSS feeds with password protected logbooks?

    RSS feeds normally only work for logbooks which have at least public read access (via the guest menu commands). There is however a way to allow only restricted read access and still use RSS feeds. This is done by adding an additional read password via the elogd -r <pwd> -l <logbook> command. This password (username may be any) can then be used in an RSS reader for restricted access. One reader which has been successfully used with this kind of authentication is RSSReader.

  16. How can I make a whole thread open or closed?

    Sometime people want to mark a whole thread in a way. An example is a to-do list, where they want a special icon on high priority things, and have this icon disappear one the task is finished. This can be easily done with icons. The configuration could look like this:

    Attributes = Author, Status, Subject
    IOptions Status = icon1.gif, icon2.gif, icon4.gif
    Preset Status = icon4.gif
    Preset on reply status = icon2.gif
    Icon comment icon1.gif = Closed entry
    Icon comment icon4.gif = Open entry
    Thread display = $Author $Subject
    Thread icon = Status
    

    New entries get an exclamation mark icon for example ("Preset status = ..."). Replies to this entry get a reply icon. Once the thread should be closed, one simple edits the top entry in that thread and changes the icon. The icon1.gif from the distribution is maybe not ideally suited for that, but one could make a green check mark icon for example for that. The "Thread display" and "Thread icon" make this icon appear at the left side of the threaded display.

    An alternative approach would be to use two logbooks. The first one receive all new entries ("open items"). Once an entry (with its replies) gets closed, it must be moved manually to the second logbook ("closed items"). This can be done with the "Move To" command (see "menu commands" in config file). This way one nicely separates open and closed items in two separate logbooks. One can still search both logbooks at the same time if one checks "Search all logbooks" in the find page.

    Starting from elog version 2.7.7, there now even a third way to do this. With an additional line in the configuration file: Collapse tn last = 1 (in fact the default, but to be explicit), then when the thread is ready to be marked as closed, select the "closed entry" icon when writing the last entry. When the entries are viewed in "threaded" mode, then the closed entry icon appears on the last entry; and when in "threaded, collapsed" mode, then the closed entry icon appears in the one line that represents that whole thread.

  17. Does elog have a spell checker?

    No, but you can use any spell checker which works with your browser. Examples are IESpell for Internet Explorer and SpellBound for Mozilla-based browsers.

  18. Why are entries with large attachments submitted so slowly?

    If email notifications are used, the ELOG program has to pass these attachments to the email server, which might take quite some time. Some email servers even don't allow to forward attachments if they are larger than a few mega bytes. In that one can simply turn off the forwarding of email attachments with

    Email format = 111
    this causes only the attachment names being forwarded, not the attachments themselves.

  19. The elgod daemon crashes from time to time, what can I do?

    Bugs are constantly fixed inside elogd so a upgrade to the current version is recommended as a first measrue. If that does not help, the key will be the reproducibility of the crash. I only can fix problems if I can reproduce them. Sometimes it's related to strange logbook entries which cause elogd to crash when they are edited. So if there is a way to reproducible trigger the problem, I need the files and confiration related with it. If I can reproduce it in my local installation, I can fix it pretty soon.

    If that is not possible, an alternative is to run elgod under a debugger, and do a stack trace if the program dies. Under linux, this can be done using the gdb debugger, which might look like this:

    [~/elog]$ gdb ./elogd
    GNU gdb Red Hat Linux (6.5-25.el5rh)
    ...
    
    (gdb) run
    Starting program: /afs/psi.ch/user/r/ritt/elog/elogd
    elogd 2.7.5 built Dec  2 2008, 10:47:09 revision 2147
    ImageMagick detected
    Indexing logbooks ... test
    
    Program received signal SIGSEGV, Segmentation fault.
    0x08054beb in el_index_logbooks () at src/elogd.c:3892
    3892       *p = (char)1;
    (gdb) where
    #0  0x08054beb in el_index_logbooks () at src/elogd.c:3892
    #1  0x080b8774 in server_loop () at src/elogd.c:27565
    #2  0x080bbdd5 in main (argc=1, argv=0xbfee5b54) at src/elogd.c:28923
    (gdb)
    
    So the basic command is to make a stack trace with "where" after a segmentation fault. This tells me where in the code something wrong happened (in this case it was inside the function el_index_logbooks() at line 3892. Please send me this information and I will try then to figure out what was wrong.

  20. How can I create an ELOG entry automatically from a script

    I agree, it's a bit hidden, but on the User's Guide, at the bootom of the page, last point, there is a description of the standalone "elog" utility, which can be used from a script or from another program to submit an automatic email entry. This works locally or remotely, with optional attachments. Enter "elog -h" for a full list of options. The elog utility is part of the distribution and resides in the same directory as the elogd daemon.

  21. I want to notify different people for different things, how do I set up this?

    Assume you want to send an email notification to person A for a problem report, to person B for problem fix and so on. The simplest way is to use the Email <attribute> <value> = <email address> syntax. So you could set-up following configuration:

    Attributes = Author, Type
    Options Type = Problem Report, Problem Fix
    Email Type Problem Report = person.a@elog.com
    Email Type Problem Fix = person.b@elog.com
    
    If you want to select email addresses directly from a list, you can do the set-up as following: Attributes = Author, Notify MOptions Notify = Person A, Person B Email Notify Person A = person.a@elog.com Email Notify Person B = person.b@elog.com This way you can for each entry select one or more people to be notified from the pre-defined list.


elog-3.1.3-1/doc/thumbnail.png0000644000175000017500000024470712556103750015100 0ustar rogerrogerPNG  IHDR5&j٤m+tEXtCreation TimeDo 21 Feb 2008 21:08:10 +01001otIME  pHYs  ~gAMA aIIDATx \E޻g2Kf IH–H" AD+Wʽ?sEQ1" $$d>陞N/gzguSs:ypqѾgVZ|u;۟X!|S&B!%z H%uB!BF}5B!BrDEK܌1Z&01"\(n!B-"E3pqiQehcñB!f4'O"12%ja3zcB!c$$D>̺2RQpIX}FB!B'Hv̨ ѓ\7bq3\QJ*z!B! =x-NFpDh`iX{c#!B!$Lѕx۱6Z:Hr]R5DLuNxñB!pt#f-S7IGԤ 3"bo;~B!BPt4%Q's3,ltMz푊$&B! ሙd&Ru[3$l#ƈpIV`;2vB!2|v-, =9wMI%j #&Q1HIB!r$z}'x/ƨ]peBĘ ^l!B!d=>#)h49%/!DMQ!c2/]q8B!$lKSzd3bъt$0d& !B!CI')@*!#b&ZԙLɜ6@"Ql>SD6L@!BI,#%"p"{(j )gh& l42ܨM"!B!Xsb&8'l $"4Ɋ9vv#B!d"b$YXA!$48Nؖ2ZNT]ϒ DdfxۄB!0$3,B@Eډ#&1N$t#lO!BD$U׳h1-d"B%_<3/XkkhDÉ d%V Tl5RmB!B&*F2?Yܿ΂I!Y&i&7MiDXRl'$6BuB!B 6S e$ @%6b$$2VċĊTD&]aPB!Tl&pO@r_=~u,:#6C57fH%$N5dSB!N:]RI$h2Q&UWod#~,ROFix&Uƨ!B!Ig,MMΒ {bF|a3nhe$Q@H5fI ql&B!h38hAc`&v&vfD>M0yj⍩HQ+n xЌ[C!B j^T chBG%)8"5&U&10 3B!27&Ydh Tb&viZ1}yk,Q;+WDXhq,+pB!2I7A@y14ɢ@f 6#%L`Ip Ur#pؐ+Z :)j!BItDMDHR(M?{d")v0-ḚTcju4R:Gc8G I6FųB!N"Q,Y$:5FDDg hI'sLh&4 -ZFlaB!IYD$`)2G/HBr1@|A[ΉI6&1-B!LdRrN,Q&4AY"Bl#2DW,N9.hL@!BHjM,\4љhAO$iDio&{f7&^Dƌ#MB!Ldb#5&njd<3,J'낖/E١0( Z552Q0RC!BHjDj 䏭'vQctL">ad:R%ĎI6&^x cE !B!'ju=zd>h?Quֆ,v;tƋ$f$RLPB!d_gB%ĚΤ#hDj鍔( BniE9c7ё2"j(f!BD%YXQLNϟO,NDF3{DLI9b4Z=B!A"B&n&:i$H&lJG@ 7Rc4TLd5 !B!HMI5&ZԤ1F"3Þp3T&GЌsp&$B!|HPf$ΊNx1IX: Mld;QDsB!2H4OMq39;ݓtN# +%ʄ`dFL(B!L$% 6 o7,U0SKF"8{Fj!BJDDc0(bbodBT:aDdBĒN&Y'^5QB!B-h"ݸbE I8v15?}&Q W$$˦0*F!BH',V$'cD##5no;՘Dgd!BGl)8RĻG DыӉchA!BgĎ1`# F"̌DjV2c8# i4D!BD$/W9n6+*?R}qLJ;f]F\B!2Hlk1"Ǔݱ@Z!-Ln,#<шK!B!dd~seFTLd aᣏmxFh!BIM11Ǔ]NaL|5 B!#w~QQ7D!B7!"B!d ?<_D !B!ąB!PB!BB!BH^CQC!Bk(j!B!y E !B!$!B!55B!B!PB!BB!BH^CQC!Bk(j!B!y E !B!$!B!55B!B!PB!BB!BH^CQC!Bk(j!B!y E !B!$dۀ|ۿy"&B!BIKepB!\:!B!$!B!55B!cjȨ[-xgk'+I++|aJm6G !FjȨ /ۆ| 1_Ǿ{!>l|ߤ aDớtv+/gM[lCB!0RCFW7';00-QSѣgOmg%66|n|XhhChC3Nlhy36$B&.93?Jl }[pCyZ,嚥3pKyxm姌ٽ"mxG4 /6\RuLц|Q5BM(jFxQDxǓKTHDij' 3S}ڔ,//3?m7$nD%Ix$~mxMFhz-[|4MSmxGa{9o3BPԌ"O3}r2JW,{%r}{ֿ.?kez% HdIr[f66ɤr MYDFC!>59B.m- K?^^r ;"Zɯ 7d]Rldz̋LE=6߆r r6!Br015.Q&%;Ht}L&y;D+ٽ@n9AvʕȄ qlTcjDצ{[5% %:~ՙCl1 $[O-r9 J!d=j)K(j)K0DG_FOLq'XF#uF'ǖPg`d6!B!c E !߄Bǔm!B!d$0RC!YA=놹3@u9t{ )C'~tB!$cPԌ5)klM!C-JHy}nWmnjS[պuoZjd Nban1#PWJ E)Pjٌ`ECBIw*#B!5~z`}pbE,jE%^lKK )R@=PQX$D_!"OQ≆J(L/uAQ SO| S83Jy䳆dR $3&W**.?$! EMI%h1BcuAXw+lV%Fv V)']ҹG,1K!"s?"2"%_m%1"""HF8^ݔ=e[ʶBGvϨrd !5ˆKB #-]B<TA /^WhڳbA8PYvU,5ۏUeCuZi}R/%[/'.ō*BrB!F #5%zƱ1~ѓ-uIu -a۴]-x]J:ޥs8򣋌ש (AFͤ2&=.nQwLOO,:;2FM2Q3Q'}|jxb"VDog®TW)2 b6P_nE_p1L@vӓŷhF!ePHA}wor+hI7D!$FM"Q!_1Q$Ƕ5,Zon]}CtV{[ 9͞D61ՠJ,{m`۴C/7ro4H+.!O(j2QANd_cE֣7zg2\~695 b6UHW2g½zp dY];,ōwU)(c%ZwBr 0RA#Fu+x MFcdo1=}"rHbENFm#TT3N@ǏcBr tl9=hZ˱1G_F?^R%*t]q&zXEC DN?^VYtYg(αC!Yf[5% %:~ՙCl* '!ɐ_x E=RKʤ;Q"!kU329L0 Ӓ&CZZ)QQ_QS %H ı%co! bk2ėrN/|~@!a"ZZ8^zE|\ 8Cz.< ߼i6B-5\NLr9^V|Մr ;8~e&$ "sz5FfQIdo:I\LY(jș׋~Dʹ.瑑ZoZ!ùcr NЈ"đ]9egGuj ?BHf!dannGQL=j}gmȤ߸_Vz뽨OQv˽SJ܄ m&!55X1 T|Mޜq.ִ$2AX6/a_J#BH~BQ/1jp[ dؘ.~(ʔSaE:^;2* KmÅ>!v|bj'6B!?NCx~V(P"9Ş={m!3τ¤Bg>[nɶE3!y yoYY'֛oFT, !a<m!d Y& Q5KmBD.~:fԩRiؽ{W*ٷeZ8|>zO?˕m!$PgL7ղof۔ٵkWF&ܽRξ-2m4LW@u|k;y dpF[dTvL(j#U^m˫mΰhllX (/O>!(,L/SFێ_O=ڐ:x0B!}*ܹ2=J/} >خ;]4]_ z~CG@#XYzXI"70^$;ȱXyӱ/yX [2|(j#jp BB4]44^%jp%lbd|>DS~wAح_+sczLS#wes ӎ8W:ׅ\Iá=ط)#&#HG_F^ޫcR۸;oNWZ:\kw7ɁQe:sitDp3-E !yX[ѽ|mMтvZڷߖR{AQ(to7/GEM*^pF:0%l/MxinlWTo! b1 {?SOS|e)h^| f~\%h] g8ttpP \%΁v9<۰n] 9>rB>rBTs;נ6*B|/ΰL,{TtET }t`a!>'dbIqcUl_'Ҹ"]g!WW:'#OL#!U}7g+O} Lݟ._& u3o?\E]dW-YA:>׊Y§[G-y/~)8cP]_}ųXgdO)1#KBp%̓?{~먵x{{NQ8F"mu_< Lo~_QǹW/"h$+N8c}hf46Fy=55oBLEA{n}k /FZըH@Ѽ&dr,HF6v; oKTW3.G~ξr4IQ!,'/GN^ov`FSlǡ[BpXrR=W"r1J#"&#AV]6<N *g 76%,Tr+֬)F[ٸァ˞}vqyyucaH)Ѧl{[DDFr BO,t~^msFhv={Q }-&18kNνiGW^w@uU͸ΝY*+Z:zDl-my4(_^v{&곦+aix8ci_y ǟ>m {ѵ]]?`Ӧg\uw-lwpe q4WE:1̩όѡDֻ1'Ig&?!$OO E zM~9e曗Bo>_c\2 ,ξY}Kb1KZx *S:=d]nALq*ĽyE*ۘsO A3{\6܆*gPe EYgUwYhEz^Zy~|!c-*bփ64.jf .ŒAu5S!=}cU+z,@{\f?{S4g4|?L:fKt}zG&.OlH]h#~>=E !yD0tuZg9M_G G:! ;{MlXj[v]XL/NGz:.)"gOηcwY`AFId$CvZMb눞OHקlg!>|S +,B(o.?ߏ)smc,r hhx.̙"h<։M†ȸJaw ϑAT|M1o4[5k?brΆ묥k9$y2yp6Ļe܇#/L}=tm6eDHgG?ڎ;Mلmg2 olj']^\wO*tõu0Uv#r/Qێ;0G?‡c, SHQb۲:nʔ;CI#5mʰin4⢯,9 AcQdp,8p/2}?zGLoю'lɫB̟lŒM)lvʁxvzG|kҋgٍCK'f{^Fk.l 6H[zmoQZkU=I>8N/4<܂`U*ۦ sPBb!$ϐ /eO)MKKg?.e<]B!Zsu"=Bd֖ _6f.i`ar]1ڂc)Ghsʶ&9 f+ xtf8"]R'lو?6?k&M|1 lfo_\˅]78tby$p>4:䳸F7!5!h?|L(zl2Y5pVoϣ@z&\J40=]l>?/uU3:rZ1 * >:ĒBHBdQrξ4=ldha#_ilᆙXp`WN+Ƈu̓^J%SYȓAHsqg']=BE !)`Tvmjt=GeI뛿|￳)G3\(^8_|Ihp~d|=ۆBHAQC8Gwcm"*ϭG*skm"!mJ Kyyd4BK(j .i*qLzv̹jʘ&MǚOfDB7xJEf;LMW)f!dP27hrͤn̯uθXCFc2ZJlhzM#s>p(Ul~K?ܙm _QBt]Dź)D0[W]ڋxY8~aŘ.P2Ai? TK 9ވNScj2:t8,1 OpjLA8]&tJ-Ѓutu4plBaR>z/=E\c"ҙ%r?yXo}o,>ÌF M2]f ar~^_m]܇W(|~67;@C!IH<(jH|T)*[T<h;3JEbF{_#6FU䌮C\Qzb/-so]s'eFc׮Ԅ+טӇ6[Q̝Cs;{}(.s>] _vB4$5znQF 9Fo,%pO>9KmVSs Gn<|R\2EԱg/ -n,b_KZCSp?Dgw/~ׄ@І:`vwbi]NrOM,DBq{cY}fWƎL V 6K^/)Qy6GW]bWXzQ?Ɍ b˾>,`7ppJ0$5#9 |ժ8wnVd89S eܼEnOuXպ{]BX14pŻqGp T%JAHcXaqaL?C+ɇMo.EߍJgxb]0ԍN:x-T%B$ǎR>0 .SUxQuf`( U^͊f!ڊql]!Z~7F^,Y(nO]Bx&yf=gv |}M YW`O11l'vWۄޗ 9i_H :z5AqB6}]0wuCw8 7PB➱PeME Z `@}|8Nnw|Q>NKbSl݌컱?wnjC:(@3廙܉(J&5` LA [{QSĴr+ >mTPBsP%DIǞ.cV !noGKsGCߊrfKo@VE0 Bo x--f8wfqF7>hˌ:wp:ЧYkE8r=*bqq `Śոe3xf{(j!)(J[G2qaza@ˏޠMz}i ~VgEw $/,"5=8b32D_3>8b<֭DmM p|uz+&Brk}AxQx!hlϪ%p^BG`r׬нK8-vNA_h{n[ ՍJ 6NC^_quZ(]B:L^!F#'UmlPT Թ:!Jd6]eb w Խ$?Zx_Q"e=/UVnERj?gg^U;EUc9M+aߩi4Siui AqςQN:^q/@gsANL:7ͯz绢)ƒ-Ć`o Zm8J?Jmhe>7NIٽzFdRX-0!%$DѯzH+Twwaw~7!dB)(@'"jQ&,rކwDw%n<-R'XUm볆fpхTA#~H^:gI <~ ~\W5 =5ЬAhFp\C)3]7XPd5z]Q%$ѤIG?Cmᡜ C+;$<>x&Q̯ {3O2/kU^b¬)aL!_Elg٠FaM;F/F/* :m: ~ۭ*-AÆ=-6iS.kIB E !$j+}ᩪHl[w+qx]8ǫPrNgł]<*]!(KHc̙Eii)J` 3y_ʪ*̞3O?$Ϙ 1pt76⃭[Q5i&O ժ\Fshr :PVYm[>@mmJʊaed!Xlp!"|^^01w\\6v`rx}:jV\\<|Ү>%fdˆv\hj]/ϗq1"HCUM衾-|kkFvu+]YzBBȘ##9DId4G Ǜ[``!#:BgfOQePQBk;PPP0[+޵ %'͔i&.y?2R'D+S\R~iWRb]yPVZp kU4,%%GYIfR /;Q:E%EعsHA"PXdw:Yx6KRVV舕DF|E؅H:nT co㢋g 6~ 2GDa W1κɢAh6GG/kPvDJ"I9⏉mwR ~~;()(CswL&$!hNϧNgݾ-UTG.UTW\'dQ^)5yэM}Nj}d46Tъ+V(jS ۱` ѳ ;TH/E A$'<v )Q2¡ӢmhhPeX0-\& n_7^v4\N%-\+^lWv;* B0!)\d2M,9&);BcO<Hm`$@&!>kt)+W"\=j(kv6SQ}?՝ ZdXmj/e6 8ŌR[ߞ]N]CIE !$gjP 3uMRvc;~tkBeO G>/ZVA9H_d#shQQ 'B1zAHC Q4.EA '0Y?rcZ18H5nRy9P*PLoۂP<21!,S%s[,º(b+MىET%rzZ,'!PW5 ~F{|+SH,1E>d.@FAPk!}M 9;b`HyIw"M؜ sŊ&ainImGGJiY@}ZPQQkLϙwn]ѢAWu]d\|[SISttReԓb2 ^_>S+"Ec3"cѬ"!0[!! dEyGѝб= sṠa9 FS\2J %T^PC uL!aC@ yN@mMUt8QZlFWh?f GjAڪƱv ;pNCqTN8A ЀЌNuh F?X7yu4pH6{ٌ7nUB-*1Y/&4`(Ƥ#*!j eDtzcG}{f1S,~9'syBȸGFwKgQb` ,ď7G ۆ$0HEzA8s#?^y ˫9Go{(DyYt8{H.o/5h{ K?NOv>XBA, A|n"'N^WߤIhB_^@`pzi^o yȍp|vc|d#0F$aD% R.o/QQW6ahJQTDM67wզG0JW+4{ ;VlVD9[#R#R ;Ey (j!̛f|Z҄&@&hm)$.h"H";qxȺX$Qz'!`01Jjե'PV&!fȨˤj^l з}=chXJMx(,xCh,rԆ2=^ '"DwqF:{ɮKV_$aAlfB&G5s@ss3zt`_ %ڡM![^7 A (@Y]=!!TlAܣo k2s{p;)~?!Xh 1&~ZZڡ G~#B152 dJjzݰX~V[mĿnáN>T˱v,rrn5id$kE@S^ E8ť9 ;@RT'OY8&&Ӊ"L3 lڰƎ5k˾FYPj]ύ`Q lغ%t6cޢ$=ر+J@qYt*cflFWW7 +TOW^} uuu*ـ@rnYfK}T2̸&'7l؀O_eh1k6V!.>L1y8`Ї‰4) iii;|6RlR7t8p@n޼Y9rYfR{>J>+#EQ䄝M6ke~Rȹ|C0e%.1"P}}=vޭБ?c^\Ξ=['mhllTF Yך5kԺ] -v)_lBG#Ϫ:3\oPȏ>8BxN`Po/npeH܁10!G_2~q]HS/s> > "lߡBvm[{صC]nx5X8Bb3B @QC!YDe5plBh@8Zt!l\?4F}_JUaJmrҎ9UϚ(paq"6Rdȯr"Ңb5٧rZYddDVv1bɒq)vϑm(GI =*#\9+ĺEKF:|vKv]m)Teve}{K.Xۊ;Y;ﵼuj;8B~QQ,bc+-|]6+,3kA(rk^ 2 peעFQ>f&Cl)G3ÂL>{r?+BՌ·lظK5>Rd/|5ȖTTjz*R8,ZZAfZ oہF-|Muu|t;%i0\٘Gy'(fϏ.]ۓ~(AE(Ja)`eJrRɰyZkeNx鶇 ;]Bf(|F xH)ۙ;K<45-@X3XYyKQnBӗ2 'XqMQET(3œ-^f]6 q:[ю1jy:#\ xaa|76W+ P{Pݬـ}͡ivEn_Euױ()?:%<ȕ*pE*jEQIDmZRF'5XޮVY*culυzw-(vKo H!#ewuKת6Vk5#$NoNfgVex}<^îhPythNE*jEQAO"(.clk/*(H4@TٍvT3+-;/}X[Icjʅ\ΉC*{.Cz'QؔݨH&b}噥Xv"]pʏ|}dTQ;QQ(ElRܑa|t6~T5k%X)lv9~.xaœc뇹9FJn. J@ T2^u鲈wE~( Tm:Ey_Ԫ^E7*jEQv N?>cF|}*:rYԌP1dQSnW3bqk^om/t(QQ( /̥QDžpЏB"ၸ,* [Ց)vGrlyB!`~ރ瞫`qm |@0浗,y[f|QC*Ц(ʮCE(n$bdjn,dYK0f-S@6_j7l N25j`uu}#vMe!9$؄v9;v_7;=m/}2:"{(ʳFq:uNjO=WU+%L5^'({ 5(՚BeP4T5tH\VT2l!݄jB/ߕ5b5mpF*@٣J$oew 5{^m-;8tn)T($ ,ϿJw?ӹ{Zt5c<8P.WyP/j׾T-I2h'[:FsᢼVcqFB(;PQ(`oLz0U̼cv`fnK $Yg>),\d|4Θ LAVCf dHsB}1i:!arjML3;x4Soye@Ұꘛ^pr7'H\\YYBuiܯ!.l RJg~sɶ+,"7t`ny|z(ʣFQeA& X) `(so*,ox= (k1:ʆ5bxzXMEPMKX^͊!,O;G 2"$AsQ\SK+"LULݚj•tP6p#r^|EB@: W^ARSF 9024\&cDN <|A&fĉ.7p椹GV+aaiӳsL"7=54FQe_i< $\z6BXJECTˈb"u,Pp e- Ad4(CZf"aev{>HΣli(3 /W7'KL}4G@VeE$ZS J0<8b)g?]7 C8t. #[* 1:4f2L W@E(ʮ!xl*PAY֙"ij5{FnS<37[Qtmj=*Ogmb 7#ERL<-;-h 0􌴞e.Y;|csqED'QH#@ oaDL Gָak5Tr9+nl#2f_!YS0/U<*jEQC:L3+%Z}W0"aJޮ@X]5N'l \]&Z^Q8=Iamۦu.8*j=κ\nL$ͽUb`Bx4nܸa~#6"|2 KV*%#3#bV1R@$xsfgFeӈcǎblߨ4 Csȸ5?a[?sisoa ^jA'Ey\4FQEQnc1\ F 8yslº9CMH%p$ nTkV=$q^#PNI۷͘Fč(A:C(0$/5ʐ5kwM0}Ū4tf~#0gEQEQnc\!oČ~/(8`DF/BCs`b?Lw"dłٯj3 n囅}אJ%P)W011n Hue122~ԪelJsG:BB!k:g8TRBfl6F1f4-tv'U7rWz(㠢FQPxn-mH24N7=x# #Bݼ4%VYB"!?|^䭘'HmTQa7\L4p=2b|s-:nϋZ| ~,fWݾ&A+JvEûQ=EQ.,T,{'̘_yP֤&giwذ1=aXNN!%E َE Ac +裥>^g~ ieGhupg>9mny(_UG)-r@(ʣFQP\|z?0ƱG<A'o-`l|885f$9h(*e '~iaC>z ߈I(&`ٌGQuqL ?ؠ/p/W7PT1ht|l֯ۢK#c,bEQ.p6ks1(_u<*jEQ \} rU<4T k+D!Lߚs>\*fgkơȤ 7q zz≡ge݈oK}E7"nzHQTh$_~fDJ3u*QstNm>3kl$^(fޣ"ĸšc%#,[(|Aݞ(W^O a =,`8]x<. cI؈zn &z@R@H+c7HXlg^@(/ @ "AT_>fDOR4^>!bu/&оZ!*l~7j:8!/BgPfNcC"м~϶;Uƒ{A򨨨QE"|,e.veㅵbV]8r77fXK@mjZf6sAH,[5\Nˈ,KΆ<wz~֛霉|uq8X.od({5(]8yIJABepۥ((~( NogAric{̲[^7ŒH$Qq¯Nu -#<.vÈ+X]I6,03w7N#eEӻ"^`xWxm8F(Jg㰬5 e?qR.r!AC/im7+w2*(;PO(Jbxx\<^@2+9~ؑH ^cɓ/IdnS*%"i7;:S(HXNQN'&kqaHY0@t"$^z"K8o|H+DZ] ԌF&N9"bSPz|>J 9=Ibslu[P'3Ey,T(tccw?FFEdu8loFÚץY==Cb}בtOnRbN#04.@ Br>h_Hfyr0bmLJD}sz|9/[S~Y{ͣGM=W|L ׺Pc@o{x{%UASS?)nTQ*jEQex<ߌ^Dl͉ ꍭ𭻙n[v,5Wv?һ9OP=EQ.1BUbOtZK!x=$oɔvCL:ht~渌xg訨QE2ĺK?fLzoun?7 w9 jbɋX i app K){nɓQ*J|U1mtCQe@c~HQq yXsϽӍRX,B6_G8RH? " MMMb}Aq".N:% 6b###{⼔LݯEyʨQE2t,/IJZl[~^-H/Y]]@$ۈd$ _4lv]DMoB2@ E\~]ZΝ((CE(JY[[2FLǤ1HǓ1gnjOF2YZ[)~A8M|)8<#e7 7(w:;Mh uڍEyuN"rb~~h< V}EOpBY tz7w9We'*jEQv5/-cqsRCCgP4zjvs/gΞT^xxW(>:lDLJ:8Eplldq ҋvr}o-C)z(~-5`@y^ pJN8XG}_o@`T(tkw1ؘHlH$ ϯa,>º} BVv3LQ\~YFXkͪgrsY+nXOHCAҝG˧|E`I@TQEQ.Beq 83yX1y)~b}jݞUf!3ޱcݞ9T(tӱpPgaMO(TtM֦!s1et$_4DؐrJLYƼ^JяqYHH#r\aZLQeFQP^˵={ دi؞D|ggSym5s}CCqLW } 4T(VGGE(J0vz !>rͼy OaMw{KMTeϡFQ?jgX+͹xʳ-/_ Yʆ7/_5o]QE2.t/Ziԁ9( zpfu$wwد&o{te44\IDB= t|ѥQ?/E2PQ( Ѳ$U,+F$$T*"tloI0?rϯ=oJ7ۈDf`OY__7/`@ʊ۷oҘ\.cjjJ EQ)gm3l @_pA._,=m(٬l&˞y9&GDQ US,+=??/crN2Vȑ#ݾ "3,IFQ^fx/yPV caa:ǏHg}豫%(*EP,͛7EȰ HaSNn _F܌1==-b(ϋz™A(D1BS|3ǧ׈‹bhĈ$sr\X]];;C1(P\\)èyLNNW<OC/Ed)(5(]|39nyכywIɽ=o7tlݲ| =vz6'ϑ9zG^u˙pq x4p48p|?0hd /$ms 7V_Ч] 5(EQ.:I@ElI{ˌccpw~APuTԌ}O,#<*jEQ&JUl7dMT4jTݞp;-n" "5QQ5(]IJhp;3kU1pgǣa4!lYr55OT(t 5x]c%finD H?i~9vc}ږ7ae07_;\<;'ǽNgsέ}}b ;Jh696aSk3Gg[0>8cݭKqy?GǵGj3s!H|owr +]a+.FE(ʮyz(p:,x~ԍqU-d\.y[5+ BΞ+^~lS,ȳ7-iXIq  ՓJbbUEjp6Sh,BnCxg_c8p??H4GpKxoW218׌ُӧ_+}]٧u+p)9 ٣K# Wp4HX{V֐/a3 eYOnTfuwnb!!I~C k 5 uKOL%G3eS=wFhn_zy`Z^_Ce#s z{SFؼSnn9E<:hO'a[7o?7z==[?sUs ~wo_}mq'p9q"{446d|zwx"dkkrPoT("k5Mm`|k҆lySt5p4brp 9x37[bI1BoW0=UI5/~E36b?e\f~Fϝ`zjZΏ8x4b_^Z6tJU [4EGʾJ{0Q/³(_w[x<0$ Z?smj P8ae$ƹ'c,,T1 kVI13j9иdpn7W&7ju1ؚ3E$B~rGeĀN5|cŵvhֺYG~m2~_=3}K<0ɹ?JbЭI%4`\5'|fXQ/3:>U1FKgkmEfPlH5[sU9'G[:?}ο.׮\kz\xcp_o| [ƬLOR)̛=F9<(599ee1.Bxsǟbeye7"F0Qk j\^Fڏ/|r/n7ǽ=}i-ZnܞO/`#aּ%Ϣ@coia Za_<ֵNO>s˶79_)ʜsi?L ۼZ^KG Nx cFcskJ!;)PQ(k2Dip/rgdf\ݬ"D%hJ aDa߹[/ՈI mkbM Ţա.*?fم#cn08 ;DVYu]h_-|jw/:nB4ՕEɣᙅ#^ DDxZfZ5czJjA> u9Йdق zcZ!pܮ킁YDCz W._IN5=|e i2F0=^6;$FO3", ]E!d9_cۛZ KT&!I[X,ȡ}ze{w:L6?0gD[ZADH$v$߸5Nj>8gvukk |E~7C̟&zFG7^Ǚg:`f=B^Cm[AEQv#shx'z1Y  ШC*K@H;`nz :;Z4cGcdyL>  z%'_т5/ NޭvD\tg [cԌ0릙G#s194[3s0"n pqiyK\*Μ9eFL/0Vxixk u1B:uKݑ1ospa:C`lCA[SD⨧r|Gü2.!V ~?e6x#vZ =]ˆ/O³uxl![a̫k(&\)8aZyV.^pn}dBBsYs = %r-XM5"Ӗ͎NaoNRb:GsaY|{5v"ܽ.$V+o-nRzvlpq?0fDQL&mkkxlT(ǁt ^t<xx_ÿss֘qe.mtE v?l/9ermlL9?}kRj V" Babqx=mFH9ۓ;9e91[OF1B] z0W|ܿTڔ @.X77}G=Y3Fu&[Ɖ]G}Y&EHoc`(w#㪋dž9%`,hslX[a(K2/ϿZ%~2_eȒY6ngQVN(y޼"(r|?1^gE!pCZvv\iy}'ng!{ML7Eo z}/֚nkv\"o6PI?St:htcÄd>: kyvs-9ԑAh~4rϝ9nt;wC'vrF͒ͩ2VN$"ۖ.~1i,o+6hf8pt];a^*UTFIayv"mRjw[vnh34pgN\ͼKwAyfVf*s sQ|y{L^WvN=E෼ͪh}Qٽˇ{NaS=(jë(O5(dkyewU5IO"4a憔cn̓T,ͽ?ccXB,-JܶaYu>eNɳ!!PPd5tռvf،5#m/ S>qmn3Dxa^ʋxI%XB`ŁCwoۨ1<2ҍvR7N/ɘ *z73\Ey0*jEQv#Ƙ˥Q F1y; 8?)1އz\trS 8>4/Wx"{8xycxۿk[%bbp_rGRF9NI(a/\??ǫT*%%9$:t/^:D44/߈ހ#P+Að?;wя8<~wqu;Re2,̞5 {sO{¥O1׳i]>B7f #mDd |eL˫wGܧIQC☍f[,EčU#r)QQ(LfWޘHF֌ ?^Vm!JӚŕu &% 4A7ResiY1:]knӷf8ܞl;l^- аgݛ*M)| ccWRtaĹj^|>F߀1_;+]l,!͒vH/ƒv(VѺD" ;_YXFCz %eo} y6mpc椈-ΑMWPI9NMNcbbBJ&ʊCW\O:?Deg?-ozY7 CK.3«(¯ۑÇ.Wm񶴴9YNuO$,}#Rj"1 ӳkzYG_ā Hl', ;z!soDR05Cj歹c^>փ PRƘ(-՚ Y#1L䟞2$};wN*ky<?2cv(>3-%6 , 0},kF-=d ]S-d_Ìf09 E Eˑ#o̱}`|[̰cRp,#A*r2~㿓Fy}ğٟHikϟ `٬Ӳb{9(lVIGyx(((j]>1s/J#mNsbqw~G?#ό=k":U(<=_XEQ] _>qi*k zXzph{nK!@@yV3x@F8l cwcF`؞luܖrFD؞zj(NBO܎^no1Læe3^plPG<(.(x \F!AaBk|(++U~VO3Qz~zW-{"ez`Wy87z\xs\8_ BsOQEy:FQe@ő#GH=/CCC̰44)h$nE F8}zܖ" 7%ܼi{]/b-{|~6H1ޖSl RЋC-/ط̺!3Fou beBBb7hvs|6c:ƵhAN-Cvy-:7ѣo);Ý0לbD!IB(l*R"fx@Q}"(J*jEQR#H'q_ՀV蛢pռZ1(CEQEQ]@4Ey((.Vq'*[JQ{EQ=KTv.A,g*uYIܧ߂}Z01r<Λ- 챢(wbGé@+C(OOꯤ:ק~*(8>,/KIc 6}T k6|WE0ٝ9%,L2ooa g5VLg>Xse ׯn__ԩS2o7~7줙^g`T(@@*T0a .رcR #~m5B/677'C c:,M|U,CDiP]mJv,GѐeEv4yJSM)*brN,U((EQ=wBa@ KS+ل=WXznB0ܟE|Pذ1s[/:|EN?2Y`(8wNd2zb8öHbU2>lyYYG!CM(<**jEQ91VC(J( "~7;"l1ViC'O215#bR#v9MJCIzog?6l43'uqL>Zm$WEQ/EQ=Ŀ/^KH÷y``H"1φ1errGA sqL =:vLL͉Pa.E g7@b8sk^'NH(?/^,!f qQEQ'EQ=s]("ZU>bYr=~_oÿ}],,, [` :FEMgaX͛=Ǹb'?'q(b(Kc{')D)(@E(DPPzkX.ACFA赡a sjC/  E襡`i kzqȊ$7g[Wv2r,sqDL&d3'UVbZQEQ5({tB!*c cYp>(>~}F04agGFF,%33b֭[Y%EQIMEQ7nܐ/-a=0\ c /cV Shb2`0Y32zz/''1)c_GhhX:0$beϜ sn:q=.]9;-υ87O+o|1 χ9F-Zs.sUEQS(G?oͿV?p2 I3_>}ZrXX,!h|f?aEsj:Fa26۰Q&seY.(&ZhvF''~8&677%T0vJyļ6BDz\Kaq cE7_ܸy3kQY*))n/="3'ލ2a!$.P_$&$#/d$H&'CBmɇg G9jg3$64k}qB"!mq;V#$j麉@w72vkk Yfd2>ܼ9FdoMj4444` DJ= AHGvhi[ FCCCCCCCCCC㞆&54r<0(KZO~s=Kҩfu?F#uuuu5l^}3CGs.َziv_XXPQռr/H047>l"3?3C?s~ /y1Nj_[^{MBuI<Qָ&!1?~\}s<%j]İ @RCihhhlJr)S7Et"tS4 $9 LPP^(T|ĄCAri~~^7Is߹oo>oq55jHlfq8Ž⾑h8iBAkj4444l()ĆBrPJW".—)BOPȧ?mj?KdIuYαxfc$tP(j#ޟ%I3xIEq/gg^ppTqamMA4aRKӜ4+ !#B}{|7*M}~="@[:h۹ vii@mh h_ $[̐X4H!v8'IPy Pyr4 cAs[_&5Y= iѻ?M5xIRh)cOuy;=khIF %Tk$"ͺ`ծ5؛3<{+\1jɏr̪ "1PXLe*Cߍy6?h%éɏ[hhhhܣ;o%q|ܯs۱Q.U413^3iXgF0;OI\ L" _è~Լ!cm55B"|LV=_>C@{zUDU7!ÜLmj:Ha-wMj4444ˤ184`38Tm0C crI mPUَI, lOΝ/w? i} !alϾw| տ /G(M:?s?ɟTuH~WUI~'p W~WTDjū~\)g)80Y  _͹Q7t낣y^Diָ^Den\!W b>m`[aa`'Xq0h߂Be* {@[qJe"NL1S'MC5\t0L檉V7C۶߇z̔lK>?ǡ s\G_B21}UD.-l#\ckC -X(7 3g80y Z~F?ہy|nB`t`jz=]=T !_x9 ۄ*#ߏTr@v|oG|W>s@%$9x}"W˯__ۿſ ҵw읙ۗ1 /ǜ>ߛ?84Wˈ[qjO\=ks9q>| ChmiSv}]Dz6ѹ\'g\*nԳWW\#_k صFK +^;7`Br xiFMd s'Ik/ ;Db!8q׆8XFEn>xOȊ 9/XJv=?6>?|1WC?T&G`s/l N/?px-'D+)˸Qy/ՒCylWl!Kyt"(^RҊ!E~vj% Q*7{5&5[c$v_w,}"s yW8gx͗qÇ R~cG:p{<~x$[_!Z!gV`+V0h;\_??>Bpw"OW, CNY `ԿCw w(x\=+ _8u7Ò g?3v'çk/9pvtm=(W{׻@mqvSkWb! "5DSMGmOr >ء0(^Rv :"W1Y +1&i|Ͽt:3mPUo%R#kȏœtFV0Y+3^E!5YAB|1Iֆ&5[ԅ?o8|KKt fn-Ӂ,b;r Pcz  .S_C\/=??QOHh~~9_Ȃm00=ĺMXa7`{~p~}0~U* D"p%X^C"\͞SȄq6\VUZN9_w#0xчoV(y$d^NS&NɟRK?W8 DjX2|8wj|m g1(E>eRFuUwi)]7:ژ|G ]|cBgF|OPygK"g#p~ 2v.]泻xShRqIe8r> czՅ/u5 8\AG>jK!ۄ] !XPՑT=XK>8&2ro+ka kO=UV%839T,`$zƯƀa_F#{IO.wQ>k*W7,֮.wwBu9&G`Ǔ Lq6"?~`g_qT&QFxft܇?e=wll8U?H̊`-<)f)垬mgd*{0@ -;o0j}-~:wk}*~C>pcw;9'=YhI=?"!weIbMزc`,еTZ^aU쿵M0}k>b+ M n9+?3/p4E_`˵u'~7K5Ȁ?sOm]M`F( ݏ&^=I>DuYLoWNu/& }ʓ"c͵>*EP5Wψ;5JjlrCe$Yxͻ5<)"\6UdN$o?*g[w"I!o[6ws6x{uXc)ak"pėO3il}hRqKb/(X. {PJ"bq5Q3Z:g0?2:[;e ʕ$ZRZf uDkk+fggƄ%ɨsjx!ii֨y I!)c!(Nju;vLF'`uuUۅkVuu f=wĎ51P?h/s`$#,+V띘݅&5xv I,zm]H}߯ULutt(t?gj O>!Bsb|]],Y?RSJ,<y5.jy ]')l͏5^{LR.O|u(N GOm ]Y^R5U{׃q}t ÉA6KC.![ѼP<=7Jޅ&5V)\b NZ^nW[92Au[ yl70^JnFʌfQe4"C>]V7OTKmMTs⑓u52 ɌiF zą$ЫCdDMo4|ϥHfhg>gӖeTXe5m|P1~ho|F ЧJM**ܳQ[ hhhhhhlZ(J9p6[ɀ6{]|hly ] "iyٰ`g 0\Exs7&?#*ڃ@x| oO6n\4U3ϏG449hRA IY( e2mlC([sRPE]cmC,{8o+ozul~icBp6\DkH7EJ o[hhhhhhl㬙 ̔ .\öT?bkBt[AπmP) GTc« rJU)7|q|ƃk`hV*U/s$a 娟OʩՃxJ 5D9k�Vt:FaޫSxK*TkL@|+hxbRYnjf\$/XBA);]f>y^Lן#%& [qGФFCCCc@ jRIsE6hqA$7ukժ$V BRd[#/Y` [a&& UtN!Zg>O=9#}e**f4\&_/|I!%W?H_/ ?<3bRLt-LzBHn|^.͕V D-U(y `*?'B;((rEb]HZߕ&LZ î"rTlL(⛽wMj44446R*BpYh;C._Uo=tqοHa3?T#-GԽ>}1 EvuI ]9Tyv) a5Sq)k##k3`>UO 5Rbp B?RϜ2T1Kd>\y'"-kY&vJ ohhhhl2a;°J̦o?Q?ZFje)Gs"" [ }xy<*|~aс$3SO٨ ^O FB3HgkQn#8m "S*Tqt[b+<aZ$I(`0@h}f0-۷*&(adWQ.` YopζkH/EڵK 泡Msϩpu_S;p0_yjup}'ԧR r7|J8'Z@l&{zlkGKUr(Ln#rrBPjjjłjzU èT˨YE!L=AU^ǩ_E0]} jAY^?75[Ưe.FLXy! HHH̐P#+BC099nR+2jhXDZjNHdX{F$W!6Opj)h&zE n3T --J_5[C!#cDP'SQw^˫{ڔ!cHd?cǮQșP@OO!V1y9kaBP?я a]2'$ ^{CR֩='ya_$_$4$B"p$?'B^2jp;{HAdVD1]*qn<{B~5!1h*i^]h p'^@?d|\٪9 n"j@Hg؏xoKu$c5ѸgI&ʥ)Ej~?}T]]`Μ«/ylxs"d%~dLoRIwG،'\l_"1,U){ƦX41ӏ~mӏ+KQXm%?lPãEEvΟcێfp\ A\#q_nͥpQ.WJㅯ=H IY$Ƴϼ*ҩo ?|׈ˡZ m%tut!`cU[Hy0>MNf]$Lix&w >OH̥ede!xc-_]iF UOuGG t6ˊ@&3T0Y(rgT8Вy T\-T"cpO2:'shqU-Gm#(泍1*żnR 5ީE. pg(LYi OHKg  d 8,/6mKL*6U?Tw'eUb7N6lL e΁qo"q Y"A "u79-걽7=7rfEcƽMj444464ۿm)|CSϗpcYR"FDRe_*mϿLkh{oR(XM/)FjA~EZCJ;T?4i&5:E( -CPٌ_`I[25++s#HPSD49Ue?Uic~ٓ$b=rPQПZ) Ռ BBmfYBoݘ ѸG<6.FCqnz[~3[l{ŸQgC/z nf-7ihlhR!W!yWVDhILz%7rm덚۞z4ѷN Mtuz@PijN1F,ueX7֒ ō|mU!z74ɼ[:O[ZkTzcQ><;HBpZrWPĉ/O4Muٙ$09>^]Kr5n,"+SZm;]"uǻAz:;TdLt(3O5 ODU.s;Z0q2;>k&g04ҫ|u[ĵ..|I^"E\WqUsl~Vk6Z9E,:H |nED?N#޵ct V!>q1l,,HMj4Q5x .gSiп5444,LBS'\uF ċϝя+ųh}NvFwdv (J]SrFo%ɫڣ7~EȀ"P)ӎ6 + V6Ssʷ' o"$+9J?F2r?T+`0eNaIָµ.*Eb-kg2  +mbZ ڳ yT2bHhH`]m#=4ҧ4.I  %%Ԣ9ip_vƶ] 0hq9p '=$)%u#n+1}P#"0a/*RBhkރneRkepeSup-mI1fTd7j1ƆZ%8 <b 7o"Rْ*jO5E\]K> WRYV5vVD"e@̇\Eq-g0e͖g;񁐊E/0h P@Ru]t= Mj44446lh1anJЦ&u&UNfDo;*!9?B&h"8wiQ1 0)yὊܴЮ" a~ m7w tgg6|s\*mxO x R}oay‚@#'KWOYB;സ~J= t4ڵE3(>bK*"_.,,8/^[Z P#I)ۈvNşBCv"L($.b2MzBLN4|f*l[uAy|>[X!&iho> M ƹK*Wt#-⩉L;Mq̸y ֵ7-.Q, q-?s(]^v4/=Yڳ\/TQA ;eԃOE~?AL>U&4>T00My&HLbtVhhUT<G(۲PcC Mh쉼c]*(ղ}IELʔN 9jhB?:[̠=3?jR5/++ i" )mNPs,+WjG]PrDaNy<h!mdsv!W8~@Ra6[HBݱϒ-#ٮŤq@ MF$B.'J0 MH$6Nj޾Aڹux,!gT0`k43!YoqA%ftT$*4E۷٢Iø:h8pҌDfx`*5KHG m Imn1PЯ+ڼ97۵8y2PHY512RԔKK>_~ ׄt9#56BФFCCCc 9{ )k Pi(N$to]w*1ܵgmCh#ǣ߶KMf||Kص{VL\O6|h3DKŐ?<ʿUUbd٦qK&EWhhhR@a"M;S(ޓɫ*ffbppQnfjUiDBa?!"_ YTT@@_a|ضG.q"-%\?{~TYH4*s<D#Uf%\qfx4&*3IA0"ZTڷ A0YJ^18Vt!gӊбсc!/f |f3*P#""|a|^fp#/oG|D漻e \0AA4& XBg`^[EI=aY+8vN`va < XZ^U];1957Ξ*W F_Rk24dP,J8q-$)Ewe$-W_:|Mm2A2٪Eqd2+syy^52 SS0`߾Y 75qPeBIT&Iu-Â=)%LpZ^yy!VWr'_}l7S- Z^΢\,#Ղ~RzEUQ>ZR/_4-bbbR"K+J8ug{v) i&7K^&5 W"s r D0(HdHN~'O49X߆9V' ³Yi [ _.bW[Z=; vmǞaLKoЯfynm])\G5%-w 'ǿi!vڏ.~hMT4߰a` Bf^>}}2.4,nuJ@d-7:5ɇu;ɉlj+Bpp]ìM 51:IR ZfK̷E]_I)]U.7+Bv29-n98ie%Ey3_Cw{VInl`(Nb[ FU+)6:W%M)c!tN/zL^&5 fnoƣ~Xe]XFkkrRVދEl߾mm#&('ŋK6ttt*35u}}#gd $RA8Y\8 {ѿ?HWPC,K.sypdWΟ!.,DoWM ƫq4.9".|N^bW.O~Y֝ p QqirA 2$_WNтQڙk Di^`~0?jzWP1bNjQ(5j{8wjb8p|Ϸć$1\'#4QÐ>hBHݭBg9 ]+jh”9D"!u( nI:T?˫KR^A{W; +j+_ijhl24dPoûo}R?5,(:]r!fN}p73Y^3|>,i*{yW~8t2l;~9t+Uy i_2|#*vZz WOu;z}w߶c|/]A*U/h Lٰ[=]Buۛ$ooQ>s@܅t]7[7;;ߛsg9o"ɩ RRܥnZg*Q9>+M -!z^ 6rFq*jhl 4\(!8F;#ŸΡΖ@׌BvB*Y0kYQKEѽwPmGy5Wi| 8y xz*Ft@5o (ُ ,Cޔo@XTf=!9eI Jvn-\eՖFkeᰣAm9~KKKVa^B9ٶ{xkĺO Dkc {n. WVJ%CgF%UZ]]}KlVq2sϫUw] TH87ۍ5_w̺X̹K.d ,-цfu}vtd5|!"tY*ķB2`gM~tƁ]IԸgI@s"< Qw+ɠT!A25gG繻Hh|Kn9/L8=56%BJ~(S@ Ȃ%?D \X5n5i)S,X\WC8x,B+\GvCwT2PmH r=*}+Rx#V[1 S_Z2cEܐ0,..Pz O>d#wD@)dV$'ɹ*LǑ#e)w [,6'N?.e15ŋkjNSEwm|ԭuLP]*C䞤>EEw\ʊnid^z${cǎT{_gιǛw}5[ "+vùsA)e~}·1q}܏Y;M/05DKj`qs U8v^Z:07?n7:Q!_<ȖPHQ+˵fxkФFCCCc3Q˽ygQ* [Ȗ`jVE 7uWU/vj(OͶmUEL؞7#G |8p@ΝUuc"i.\$Ho.)`Q6?Wq^'ɝ'r$7ןJ֍8αcƾs_/7߿F *j)OG}g}#b$nN \K4ݽ!,d ZZFb SY|>fBhM(_1Vf $#5?ގ/}~t{hhhhl2T F'uuMZhڛsqnkR&MR9hMDr"TE(ko)\pIEsd27Fژ&Ov{t}ml~n44H[܍Ö6o.ܨqڜF{nxkr5&բVjNz5"hsd&:׮|}>_cer)pLpl Q>54s$ViY@Bcݾ{6ي8'&$i+(EB5$ 5ŰXO}x@5H0Rع'PA {hhhhlPz寠RڡH8".\xccGR)/BD 2e"ժ*}&"ubGN:lv?&uhVL6o Eo4N/u6~7s\.-{H[*tGErCVKSVH-]96ѻiGdvͥ$ӳ\C>{@:*щ n,p}U֠׮Gssۊ65!)F+7|;FfPVܬmX%7*jS{y` ﹣אz,֖ZD+" t6oaIxNwX,nDsf9y 7( ww4\q^?55EZ[k]bESd9%> jhsz{QZZ\O`S8!u=dԩؾ ʕ+V"( eNAٺJǏ#hl2 ~##c-0nALK_|3 w J"Ex6e[z4VYyڲG9̼e>9\Hjb=I2fcxb)=tE~CݰAPN.]EBroxeCM1l_E<w*gtבgV.Tc(۳25q( Ζmm֖+o_};ve#+K+5KiGI#x˯s$rٶnM9 JTcvd is+,RGlT^kK]: ƃPaƒ6gf08i;iqeH{{fETD_Q"PWWTTD1<,ك7̳wRfjsL*@ؾ>oYY9ݳ ~p+Z}cm:A@{lAuM%-Ѓg'nlފ!,/*yTUGmG;Nh˘5 6eQ[FsK+ yRL1 IkLNM#08jArw^;22;wn4F$}y41;3H-wZ߃{{R1"ir7 !#.lf׮pQo؈褝CC}(6SS[zل LNL[in7 r[wgVRoE9kxS5eNd_ڇ}y#F*qr/\JQa3;=gLv 8=綱cB!XoL#ȩ^7.#5>5hslW$D>+vj-QL.چ4}td@kvU#ggCFh/cJ=.u٬mzq끹2)ʍ ;!b1LD DQ(h;,/}sVZfQYSR=gGW'*矱jW3^a@yE)>WhQ*ux͊{Kqʬ"Bu]*++F.4J֦ULw bi--*7pe\۠ %ud0R0i; *ӟuݭxD=7p8VN*7 |#N40J|+E4N0\N ư'SƝhu;ʬ7q7ouov5WrqW>ޏ'VF*6WCR# .k[dk" %¡a+_۷RXao3 f=ZgԊd>m",o`d+NkV~f'h3-g@/M H(fFR#B2~@]]+R.f.6368Etli T#6;\3'Eng=rg& X\Xn@uնƬgĦN*FP:IBf4y6@4ؤ}ȬKaXgI{8[lϟb@]mE.[s8_B7Am߁<og67PP/gNWMs=:p\ր}$6}~ xE!'--:6*+kqq'Fz@i(h1(74b|lf.{V^~080b%YΘQ8 31 @wlm()F: fPl4^˴м'/օ)/fG!Ejb1 Y;g;꽀 8[8fmmm엗GQQQeċ/~ɦ.--GIIw9nq0mMFHjPQYffpGQUSa璩*/[ g@s"̖|2(S/?tp0Ci%wb'F03ڶZyO>sFى7)DN?1(眸!zaus33ʘНtFA7#%6UsW` \@ssTiWc~^񎎭Q| KKevv 5u]EMsh]kr[ȭ&@$"Fn:*j[+KP^"]U#'ͬXQrJOmy,Wl(K] !IBX@Mm9Ξ9(Zl0fqFZg8/u Flis(ǢTg;h8BV]ŜPjq 2մuR/g g%~9 @MU\/ LbbbN[3k LOlFq̇FLsMCafaaF4(2ׯ_@eeFlf˖ݨK =hhsW( '$KK5DJp.+8gO_^4S!6'@ϲ&cr )'L*ȓq_ I"iqZ@C>C0? ̥$`HMSLOOv7#`<>x =iyE۔;w2fv?KXMs[N9fD ʢDkojftLوz9Kv I*{Ѹ$-w{q/x߾]kIdW[(2##6.^2l:0ftBRmD8yқm||8\ɞOu*`S{s>ӁX}sOyԬ3aF"vw1İ$o\.k&g5[gԼA DYt>8/Yry cS=ЩP0:}F,NOe 6U"׽FQ!PX\D5;F&XIIldP*Ce`l]ً̿L#ZZ."1Laa~f6#L wc2c3sV԰{h4,1oC& eΩdevis ^UzJ|va:;]x%n#Ji(vv %#,Hj $[7^_Yq2#h|fYzl2&`6'155n0y̤mM2@I7=`#(bl6P}hn@F'k$gab|^K1g ˞qjM&ёxhnmxP(Z0<8cdjfz֖Ѿc#v}f˽o4gU56Pb8&g`3RWzŭ1X^q\zWM/rv] Outi~!H\ShE36Zsq45uDtw_+`۶=6BsA+(ںj;s;vw,{FjM(G}Cٞآ"r ?7\cמ-vNF(),Q;ͥ VH>9}HҰMP>ed e??Zlaơd ˦6S݉SGM]N;Ҳx 5 _o0Di"&\75?}&۽ .jS!: Bssia׮T(رvGqvEcưyD쁠-i=th74eeF9}1-Rѥy#:O>he ]{Y)DJ6L]d _}HFb4gK d#5FaY\\W]Q5G׾{6X!*/"uرT,H#~b!B!|fԣ5v5l.\lf&K8fq)ԴV J/ZBW\LFWi)dDr Iז ZnIĒH0#/e IؙdpMo.z9)IEϝO̢%jh^.'{;σ/.P"y!X ,-jݙϜ@_}koh%Vam Ѫ>]Z>fpK6 ά.IU̦pO}.T72~N%aU))/;[ؼ/G~ݖPseA(CجHjB75O$Q]Ceyj,,0"m*vb%T́E0kjju2ucXV0Wc{~]R6DSQ`C$bfEwFMr%L؀!%ZظM>Fd7oD]mʣG^%cMK'BlD$5B!Jǎ}d_x\x6pnnnԄ˗/[q50km][rU[O?#Gזo>L=?z= #46]Tnp\S׾C0a'N6>$UU UW?d3+W.S5ǭ`ر>+WTUVVsNvi@w-[b=hþ6dC}?sϙgo~,y򤕒v0~!|ؽ{OYgF(Aÿc,/V<K.۬/[ֳX$*HjB='l: 6-gÚD f-՝*HcÜ33FbSaj m`P8TBlݚڦ1igDO| E^J1ALEqjiY2VXg"ܦd| U;͍g>+;Qb(ytuusYO}n>-  rc!=u';i7Z?$$ŷv8_b;k]M׻ COU- hix ,<ﵷyIcj*`9] y3כ sY6n9ݱ(A@\$:](m6F{c9fbU !n9ַ̛]Ev$]k. ךKܳppݒkYL/ e޳eXN|wߤ"5B!*Ԩu眕\A"9+5~i3&蓙 W9~3/(UFVH7 r I  M+B!HjB!IBJ8  S ss-qyuqbzsX|.BИ!bp7Mf1[x |S[oeǎ| _X+!B!V%B9ᤌtAqK[5/QJOqqV{f ctspO'54*ESOٙ%zl?BCx/.lݺFo,aCR#B+)*N64 QFpڵk\ɲ}Prxe}xF}" ]͜q=9 ?|zN!#$5B!*񎛡l##$ `0sNβ1Bup^GA1C(+<3s)DGcb(>QޗǙe02mWj~!fCR#BvbQBhP*})vb2Jp0%cfxAaf5 g?G9uqmvcr%ŋ\gc3|l (qGn"B!V3qMw,.n]55B`72FV=V,eϡT'?BA!ᤞ'NK_MyFn(X aW5N2YvO~Npދnm>QPx>__W^߰Ǒ5α}*o)y_>'9BVHjBU׾5wJ#lS(+\3jC‰.\0Cxwm?_C=(Be禡pP6A"J ;}G.q,H D!\9h 8#N,ϙ32h'e=x_v>p2r͖Ar9N~X̮s?N2t1m1Zh qqL?c<^CaBHjBU)q$lSZذc\w8,gvc׮]6Aap1JBqqq䂂"ŲCrp&,ӽ)e9·0UEI !?OU#6!b0 GG![; x S/3k)gT?5n^ 6mXe,sQ|vr łXŅ0Ja78֟YX>1b-İތeGh- IB ؍cTܰ3 ZPvEqa- /`@xsQ^(%F`W1nS0(L\8n9F)( Nk։x-~'3DDaR Bib:ivcsg:BFR#B'tG' ag( 0Bp0>sL g>g0 Mqqͬf/#(Bsҥ.f #/kxF8&(QJGI{myی0Dbӟr3ʎBl$5B!*'(#"~13 ee_)9\>|ؖyh {饗wqq;È Ň).Fkf4w29cq_#IBFPh{HI 4g:ƈ#*^r*;{ !F@bB!IB!bS#B!`Θ|`3f[-Ìg`}x3qZqe x_& ĀK>04$0Ze^GcjB8v?+lb|tϟYȘh.da.\kwػw d 33QL8Yјts?~7nܰ⌭Xޟy^NxɌg̤HgpF(e똵̙0opxSQK_匌Y̴|^ww,װ,ޗs0wzrLr\+c&8>佘f*jօ|>%,If67vb~\<|\3+ދ粜()!>{E}m^6ޙ{zzlcJ NԄ 8ef{Ky'mdz.lx_z ѩSl#{6)<04E)yEucyvFQbJg?yg$gΜY1 Eؠرc\NrX&eRBqx^Kx>8e{rfkޓQ~91)wMc6?xG(N$ݑP֝Q)nsf m(ŅD(NdBl<$5B!徣.lư3$͔6fCT1Fƙ ΔG}'Ny-# f2ccgMBY>JQWWmM)pǜ4y/l;{gxo֕>"3qO`}g>w)Jul #.=}w}o7C{yeޏh #Usl #m1 e!݁)c3GOu3* NƗiio}μUkGҵp= m~ɵ,2Y2݋zL}b>uIx<"5B!D>2WK9h=龫j!NN8c q*;#x(B!bS#B!BljL!XP_q$uc3L|Xg 3!>Ο;ؒ̈́o>K}AR#Bd$'O(EG{;N=09sѪ(vA ŕ? $V,-Na)QH$ ( 021Ņ# vJy oo<+/gy0‘s {;QrpsT~`?9?{؞CmTUWqS_5:`CX7$5B!DFرc;G{ :6Z t5"{F0{.3څ7~&C7ah[%ւg>s eH&tv0Ӎ/ڜ3?<)S!;y)acNt h}y|_/}G?oBEezYBIBhy9{EogsNDZ Oٗ?,~!0Ԝ_ܸعè>&1@C_<>>~cځjƞ={QZZg $/ư<ԤyƿŻomS2?Shji8~9a~*~[Oč~;p]B#Xw$5B!D?{w ߾‹x/яWXHh5ɯ y>CcJ]1d"fJFˆK=(ڹ ]]{翼ΝJqz}y40pH,ޚd&P7`8_(qm8 ["pgOE{0} q <`3gv P<@q8H4դB'Bi܂Xk_8M=LM@ U;P}6p{.WlNQt+v&zQ\Dcxq aaԖQJpY~h}lъz?,_jw5O.saF!XQo d(_|:MoK~]=(Ag#9!>4?7u ỈÔAuX6fF@B!Dp`W KF95 [Va΍ѩX3hRHEb}3R,49l v?_G_,g7+Ybc+$F&bK6\. bH˓Hn@ݜHNy(:wW>Vn`s|EMYiLo#rS"=9)ϟ4$m@Il6W#x!mEY*5&PdDnbuH!:ׂDU #6.b><@豛@C681R- ,~Z]/!ЯFL)q؈K${)$`$VC_bト}_bd@M0ā.,u#9bHެ~Y-~ps H%J:' BIBJi, m'~RSVuc(PTJlpW`dO<ք-=sj? ,y)"gMYɒT=5L+C9(56רg!IB< ɤgw 3LkqFpf]R}ʝqm{3Ȇ{Γ$n=GsޏùU'gͧrW*6#!McgR}#63g<=ᒐM8H 1[#aֈdY4b/!OKsIY1bq$I$h1fa6~W$*{(`~bPQˆ},B F!PT@}Ù/czjU%%o&F$VWgl;;3460\:э;Z0:8aƥDTFzl;E;@"ʹ)DKcC+#mBlTO!BU'f1772%T\ɱY!TEwel`j|Fg:0ef0J Y2r`& FlԔY3K,B<Do9_B!*2","6BCUeYIi1Fvlw*{.g "%|!ܜ 9Z=:HubWXZ5"B RZ"4ڽwt'S3tFh1 DSCi[#/Ù\4|N!Bl^5k%YbfYr-e{ k)}O]:ϠHyH3EDTWU`,x4$Hl %&=XΙ&1 !B IH)JJ"Vd,;6Q@mۙ"7WrRہ\ѝɍB!O2ENk]z|%_Te8u!BIaXHM2*ˈw;UF"ނdNupw ([B!xPrw!B!ĦFY9hzWI!BIM/wm!BHj [lat]ֵft{sm#WqB!ؔHjoԆێؤ֩Nm] ȵDu[$6B!BI:!B!VfqGjrN)B!ERC3MJ;ē3շadq!ʒHL9u 0$7]k[?,O9<|cױ,B(L1 ^F}}ܦܚY\W&*G"B!Ƈd`#CH"Ȥ%)1f^PdnEbZYv2HĆymKB!xLx" $Wp{G;$] ;.龅"9B!GRC"hdz[m۩##&8d삖TF!B<Hj|-fe_$+{- '#Hj,B!B &0޵B!B&MձB!bHj wd B!X%;/B!B!IB!bS#B!Blj$5B!BMF!B,R\ !B)Jͦx!B!}E^Y"5y?P !B rߵ$sl>q!B!met>ӖGk-5#C% ,'WyB!AN~d5,q?T>Y|^&B!&&W;W;[9#֬zlQ|(ۋt[`>@!Btv5rgGm2\:4%)f EkB!ăL.IY9pgPȳX ~Ȳ*O[3B!BdXvx ;0qcj2Z+E&zqd|9MF!B<($fwc-7޶}>B)rYWj __(|IE>//[3':o3@#B![h%PeJ d(OR#B!?٢5q_&4 [f6If/~R,`5s8T}OoƑl%5B!BO!R ZM!| B$]ڜwLb§<K{ɍB!xɕQ,a%hjƛ/aΙ"5v䛙6vwAcpd./̕Mh2 4B!Bd'3-R;MΤk+9^$3@~QoL7B!L 8sGl䦐k;&"J (R#B!DvI)Z>bSH&oJg)i[lu[N| @#B!t2e7a@(Ov2&4tvɖ4+3~bKЄpKjJ B!sElb'Q_gx cޮhq2ٟdGMh$6B!Bd&۸LbG~ri;䛙k|M -7^CШB!SH|;f%[3 xɖ(;?'e+3MD^ d(+6ԸFfEh$2B!BG&q֫?$2^U?_IV[l"䌡 ^mtFiB!XdBsօd|Ґ|R:{'%6$SWLɌB!9ߨM&Yɵ/_əڹD}H _f&%3DFr#B!Dvre˔87"mͪQGXﱠ>茄F!BՓO64s&Wd7M2N.Akg!B!ɕ8.4'56 Ss25l m!B!($yΧȬJhH\B$&_!B<#7:*#2ƯYBwyEF#B!4 Rj%WR^h\J-nɌF!B i;X{dϼb䖛|#2!B!=gGZru/[g4*#SWp2%B!bmIx,hK>tVj7dWhx˯,qL!Bv܉{NŝJ"+VG!BV$ /osnD[B!PHN(L%9~JNB!ؘV%5B G% >oWޭ g}#IMșªϝMz~:ayF)GV%vnWN9qs2oDm'kݺ?:uk@ ++wᇁϏ |Mm#kt7^jPxI"}F׬@|P;/ ;U=ڣٛv1NbEG98ˀXXΆNx'̞RPZuխkXlXRY՝Ihuk@ +(W/W6_~KZ.QტX-m V I=g|dqxNMW<'14OGM_Q_ >vU<һd T! !(|d]lQ}6.+]/ʺ$| կ4__i_ ^w&ãx]5Iig[oG"H @H+ٌd]i?_ XguhS=Z"y:[3+.[y9qNyQNU}5啵M6)r(;K_?ZJe ?: e/7m;]N44GND8>&x^ .∵9.$H% $Bb rvo567.MNJ)F%h|]==$qZJe ?:^xvv H,'Zc3 |.i^hмO? lVд|6O--;h`21 I7sW%rf>twi9'wXW%WK_FO^:oļvN2ZJe to$FݝhH\) q<Ζ!nn@aDI AWnU ێks,=m}mkkϣg=\BR E$[?p?ZJe ?:Bn4]B} A Ʃ.v(Ut+۾ ?Uh!_[t Nm3vdpf;zbɥ|. xMɿ<_i_ G'Vҿ¾+v!ͬ:h5]kz,t GG]bmw\Icq)G;dHԩ!_MF-4? wj> ̶7&J꫕i [wz§QAAWn-Tk&oO!89Y?Uu5uk@ +(W/W_ZrKZùү`i\(&5ݵXRTdV߄t?FS\[ oYM`ֽդ;4k;:pݵ3|\4'~-<<=kJ.ZG կ4__i_ ^ᯇς, GxKḠ{w|$ʋeMc ɹgU +ڎ9R5=ޛmB*Y@sa/;9٥]KK>WIs)m]W7oN2/'v`WW[$+ڱއ*>w?'ҿO-*sdoCQF?W]WQ 4_c*>އ{t[JU ?<}GT}]vwΟoi_ G'ҿ¹7#zʏyym@ +W(WW1F?doCQu=:O-*[JU >އ*>O 4_oi_ \T}G`{<ym@ +W+#zʏ7w?'ҿO-*sdoCQF?W]WQ 4_c*>އ{t[JU ?<}GT}]vwΟoi_ G'ҿ¹7#zʏyym@ +W)ǖ?¹7#zʏI Z?<\[Qo!t[ IEi?*>?VsΓi?yo@-'OG[Awyyo@-'O-"7V+tT}[=:OO-" Z?eoʏyI Z?<\[Qo ;?<G'k+tT}*>w''hZO-seoʏOGռZO-[ IEoQV{t[ IEi?*>?VsΓi?yo@-'OG[Awyyo@-'O-"7V+tT}[=:OO-" Z?eoʏyO8,0TDY( FXou.T>H;>2stpxzuO [\ԵQ Oce$(%K*q+3LUlhӦdەn rܯ'OZ|C.QÞPeD 2@,F#'ShWtK}^~R2Hܫ<̹n)mG^pSh{?} hMkz8׭thwBܵfk=6!#q=7bƜe-Vw$| ]sG^ݺX^+8H? om3M+vTn ;TN7?%'y#*[meNs$싿<+?h-k~ zi5 )f_&+GG m^vxǪxoY]ľ+6_H"C\|][81.S,65Pw圯h,0g +;Z]+m<\$dc(GC$|\%nM=vyys8YNhiڍ1iЧTs e@t7#l.4oR ?| q;3ZgB#T5Rm5zgf[tFU{m];Pi<:sOkEXmqWX[Zep\ê[xI\C%6m 6EEM*,A "l? pxKvnYe yf(҃JViZ8Ye_RZ?uZZk+u; .SD[x~T{mAn#aa/"c#-+im5\ћAđE:،|'$oWÿÏ A|1q/|i(<Wvsnե[>p`α);-yWMtoUџ >t[ziR6xFU_p }OG:}sΣs"fgLs|G[ơOXEnK,Qgz2@_8u ZU]]ݖWe՜x[^Sxv쬮vZ+ʟ4kH+2BJK$qWGڿ4&Eo5y/4 ^[y̨ $|*m3DŽGkDŽGkUzЯSܡMo=U{ hӝ(bl}?$__wzeJXy^JCJfݜ*6N5Cx4Z3^iщ2!%˙I; F:|c[ x#=;v.T O~d' _2|4&4sikuec8wv<U/2[x'N-1'4q~x(oU )N|%;_7I  |iJxm>-wWꝘ,2UU^.M x"RB?ZjvLW0v mC*T 1y mGρtT>KYEIʼ/l0>1_#t s$_#u8'_o RNy;ˇ1쥊mk[/[ N65#%7\1戲~\ ā赯WZίP\7.մ}Vk[I:kB0>y¯o ҟ KvndWnݸEf"Ok+m^S?i>6Ӽ'K4&K)a6vڥc'+&}aijǚnhګ嚿J?A1s/?A1s/\agZKI[:?F/_EWW}x[X|˥hPk6bC!l{rNFO9Ƨ xn[]Bݭz~k$ IoW Ͳ5w1b $G(??N|%;_7G(??N|%;_7[SlR2ߡNkEƦ*l?%H'i!;yX$qtF7|9힊JhM.;YIi,~\H |q_?>9U |$t=KL/ :-%xC$2AEvD1,N /h/ 5R&]msOyj^^Y7zEP)gݐUPI$I){~3Tvc)>(\x+ŞMlJFnde,,]`R.Dz%ilΞѵƒ6aY2-P KvnP Kvn-VSەjwT0f4졊]]O?:5xbZ?6[Jv$sWMa?oC+swOH{Эu 9V ~Ё\l$r    ִx'T`Zҵ>OkSyr~g掃R։},ct~|NwFa ,q=Thw^4Þ 4t5!G;AA ?N|';_7Ub3mNM }F&]"R42)B0Ln8MVS--uOkWC Ge [MI4?4n>+\7gYi4^iz}Eh4,yey9ƿ>O:-5+rkk\{@w0%Q „eD#t $#+7q;OtiVk{ߧvO?[  yiIQb-6 }%6ѳbWB|1s/񺫪Iiw_u/qswuZE "yUUN`).QRV]w8<%OW)KCFzG?l?Ovn [ wE<'G/S/ѳb?+p|e'>?>g>K"e Q_6?Qg=#~„e'^G(O_N';_"xOG?~QhH1G  '/'^GE<'A?(lFzG\ ׄGkQ  "e Q6?Qg=#~„eD#( ׄGkRYH?z}hH1_g'/'^M ׄGkQS @z}7hH1_?~:?B2u?T,$=OFzG?l՟P $#)?B2 "e Q6?Qg=#~…eD#)?C|2u?*?__qOg=#6?W ׄGkSB3u?U,$5OFzG?lP xOvE:?*?__qQg=#6?W(oN';o"P xOEp / Sѳbڅ90>VC|2w?ZP OEp / j}7G_?; -?C|3wO[+a?_+F?~ShXX4hXX5  7?'~%K"e Q??~†gD$( ߄ImRYH?j}7G_?; -?C|3w?[W,$5OBz}Bz}Ճ ?I߅?ImQ_ @?ʏ ?Ə ?ƿUC4wO[‡iDŸ$)W,$5OBz}Bz}oP ?SE; -?,a?_SаChаCkTi[ |i߅?ImQc @?ʯ ?Ə ?ƿUOwO[‡i[ c @?ʯ ?Ə ?ƿU?D|4yO[ˆiDŸ$(YH?Z}_G_>< -C# "e Qj??~ˆkDŸ$)D|4yO[X,$-OBz}Bz}!# i'%K"e Qj~xstJ®qA흸@׋<c%9NR>k!#ѐIfpkOQ xSE>< -1#yN(ka8[*ߺӤMx3X׮|s_BNcKImw;\[B&g%Bãڝ|;7MUi6Z]ե 6,~Xr >< -?D4yO[S rz^nN[_]cz|7%HZ[/j/[>7hŕ+>wVv -Hͺv`'*Tw(yO[|8M_R|G[n#`|ǸPN1^FgY.u]pvJZr-ZWg`3 V q{ѽ|YxPz/VV}!`[<}Y_.5_4is#];"0 pBh5>|&օ?)m<>w$-r>aܩ |!߁WtMk7NB,2qaχrPj' qi'Ev=&ͳ.X|uJ~S!J?,7j)>v?V?xo?w뿴G?Z?c]y~3֌Hhp|@|MҢtF8乷}m $<=A3#o#ؿ"{[}$55 J,q Ci6O } #5ͤTKFx,ᐱFď/͇C&exT🋴;&Qkq|^%IS#  >P|MO -w<ּAQGڟ4[#JEy/ i& oxsōWK,Ux/ͩoHqoO rl`kKtgI1ln9>?˯?<]ymٻ_%ͻ3@񿇿;dS̻~tk!ˆMcoa--W(>ɘdQ|m13#9Vg|UE? F xִuZ/Qw66vm9|o# 4ogO'$:5oNfo%C8Dӏ>?;dX>2>KX#NxKV\mgp,T2=f|j^.-gX/A5oicZkAI-k$6qIm xu>#ƿ4O _< ^$K++|Ekj4)1s}jC.)#'Q<~K~G=jzs;縕~Xs x??2?=S> $E֓2ʹ=j˃'z:IImudǔNFfC× 5i]gw9?4H7`OoCgG4qٞWgGe?+"N}{sq}:(cg@u.`Ys>񭯄cm/Gÿw:=x*__zUie/۫[uYx[QL0].n?2?_wMƗ2?hoI9?^ωZ]C%Ԗ = yLIJDxy+rj,tڤAGZZz ⰆV1EGC'oG4x?2?~(|pGcxx^]ͩSBbQA}jv*nҤymGQ[b[=* {m8\-I®_kp;G-⷏'NAKoz|iw5"7ȁ->?~\կ[uq|zgK qB[$IŒQrI'GK$7>3*P^YF݋1$M,xߋ:O7%ό ƞ]ڒypB1|ֶy:7FWk|#czmחW=ėYFAc~'$;M@SO!7#_M Һ v_M4i A7.fmQXh.?Mo4F A7?4?9\J qHݫ@?.?@?.?tSk @?.?@q +-o4esMƗ2y?2?9\I īlc2?7\G2_r>Sܩ#8}A!?,:'忝q9Ϸ5R?gZ澁o\.qM(WR-wMYgZ5ؤ>s8NIDKgTQ3EWGW~1칃N?L_- x?N+?xOo/*E kYD^8/]"'4n9R6MFI|`N'܁R}oѯ5 OhZIp[dR̪m!W:EPiϱY$ O3zrU=f~zoFSgkFFZI6?ֿi>7;dF Ԁ3f $G \f_I*+!T^m<1@ %S7NRKiVX\d2 +{SWjzjEF5d]S\YT]ݤE'}qowx\ a2H*7H0o=6wxLK#0,$zWx{Zcs/4 Wn}Qܜ m)AInc/ל G&IV؂+[ VeFC5!8tp>~yyk>oeml;hu B8dE(T9vOf]V٥^oFU|ds}x,]!Us@HH&I>[F79u`?_x=[yV8G=&֬\Ѯ5ĚqfDbMyƵMIXfKS8>ǃp]M=4|}ǝ/i?g?q|uBgs**Gdz4CSMlu m!GkJ-R%M2ۡ7ZA$aKIQ@$ #40=#o6E5y?{k6gBQ0&tTkP*|z|䠮`?ׄ&ך:ͳJJ،Lͫ?g?i F?T?Lycݟ11NO0 'AMUttGWKe º!imŖ"$6R% 'jOKe&z>G%+ϓnaݳ+I1d?40Iia^Eas·ĝ40?t/tGWa~MnZ.e6}s崞L!GڀaT($M[VU#}JgYTR4ʫ2b0Lnv.O0: :o #?tӦ?*=\Y: :o #Ӧ?*=<NCN?d_40Fg<NCN?d_40F s 'BNU', ;UQпiӿaG, ;UQ: :w #ĝ ;UQsĝ ;T: :w #盟: :w #?4/tGWQG o 'CNU!F;U4Qpt/tGP~$40Ie<NtGRiӿa^^UP?kYN<)j\F;?t?tGV_Oa +X 'CNUt?tGW9?[ ;ھgk̐HbHr/#oxt I&d6ܬB*9zUmBWkGОY{x緽3 ܒ.a85VŶ\ w#8'_ ApG vf?Rkwh=4|/ PHvpASjSYBOeQ C'\yk-ռ9SSQ:ݝm;`2˴*w2PkBSU.m&s]XiڄWW/"E6Qd Hb1Uա^7Vlz"hӃ־OO?⣲7o)AO*s>|UaU;jΚڝOCI?iaI 'D: #⫽3\MCǙYM3g5~+ MU 2*싱(@1{׹O?_ɦOiM%G-\`ץ5JRnrk~%<2Y~^ZKf[@|AoPӯ!ͯ/uۙt+'biee+&"&BPw<ÞabZ7٤QNWj[q4?=4yqBX#rYOv^ ֔^\A9 w7]I &\^B057 [\]nN~c;C4gduqzA gZ>}\kBvI\#q;Wp$]ڗ>7 mhwhW$N;p~$i^^h:tZ-#cT)*@N?~?[Ϧ_h%F*Uч*@ W|.teI'plݹ"Kz }vx,Ki?Y0kҋk] > xn lmb9'XшɑxsVQO=s51 =nfqkxY<;H9.UtiKWoG?vFҚX̱y +Aez $t5+}^[{u6ʩIṣ;TO8ǠDI7zqiW-Mx넟a`ߺ:ש7j:E{m\;7?׀;W5.~k~~&tt:O8>SW?X|}VhO[BOnEDrrR90J8Ia.WOui,3!PN3Ocx[}_UV{viu#HȠ}W9'<Ga=Ց7]J6tt6.-<ʭeanDey9/QAv!_haU@eAT?ii-S𽉲Ҽ-ڗ2yILF㌞mfT18^v3t ]4ߌ?xWOCƾ w:Xx[7kE4[Hy;>(^Ҿ$x@],{suٮ񝷣F.m_W1'4WWm{J5? hZ{ ]YO,3UtkRYIH Asy?h½o~n_|?X?j:I'WL?7xsL!idװmv>=Ʉu_xW jʆ[o^_cRzGu7\&->YdD|IwNx۽'Z.KuņxZ,ZvͮnQ%4lr%cc%" 珵=+N-aаѵ&,ZeԌ{,tM$[JѲG ~߇oh>' ҵ;}>]\ԞgѦ&ɛP̛\`#b v|eA0 +5'&~$y?xYo~9n2Ƴm>4M;B9r}eJO8ѽhq$| {M?G\ڔ6W ԞHwD"(ʻHLI@ +5/&^$4sǸY6YjWD5=9|G^Z*w-4leԬdјJrπU|Q`45q~,kz~L-0h?Ѿğҿo'#Q ?OF=8o:ziϋ|s Bm"6#Ht[F^_[}޺xg⟃QéaqWuMJP6 I/D:eQw6X[DCCgOi_7ğҿo'#Qf|W&x׆t>ڟhK닽~jfmWr9x 3oMO`1ffPq|z57OU3P.t.佒'ϕaϗȈ@T^OSJ'n5N,xgxIPe 8 ˎ]zxߕ4wՃ9En8OG7> G_xB+ f5xYahQ`?|rEο[[zjz:uV-2kP*D墤ʱd ܼf<1>ڇU<{IxWEagn|'g\ť^\ U@m()Xz߇tyżC<ͤ.;H6z?*/xha42o.VB(ǹ玾ҒO}ֺ=ZhKe_>7y?K[arslϬ]IЯ k f!8_ [,:&2Iuyw7ef~ϗxy<3CT.4vIt ]xrGʼgx_ jJpϱQ\'"y?N"4KGqYrTo'^"y?OzOؿyIT.1qf39'V n;3\n/uH6,SE2ndV, ,<5+mFt-^-(4x!T zMo]'A zl:`FcXמg\vrmO>;Ō1=;u?<[>?K+%Bt]:62 TUURB*fv!UT k-,g-?wec{lboh/ſtb4A{TeՕ5 K缼+HW$bGG.U̫e:ǧZ\Gyuq{6 i~\6±2&Y5&'m}'G~-mq|/VDKE5s|$O Ʋ*%MQ/O H.7/v\nA,C!):e&IMW/Io?U[>?K)?[>?K+?H|[cDCe_W׋_ U;u* v8?϶?[>?K+5a}Q:_1]{48/϶?[>?K+eax}Q:_1]{(:_1GGwRQ ſtb/ϾD.p^-}ax}WE!sw/w/@e>-}`}WQGyG>-}Q<ſtbϾ (Q :_1GGz{(?o/ſtb=B`x}Q:_1^E!sϿ<[>?K(o/Ac-}?:_1^E/cg?:_1G#-}4S1fy#-}?:_1^E/cg?:_1G#-}4Q`G[>?K({ſtb ={Gx}WQGs3o/Ͼ*(0fy#-}=:_1^E=wſtbG|[>?K+Ш9xh/Ͼ:(0fy#-}xh/Cag;:_1G#-}tRfy#-};tb=y熏?o:_1^E<sſGxxh/Cag9:_1G#-?K+@9xxi/ Q >fy#,}xh/Dag7tbF[<4WQGs3o:_1G#~-?K+(fy#~-?K(oşGz%}^;oſG'#^-?khza:kſG/#~-?+(Ýu׋?熏?g6_3^EWF[<4 ׋熏Z(0g5lfF[<4 עG;< e45lf>Osg:_3Kϋ?熏Z(>:gşG'#>,?+Ѩ;< eF4]J;Q*[} `s_zWU?6;꾳B9S(Oկ%B|+o xw+OgyF6۴Pv\C,F^6W̸)q9>B/키tߊ~n5Δm]GQ.$G:yY)+@A%*2k]#Ķ{CJymSͿ1B"Ȗ=` b(QwKڱ4/Wlŗm+ R0-# ]E~(G7]E~(G7]E~(G7]E~(G7]E~(G7%I|]n^As|׼Fpf9~+?V_#|B}_+K/!׍󾯕<Jw?VƗt^GKǕ- &XY$Kkin5bB"ʰR۶6K_[q-ހ#{)A}(IvP;]De^ >"jL9` 1 R!xԕ*6UGScxO׶ ֽo"ciDi%3$ܘA%v ̻CBDqj7TZ=utIuh]1!Tm:#uY1Ǘj? 7ú+x\n/I4 =on{ef2T&ᘦ2ҴSE$PYf4!2&|?wx*j_ۚpHr@yb 2\q+ڹYvcz᝷ouev.ie]hˤً.nR1!ETV~A;]|3|Y*Vm<%#%&2dP&.|evgp7>Ү/[Ȓ)$HjE E `b _.hf|_xZ^MC5‹8<3~ou*'sra)bQ7ź(Kں1 !m6vs> .h',u)AK#;)vcI~YEMBKuMǙ21k8O~Z}B #uFmUsc_met-b]:P;I0!WD]_nDH[b>_5!{F+ R]Z%HflYd o.~b1$> *?^&W6$vΏpbQKH1V Bz~/wob  Yxqh~Ai1Y:o~|eYmgXoE/_J,4-%LѤ[iC!1x#þ$|%wi7béө $#awz~l˷vv2G`b#t%/V\ i?hm/!]ZOҲZ B̥Akv0;Y hE[xwZJ"xp#{@'LҸtsvwsEU((((((((((((((((((((((((((((*y9ǕwgCVkX@F%ksYpj>,e쉌z La;ar:uß|gR@Zw+? xmnH`!h=k 0e4`r5~G奋n;n/+BhUb]w|WsGc|/e3ekQFVl92?%I+Ώm;{k=C|o#=m*n3Mwz|"'n/tZ;lXٻpLl:j#}nJo'!?OzW4kŽƧk,_Gc 얢v;FC!vl yN/uu4ԭ|5IN`?)#8o}E|{ϵt/?En49/u6յI(0iy1) <vT^uWwt/Z3Yy,LD9̀t\p _[K{t}Sϋ]\+c=B[4%L|ƽr>^NkDŽ wW1EAҬbCo o$snH~Wp)[BƏ5A?u) nw{F}k"/>;/Vc ԧiY!F.e-p(ɮǼ,>ݪnYD9e{.M*x/,̐7as%Svuzs\g_E(׏8ZYȍ l?|q 2_i'?[p;Z.?~=Ÿa8ʯw(*]3e>/*X>G־ˏG\o튟ȏK1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*X>G־ˏG\?*"ǿX>G?`[]Z.?q[hة>?`[]otOk4k_ob {ote>/*a?}?Ə튟ȃ1ce>/*/˩u2 k8oTqԊ־ˏG\ͫr!S1?hݦ>y~ ?7o|*'"6j0XOJ5?6.ַD0PdG}2HB b UrA}?ƛ*=~`G|.?o ?᣾n}}r%{˝fg9?APi~)YQky3AVS  E/Z=~eG|.c?ڏho >־ˏT_RWG,Zg #;@ Ic#Uav'E%voih,m%̸?h3Ee%v?k\ÿzğt ZORl6ڍ )eL[ 'MSHDqQ VѲ""*BWTWV%&WI&V{[BOsϾjw)罝9Y^ 'k(򧫵#y+Y5UcOY}Tmv}"5쐇-7*m9}?ocw+%,2{/o il>\O3krGs&|7$[YV9chء 1Akz~x:4mVm>M#&Q{[hd rapGkcxwqtMZ/ w)s]_mǞ1/nUiv^]?Z?4fuVeISs/m)}xxxJGnFe{2,bgx BĮ +?jIľ 4+=v~W ĮD79pfl-hOmycx֩e{oYC2mq3)$ n#2Eϒqʥ'kI&m).]Fwtާy9.M>֋#;!cvLಉe$p>Ϡ!B熼R47Ctɣfo5䌨 g.H `|Yu!_ sD/Mдm̚{-omHkk \L{ }>=.;"8$H6HiSOh⛍d4^ܲ)sM ~Zxj\_ֿ?o~32RD 2!1㽥Q,O K0\IayceReOH $!7|$ӌ?xNJ GLhftd7Q̲4+)IAZ֥]אm]ZQVjWM;2嫵~N__:߇ڮtJ|5;oܼHRAloiT!e8|owּ;%h0k># khZXJX= fqˎ99Ca/[W%{yFT6E *4@! }|𶕡ŢhwKqfGEwBRDX$0nW"BK ˟2^yS+{tҒ9{D-S{>|QjwV6~t]Hj6I00\O1BNC3w5o>H.5K_S4eEUcDRAb lWeBX4mٴv\75;_ZsΞ'q#Hzq $t +k[ xfN g\55kvfQޥsu9 :`@$`>u V/xR:i]=ѕBٶ6_XvfFo-=?Ğ_ Io=_UG}Ddd#nxeV  E]>hvtbPW ҿ'ӢFuiq4y-5O.4 )4ʑ'Atu丧lj {vvT^j:&N\H_sӭx(tqF4]T\ݤg%*,F8&+9[Bα|* ?ɭJ2ݬP'+>脖XV~ Լ3χ:jBLfM$ H9'spxt:vt lda̩s ib:[" oW6xYrWk:6ݵR ^.]7T[;Y3 /+bWp#qߴFy~u:{ Bŵݬʖ::UA kk ψ?om:X!5LbIfxйPHPXdN3䕓e:ξox79{cHqI,1jVYQw+Xs+Ǟ Ǿ}"k/*+?;|t8x!21] bԭnE(uudK;$ZGx_x-R6xcU42b‘\BI:}xcX|y[oǙ;>g|$ӯ4^ĺ9K 9RQm‘YCT"'e{-𞯠X.=Q2GXqE (d)!veXüMhڛI3i.U>RO$ KFWTvh:dr$ğ*o>&ͧkm& }OKnvgsV=:Cx^FX_BxR+ռ[Ɛ"c4rq懬Yƙ F8Uܒ#.Q߁Ju*6k[ޏEؙ&և=:ý3Hҍ&泪56c-qu,2Z+qąsSuZ6X7~* XLtex!A2lIJ"]|+[ kݝԭC,wDa2#ǰCgǚLi? 4:L-SV]QhV[ۣb ʱƑ[wU4NbF9%{(Ju+Ke(z.1Te|ۤ>ZýVOXK :̱k2~t_+c2xKDKǃ|7h]xE //鴈X\$G%E ȧjyS[j~ek!kqi?)W!I##n?~Z֘ν>ntYlݽ.nT+n|fYR8YJy(NϛuN>ocό_k=oHѵM T]6hAauʺv)8*y9Ƽu? UR]kWQ\_vӴ,Ы4:M-V6y>|w^oJw쿳J]<i 2 coWV^x^MI=hgfF2~ӵfw#&:bպ/Wm{ֳV{kky+U8oM%XMѿ'MSsujXl $clQq+hyʪp>QmYdu&rF9U{Wh?ŭK~iw; P7]DXf;v$q^_>Q Ǿ[jvVw]\FCqTaeaAk'?H%~-xO xj-f=Uךfi-\|@UD d \9~%SN_k]+FnnTm> dUt]!0ƠQQc+<П~^េ^#|C 7m;Ge{O H`bv5o6q`|hX 5ţ^Q {d8a`@5>N0 myiu׶ZI.xQ㷷`Vi#"9b#rQ_:?+^|hoGkeeg_8oXgmc?ټ'v͇ؼ;]vippeΕ_x{K񎣢k#N絹Ki*Ks87oY 2 URpw_@ } /?_VB-[M'HnbInmPUѰyV ߂?ϊ&gU#Gޓy7U) #;NvQα2Rw=go|/W7<}iYoWqɿ!?Ϸ+^wƫ YC }ROڙey#F~l-e+Ӿ:xgD/:>n."eIE=˻F%M턕gCVMS{/_@ } /?_\ ~ź|8;_m&Q;3G"cV8Qգw7Ȫ LmhޭMѴ\Y *1pުњq[я.{%#^򾏷+F:SV6:8K r7ʮ>v  5檚 A%}ǚmx.<΅<ªҏY*J~^a?/oyy>_sß<fJҵϹMѢ\*{9,wDхRA|ew}u[I/nv>l5bnAb>jnO9-G$3?_/oyy]ԤѴMCPTI)ખ]H?>?x7#j^/o7>4ɕeZY]4b>g8*p/7<}o|/Wב||Z76v:޷q5m>3jqWDymwO ˮJW 8G+$,JmoA?X^򾏷+?io7Y+Z>EquO02[22+n wcͷj0xWM}׆$^ L/\E)(Bn2Լg/H~3?_/oyy/_1KD߆]MĶHpbrc2pP*:WyXu}"Rɵ,v/4edfy0\GrLiY*N4$w7<}o|/W׋?eHЬ:vD^Ӡi,Nwaoc [#?o|-d5;HHZ >HdUR!fˆUペiNAO#Ϸ+^#/~׾/g%|j:}Ho$mfYS 7WsUcS9iJuW]I7<}o|/W֝?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+^zM?^򾏷+N?ϸ{?Ϸ+O?{MH)5hH VGTSUI%A}]_"bQ8c%!:IƤƥo<\YGQ_ч~'?[p;!${-ԯ_\]mxZ5^p"Y#|?H\Ʊ%*$X[%S[bnEB-mcJQ?WG u_?}?_}b?8wO/|&[zwڒFĺuo [ei$Hq,y1E<=O/~ o5I<35yr Ȅ[ѡfe'7JQ?WG tGiM-?+>1B}?>}7W&mͤm>#.oڍ4Q¶N4;xڧt@ܚͨMk^[hqW|]ee>E+?D]?RsOU_n3G4wa%mAk~#\Am1(ީ#"PrRFywǃ>b͌O 9/Z2Q`Wk6G\tA)^9'*Q<XZ/;>+?}GWʺ?x(Ԍ_ʟ>3'_|wYE+?D]mb">Ua=xƹO;D?ɇZ{G6%]c [-,eխm+T-F욅#Hɹ`e#`s5Y?_I}콘ُ3lvx(}GWʺ<yV&_>'McM7͹ook$#׿R'̃B aw7W6P<=&]]TQ1L276 aĽJxk^xy>&x=>Cu9њ_6 >(·hqw)`TB`?5*>94OVW7`aRNk߾ۚK.b#3%k ekMmOĖb˶r.$@:av5*>94OVQ QDeyK)u+?*G_g~u?x;_ӮԷwRiHK4NЅ܊YB\;âg~ +wϥͭx]M0#"ڥP$cXydPnv?5*>94OVQ QDei,ܤԽ븾3ɾ'~|<Ҭ?x+ ѵf{GA4Vm&X<Ŀ[\xƲ>#C75t%-a+~N5*>94OVQ QDe\rp.:U}w?<3OPy}C><i>~ʚgkmq+\qj2# MftQQU'+) Zx_uo5>6/oO$xYu>D|%ۻbGHuvdn^e :`箱 cU.9xZMO_KNmZ6TeF\?I]W*G¨?YYfqQxV^UP3CV𗋼}zkXu]\ͥiOgd HtBK3hO0v\oq}>z?2\ uq4e*l&Cg<U'+(QQ򲇖ign+/uه#y~)~[[j試=q^"$f7`. _ t?z ږj&'/ب,+{rVB*R #Tj?T|s|hFEG?Ɖ[IǑi[ҷ<G~47-48}&=Z3 %i.uk*m”$q&_ t[W ::;I%̦}7Pk{!*j!mKW5*>94OVQ QDei u/fכoa?ϋ5}SMKrC<=ƫ6Ѩ13H$+Tj?T|s|hFEG?ƉƮYJJ"+ΒTj?T|s|hFEG?ƉRf=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ctW7 QDe5*>94OVQC}տP}f=gIEs5*>94OVQ QDe?:[ct/g&/ge^ӎ_K¨?Y_8@a*]/Ů<:f7z3$v@gѡЎ"5%9*z(i䶹fUL4]?T~[Exg/sE 7C C'~ V\&H,4LdْKk}"& 9"ߍ놛קx|w'ռSYxZXvZy$C/FBgjx(zJfw/]xSN}'7E3:;l)2W.x^g}EJĐgkR[yiڴBbe+.U7$ %N>hD?}SSC,hql-Ka=I p%Sd`Hŗ=7BҴm~!ou&"}udVb;L_o9?S>]]'|07>5I'CB'źW=3Ii˫XXE.qMnLX$ud}d +}rvm4KuYu9P[I}<d{ ]z I{AEV ( ( ( ( (9/^KZ?x^yk~扴g4NZdT\]yXzZhwWw7^/Mym&E* ˮݾV+>%AGvp_![rמ.Ӽ%xO𗋗[-Dk+f\QcktWڬZ=pTi-I#mG h*vz^׉O~(4, V;khciۇ$)@KFnsZ[> 𶟩m^d3tv@ȼk$yyEY>=k5=>~ SOӢ (nL *dQ>>WsX^\he/ xu_-.1';mMÏ7e\\}úWS#fOGj/Ro= 9j|k_*K,xOM\xJ([v6-q t;в_rWbOо#k2x_SPC5YYȍ5Ϋ#Gz F! 7;|_پml6s>#| Ѿ%ZjiRjE΅\i"=JTѕ7.dT֍u ό:7hK/K׋jkn|IZ#G{$|-]}ot_:Njb4[W d;݄r@uς^YmV-Ze4ͤF6#s4k;r\M_kLQ>c"ZG*)UXBA,F*PfQXQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE&]ecZ}{_!~e_>jUoyy'+_$~5o{^ sO-$~5o{[_oV[EյU Ӌgm[ePS8"G|?"?ʌ6m} /-)χM< h9yߏu_>E6-=t+L;}P7$Jgc flҧn#Ǜw<_=}/ErbѬZ+6wDbCØ^C1ޤzz)χM< h9xğ?b'ěk VOIâH˧[^gQEUpD "B!1I%>3c:fˡ,h%c%`w@նKbdU%? c3Ï&[O4œqDKif+o'bc9>47>,{)χM< h9uQW9cN|8io%3G)χM< k'a# sO-?N|8io%3]u}w?e?X9S?x m? sO-먣븯/ rDZœqDKifS?x m?]E]OÖ==x;Kۥ/}焵ZvEsG)FMI#Ja++O7\إvX~_-+SڍZ-eiX[?R)UY.ܦel-cӌ\bNm-ޯDEtz+Ѝ_Z?Xx#o מA5pǏ]?RR%Ԗ[q{ ɕBYDC1Fv+s|r>3}π|7Kw#^Nn°G-eOIŻoi:||L4;[6K-uˊeiPI-#gMƿŸSk1 }iim.ʴnvKX%GE[yyd{+Ѝ_Z?Xx#o הDKIA9Юn[iNa<7.[h}Ś@Zz{O|X'_j%FmBWE$iw-xfY#?bdz+Ѝ_Z'E?|2\Ch5HdYSU2d@~˿I@>$K\͍jx}7ًUΚr%ԉ7c00fL/W}x&_Hl/߳#}?vl8M6j<B7Eia++O7^-[2/xg>#ga]h 1 {hQYVLѼ?sxSgS {CԵiŕV3XFb@ 2;_s${-/u °G?F?tV6Bpk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?<B7Ei륢 pk?F?|jZcְEkmu8qƂTU@@|oɗYV_{RafhN˧(?Y-izmtmV^2%uZ˨7Y#p GEu=C(# /$~5o{^^ ӣeRiD[wsq!E*JU܄RB#«}*1 wM _awLrJ?-jzIf_"!q$M{,R΀nىؕfX9ȼ+VÏ$!ԗRԾsYCCc5QnIAzcxNJvHe0Ooyk-ͼ,*IdpA(*MWZ~}y7:YY&o6eYPJlM५%w8 i OXxVijMRᮩ5ն.\dY6,0F $s[qFQPO x/8%_k"Y#FcU2@'=W-}% r?]"Q . %uUs_r?]"Q . %uQυIK{˲w?Ck_G,/|s6ȕQG>%/@=# . %]"W]E_,/|s6Ȕ˲w?Ck_]us_r?]"Q . %uQυIK{˲w?Ck_G,/|s6ȕQG>%/@=~5ۥ.{t𖱺; jVɺGʎKuyq `_?K~[+#~yoش6}Ɲ5E%j'paQ,c#h89'|1ŏ^1N}&{{1kI4&M5Y#Xɞ<+X>ER{]N5wƮi+ з,S8"G|;*Ѭ./g爭\]K5$kDiy0HLVpOMBXMz?I^" h RUN3=Zgvy|{kyv7{ɏ9ظ(r݇y_5zfh &ѴbV"k.ɑ-q#0#ùOhpj/>o ݷ5#,qH"ٲQП1QI=[ O'_W# UKP }e@)=Y_Jj]-ju9O%5OG/ MS륢B'5 U}e@)=tQZ}>??O%5OG5 O'_W# UFi_w*STz?>??Z(->s_Y_JjG'_W#KE}Nk/ MS*STzhP }e@)=Y_Jj]-ju9O%5OG/ MS륢B'5 U}e@)=tQZ}>??O%5OG5 O'_W# UFi_w*STz?>??Z(->s_Y_JjG'_W#KE}Nk/ MS*STzhP }e@)=Y_Jj]-ju9O%5OG/ MS륢B'5 U}e@)=tQZ}>??O%5OG5 O'_W# UFi_w*STz?>??Z(->s_Y_JjG'_W#KE}Nk/ MS*STzhP }e@)=Y_Jj]-ju9O%5OG/ MS륢B'5 U}e@)=tQZ}>??O%5OG5 O'_W# UFi_w*STz?>??Z(->s_Y_JjG'_W#KE}Nk/ MS/Rnb]6uIcYD}K<=^ n<3Kձ]",Ά+U;ټ*Is"3̞g^x C5k_uxÿaT.˕rɵa0I#YeFN:~>tyſ __7 GOz9-u~.7ÞF/7=Wv CDڱ#34O,;YPߟ£PMx C5tvCbS8A̎|7*h#Tj;$먮G@OQ??< Fʚ>S8A̎|7*h#Tj;$ȹa%)k_]W;\O_?xYf]fHQ,,me-nV)Y>l[Z9 1?t}xb+-csD>56vAWx(kԌQXHfĽ۶`$@@|.ľ8>#x~ Xy+[oSDd]Yщ,fb#¿?}h<+W¼CjBV?jgk7t(xޣ9bgm@^biW3@ 00~%i5үA{x[^"ݩ6f,&2cc"d+x+G_bVZ^s>g=?Ǐ1xFڽy7oz4}7zcmF^@.yV)Su4O᎝OaY_K R%tt` AdAߴx+G_uyխk`'gKEs_h<+W#¿?}cqs?-}_GxWμtW5#¿?}h<+Wp^s:Z+G_򾏴x+s?-}_GxWμtW5#¿?}h<+Wp^s:Z+G_򾏴x+s?-}_GxWμtW5#¿?}h<+Wp^s:Z+G_򾏴x+s?-}_GxWμtW5#¿?}h<+Wp^s:Z+G_򾏴x+s?-}_GxWmC9ttGr|sHӪd+R9*å}II:o'eQ_g~'?[p;WExsÚ5jVz[[r*A;7õ|T$~5o{^s~2Ӭ-S^ńR.q 1_"PdR"DVO8wO(rm0E֓Vؾ'} v${*0tN+_6툹NZkh~ass}-{klAjdiH儸!h ]%hjE&!uvmihEFs|=/ jqxcĚ$~:\}or  7sk[=Oo?nw/<L3V6,q4{7P/ Xt%]WvWRRİ_%thιE,8-76О{Nĺif6^ū]:,rO}AF%e;s~~z_=5I|] tk%ȚlmْVoXԴH" uZzB]PCÚTIk>ciphn(Ż PJ$u[]i6{oyZ]^j:m͔ E34sFyp  My0/|;xz+ E2;WN1(YU%MC'lޟ뛭&M^EmFkK6/y2Q7F1.@bj߽ (B(((((vKׇRֿF_wO<j/5 [LV[ۥE\F4jw ?x-kKjϷį dzMƑ}^F SRծVuK]mkVp=Ğ'&(߈_ oQꖚd6ǪK ,CtFe) AoqZh+\Oaf, լexlci{ Zv.twY_Hv}h~2ɵ 4Z&-M*/Z~2xBe#Ly=K2O[iξl[5dP̻+◲?xú~5Wm֗-CCsr,/!*&+$~<)godp,-){^cƯqlkuv@TA'7$y]m/RI5\2W.nr2)fE_ƻSxEWE5&>yY7$i6򲥼 k^_. vp :XuK="uZ!xIAU-bU /I8CCB6O-ALYa^%hȋZ-װ{$#,en Y`p }}?C"3;#W|7;<ߺǙVc~[e\K[?tK%xܭԈJ@\WݗtX_-|J5+k!UQmEPhrH5s.'dcO_ tBۛνk9m{|?`&%4:k^tL^__ǩf0IA!˽ʑ7%aZS~g`ﴂ'?&B#z/#,o)ov.jjZO[Q/YҖ"-KN঱F*P?:=ţ]\"K]2'*DrR_h{ū閗$u O][o*(`)^6]C@ Yt i6ZƫZC udI7,{ؼ.p:Vs= ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( .TҝVm2/O)kxJL7<b~TQEVygW>3w]H;kRpLl`$eFpAq_Y6[]_~n?WOן7 #_XFxwGcxRak[n mqe+S+0j/gاFhFvߡRȷ {Eo?EuͷVs~4_WW)GŞ|Y|>6}S/u u 9d^CHGV mr`#F㼷[KyE̍,oG7!6tͷVs~4_WQ {Eo?Eus վ"՛O}Sc^-K"mLK-.R%ĠC~%|S|umRTmg'ӑ pE,^v:2G s嘥?Cg+ߎƋ?oh=2=cO|^֫u/4n.a[bi"˳Ȑ毃:⽏>wV7Z .C8{\[nӽͲYYba#1OoxClٵ4y~!RƝ6o=iK=ҔXK1r SWQĚatj&=U49x*Hey#1T|1w>"|'Vsē}4tgyfh)ip-êJ$yǿ*+VI]Z6[/e鲯)(*:_!Q j'ΗUkBios~ZW[A{| 4uu=>dr+ BېvWQ_o &7VvvjЏy}GT,1laa=/Oߝ/5{E_K*o2KJ[}3]&iOphzt4)-yYa "&Vu{_j>w:qjRMul\{N w" `Y5bs"ʪY񹏨a{3+Oߝ/5{E_K*O5?Ϳ4xOԿ.olz5+ tћUfp("RSA~ԺCº]n[Y%swihH Y+ )"%co_SWQݣdǓjryWisZK } 宯>5w_I,nR+Bo\>@ o_M^DW SWQ3VӧZ~ɝ|8[1Ğtnc O2So_SWQƌw֯~3|EVVY=QH"Z6gtY|g>_{y@5{E_K*?M^DW |NfOoxUx,eg8'!4p\E-\|6}i}GT{{-7w#ۤ4ɢ %P`aa*šx^N xU<soKxj{'[jMZkሣ/,'<ſ/OּU6oꖯwoe{t&r4AD,cP/=4_Sx\ &B.P2ŸP/==kVMg>K{Qj->ОT|@%Jx➻xn>5LJu+KwbmNkA~"PIX: 7/{)(*:_!Q j'ΗU~>8>xqOiW?&ͥtEǛZA HR"w}̞bnMhYXxCR.4|j62Xl%'0c?xG)(*:_!W~վ!g"Q+x??ho<ߕSmּIC|EJBq<܅Ӯݧk8K$J I0}C 00 j'ΗTšZ)C~|A޼ /^h#a ŕa /trvP/=>ҥ|qI777&[ۻJ33H4R8aQ@HjuAm!z/| srk̛w`!70@^E};#xfceݳlݼcx:?Rx |W:[ɂWIR5Ss[IP לWQE_y5)U_xMAD;V,nNܠ-[% %پv7oz~n67Qz}-.xKO?Uھ72wU|ߔ )O= _YX{xkd-qmg=ܰ3!Grjv3^g K?%Ҵ>ƺ~oi5<6P. [kcyLv+$o_ٿovygwl|1oz]^g K?ɤ|J&YԛMԢ,bHVhXɉ'Z4r0;Pд> V_a] ֚Yn$ew˴vw{^g K)sw2]WA-|_D4emm[!^5H@e|G_jt}Lt<]&4u-ťOYܬd pc/O mj:htzW[#r` K|/#y*&PÕ>]xF~'x_·z]Zo ]Jݡ-nΝWpF+'Qz}-.|O| >>Ǟa׉?{-͝I c#X`dh-/+]G,-Va݋,<ݝG,ğKiw_iWl>5^h-aǗ1)Y=DrO)LLb U|}Kann.oLwRU'gfiq¢Qz}-.xSL?Pa&@䶶m$aD+5Z6*Ş~_ ?cg-re&L[*~~` (><'_uw}ߌ庰w)O;9nZll/l5ڌu5Zi Op|mE}jU{ ]Yꖲ>5+Hn5MMW-lB+?Z]R6[egGuI{p7j.mRo*Yhwe3q-/+]G,-Va݋,<ݝG,ğKiwVO-M2F}Ǘ:\Kmw,`9iB29kK{_6Z_? ~4drc%3[\w17ej(><'6ejvo嵰ԶO8Vtj76܊*A>(\xľ-xL:.x,oϖ#YauE$T`*aQz}-.x$?Zng[/^H;o<0$6pDb$۫GK GB.;4?a_"vؾ7nf\6GϜd=./OϟsKxOT!6˝2QS-nv1cDBJjz-gGy aO3zHc_om?vW1$^g K?E2V\񮞩BmK]'Ǘ:e170Zb yƈE׋-xQo5_hjk VrnmkhnR0ȫ@0Qz}-.xOmx]uOh% +OkamPq\"o>7$mTkjz-gGy aO3zHc_om?vW1$^g K)ּd&<}$ܭώ&LYb H46$V*1 Z5:TV׎<>ewk(V_2 &Iab"ԲI"<H²٘w#_*>3YXY\mM6-ߪygS'Eh2]TĄ2O;-6[m裡 j<_KzO]  vUe[g-ՆJrcg|dnfSneπt/?ih?aWn/&3SZ2 fGXSw3eҵ95{}^KC<N7xGϵjǴ.x=c^/Wq{c⯲]Zh'[v<) Y7!["I7e^ӼKi#EވHXthhdDS-%*GqGgx-;_Ƨ}a]Jtrg2lTEB pdVf r]zZ1rV^ZSiZjx[PLJTdSS3 $Y"ÀW!5KtYA{x`kGGmypA'8pڈ(;iwVO-M2F}Ǘ:\Kmw,`9iB29m[ۏV?<~ k\+p["G*VVʸ(></kWz8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw ß/o.j(>8^m?x|99붢iw nkmS5ǚ@U* 8 %|qRnӿ`I.ZQF+w~GJ?_$~5o{_}Ik\4øJWKYR (Š((((((((((((((((((((V?5_/o|κ\v$(wP#hAU |=4KI'm4Ou=Aw 1%wr1]ks?ks?9gτEV𾍩V_4>)!De*P8*VlRVlgOGGF4˩4[;HcT<\=1Z^]GO/ngq\/c(Җbp\9*I-ըV?/=b3_˥!.ܭI$;ݛ&6{9maS=F] +lEkMY%Ԝm4M*8ZY'h"$vG zbO${i:gw+OSce[}y,3EӏcFs>ER$((((((((((((((((((((+*nc_Iw_dWU?6;u/3suG=Q_~'?Zp;Nȷ_w~PcD2_zJQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEOMM$+*nc?Iu_W¿:g(?Y-i}_KO>׌YiX4,\$[3W_Z' a8kazHd0#YD?U!|:~qꖗ7^ > H`{'#C.i@ : |<7N+Uk?<!^2[xfllf#A>;]`Hq"'_\t>??ʚ|#ό|7OGxJf1T'όjoo\ D=#|cSxxe'όZǵǵ/=#|cOH*==G"zGVFEOH*???ʵ7j7j._"zGQ>1U{Q{Qp2??ʏDMڍڋ>1T'όjoo\ D=#|cSxxe'όZǵǵ/=#|cOH*==G"zGVFEOH*???ʵ7j7j._"zGQ>1U{Q{Qp2??ʏDMڍڋ>1T'όjoo\ D=#|cSxxe'όZǵǵ/=#|cOH*==G"zGVFEOH*???ʵ7j7j._"zGQ>1U{Q{Qp2??ʏDMڍڋ>1T'όjoo\ D=#|cSxxe'όZǵǵ/=#|cOH*==G"zGVFEOH*???ʵ7j7j._"zGQ>1U{Q{Qp2??ʏDMڍڋ>1T'όjoo\ D=#|cSxxe'όZǵǵ/=#|cOH*==Q4e n}pH2}p{U wԶ̚;Rx`AG"Ƭkp<֨~O֋NԬLvEp) 0sp9XLOuʲ7H=EeQKxK[؟AVzLSw=^ i,Ja;=#|cB [ ~3ԛǵyje'ό`+cO z{>Kſcm 20ߌZ?$^1&$uyM,w i/i;ޯgi}v'ȹxbh4溌*YT5fw<)}kX,tyq*/o, |!u/[}gu2[F Kw>vӾ jĿ E[xsPݦF'}SV7,MB6|d_#?iu:ٿTԧKHue&`|4ڏF-OkD> d[;X\o9#*OH*=gᎧf\ 4 n Y$v e@#/,]z:@i7zt62/'{])wi7HVg<̼n3G=#|cx_JgõEdg{Tv'PlRc`g%ϔ|m4mo [o\֗զ[y6ɾF$־L@ d'opEЏ+8B>gTQ3EW'_9?]i5bvCNN.31?ymd4WpWc*cqTۜ{IIi腇STT_ =={4H>;#J_:~G<){+~_nE>e7~/ = ";xmEEfC>e7~/߆_{oH;xmEEfE>e7~/߆_{oH;xmEEfE>e7~/߆_{oH;xmEE/3"2}s?x_=$QkGGGGG"(hd?_cQkGsyzvҞE13}J\~ t(,l57{ݝsN=~YQONoe/ 1(^O(Tvn&#nc#R~"(__cQu_{CqgB# T? Pmj(Sr// 1(I7@C~o??[gڊ?܋|ao_ ?R Pm_P߅ztF+K f2ۊCgj\ +?܋|ao_ ?W 玿JQ^̻7mm~1+?܋|ao_ ?U h.E`HؼFQYX( ŁYg';.R6V>6dm~4 9,f*-GMUzފхYge`a2sgw пQ*MzW"`T}(,e65iԍB!y"g[#soOL Qf; H͗Rܼ֫s3Oٹk\jxD.__uzfgWqǬ] \3c2 _3{^{ifG ;s3 ]J21܁gR-,8^%oFnM;^W 0ln\YY]RW 6|13wx2"%I$52K]SW}ލ/k5c|bTk-Uh0T7 Wsc{1.,O(T)*NzR>R,U":5YGWffOl߲ܣ.y%b=={" xgĭ(.fTf R}\OȏqS5('*2'NCAømٽ@>gҙubpgYm[7ߺûƆKE7xR4o\e 5dF@w`Vf:e2?b:{?5E%nNe_x )_EU3V],o*C{& wmGX[\@ΛR9{f%'Nϐ._%ֶQM5[ ̴JŚ΍xIt~eZV #$}1in[HmTfsIN"m33 l:GxZ7v$(gx^NgTh6VcN#b@P޾;oGsLJI. ۔7np9_m5k0[ JͨM=s2yx j L$@ Z5@T\8wƨrŒ5N3802--ϑsG͘FWg i31+X;Wd_<괲c`Tbf_]p[JJf\[R v_fOJg2eCBy˴&=2_) ^;4b]䧶⚯U;Dcn %#wuwkdSY2Xp9WP {nn9PꭳPԺZPqbV`2yMۗ0]B(3W(.ÌPoF|M+_iSySr+i{F܉YkiuuFͅ`)Ӓ~3Rޑa7P=W9tȐӜr)UR 2Vo21sFOg]=ּB|]98ۓ#s#Zvdj2_( 2X뗡SĬuΞu} ]Cb|z3x;S''GtΠvxs tuf|%ԣ qSuej /2}ꑨL <ԣ \(w,)_]~tvhWXVoc0NMvϔ]0[7"NBu(֌|D߾R.9ʟzBmœ䩪 C[v9<2nz׹ŔjQfh9 'Rb}HS'2Wm`nci rpj"5Ҧǧo2]kݶq\˟y+vAT_oz^vJeۺӅ-K.r]JNN?=;kfXq ~dDzzzܔq쵐7²sA*m#f1GH Q} H!Fu]2 j*Ι=unxʹ8 ~i˿[I+[ym^08!f7;Rf.>nyWtb*"NqPyEkxk¹3]|6`T( sY vݐf:Z30Vfq|G[n2LMOV0ǿg{nZti~|WDve >:ZCK_ n* _ʟ󾤏^t&_;a=R>t<@Jwcc@(P2%L@ (PH2oƳgφ䆳g5vA?|/s;.pRXh 0e:g |Ӊ^럜ǓSʤϰD$'UjcώܗᕠUI7- )!ʌOwR̐s2L 4Oóe`n;`ƧLϧҎgdf?a, A1f8v!!'3HEO̜l̲"~)V8?mZ@peѧDMZO{'(c#, OWo{ZG?cJ[EL^%e=Y)Zt?JU:5`Zw|k O2UZ Due rTIV AX])3X:^GgTU(3ʔN=suFA?(PLqGg(pPfڔ4"eF8] @h(p.krw-ez. I^̜2Gao2?~q+&ֵL($Hpeo'(j}Ѯ?p)݊<lL'uhGDtqp?~2+w[zOۇ[sgAL-ø, Y,Li'ZW']#2U(iFGa ][޶XI ^I޶ߚۤb5O8o \`eW'2=~SFLG[gC/ `7줸PS:gH=5k ݾieOΎ.;w}oKK[跃51SH"Bk2 b^CLq'R*3]`Ib:1O|]ͯe} KK\;?i.Ĭm0ez`2#ѻ)=Y}-SQfېt'fӆbYMj=xdžLSڗ0//-u\Upz5w_5/?s˿Oޕtȉewx3w޹_DWj}M"k5;KK|%e"{vk.Z,6΋vg7]D GOM> tȉe]u\4.[a7Gk^f闫gr׾z?M=k7'rb@O?g\A'^/΋RW]6_XenH:Ā2yB+ҥ7Kun5IǕP& (eJ\QV~9x/GQf>0?Kz@D:ع#|=c +|X>L^XZjDP?1 m )~4st#==UsBWeY?Y >L#Y?Y >L#9j>TV#f(8)-N8?r 75o?e+9s8g1:lvm;J(3nI$'?x$H 2i&s㝝5ELyFsO=,*SqTuPXKIC3&MZ9S# u2_yȑ#;zwFL^z(: e2_L@e1Oo/)xޮ[IQ]eުt""m]X1fW,Le~zwqov>g; x`wy$^ûdEKH~AZ-Q""XhC/kEL"$[S2\|rjjjppдsoxnbb;|K[ x,;P"G3C?O7_|c#!')#G3C~ H֏f2(H֏f2ʜiz+YZ2& חtǀbN6G3k:k:ud-I ''7lJ:C=۶m-5}HEBEC##ߊNY?f{X.@4!oIG@{^1^9yN;5'FML &G@ F~4LMhcCBRZLPk2 JVfGclDP 딶Hbh"Sx'YfqO'7?Wui~ސ #i?o&q2(GK~[OtO$'hϹm`i(SV(STN8'Y>Q2Ii_2K |Ҝ@P%qHv 9M@x*3*GYESܜ3))ˬSbHn)e.n,qz $e4غ4]Euc}Ln"7BI^n"g&:$zN~YǬ)Fk7]^bA`ukI2VPq4(S*oOLV"AylbVzrW9uҵтT@!ţU=)H҆2o=2 e+Seބ4VQfQ *i+eP1vCkSDaŬg`*ǣ*2J&>neS&|!eS&Y?P&&2ϟ?yNpƬ3ѐy˻w<}쑤c˖/]6(hO9Q}덎~أ̈>}_}ob s<Y۶m-5}HyscXMG݇1$E֏G~AH&fg<9ڗ5a'fL G@ F~4L b$Geױw7L1Lte L#Y?P&1^ΆDGt:#+ @|@ F~4#Q&_D%deP&ͨF 2(H֏f"Te̿K82G&xQfGGLdQГ)ZZLnKOa,IE(ҳ #A ԓ_LU*;}xۄF:e &f]8:y'G?>bWj~'Yٓi q[/4',Q $-)3x( oY,-NX놤(2D:vJo*tle4غ4]E^,nS6ĬBmMԚw9HLS /i1-OcV8y"pQ;+S`џJ[L2Tצ)W xtOq' P)U&3Nitq@VL'$26yH*IV}$Hd~b(3N 9, RCD6I224$n:(3z_Cihɼ&NeUZ&$tbsDtEl)SТJ*SpsyMV` W!&e 5dDU-S XqyWf/W:{ ?$,wX/$<^LX1K"=o/mM2CxUDqeӠ}>[s0e&-2y_,`u%nsE,T*-V&Q;BWQ92LPf@fL>'-z:YUOaӉ΀5O dOe2T|PFfL!~4\䙒7$P&1(֓ @ F~4Utɫ  %@ F~4p-0wyP&eLÊYT3 @l@ F~4<@@ F~4L b$GЄU'?> Ƭ3ѐ9|ˎO?tzlk&3O>o{#rL@ ( :oGfC3t➦yⷼxLL$0 Q#$g(g/^ t[a@BxeUb<1P&beJBuGGlH*t@H|bN 0$,,9n)X)P&ZL!&TIc2P@sb~KVyBLz*.!8b`@P& (eJ"hYIENDB`elog-3.1.3-1/doc/adminguide.html0000755000175000017500000005614212556103750015400 0ustar rogerroger ELOG Administrator's Guide
  ELOG Administrator's Guide  

How to set up and run your very own ELOG server

  Installing and running on Linux  


Installation from the RPM file:

Since version 2.0, ELOG contains a RPM file which eases the installation. Get the file elog-x.x.x-x.i386.rpm from the download section and execute as root "rpm -i elog-x.x.x-x.i386.rpm". This will install the elogd daemon in /usr/local/sbin and the elog and elconv programs in /usr/local/bin. The sample configuration file elogd.cfg together with the sample logbook will be installed under /usr/local/elog and the documentation goes to /usr/share/doc. The elogd startup script will be installed at /etc/rc.d/init.d/elogd. To start the daemon, enter

    /etc/rc.d/init.d/elogd start

It will listen under the port specified in /usr/local/elog/elogd.cfg which is 8080 by default. So one can connect using any browser with the URL:

    http://localhost:8080

To start the daemon automatically, enter:

    chkconfig --add elogd
    chkconfig --level 345 elogd on

which will start the daemon on run levels 3,4 and 5 after the next reboot.

Note that the RPM installation creates a user and group elog, under which the daemon runs.

To start the daemon on non-RedHat systems, like SuSE or Solaris, a more generic startup scrips has been provided by Steve Jones in the Contributions section.


Installation from the tarball:

Download the latest elog-x.x.x.tar.gz package.

Make sure you have the libssl-dev package installed. Consult your distribution for details.

Expand the compressed TAR file with tar -xzvf elog-x.x.x.tar.gz. This creates a subdirectory elog-x.x.x where x.x.x is the version number. In that directory execute make, which creates the executables elogd, elog and elconv. On some systems like OpenBSD you have to execut gmake. These executables can then be copied to a convenient place like /usr/local/bin or ~/bin. Alternatively, a "make install" will copy the daemon elogd to SDESTDIR (by default /usr/local/sbin) and the other files to DESTDIR (by default /usr/local/bin). These directories can be changed in the Makefile. The elogd executable can be started manually for testing with :

    elogd -p 8080

where the -p flag specifies the port. Without the -p flag, the server uses the standard WWW port 80. Note that ports below 1024 can only be used if elogd is started under root, or the "sticky bit" is set on the executable.

When elogd is started under root, it attaches to the specified port and tries to fall-back to a non-root account. This is necessary to avoid security problems. It looks in the configuration file for the statements Usr and Grp.. If found, elogd uses that user and goupe name to run under. The names must of course be present on the system (usually /etc/passwd and /etc/group). If the statements Usr and Grp. are not present, elogd tries user and group elog, then the default user and group (normally nogroup and nobody). Care has to be taken that elogd, when running under the specific user and group account, has read and write access to the configuration file and logbook directories. Note that the RPM installation automatically creates a user and group elog.

If the program complains with something like "cannot bind to port...", it could be that the network is not started on the Linux box. This can be checked with the /sbin/ifconfig program, which must show that eth0 is up and running.

The distribution contains a sample configuration file elogd.cfg and a demo logbook in the demo subdirectory. If the elogd server is started in the elogd-x.x.x directory, the demo logbook can be directly accessed with a browser by specifying the URL http://localhost:8080 (or whatever port you started the elog daemon on). If the elogd server is started in some other directory, you must specify the full path of the elogd file with the "-c" flag and change the Data dir = option in the configuration file to a full path like /usr/local/elog.

Once testing is complete, elogd will typically be started with the -D flag to run as a daemon in the background, like this :

    elogd -p 8080 -c /usr/local/elog/elogd.cfg -D

Note that it is mandatory to specify the full path for the elogd file when started as a daemon.

To test the daemon, connect to your host via :

    http://your.host:8080/

If port 80 is used, the port can be omitted in the URL. If several logbooks are defined on a host, they can be specified in the URL :

    http://your.host/<logbook>

where <logbook> is the name of the logbook.

The contents of the all-important configuration file elogd.cfg are described below.

  Notes for various platforms  

Mac OS X:

Under Mac OSX, ELOG must be compiled from the source code. The OSX command line tools (compiler & Co) must be available, which can be done thought he free Xcode package which can be obtained though the App Store. Once Xcode is installed, you can do a xcode-select --install to install the command line tools. After that, a simple make in in the elog directory does the job of compiling ELOG. If SSL support is needed (access via https://...), you have to install OpenSSL and turn on SSL support in the Makefile by setting USE_SSL = 1. You can install OpenSSL for example through the MacPorts project. After having installed MacPorts, you do a sudo port install openssl.

After successful compilation, you do a sudo make install to install all required files under the installation directory, which is by default /usr/local/. A subdirectory /usr/local/elog is created which contains a simple example logbook. The ELOG server can now be started either manually with

/usr/local/sbin/elogd

or through the daemon servics with

sudo launchctl load /Library/LaunchDaemons/ch.psi.elogd.plist

To stop the service, use

sudo launchctl unload /Library/LaunchDaemons/ch.psi.elogd.plist


Debian:

A Debian package is available under https://tracker.debian.org/pkg/elog.


Solaris:

Martin Huber reports that under Solaris 7 the following command line is needed to compile elog:

    gcc -L/usr/lib/ -ldl -lresolv -lm -ldl -lnsl -lsocket elogd.c -o elogd

With some combinations of Solaris servers and client-side browsers there have also been problems with ELOG's keep-alive feature. In such a case you need to add the "-k" flag to the elogd command line to turn keep-alives off.


FreeBSD:

David Otto maintains the ELOG port for FreeBSD. To install ELOG on a FreeBSD system, you can simply type

    cd /usr/ports/www/elog
    make install clean

  Running elogd under Apache  

For cases where elogd should run under port 80 in parallel to an Apache server, Apache can be configured to run Elog in a subdirectory of Apache. Start elogd normally under port 8080 (or similarly) as noted above and make sure it's working there. Then put following redirection into the Apache configuration file:

    Redirect permanent /elog http://your.host.domain/elog/
    ProxyPass /elog/ http://your.host.domain:8080/
    

Make sure that the Apache modules mod_proxy.c and mod_alias.c are activated. Justin Dieters <enderak@yahoo.com> reports that mod_proxy_http.c is also required. The Redirect statement is necessary to automatically append a "/" to a request like http://your.host.domain/elog. Apache then works as a proxy and forwards all requests staring with /elog to the elogd daemon.

Note: Do not put "ProxyRequests On" into your configuration file. This option is not necessary and can be misused for spamming and proxy forwarding of otherwise blocked sites.

Because elogd uses links to itself (for example in the email notification and the redirection after a submit), it has to know under which URL it is running. If you run it under a proxy, you have to add the line:

    URL = http://your.proxy.host/subdir/
    

into elogd.cfg.


Using apache authentication:

It is also possible to login via an apache-auth module.
In elogd.cfg you should use the keyword "Webserver" for Authentication:
    Authentication = Webserver
    
This triggers elogd to use the environment variable "X-Forwarded-User" as the logged in user.
A simple example of a apache configuration (including the proxy) is :
    # this required to pass on the generated env-variable X-Forwarded-User to the proxy 
    ProxyPassInterpolateEnv On
    
    ProxyPass /elog/ http://your.host.domain:8080/
    
    <Location  "/elog">
            Order allow,deny
            Allow from all     
            AuthType Basic
            AuthName "elog-server"
            AuthUserFile "/opt/elog/htpasswd"
            require valid-user
            RequestHeader unset  Authorization
            RequestHeader add X-Forwarded-User %{REMOTE_USER}s
            # elog doesn't like the '@', so we need to cut it
            RequestHeader edit X-Forwarded-User "@(.*)$" ""
    </Location>
    

  Installing ImageMagick  

When images are attached to ELOG entries, thumbnails can be created for quick preview. This works also for PDF and PostScript files. ELOG forwards any image operation to the ImageMagic and GhostScript packages, which must be installed for this to work. While these packages are installed on most Linux systems, windows users have to download and install these pagages manually. ImageMagick can be obtained from www.imagemagick.org and GhostScript can be obtained from http://pages.cs.wisc.edu/~ghost/. After the installation, it has to be made sure that both packages are in the path. This can be checked to open a command prompt and typing identify -version. This command should return someting like:

C:\>identify -version
Version: ImageMagick 6.3.8 01/25/08 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2008 ImageMagick Studio LLC
When ELOG is started interactively, it checks for the ImageMagick installation and shows a note if it is found:

C:\Program Files\ELOG>elogd
elogd 2.7.2 built Feb 21 2008, 20:00:42 revision 2051
ImageMagick detected
Indexing logbooks ... done
Server listening on port 8080 ...
If ImageMagick is not installed, the thumbnail functions are simply disabled, but ELOG can otherwise run normally.


  Installing and running in Windows  

ELOG is distributed in binary (executable) form for Windows platforms. It will run happily in console mode (or "DOS box") under Windows 9x and ME. Under Windows NT and 2000 it is also possible to run it as a service (the Windows equivalent of a UNIX daemon).

Download the latest elogxxx.exe file and execute it. The installer puts the ELOG system into a directory you specify and adds some menu shortcuts. With these shortcuts, the daemon elogd.exe can be started directly and the demo logbook can be accessed with the browser. Alternatively, the elogd.exe daemon can be registered as a service under Windows NT/2000/XP, so it gets started automatically when windows boots. This can be selected during installation or be done manually with the start menu shortcuts.

While the pre-2.5.3 methods of installing elogd.exe as a daemon (namely FireDaemon and srvany.exe) are still possible, they are not recommended any more.

Under Windows, the ports below 1024 can be used without restriction. So if no web server is running on the same PC the ELOG daemon can be started under the standard Web port 80. This is achieved by changing the port=8080 option in elogd.cfg to port=80 and restarting elogd.


  Server Configuration  

The ELOG daemon elogd can be executed with the following options :

with :

  • -p <port>  TCP port number to use for the http server (if other than 80)
  • -n <hostname or IP address> in the case of a "multihomed" server, host name or IP address of the interface ELOG should run on
  • -C <url>  clone remote elogd configuration 
  • -m  synchronize logbook(s) with remote server
  • -M  synchronize with removing deleted entries
  • -l <logbook>  optionally specify logbook for -m and -M commands
  • -D   become a daemon (Unix only)
  • -c <file>  specify the configuration file (full path mandatory if -D is used)
  • -s <dir> specify resource directory (themes, icons, ...)
  • -d <dir> specify logbook root directory
  • -v  verbose output for debugging
  • -k  do not use TCP keep-alive
  • -f <file> specify PID file where elogd process ID is written when server is started
  • -x  enables execution of shell commands

The appearance, functionality and behaviour of the various logbooks on an ELOG server are determined by the single elogd.cfg file in the ELOG installation directory.

This file may be edited directly from the file system, or from a form in the ELOG Web interface (when the Config menu item is available). In this case, changes are applied dynamically without having to restart the server. Instead of restarting the server, under Unix one can send a HUP signal like "killall -HUP elogd" to tell the server to re-read its configuration.

The many options of this unique but very important file are documented on the separate elogd.cfg syntax page.

To better control appearance and layout of the logbooks, elogd.cfg may optionally specify the use of additional files containing HTML code, and/or custom "themes" configurations. These need to be edited directly from the file system right now.

The meaning of the directory flags -s and -d is explained in the section covering the configuration options Resource dir and Logbook dir in the elogd.cfg description.


  Secure Connections HOWTO  

Using elogd itself:

Starting from version 2.7.3 on, the elogd program supports secure connections over the Secure Socker Layer (SSL) directly. It is recommented to run elog only through secure HTTPS connections if passwords are used. Otherwise the passwords are send over the network in clear text and exposed to sniffing attacks. To use SSL, put SSL = 1 into the config file. If the URL = directive is used, make sure to use https://... instead of http://... there. The ELOG distribution contains a simple self-signed certificate in the ssl subdirectory. One can replace this certificate and key with a real ceritficate to avoid browser pop-up windows warning about the self-signed certificate.

Using Apache:

Another possibility is to use the
Apache web server as a proxy server allowing secure connections. To do so, Apache has to be configured accordingly and a certificate has to be generated. See some instructions on how to create a certificate, and see Running elogd under Apache before on this page on how to run elogd under Apache. Once configured correctly, elogd can be accessed via http://your.host and via https://your.host simultaneously.

The redirection statement has to be changed to
    Redirect permanent /elog https://your.host.domain/elog/
    ProxyPass /elog/ http://your.host.domain:8080/
    
and following has to be added to the section "VirtualHOst ...:443 in /etc/httpd/conf.d/ssl.conf:
    # Proxy setup for Elog
    <Proxy *>
    Order deny,allow
    Allow from all
    </Proxy>
    ProxyPass /elog/ http://host.where.elogd.is.running:8080/
    ProxyPassReverse /elog/ http://host.where.elogd.is.running:8080/
    
Then, following URL statement has to be written to elogd.cfg:
    URL = https://your.host.domain/elog
    
There is a more detailed step-by-step instructions at the contributions section.

Using ssh:

elogd can be accessed through a a SSH tunnel. To do so, open an SSH tunnel like:

    ssh -L 1234:your.server.name:8080 your.server.name

This opens a secure tunnel from your local host, port 1234, to the server host where the elogd daemon is running on port 8080. Now you can access http://localhost:1234 from your browser and reach elogd in a secure way.


  How It All Works  

For the technically curious :

The concept of ELOG is very simple. The logbook functionality is implemented by a single daemon program, elogd, which is written in C. It contains an integrated Web server, which does not serve files like standard Web servers, but reads logbook entries from its database and formats them into HTML. Since only forms and tables are used, no Java or Javascript is necessary, which makes the logbook display very fast. The system does not use any images on purpose to reduce the amount of data to be transferred. Since the ELOG daemon contains its own http server, no additional server like Apache is required.

The "database" in which ELOG saves its entries is in plain ASCII format. One file is created for each day in the form YYMMDDa.log (where YY is the year, MM the month and DD the day). For ELOG versions 1.x.x, the format was YYMMDD.log. Messages are separated internally by the string $@MID@$. If this string is entered in a message (main body text or attribute), it gets converted automatically in order not to invalidate the database structure.

If attachments are submitted, they are saved as separate files named YYMMDD_HHMMSS_name - where in addition to the date the time is specified and name is the original file name of the attachment. To copy the database to another computer, only the *.log files and the attachment files need to be copied. To copy for example all files from March 2001, just select them with 0103??a.log and 0103??_*.


elog-3.1.3-1/doc/download.html0000755000175000017500000000675613023515067015105 0ustar rogerroger ELOG download page
  ELOG download page  

ELOG is distributed both as source code and as precompiled binaries for various platforms. In addition to major versions, minor releases are made containing bug fixes or some new and not yet completely debugged code. This is usually the case if some user asks for some new features, which are then implemented and sent to the user for testing. The minor releases are named x.y.z-r where r is the release number. A web access to the source code contains the complete development history of ELOG, plus the newest fixes and features which might yet be in a release. To check out the GIT repository, use:

git clone https://bitbucket.org/ritt/elog --recursive

Building elogd and installing it from the GIT repository works in the same way as building it from the tarball. Issue in the elog directory a "make", followed by a "make install".

News for each version can be seen in the changelog

ELOG source code

The source code is available as a good old TAR file. Select elog-latest.tar.gz unless you want to download a previous version. Untar the file with

tar -zxvf elog-latest.tar.gz

and compile it with "make". Then follow the instructions on how to install the server on a Linux system.

Windows Binaries

The windows binaries are distributed with an automatic installer. Execute the installer to install ELOG and to register the elogd server as a windows service. Previous windows versions can be found here.

Linux Binaries

Linux binaries are distributed as a RPM. For previous versions check here. There are also SRPMS. Thanks to Roger Kalt there is also a Debian package.

elog-3.1.3-1/doc/wishlist.html0000755000175000017500000000774712556103750015147 0ustar rogerroger ELOG FAQ

  ELOG Wishlist  

ELOG Wishlist

Here are some "wishlist" items requested by users. I will work down the list as time permits, starting at the items with the most votes. You can vote for a feature, or suggest one, by sending me an email.

Feature Votes
Implement groups of users 8
Password expiration 2
Account expiration 1
Make attributes which whould be displayd selectable in "Find" page 1
Convert elog text files into XML files and comma separated files with elconv 1
Implement multi-line attributes 8
Derive attribute options from list of entries of another logbook 1
Specify date format explicitly for substitutions, like $entry date{%Y} 2
Change attributes of whole thread, like mark a thread "open"or "closed" 4
Automatic unlocking of entries after a specific time period 1
Implement PAM authentification under Linux (Which includes LDAP & AFS & Kerberos & ...) 13
Implement attributes which are math results from others, like att3=att1+att2 3
Show sums of numeric attributes 2
Let each user choose its language separately 1
Implement multiple selection boxes 1
Boolean operators for queries, like "attrib1=a AND not attrib2=b" 1
Implement "sybolik-link" logbooks such that a (virtual) logbook can "point" to a "real" logbook. This is useful to have people always submit to the same logbook, when the real logbook changes from time to time. 1
Select individual columns for display and printing 2
"Incremental" options. Have fixed set of global options, and in each logbook add or remove some of those with +Options = ... or -Options = ... 1
"Include" statement for config file. 2
Use different styles (colors) depending on an attribute 1
Add native IPv6 support 1
Copy new elog entries automatically to other logbooks 1
Extend the search facility to (text) attachments 1


elog-3.1.3-1/doc/paypalcard.png0000644000175000017500000000725712556103750015232 0ustar rogerrogerPNG  IHDR~ vIDATxYkXW~c@QB4(U "AVPѱ`cjF;ZOʱS Z-Rr D!B!p~|cXgiy^{wooٱ:::;_Х746t aAO۟"ޥp' ʸ_l_aݝkW:L'v[(4R #7Ԗ?TQQk(r{c^fC}̅[ܣLݱ4j¢ G|0?P;/{ o%c80 ;W)&$J@CNqYZKeؑ kP*P>)^I$ߵ1d\b_,}՝&  GOO }܅*s^.ks",j\{6sY+[wI<*e l7;G%sEkMUt-eҔ)Fᦾla(QdWȍ%:v9h[0m3Fr{I֮..d~U\.}cb-pY7f!$ {?uV-1cSp4:f=t5% xEQ5Ҕa#h(ouKov$&Ţaݬ cCgLM~+@Mهy_-1i ]ǽ4Ӧr[luKf%XenSFȹlƢ丙?U5=/g3uzÝ6UN1"UPV n0`Y3g(KaA?3xJLA0ZghUWnǰ}ו DÃ]~iey"C$BqRY狫i E(d~]'k۴:2:Tʟ'<}"9vFBTBYEKDMm?o wGYJ#{퉡ikKt:1ʴڐ1jsy&J1bʁ}=aYNgo uPi#__=FƁ*D=OW#e܂Y^Z/*4WӖ d4pN`Lxzb ^# |-gnKzc}M8P㞵IGO?2_tr[pp?TTsp>/\#[Hn,Vvq'ڣC#{yhkW|#P#;K_[S-rIbZ0l(6<@`;r7 iK<B$Jʥ"AJg9wRC%}]8,I^K M&B啬;n7/^ޔ\s(Nz,O A[SXr$q%)&"IRBX]/+L ՗l!kT=hk{ҨcCM9J&_uBcECBU/:pq\m4W[/=]|"G0Ύ `V׍C[_F]y[iiIOr0ݜ)՝ݑ!q^>f2=T.=m\y˛(},'/ҷm*E-I)<;g ޹tFࡵ3L}YŨԹ b9Z'+X?%:OiXZ2eF&uQs29|'[h̅~gY6No(YQr!~cےls]*pf-;m*@ټYA^ ; v#N+}:}E]KΡ3u- KlKZn{ܮ۫Єy**H0:Fg0*;vi 1T]Y~[^.%%?BgÕJێ.- ;:i5yG E% Ԭ&uv!5"85=MwY8Ӱ}ey\3#^֪wNμucP?OXI#o\}BLHtD75EDL.kQwuqT}B7||4ڗdÅo4Kޙ!ΖF=]O/8 \ @"( ytߨT o*3*/'&Nnfg "jN 1b{Lc])LvyLj୸+Ώ׋>Lh?T9?pYV=9(hpu` 6OTDX8Ù7'@SYڰ|n)8.?~?g<=os:μzk}_ʰ1a8{}d_~m8T?|.sMDSy3?am~cδ+#V0;إ2(YiilEa5;'⁃ I+zwxsaMVF_s{no~4Kn7zC_B}fۍH)D*[7'nNMyNTNoӇ$Θ'Twv8 7]X Ma1>=um=+&, tO)k$. 5}G29`"m+#,`0f*\ѥpvRGcX;*0MJ l KqrDmbe`Ѭ]YnN6 EMjǞ >OA/lrWE- z2W$[Pġ- -&gKy@Q8\BBb 4RY-;$`R90=e<檣|F?ڛ<&(SHkM=iZF\4fC9y_Qd|וnIXܜ@twg9#'ݮެ0΁a{W<ݖ6jmuhdGZM0xskΣ=VO,;J5]FdPh; 1BD!W=rՁUX,-,.>+k 3y|PmϋWFK_5W!|H}^Sd)\QT8kEc졢127ƬhL\kWlzۿN_9RQ𙅓[a^Sްr;rJ/+;QbkCH T:ݺRO|ⴽOO#cރ&STGVhI'dj X D2EƟEu'K$m)|V#&9f1)cDZBP`ioKňS‘s9,s$I*( I` "" N}WWoS}3 K),,F @pHT'g48K Ӷ2}Я/]Ymt^ޑ@Vd &s#{ED50D'H3b?\[F̏""|yb<}:kCCGYӏYF,88Y:I$}ߍB?2,܎E.r 5G=$e?occert_ 5ݗ_ٗG^/L2)Z~w?sڟgHZ|" e#Yo>`_xFm!:Hz{ ̀><@ |Ga |GnhX|_69"xC<%xdecuq1 3N~^EX?c$QZn}1~жg  00Vw! dPZٛK`+~GXp) d\P.+:pt'c` ̂E7AAx QHR4 } w # ( ʆ 2 ݅:0/6 18aD$L 3|aIX:V kuž`p&8.W-px</[Q,|B<q*aD#!! ͈GQb Cr E*Hc/2CV cE7 ńD)PNa9T ՁF-6h4+ZDQEMt;zޢRpSPP8SP$SQܠx@1BDCIK)@BiIEOAyrrrCĨc0L 1fbyXk?6 [`ߩDt\ҩ*:^Pp8"N 猋¥pqp[xj jcj/D"f44444 4y4 44DZ]Z1"ڻtx:i:K`st7ҽGO_HxO Uc"A!!#=cc}Y&8ɘ))iiYٛ,s & ;7K*K-6+>k k65[,%l /9`"69pqlprqrq^|ȹĥkϭϝG'`D&;ɼ3|>%>.5~n~s#/(6DGi;AAcji!PPsappLD^OHdP& /Z":,S +`0H#Q_Y2K[򧔼TU)izidV/2"22E2eq-D.MOw*(*D((,++++N(1(Y)SQF*('*SRIU\5P;5A5oj $2Y  wRYM&I\s^OKBkI[X;@G)&M]ݣzp=CT}z}{BWk ;FFYFƜƞUk&&GMRښΛEM/O[XXܱƖ,g­ڬQVEommtmlo~ӱ˰rqpqrtsv= ygNlNN-hg 獃s.Ȼ<w+k}77[;%aQWﲟ_ߪ{AAwCCCrƅ͆熯EFTDB"[hSs11E1[ qtq!q}"g F͓˻ɏΟ-0+h1B±"bś%^%#.\漜vyԿt̰Xwu%۫W)]`Hح nsQbU հ.7nj)eMuuo7jPjih,n76Ckw̶8 5ժ&Vyp>''otuvv.tuM=<#GM<1x[GSw{z6~Էv`ms/Y+}ڰx-fJ߻vb 49F > JpNS 78#_ hVwE#Ϩek9&G6-c4jXEH\HnyoB:o=G"LZFK~} 6'>*PItQ9y-EE>Fb6Zb:Lz]M5wƳ&fY4X޲a]es]mdžN ]nuuwo!uzyzg($bjq>"=z2se'^(#>E@ÒK͗+P_h^d_|RҤ+MW{zdM[5qgnW74467?pX佩i_?d|$XUgOg}6G~<{".?fB#on$’0ǩNN9=6#877;BDNB E ꊛJ]tMU5 JUn7ªnf*_{{uûƵ]\+C=b&]N%>𤲻l>D?qP~Hglv˸ۤ )4iso.lBmK}f?J#ĚgA}(vleK;oۣ;u?47c#EϹpj<||?t!aaAQ8$\ZmU8tHTEQOZmK}AcHCANn^~i$80_SW3;sc K+Ika-=ۑ3A*!ЮwM-Ҏ'̋›ƇɗÏO  Q 3 p JΊ)?0sx1X"&8e2D΢oN>?ՕR{LTR9|42糦'/LLț̟,8^hxdRr[]bUM)qKFVN^~^NVŻmypwwru?XycӠޘg}g / ?O89yE)iWf=$/澽yUd?}1X7MuSaK6q.'nj(rMy'O`%U` `:&+ `RWD.<)-` 5p \S&̀u  RJ C`fZ\߭YFx-\]ECJk. Հ@(X((^RjRVcX705#y SHtTt9nG%x&,S)"K+͂+{1!:g)96w%=Ih #!cB‚DD4QE~RJX2ͲrAf 2\JxejZUf5VI2N)R6IM+Vkuo 6/[:%;7\~Lbqn?SʕVpIs-qs'hh&hmҟ34F򙥙֧fg;cpqsEp pO _x9+BMI"f̢ b5 R-2޲Zrr;3 LPH\F>}VkO]WEEAw.SM37%m(me\;ʭvEY[,ׂ~?ݻS!x'Wmoz4},fTLy7Kŏk#C[ۯw9_; `v{8\ #8ΈB4E!LPEOh}t zžӊPQ=`{NmFFV =5C) &,b,}!lx6voz0.!<2F_ʿ<`32:>U#[ϑPthyHRSR~L%՝WOB\.USEK.=* -2~-ҹJ͞u-m;+;S=zzK" 5O8>2mw!~R*(׮o;wbvQ|N nNr1 @GПos -D\'U5߿qśPgmpt pHYs  niTXtXML:com.adobe.xmp ? A IDATxw|UEsnK'=$H "* +ذvׂĊuWWP *4Dz sf?&07 y>|¹3wf{yf搋ٖ[-8uA=e"עE-ZhѢEˉ+s$;8"@2m+{s9-ZhѢE-Zh/%U7.YôC~} 9-|> p_qz sJ)cL%HJMDx?ڶmOc[BJ)WӉP)Z@ׄkkO1n2 ꔛv \\ò Z]U9b jRXrDUQ"nB U#gD؎mVSm["ΫwEB5_5_Ovm[/gd$ǩ(މn^o AwUEIqۋ("ƋI_ ~!ߪyj Ĩ &@ר ϣjkVU[kkb|XHsjԜ"ecDlM7b oTˢkkkM']Z1?pV)C{s8unZmLu+7$YvVRn,صgᾊsz_8:}wxx\![-smzNJ_(),ں_CΈI-8{,Tj @x(ta-+Qhķ Q-fD)G4fzKck敧.!ny0XO70cѴI;pˠ߾sn 9w-[k קKQhL.rev#SRxћǿj^k'\zt\Ryh嚂UAԓRjۡ߸u}ߟΜlCӟٌB~z?<>snfuKg}TVզ7w0D7f&c__5 gty6/^\r.rZ[FQbEzE eЦ%ұ#T tt8~dgQ%91ih9m»o۱Т]a~#}<h5je.a X4,jڎb}Q^Z7 P֯*'2`R%?6D hYZ _O 098uڀ&IO]btgG9!換/ࣗf.ތ3ua$ڌ ZM:t3j}P$jv('"+pxy=3{yG xk~B)pP?bP[6ҰڟMjn9do^y&7qC{Y??c5 cҞ!nVzW2m4*>KgR^^{9%9Gk[ fԬ QE4?WoѨ1vk&Ӛ^b#KsJ)3 k禟i3,./ṃKfsFFmQ(1V&-ݰGp]ώ^=~K/ʍ Zª~vRZdSvs,|=G;)Sf}{]3yt'*5_oB , q N猍\n  Bxa.C<]nˑH.1]-\QjwUE=!2a.牉s{܆e89!f O UyjeY&Z-UBp9aS ҟC\8ܵoY3`Yjk2r6 Lu f[y3"hjAS5_?] ^hq[8g (H Vbn'J?]˥RJb$"VYZlb'4|#>_8|MB[XoV jՆJiۇy' \RӶضM`Yt/7L1>.ۻ }g +'*5__7 x mV?Y"$%CB^/TTYQmV:ry$b1q4&ā#H V5Қ#,P.3%܆+s$ĘqjPeYdzsԊ%}J R]CJ)~5nڎZ<ՎOZPJx%Ge(1_~1k'pl1W@|pDNkpVj lawZ]ǖ18_>L={s;=r8Z<^ `up82c69b17o h/)pq8a)r8:ʫkfFf&lyu8Lp __5s,RC Iy4pZ[8-jR gJ&Po۶<$ xLJOx;\fФŁpR(]]~¿dEf:#1R:ow$dP !Xť~sR2R=4k8J( %C).l۶EKi`R 0@PB('05_ob6x5RhEki#.9m@M9wztZ;g[;tKHl`3wo.F.Hvn*IB]&5 CpSI3eg2%۱*ow7?Zv|= Se0ᆚa'LJB&? n4 @,&VĚttۑ?eYp8`.)P(UD8 WQ)c g\64aق7&{?st轗m;{?B??3SJ8SRmFJxWi}s]?eYq %1Զm3!Rj9Ol'gP~SJ)pA lq_5Ϳ)&p'yǧe^4/ٶ9@*5@{osNJN4=)e?ܔժ<5=JSdq%[r:W|#BH|Lٝ/\uѺ9-|p.ңSf )PHm+),R@ 6eXX;1[NMJswÆ(Zz6%c(M艬T}](T18cJ#_O8GYϯM5?za3'~iOzM( PMO==/<In#34(c\~l+ CN]`gKR^uV3yt!]YaeYhpP9'b : DۿkəΣWaBDzBp˶nYۥuBⰐDx0p8Vxʊkv@lBvrf״3#F c4M0|!W)ͲZm- U#cHD%EdL 3 xL 8YkjEK'p"jȰ]cUylTGmeQ4k'"Y@U\Z]p'%_EU0SғBU5!2\ pEYv&Ɔ*KkB\k S 4 Mʚ c̓ WuI|r B{KRSŵ#%-VW 1SR=4XR\ '(5_Oq;]W0 ߐ+НNv:\ @8Ƅ^^4Mt:Qp8m۶~0a&ܶp8бc\N$(R9} ƇISLԨX#6Llhd(QybRxIMhs*_Oj@iPn-͟a[L; 3s΁3__Pg8  `Y>/Q0&(x/eY. !R1=%P( B8MEm1QT+S"U|K}ʈ2M!DTRF-&Jl Y WÕ9M7ZGAmXmmϟu![?B\ko4x%gz'mE{ `E \ +@F^les.LI,VE(چQ &W`Fj#Syaanq5?=3_"*}8kkkhKt/c::!![Ya!e+)rD%$܁KoØZx5q<BjͩF A'XsFԄAp@`d1G&7lVbxkkJ}.# u!BϗQr4X"1}]T<x+Oc;r(Ay ơΔTW46 |zgb7zS(H-ڰ@yƔվB}x8oWG[6Huv͛;} ~0hhԞ7[]_2M4nw\\-Z5:!@A`PUXhZPC[ϥZRFؖ_P)W9Q=a^"/̈kY@/n'fkkǑ Q0 Z{<1DS@, .ܹsgvvm{+<PeQ?Q$j+tBqexkD:eY.˒ku~4kmϸOy |ٳ /dYc-ZNrmRVV֏?-ZNrV혘'%%A ķL.@aDFBp9A5qI蘦\.F*۱ ".@L."2T }r(gp%v{MK-hb+>AN*Ei]hA x P;5_579"Ƙ؊ + 3ce| ?p诘y儗`0hYhPH`0h(;ˤp@FfP(;NQ QB+.Wp&@BÈkĠ% @h'QaP/a Bibv ַeJ-?En[hq+3g e5B"R/re3?v (qm_5_& Hd*293V^5>}E -'kYp95L2dxQ ?S9{vL$r8{,Sjsz/"CŽ0eW>a2gT+4) ( r28),RىNf%3cHG} CDXϔ_5_ :Oqe4.n?*K+-0ͤԤ=5鎋sp-˲,Kf 9篼 REo,Os Pbl0 \ rE7A3g@ 8vr* IDATB P2ҵ-\'PZ1<+=A e(@YrHDL="Y5_5_?^MyRN\:y ʃ*)Yii{O*/86_~͹~ש^i$ =3Їo'Ϙ4xyŬQII`5}{>/~Cw] >}LxVo|=ef#ck=r̝2fxx>Kܲ? Eӗraww [aˏd8lgr \gōܢ[UTA [e렼^ɕ*(m(ߪ 8N*5a2΃CDŹTyG)^aR4_5_?KKKoᆏ>0;;kIIIMq^d&|2"#Sи|Gwn7@Zb ěQYOߗsusAF tmxbۡP;]}0Bpù|ᐎ;+qg%<9wkqOles=R]_g~Ysps`ٲyQ#fƭJ)u:Caw}pX|L6ɱq/T7ЂNPȕ{;qL1 mHM Wc7;Aۊٶ-8аbT rE 0<`|V&Q+&wiդlߟ@a [5_5οjĈ ,2dHyy!׭:/F-s|,q0g=^mT„;w;cl0YN.hHȖU⫺k _cF+>Ytt/>Zpءw2ywL td+,n`Luߜ-Ύp6j<}Sj~pv?.ۿL˲,aiC1)S|8TP($)W[[p8ޡI2joD0 ߅l+lH(4*:?PHPĝYqMTl [%¦ jU N) G?bKu:xA4"ٶ 5_5_? $x8ةWv9ʧӚTdWȑȅVz1<|Ŭo rܮ{% pƘH,`یy'!=]":lWՈЧؽ{7 GmۆWP^mt)SfaB.QOq`+pN $FSP=Kn҇&],c!0LJ 1e &#hHG8Hoah`kʥo0b G 6V &N?4_5_?.l`I({Z@5!Zu-xj9Or%)op>qho\纍n|{~w+鲷^}lpK#`YY&.{`9u^v0*kpy's7[Ͽ}λ}PryYt/5vϐ[\sn:c VıP&F(B#Ψ=Puӹ\e&J/4M R7CiZ80Dn9Ȅa@ޘZrgjqX@+)م ,W4vXv&x1Q^>6W!X(*#F-朋2x;QJ s kV5_5_?nOp[GP]bw*rٸ<&ɿ}wO|C pC\Gdfk|apVueS+㲪Jk,;..yd. Yꈍkk,0b' ~W\F8G2`& f8 {~px='.A[G\x駟ܹsAAAN'!DLrt'N>i7z2svB00Za@"ڋYS4j(q2Wh.Cp-2+.a,V氢cLPV kPaqH"cjWqR5_5Ƙ NJO&wp6m֚ _uUYoU(dK2 UyU@m8@ `,TFrC5bQ:(΅|PKUab@VI0C|^L6ԠXa kZ߯NFa!eM>sk..w[~{Eo .Ƚ݄m•-r>욇."6"—NS *Վ pX(r)H0$Jbqa~yl`Efɭ?tL8|Еe*T9#EhT"TC Xk!~?nT=;v]i=lqԚ# ~aYNuMM \DKE]Qt$0[2ALazEm[%Tm  |.ɥ?*@/+ݩRÐelT 3R+x@8H\ Pemږe\.1!rq "t+̙4MJr/8Ba 6Z7/4^hhk\(9!RZVV2޷~zC#'/֢&{SXx<`0(T"Ӷo"DI x\@Ft8N1&:04FO"73¢ʲuq{83 ǒl>oHȁ[neA-'Xr}TKG)šaCqO=QXJ81a=öm_d'R GYO\/%+2\.Êէkk߱o*|ݻwޤh95sw}g[ѣa2Xe\Kh@.2nxcmY\CŖܘ{,Q"˹yLedINM[b GU^dȝ.CTN 61?+@89 &(M )$ R5_5w埚׿W?PYQe_ Z꒛՗_ؘXaRrƙ͘DZePsn-LǶmĶluN<4.6@otnaDep guMu//PJ9sJP̠u .&!|7*9gS pZlfȅKUM䌛)s3n[A ]SB)f9%1"#Ji1q%1fPi 95lV]ٌD`&b5_5wO:GP[[v8#F hrJ t~GV 111|Q۶kkk].餔ѢE-Zh߾ Mjkk"X1 \.H-ZhѢJKKZmxkE-ZhrJSLN2iѢE-ih9Ťn V(:֢E-Z"\ BrE_H4v nˆ{;r990s;'_kP&5)%sBprku05&r򒒒ZMMMMKKKNNvݘ(z/˝Ūvb1΁9Hj? :8t#KkZTN P甀Sp["_o X%%%;vԩSll' ֖8pNKKKHG78`l B6m380R3Gzc?Q`pPp4A~Α !"a61'bx,3<ʢ????rW8(xLN1ip@ 4񖢣5x/i""<۷o_mmm~9m7V$BHBBBϞ=[lf͚͛#L41c 3YP$%~Z0aТE-Zj?a\ @,R#~gzcdѿh:۵kWLLL߾}=mM'7wŊՍhٲeZZ8Z:H}G9a*$E-ZhiJRvТsI8b2ixG0ܺukaaaiio% 񤦦fggkN}/!'8pj"i^GIԋl4ٷoٳ<9wg}a;wn0@UUUՎ;.] qb AeO[V\ ӢE-Z-9WT 8d`R2IJO999gyf5kp8_ $WTTlٲe?cnݺvjzMO%%%{СC󪪪;wGⴴ-))Y~9s'&&QgeeΝ;-[֥K1)^o8@z}5M14^z%$$P<O.]86ms68lC6g1GXϯD૮[֢E-ZR9)nxoyqm31Ni|嗉v[vviaZ4k,''gܹs{hCQQ9#f=&&&8"ߢ>[\ ~u^:RӨ͛uvcM\t"Nm۶k׮m6m;;;{u ! >ƻ<CmѮ-Zhrܹs%$I='bcIW}yyy&Lh׮b^~w PuP6 F# h9B1Mvkn„ yyy~kxٿy8[49nT999ضm?/Gw_]BbccE]g806, eLaΌ)_Af^oT\O.t5ТE˱}iYց222:@1`sns`39{ض|򼼼/XL-0Lx]Qdzͪ>g5>B 600w$ÈZL ϟsNhݺСC;uHq#GY|y߾} h,5su(z_lJpZv;Scǎp8\\\D>?VTTY/_qݻ}7f@mmf%$$xJW\8?СCC(".cf j"< xq~o8!j_}`usp)߰P f͚eƈ#/Of͚աC:D\p8:}"bQc{K n)S4{G0 I)u:n_]G۶eY |D4fEMZtW_[o9šP(++؎/--6Z]tʃ{{co}ﯗkW*)L6|ZzRn+ⷿmףt o~ucw-%i:w\{ so+:~Ѭ \Rg2࿁Q#w)]VcE]9C~-ZNHa߿˖-͛7w:.Eo7J~~w߽dɒ!Cdee-];| :/_~Q/&.|^( !`Ӛ,Yf_&lNtXHߏMzvԩS>;ecQ1¡K[rj9:2{lȑ#<|#G2f͚-999KcO͛oxxo͛7#򊋋x}$%%˗Gm4(++PTTT[Iݻxꥂ/G7.1yzie Il̄z%+?[di5M/YYǶȊ ?̄_M֙|\PPRw+?;挙9|ŰFER֯_ܵk׳:SN.Bq\:u:3O?ۯ eBtbηi̙3gҥNt6 ;#_UU=~؃}8}sUO1{_qO/0 B`\|]ӧOL>="ui4 '>#c3Z [#<#<κu.4I A]4GyIygIkq#/,٩!M;_|xM6b e˖ /˖-;Mr6^otvĉ˗/OKKKIIJ10*++ϫnp9ʖ-[V^^}* "zpSvn/M,X`+ Bxh ӯm=S^!7Wxr> }ߟ+w'\'U[اWyoF|o[wjݻS߭/on@'7#ԃKYfۨ%+Ϳ_wASb𖩷Mx)ro@rŻB`otmCZ-ʂ}__Ÿo5w jbT_pܗ=ߐS^m7L]`Cű\^l7:όMtۛ>Ax,fL;K?M3ẗ޸WXipmoNsMe j _ n߷c{yu@põc[?ݻ =w\V0yT}'.XM[@iUe+N0׿ӫ~3~⧻^~J8n {ՄggOZ"2tPXll,ycJሉIHH2$ߚX*$??k׮i|駟:@]u?PoU0P,54rLUVh"ŋtƞA.@9sovQv~3f4H|@z&''w=55V8 ݻwONN[&cƌݻhRoۈ 6tN6o7o^FDd PK \ o`[h!"P_y/>Fb:V7n{(CIw5g/u9]z: um~/3}yXKaYf%vݔ|FRWv<!o ,>{5nH2-yMXhy 7[[ض kaoߎ`ݚ7.K^uÀ9'~Y|`WqANg_-/: & fPz mqA׳Z/O9'o͍>.Ż0g͸g} P60k|/P[=銼 ڿv}]^W35G9`Ne"G}_?7 o?zfsƾi~fdz`o^:umn-ZJٹsgNN5sӱck8pʕ+qH='uDK(R=Ç2RMݯ:F^5vI?-E6=Q#2(3у$ڢ-[^wu**x+cl^ }>`UMa_opވ7.fհ9KwBA#k>?Ch-ЬO^a$t [7PAZ~9rIfϞmƹ K.5ktԨQc8$_yyc6"mk|>zLȆgnذaѢEep8lY֢E6l .ȑ##G&u xDAJLL?{E={_pAm5|K%K8eyM'2N|Rf_ʄN;=bI &n ˲Ye/ Flev,gyfFa{rɆ6FD"^R\Db}||'Yu 9a0 [y=py`ݹ?%px+?^8ש3ǣwu햶ƚY}ۯNHdڡiO?Ws;Ǐw,Xq0YCC 8d悴g]W洰c}h_ԫ_> >ΣԖ* *suܟa@Ny ;urD?v`2ת8=/҅kUlMEzN񝛷yJu p&@@!0rh*BIl h~cuaՃhM4?*z($\5d׿|p߾}?0]ǎ_ڸY}꒒GbC#}Elk5Gt?T K匴/SXjuדO>) /@C^~Y؃)7d@j `zV*@%\Ss0|nsӧ4hϞ=BBBJKK>S?"%%%j~Pz ufYoBz0ĞwZ0`wfR*<8PH6H$}‰ z활և5553#U*˟|IZ8@ܱ իcU*Ր!C0l\F S=$o2fg"Ŀ54q=⡻'.^ԙ{\W725~.RA+&5~xIV3Dupu2$$7;OFiZ.;ouԎ:ū#d=ygvo^XxbVt9@̺SԱ*Ͱ{_:Kfxx%_s}~6]M2$* HM]_zJO?isM:!Ki-N.pjǥFk~vSzP)rVNtw֧K_{6 }7bc89H?~:\o8X7, бhHIW ~=}@CWsK7Fi"?;|m)_Ayq(d*;WA˰aVZVJIIr|Bᤸ800>Z-&aXvMMիWYbOK0FK d/^um?Zzm$j:շ՘ '~~~ S!ԡ)44tṹBsY*ϵeM&SMf_iselװsӄY vGvd'3}F%}dRǽ0||)mGAٟ?5Er.1B Ƣfnzԗ}ԕ\]vg i; p\)|6Լ0`'-U&iR_b^JV³ 7G^@({̝ N, oz{~|D߳]cw8˾1>a Dm{|v.|N=/J 1rG@K G'dg9u[BJ}_3ws敺zk5 O H{l RqHkq/Ly>y۫?jocueavyZO(6Bs`w\&gX,}bXxW$O??}QzzzpppPPPeeŋr#qs|}OVO}|EbW^fjCzixMeDR De/\쏿:7OS1x䮞y`[o@G >,dY[ϴvwÎ}y:lt_zz ]+// CQӱ(2f !Xm8|E|d c3췾H$? 6lؠj3K2C"gfVw}7n8ooR*;wt4P@,cǎ{&y`*meUFy=+s3_R?3!cgj[TVV:}sLh[!t6LP(db\g{egoy$;. G{+.ުUnC; G8ʕ+#G// 7/66666c۰ O 8vXNNNLZs% ԳASNij-++xѣ6yq_}$ 9Ha)bEk&M$jN,VBgx䂒]~IT-y[gKzq.5XϠ=; 9pǏpm1-鐸#dj#WY&[p$̐YovW\뮻R a^H$X,O l6Dk׮ Ɇ*+M&uVe]}u;%B <76YO^UiS)HaVK99N{{{?CAAAK%vիWHHHHHpPBnR[[[ZZZZZzՠ;Sxpx |}}o!q#aDWmHTRR"J $#¹";B&c})q=z\& .;f{ed3MVȉXu9gx0"BH`2\dUK-=E)d"^ 4IR5n'<\Bx %RT.ⁱ!q׭#inTN 2  R,:>a8xeeeuuu~ޟ`K\ĩêfp*PA!t;L"0\y9o1'qg`VTn<3ʄts_&fs޽{uSNT޽{C/b0z}>}.][ *eFċN)qp뼖Y=!Bme  fx1c20<󶇉QBn $80$$ALTѣgϞAAAZ޵uѷgHFX=xCDnG Ba<| bxm v!wƒ ӫW/L&J',kX,pV 7rOg"0 xxnt)~)o"a o[me3{SOgZf/mlczF7 Ǒw-i-P7WNgoB\9⿹r:=~"FӠN?~E7^sQ7WNgz%IuB!/ m{v┦84OG{1П0pz?r#=:Rέ@S?OSOw!\ %B![!m۶^dh^ !B!d:P(z-,˪jH0p(agkNj B!R__պ:~~~*J B!BMtBFUUUCCÀza8!B!܄W )ZxO!B!&ֶ1 B!BMjjjjc"%B!.A!B!KPA!BlB!B%B!.A!B!KPA!BlB!B%B!.A!B!KPA!BlB!B%B!.A!B!KPA!BlB!B%B!.A!B!KH~qܥJlwEN)F̬$ŝR !B!ĭnlp_ +3OG|oB!Bn=fh4P7NVR7 tk_V7gUxqo[95KGI(V#`&`޷1}n~#UcM&-'k+;X 悝c'GU[r34)}%_>+euY)cvoLcI+3K&K&Bnnl_2RX{/8rYV+=VěXLkR򚞯g_e#J׭Wίf~\}MR}3S-LZlR?\!Vsԥ?]JȢ ;7,p×款lkq|,7Sp:RSj_%xj\QSB!Oʧ<_oyϱܚ^G׭keWeW µRLuSPTF<ϲ.~S&Bԝ:xkb!U!_ Թ?}-@Vbi&͛=!f RI\Jn[X`O k=f,9LH _-ۖ%nsu*Cw檥;=rB!8=93\WF(iűCˢg%e`Ϥ̝0%uGc h4aDф&n~%k1`]8a͒uYU[Y4+ueKw&lEafzl"CI;$3̢}˪Jlg3)F37is);KĦTzpn{w%w$,(iL/?bmc oWzjOu+{r 63+P]9{,)Z0Svښݖr stu떤Է%JwM9/#0щl+m٠-iң huBUaM sǛu1;7hѱuԶݑ4xwٵ.a4 Y&n>5uGL![ An Cx|გ;zV[[}tB7s ɯ^7{MNڪ-Ǫ4|xK,SKşDrZ9a/,~=2A֚_{Y3r B%G*qD-\3*ρvZV|1'$~.>vϙ>@S2X5PzG-h'ޭJX|U 0y/☜b-c-}e>ʲf̙GCG1 }|uӉ1=KV}Wa`ek: X/h~2)FwgD/禎 h :'m3<50W^\j։rFM="rVm/+[s=̟2cvFvW2 [_1=}7ihm4rO fĿ:{\Xdm#NL}aoҀg9cl嗃ǫ;[/:#KGxy悤29[{liT!3MB0>̿%Q'O(ٛU3F8/؂ =2,zãlK?R3^D[9әV 82zNq$4_˨ul٧fOyhI(k04$~tXִLMMްdȑwxǫ] X; u?p_wm +е5 1Hh`Vq͊ڨ2!BnZO6xp1`ҫ!~S\2z4J+QT.j~ ?-'5X8f_ѺB5"=ż %y"{VMØER+򅫞BZ-[ &0G{ZT\t'UEZ9`aˁ#X\:p\;/p+fl={hڐ@5rSk䶣n_ڋ} n7Ŭ2̸̌?a9d<޷է fRnH&zDBs2 lCzQVE{;0mTM/:DǺ{h[#ݬԮOg,[)ef}_uCvʄBy?,ϲ`Y,[9ϋL*%?0/op <ݧ'8n {yc3Y:9#7X5 fSh&|ė^8nTNj{/y7w~םϘ!m48tE+m;(ЮM}6h[WbtNQz .Hs%G{~-c@%4iOhҢWVcK1N۽J|dyK9Gb(SY9붶4[a//8 +Vb`}`{0n;-x+kX $ٲl)eoStʄBy=ٰ8[dz^oQΏ*KF$Ux|m{um*K3RcyǶroE'$4LT̆T5Æpb%PXZށAQj8`+fkRכ ]QiTASH]ah_vq ltqH{5`-w1YxOtv|cWQd(9SZ*Ձ+w&=4DxT{2 5@PXsU*劖̵& 7 @B#r>k2|=vw #u ҵz=*cۢ-K/u`2-*nn)Vo͜57t5~pK'f{)]3 J$ c{;R&rthAo`* ֬7W7_eB!;x˟ ŲZD Ü7$*_ir__չNiBY{kQ>ySƂ\OJ^]z &/3 oUV-Uc %Qڰ ,xBǂ7&`l\jǎJxM@J:?&-xskP!o+7hӛ2!Bݓ ,,X y *9W?iP^#>SFF}ndo}P^=f>xeA$dYS.i~]I.}n{]yeZ{};zHٰ,˳,ϲV,ë%Rjǃn}Cc74 ~!R6[lB!ҵ}k,M>N3 p,o2kħ4 C$[yqYۓ~%lB!~o,Y9xeSA!BHɆc#wE7p'9[92[ ,YQ23kݐey+o!B!+udʲ,lVыiQ V^q88mJi<I*UȮ̝PRiB!udCTM]>K3y'3o:N?RB!BɆ\.O;r`Xx۸r30RTO.k2+ĚMl)ݛ.7s˙_ UK6nF);"u\3#04彵Cn8r]nsW/$ 6a#tܡ;kuw&ݱ䴲sBɾK!䏤[V, s#1dĈЛz 1bᡡwFJ5JeE 5"BZw3_d쌢 և*j9~MQjG`RzT}3S-LZlR?u}K씖 u땡nZF=#%w|,5?mJ"[gbK=ds}4$+%pҩ I07G*NiO?xr%@Y,0ؕNþ aWWdWs使&:v]* ̍0&|nz~ܦVg,EX61lyp Sb \ԺӅ86chp`Yٱa4IYg6x u\tW8rjR+-ݒ¶qml\=VKwǶf})+vzCvTSss+.-Ӻp hbdn27s ,YAAа)6ۧ,1ִOZB8}qօ3,Yw֎]); Jw,bMl &@Y=>2w2hdLBM?Ztlea&<ú6ՕǒJP]g(:qgA uMqݺ%)uFW!?6J6;~nF ReܙQo&O VorGItbdJnCkޱ8O_\j։r_+m-=yhW3;R_z:addeN1 e٧r lv|V(uyc֚n 䥉IXSx] r ?Ϟ7B 4VH]5Ef>R4|xKBHy{ԧ% ?G=65caY1_~>J苲#C˼֏ ~Iٌ@Xq/<6DiHmbiu: ˄x| j K j႘Q.>vϙ>_`j|YKCN:apԤnVVE/M5d}bcNK#Oܿ+X>5ug##;ʎ'F>72{{ڡsVynCx~뚿}Q܄nkSU~?{du΁ON[zdw{ִOZ8G-h'ޭJX\{fDtʥq{䤭rJwgD/禎 sഝ|̉_۷K)mw)xF<rNΚ9C<15 WB6mͻnuB[wK߶!g֜ O4@9_oys_-lܥrNxG5F@ ҧ/}>u %'`n[ fxA2tC#mkX~:/0#b{joՍbP7] t歄 K0r`?rjjd-Co>]7P9qQ ξ>r=UH(;%: `s?^w^xHd-u[fhg FM0RasH kA[}8mB&v`RV/N?XdW^&ao'ȝ=>+|l;N R_vPZġIM%\V:iSiGyTa%&V58?eIX qZ߮)iG ˹{F~g|WZ3͗ol޿jtdnvgg# .[<ܸS-ЪPf`?T6>kμp<.# 1G@-͇0gD]śwϜ7iG'(}Ѿ4 1+g/0~f<] fĿ {ƢA~kuFnW!?6hD3ne2]|H&=_BNc*) fڒus'3F9–zDYOg,[)ef}|eNlzތH~1o@fc[6$]BjAHXqz XtZ8h^ wNeY3]#s'1m)ZK˿hY 7Ecb7:_:d[mUEZ96x [an C{c[TLg`6͓rF,ΞྂmpQ} Qc#.J@pO)ߕ>q8d嗔?xDh:SMt/NCK yŚyhE`^Z1? lkI~AQ9MT W?nDm5\&v5BdC@Wϖ&3@ HJ\r?c,s yqIg쯿?=1[JYъm6{UO-Rc}Y3yc[ ,~ |G<@Է@/z .גQm _V~b 10 hM N^8?Z2B8v:a𼬻Z%l;97S6bҳ5+ؚU%m ꡝGM/{g/͌Bpd-^}鯩X>itYLqWs|;皶]cOR?Ж,{[knѶLPK٥ә1zgyVs ?40nuD!䏍|W׵,$fX_}WU랛 NÊ? tv|cWQd(9Szy+kYOxr9>k~q!̂jtc|L*W!̹jaO|%lGimj3O%X,dW| K=Z58n1\.kƕcW[qln,׻\Bcd㦨F'je, 0x<-ޏwbQıo^x*syac5'b QϟLAB|=xDR&t9sB$D3k위,{7R ᚇcIکk3O>R@1uZ2D=蹨 HXVҎI/3 CvˏЪNi)&8u)?BW :Ko0 .GB?i\G6^9N*6f野Mۺd,[=5RQmD*ٯph0Fa`wT _LV#_"¿n3^c+|CJa>"X|LaElG*J;w LK~qFb:H8)/a,]M6laQX0mAH~2967;]#^ځ")W ֛LY`Ф1Ȋwo_5i_R=J*MmUıi3''WNy1 }8S>HM'3fy.oYI(kbѺj2^.Qxj#qB3$rL՘L&vfTjqCm>d[ Uzۖ3J%k*UFfm)D(I_gc %`af֤7[:DޣYYd5:W֤7m `DK&ѱ)\f(|b;k2-ê]6YR+vVRV Ԗl\a4L-DՕǒ5 39e!+Ňd=ԨYՖkw͊])4aD0+^w430̬u/sl+v]IY{`LW+}nR}ጆNe[ٱ줹B78^=|k6^;o5&}|o;+#q_W*7},a n4ԜIM{ǵu]pVt?@&!@F@ 0sPhJĺC5gm 01~ fOKI(>[xd}Eفϥf((g^`+ޟ3&67ͽ/mu:ћ7Ǭ5e6 Y1CWVi;{-+>Ivu%5j*M `Lf QG=65cZ}I7o?u러)t};>h*^Hͺ]0kڙvv;=G` k ;eʔ܊*<2""dÚ\Yg̊u9jV]>J;{6~#,?&iasn*0򷑝|rUϏc7ΌS[S5y?v?=?f'Gcx x=ԽwmR|4wv7onߎYsV\\dy 3Z٢b +BHdZrȲN/ Oo;vesϲ]U~//ےՇ'fG'|T_\Afo{{?3}Id,"]K5l<~^72Xz❻//O_6&_6llog *g]9\.ˣқ1d4Wh4͙Nasf]2\. .h4 j/aQKNeFcٸ#lmm\巬 J.ɱl_|q̙/"IADDd=L6KvƔaE@oˌN]<ݤK@k{r@lCܔ KV nS^Zյb):ƭ7ZK#J[٩ D}^oקKlQI `Lčwqsk>j7`Eygڝ,>j@m[h Vܔc@@ÂlDQ/ic:QD^ R^n;i.~v^(zA*'(WB 5߬vWo[_F}˻ޅMu2nvceb/_xߨUƴu-[ ~n݁m}@]۪ٷo߿o߾xY}["~Xggۨ);PGa5Uk)E1_rJȀn B4 *Xm`NJk)0kWTGIޢY^} #b~;#e=\͟f!#IhSA0X[:Rif>8 bIL&䱘o Hz|J iS^tP- _כ$˖K&_[$IT$d{isrܾ.~wC?ȧSQX&8pp0gM- ޅC3HD`+Լhn sxa+F腩y&SD@75d2$ ÷Gqsst\.>?ruܹk׮i媔L&d5=lXQJ| ~i{vmf\ۑ2׼<Aᘍ3Wzd O7`*o۴S׾X@mtDϟȧ Ϧu$ ~P}8 a=}jky뽵?9w.wR(4}\jnGW{M=1{=?M.eK=TxJG[TI'!8|nxܾ-^>SNC~{aRk[Wσ.X=)Yu1G@hȉ_[h盓 Cәt:ɐ/ x\[V IDAT[uًYSǶ%L(+=X0`2t:yAN0&N}E~:q__ZNb'`7o > ލ/rkz7)?7rMyƈIG?\zܾ<u?r)Jk5OE޳)D+L+Cy@>z1ؒp(:ې~=Л{Je@WwWǬol_ϋ#zK>7-aِ絴[1]Qf=-*ײO^N;/|>p}R%$$ 5=xeÊGGGD( ^pp*C gg{`|-gTv:7Cg^KCfkuRm_mEoK^PXu艴*pI 7Ow|s;DmFRS1҅EP4oVw(jp/jE(4x} {my7k)-*FQ*t]Eџ~?tNh_/۔KDFAv ){0fޣIŀƶ<@~g͓LFVk@joEyOhh?@mȗ(6/5lW^vѠ3PjJ\M`2LOx wafNW\}:۹"hSo\>iY,^G̿+f<~դ 춏>:IW'f#5oՐ&TzƛyӒh[w;mQ7a~mMn%ڏ*Xk~T>{~.]$'''$$t҅7Qba] ̙os)Tvw.5AaW6^s?V)@q]vYy4Reb=Wk%4e+s;g nV{旽anXcFe2YmT\S^$xz#H _/z{?~irFC+8Q`,s>N3l1oCaşԩ Xs{/f -4GΚ^§o ]'m4A55ɮEb_anmK7@;?7_k_Z=,,\5ԃ0L2  "'k8tϏFOO}sa2+6|77$='o|pNsp wxa@.)ld2ܽG¦#\P9$䈀C9c= 1'SP99('b~) Kaccckkw3F""j"TsNoEF&>|XV$I 0LKN@<}zV-uffZZ(L^2M/ @n[W/طO&Kq*7h UÃjQP_0XMEՅD*igfժ.o """`AT5]z)(ɌFM $@&SJ0T$6Rɕ.ŅW^pwWQhҤNBÆ]듗WѐjժP?F׮]LMۢc!"'U &k{{jT~}6m<<"">L6BhȽ~}7QQz( RkhDDTW(&GDDDTo'"""""`ADDDDDVdY """"" &DDDDDdL6*lU0 """""`ADDDDDVdY """"" EM$/_z#+K[jQښFܛ7oV]WNUtۖ.CvV %(W) !:ﺜtqIB^j{Qi>BL6xv*ib樖(@ۨ0}G  5z cQ224oQ>\7bxO$/[\jمKF.8 /f;_3y]zbId2Y MKB'ǦZ->yLXzd~2L6t]r.u.) Z-6R X@&l4舩'>O& ZRjnx3 .@ʾPL9c݉O]OW@ dxz\ǡش+~1SLc߾hMEN:P jms4msjmPa'4}-{/k; S>OkvQ\/'$""SrؔHH]FzM«m=C֚i?;7R֌_W'OQs]QbvMO^3Co=54v8ucI=*dڥVϠ0*򮭉;a"TBga^f^oiwޛinptѾS>}4O}t.l8-x ^|gwLƜ}X#|s{>q"1.{{8C`; gn)DX䩀6Κ5׬DZW*2A$wEW?W^Oo{ Z^ N{zpMY+,##:7T (gg5_3~5-vM1WhȜE[cshfQ&X71;!sNu+Z2gq]NB!cb~sd!-񓖞>x&>xmT{X2{ٞY|4wv7W=_mRP@sf:<ڷIFڋmD?{`/;k_Ԏw>m+tp/ XȸoCCn%ٮ]pg*h,7`MQ2 `gc^<cҢ>K#T`o#=zW؍o?ȅ_a&Ww\V h`Lu/Nhs3s @@=7nU~X{.ZN{eH0O;Cv d+%ka@g6`(Jr-mJu¯MOD=SP'2F MIbvJ|\7a[Bh^<0hzI;y>-жڶo+< 4&5C&dr LV~yghIiѫZXvWN{ '_ܘ'])_o]7D+.ʂ{? {slZ: E1_rJȀnY^e? J2n5QyvV0ȎHWF|+#b~;سE* _6+bq1bbW@{_ c̻Ưq:-sOS'd5  _2#dAow)N G?K.뛢%jssʲ'GKm S~.dRJ yPx=e(\0@z@ZʲhN)PnKlc 9vI),"8 ;e> Gq"^ ?'*lQ­.z?MUP(TT@.Od [[b"K۩[R𿤢/y""I;XHa 7NR6:|E[t1ݼ~X}=]Cr.}Ok.`W|&}R\pmکk_92&^py| o]}ڴU{$ O۷tn7@)74N+w!=1lr,IxVh2^Z0k)q͏k5]O̞pOӻw?+9dYgg@ Z׷zte7w۷ƀpѳ^y{L/\se{U5Z7CӢ}Sf[^k XF5+{~=j0iukUك6t:OYzqSge"狓ӧG[{sRy?bو[ ů Nڟ<׋+4\lGSc?rhZe9sW.N(/  <#$ w3-ǭT\KWq .]ŕ0ً8{ZjO_ü$Y))Jehcc#>,+Sdddd%'vVvvl::j6^oPu?89c4nƆmS{ 2V)?z{! Gp Gp=AXDfDD L6&)??`˾OFTVܪ$f%Y+ONa:nFʪC иa%O+7voqve5 | G{ QK6<~lllTj܋ݧ<1kq B3>}O#;FaD}VY~\h ߀q;]p/LaxuT9ԉ "9IR8!@|lkUb` Xnf:YZ|_~1sZPDDس"Ġ(-3 帝 ܃U( p;#J $߂'k(J""z1 u*ơlV:h_ۯB~_\܏@K% å5"=lQu7?Mol^zQ6 WéSq bB6n܎6ϑodh-N(Qđ-<2 -u%J]T'ཅhPpDDaADVok34Jtε8j(2 ܈kK4%""'&DdMn"0_.QV6w [8nuJv"" "8xc4!j.2z2"xc4CGq \dDDcAD&O~'ǚ #~?~*1?5}GMy IDAT5=Ҙll؎a4ѭԡuqЩnEHߢ#'cC' DTA-66X8SVI&h4L*iF.TI%5| DC_oܣ"" "RI1h\sllrFP%m pf[iٹz}>* I}u4pUTUǔȩ|#߀Ap(~/""zl1 c1Mzˬ\՟bxHm4^yVGv)J&$I n]v(͚5]UF &mYȂGO~4hոBflm7nn<Ӱa !øw,GObxU""Q9}hvs; E1;;͝}R(nnٹbk; C+8}ފcU/WP0T1yIDdgWQ|!%U+TژmR*sDA3ci7* *N%&I /zPDDcADUAc͆9oV4$I2?J@AJ&[nͱSBvE%I?3qyԸOىjlI?̟xAMbZ2KV/Nc$ dds,]h=٘lQUXٖzu1yue,~߲3tGi[AEh4ITR>j5޶F(k[Y6_&RlM?4{NmW2[k""z1 `a Sn$dǧd2[Nk\޵pԻM&ɤ'VQTz3\d2]O;RTv|3)oT*Ng4jm R*$]=u5pcݻJcW6No RT.1ȵչϙL&SI*}5MMy4td2e'4td;iԠʴnP""z1 pJtg+uRx_Py'ݦ_^ok׷,aN=-FhT665x=nL٤7}'nh4YV5 =oעRs>W\>7m07*c/,[_YƬcZ =ptg̖n[/d-o]:ltQfd2{G=ㅆU`tx`kFl(+r]gV:??DDcADUբMڕyBYzN޹,Ċ.^g)-[$I¿WC_Y9qNH1ܻN$ÁדWT6+ Xv+CB{?U_-sr8٫7q.Q{O{<+. uA'W9ǥ.yweGtoo~1~bUYIe %SO7Yyv=ŏ{=lQUZ4ݰU2TC7!{ cA4JQ4/HFt^:<ݟd v9,:32.v(އhnlWlV-YhSGe.W bCvlQϣ"G!Y4JR Y~ ZQ4 b{q%=sm'qEש(q/y "'_GDU[k7Ѣ5:1d4+5ȩk?,AEI:y(x-e3DQ!$(e0j-DE_ݚȗ 6;9X]Q4YFAQdi4%Yk }Z(`[ILFQ]{L _5v,NFQpbR ZY` ~|7 ॕ_a~zFuftjꔈmADUIâsk"!᧟D1S X=֭}E40!Č_J2O}-U9 ۶KlE@3ǧ:xVoԆ D13N!k˺apjFO9sҩK‡Z Dzyrs_x><haz5k@WߏwB;ǽi %5版 6.zD Y6FeٲpzAdPeI7Z~ip)s=ns#뿴_ ;>{(6+~U>*ޕفԩx%~ IO֨O9;N+uaFжKKa؏L,nd@*pݍ 2kBD5fdWWYY{99$dP9u*q1'3NNkP d % D PTQR@Q88, 9JRݣr7*G]$L`낂]߱XZp;2m4FYG""*OJR]EF&>|XVn 0Lpm7YIll*ťrp[~L¡T@aE*HR94iP(JWV"(3 6667`MEsm PMѣU"Bfg}RB*"r6[$\^rIl|Ɋ21sQlD(Tv푈ADUQYVS#j:~ W(ܹѼy<9ߨ3޾k)VU5WnXJ:ʺUe_ѻ+JEݺ^= HO?4Οt9ww'Қ?Z4h܊cU#n̾.kaxHP(5j ɮ^=utb~O8dktpP{x5lXߊW6wf;׭=lQQ*t #'A1ꅿ߶L&h5kܰa}>d2FElmmmlVȩ0,*;VBDDU *?/G7ɄȩH)c~666jJVqn2k5(ӰUkm1dZp| Q oXiԤ,DNE܎5=x3YAX?^ہ8tbp(:4r|)\LDDbAD1,;NQIu<&_ꐙ rеvk.,""z1 " #ѩCQшVU]<.$ bѪ>[Sٌ.5=l55lv'S% ]9wK: {cѰA EDD&DdeJ|!⿇WΡ_CQ6ǣs(D Ho//>"#"F%[&`=;v} jUx.`ӫf"oAx.tVa[8=;PpDDTGv5%2`*lJ:N2O.:zᯠqg\Tb1U똹;# 퇓yA%G՗lUOWDhkG_7`gh^QMDcKhs?;%d#[a96>rrA"*С ~Ai|?m-d* [~<B7V5cl܎TB0fN{y\=﷍M~>Zh4!=Z#+:/WcdiK,ѧ ع`D^=<F#lm?Zct5d4EQtwG=tĬ*c_ ;O76-1ɳع;a!V|W3B-u5GDD(JׯCTVϕ Qmlld2Çe}\$L3A.L77{:iwKÖزwW Dgؽ} \s~ۏ+Y1n8TWpDDTeDQqrQ ye`0F"=a[ɕ ߓ{V>O%!("HeAXD\{=]$31-{M\U!?~. *t]PnuPJVEU'?$8br-{lZRG]|Ҳ]s4%ʴ1{_away=Ṇ-)Yab7-,^ K [+wi%+o +i (C @S~q;zgڕb\4'^@^[jYzcAǴœ/d`ݵN[|(,ټeye'6mӺC*]pmou$@% w&trE@Qb [GL~0(woa.?eUoG{830Ә4}oAjS/u2戹*L&ɢV7_e2Hꅥ2G777,hq-RMnt44xƽw| .3S'˶TjPU( uЙ[. dB'dL,-hϖm9-sM;[$3<5b忽Jܿ!z9,? %#p*ǍYf$q6~+m m@ܴ^#?oEYcؑ,9q Ԣ1MqBjI-4)lö }Ӎi L6 [ / `7pJ&8A^bRplX4=#h1#Y$c\}Z"a0\P8]0[Wfrmy|%j^ҸvB!d !I^;IvCǯ~D0F \tyI`4vB gSVr/8Gez? rk"|{vKGs|8l?wӘvx,V1&mؘtƺٶW\.D1ύ_=}Jnn~QfINe?_ ÀdJҩFx_\V*khڸpeEFp|nΥgr,Hrr~x]lI;]χh:𞰶FB!z$$!R@ !<$"xHM_78@ p~_q8HcIqYǛ{@@ 9w\{Ν;ʞL6MK Q&lzcAe 臰<͍mGUE=7TGbRGo &.+(]__hوֆ}g\RXQr{si{Jl=һv^5u l B "Jgm@8DQ($p$ј8}t'IQM  HA $O8$ :?O~e(Rݵygr  F Hs]Pv(\4iLcO_xWy #r<&۳ekm+Qc 9s#G+g=n{loF—\&N] {Rr@.I:B!*IR hEHR :`! "%xI[! J$:8]8ݷi !$Ez]R@) QsyZa4pg"$ {Νsvd 6r g: cufyxõeF>1BuVxv? zXvu=õG#=wUĜaAR42nβ)꺊ב IDAT%HJXB!rH$/Ó$ϔް}ow.<ǵ6p/I Rhp ï GDAۧ$^/Ho~428"0z"g+/?)H^Pbt3s @S?6i\3/ܺf3J7$2,dS"A1CqAr)wQ {}!94m̢%'e?>B!W":IK I)},!Fg1qݨxpLg0 6p\"@EJNk섞ϝ?ߍR.es?5YvW8h.B!e4O\yDIZ`D>ڊK@o?;$Qx@tH8!|@ @Aq<'x$O1Cz16$A%NX;$Qs$J) 󒨗n"[F8h.&}8B!dpz$$I}^*W^DH'B/l/),'5@I Rm*` mvP/ rD@ ;7R {}ݟ<'Ep>icYI zaz&BlM B!218p]$3ʖׅ/\G1 6}-D4"%)M.\{gB HZ.I$I$ac5hӧot!B5o`hB@t K #ֹJw.ڼOK$w7ݻ8x$uvܫ'ݼc4N+1f&}xDn2! B!m$Ct'?y,pSLlH»ر71>ĦƝ{s$JbKlxO-+eΕBg{ؙg;}8xI"d6fwB!8] ݻ :8"a @,m,ɽ%e@,OcQ?fp LRzbqO+;bMz{ilL7B!d9/ݰss-iG&mWDlp [̨FVUz57By:դh B!m8@7Fx&x_E$e>WT'iPA!B? Ɓݕ&n& B!2Ax;ǁ J=)?\~qT&I(Jl* F.0f5ҰGzI;WͮY{;n+:W(j܏&UQ`2O2RaJʴZ'K)=y @"BwҚrI$v~n>"ؐ/d8no/YlVJg~NZ;y᮹Wxqye(ƞ-WD&"ؐ/||do܂O'/8+?xrg|!~sRVH8}/(omʛ;ﮫ?BM+Po֠1=&Il0N9)P_g[ /ڿtO4k5d@y7I#Sv+󎿙wdآٷCQU`s{CLu[Ⱈ}aLJOO4UvW}\Ku !r]/?U 7ޤ6&S𙥙]fLe(,Ť <elߛmBש3ga`ow7yI諥3O[g&[*ζϽa,yE%w9yǔ^,,u#>  <&Ng!?i_@2Sroi2?;}y|)%i[2gO ʖ%3^QκQ<ⅮgUw3}*C? [^I]Yf YNߞ9P>=rǬ;}a|n/e:_5c('2ؒɒu۽%O3D rWLfKfmxB!lr`u[BP?;ǔүfϸNvMSKJKg:m\Z$d Ȁh|NL$7-20 ;]D1pP~Qa6:?RF9+ w8JΟ|c_0of@."(;Nͺ6š =vޔST058aDK!KnuOyKdbqtEK^QA<ᇟ)H_ň~Q͜5Lqǭ>tE~ 例aS1K< Ga͟fUA!BnӲdSYdI5P??le_+ yb&.^0ͼwV8f LvȼgWD?&K[osf\ NLȞc<]`,;3 Vܒ%Pu_09̩P #Q1le{F]_@F/e$Q Xm9ۜ%Գ=s˓6qI,*BX)rDcXv2_τB!_8LSU ?~-Hh81X[m-6qIܣx[,8TZXD @5pLXe6A% ,eF8nu̚5m>c=;o3J8pJSG_2E[4U% {̝z٫9WٔB/'{?P2B!q<ݨG3 g?)#٘}ye 'NE0F+qk&Y2L~" 2`W#6#Øi#|B1*He '}Kx8-54}t0SUI76z)]UW?\C boLjKcy@dL쵎g'B!ۤx<@`u5~~*a DYu9-?i cYXjɽ5O#<O.>>u,k _ Mf,? CE?v^iST)\D3fL&ȡ~QNVjd0 B!LP(4/Yw࡞~Y~XfSmϷ7*KC[B!1)QY &>o.1"֫g|ǝ3sَ?v+mwNeϟ=zln^=!Ø͝}ϥ';vd{WQx c2'?y\ޓ:%v-pLnxUʬ=3I~+W7-8̜oCߖos6,0 U;3V?x:Jo~sO,Gx/ҿ=AQE%*;cM<ճ^z)ҳ*KߍWqqlIVڣᓲKD.wLAimA3]ǻCU( mEf qztUOX8`&SZM^o\Wd=sw<#{ƽWl:7*U%ՑM{:8@{}C]"mszSgs39C;,N4yB!odDIΖqιL·:MɯjncBڭ4%iq7L{ڬ2_ݣ78vt(٬ HoKu@ҷGX_~C1)5GPE2ӾJ DdT4K'ԡ)/` PwqkJ'0ꇩEl >/33-9#;RUu IK{`cDZәt|:4) ?A臤[ ?'h:S>LԴEi+8:ںbV& rFm9~ ; w׭C`lQ?z Ӝʼn2U3!gHMUז|. :O/ {] m90\, f"k8R,N HQ}=j*|̭ XJط6 Hjc'2zLCަ _xSyVd÷]+#r 0Օ)k^Ԭ{ݣ$ os'xpgNs:LZk9K E epeϰ*^|ܲhdkl4O <\x'vNXi??sn^}cBBkc'^WW6";~ p\4M;\%ʝCi[54M WGQv7HP;u``Eܒ* _ٯFM^TOU`>Y䪊v*gDw|^UӴKUz5&.WԈr}4-pK~^UӴ}zJr|bxdVAyGTjЯiJf^j&i)KIwAql <lym>,k*.s8Q#:A)4SplU{k&@G߮WwG#_/[1]9hqW˪}<]jpC[u?i}7]齙T _=iAw]u79jּ5$p<&JHH?V+j>Gc%y8j*7i~9i 9""B֨kz|ivFՃ>z/\ WƎ!xςK$I CCC(D"(CCC$M{n .ff-/ 󠢊 jY3~MGovi 0ya͖=;9'+. ԺO)Ƿs]A?ֲ5&5\W>ka=3`~1kI13{-5P㹥v5I~9ChT1F4( ]汱Ф|<{6Q,}gkjn->ʰScAR x~cԧ/6s`=U2\.g `<^kOO[t-WfgߓޅZuz'(wZ}cԕ)))KIwQ#-nPe hvi .o?sqƾhNL'U.TND2\Wˮ6[bO,_VWfUJr4 +@yMвSF]}\ 𳂯RV/2;n4Ga3_H!ѫeG WU@knOqItw l~r*.ӳLcMW|o^!.q{b- L[7x P}Z*튭M~%IQ5I/|<SLi IDATT2S{Cfۥ&wRK>f wIV2Jܰ `;;vY>T4wO0k) e]z7{/{! gRkoӞڇG;p5vZ49ظ-J9NE\A&c3˞B@W> ]A'V[J{JL`58ӸTٚv roKs:B WITa-g=mq(=]nyh _6>4>{kKEk37W@E->"ǽL9Rpm\soSD{oGY0pP/fHY0!K{#ح'v;4DIh,YاNEtv+>AM Ms^Ya+*IӣuOtlwkʏMF6!ۛF?+I>@!_8)0ekbF]'k,T5Az8NOZK˗ߑU{+Mq/kT |f9aeT5z0УFL$2#AR.wLz!~ݨW=Wmhu v_܀Y1GɘgOJw~yڒ0-{>+x_Aݡ]! ,]~i08~a Ch.s4QJHO1=q7]p߸;m0|̛(' ah5؄͞[<H,y1y_x0%/`A[ڷ7~/΃}yFvnR~0WonzvŸ ԅ{Ur+4AkHd^鲯6F ^AqB™Lڗ-<Ay9cj|/"Lo!ɜVǡi'f_Vl{LM}>UV:u6#72_6^l'ΊX|e@;եڱ}C 20P5Op؝q8iMdWyu#:W\J3Dm_<5~kpyL߷hK.I t--Cm)b4F9eίu wqfLe*[k 7f]-JyYՐވ_{G:%-o*^qUl#{]> έ&@[t*Ucp$tuIG< ]fSE1XiE27Qo%yC`3F Ӝ%/h{^>P;hV">_Dpܾ}ycY7zxQ)) +[ ߶_<{O :{^*YxE#/x^wSRY߯_yhT0W,>yIHN4Y3Xg; )>¼ @[^X0+zvѷJM7V 08Nq)[ iRˡukTO-IT#XSiC{C7j0>c]C5H{=b.׏C@g|HL,_]z Tl9p{+/(hGխhu[KGşJ6|5g)W Gé+j)2@џ#f4 l"-_Mk:.Xܿxq`oC8tC~}[pa~2}Ѷ7l&7nTT<:f0?? 5[___}K!Pu2s}$O~c hv>!7S3+壻.k\FG}J?ԑM?_~}\4Jov-EV<4fgV]Ui/ϩjPtc8w_0n1g{{t%&~~aO{D*?n3dy11!Lk۽t+I$_Y3Ok#STRܜZ9}(pLDӴ([I*;E쒪g2j2ىL<#r  K#OH)FHRT{[2j_T8/0wG}D''b਩RTX}z^piiZPٖ<#sUڃި#o1WRKU\C#gHz'm\Q##o%y1͚>wUܛD^٨(fJWj7|\إ.Ӿ'~:ocڨዳwl`c&]5#ǎw-+*"1͗{{xt l _bX9\7i21h)GRp̃@!6synCI &4A`7%" !lfxX I 0 hę!T#*$y+ϸ)!53Vn|=MVZVefAx)#~} u }$OYVs珿8RE8I '9mJz>ER&`yӨ1Wq nE p_:Lw$7{%s575$,̹Fu)B VӕdisΣSe0NU]MטB@ww7M@El&a4a1LiN`a.;!i4-!d\ڂ% v/(M=(J(rj0 w, r JOЍ&۩}QjK!k2lB!Bײ1f"B! ݨEU9Xzaα^q`ױW%ũ6u>H*͉+ݶ)UJo^P\ZnP:ҝs#hӧYE9uYƋ}PߙӟFP18sӟs bB!r&Eˆ^n~nv/ Jn~7ȬZjf˝QB+_| `sg .۬-)gmmix׌Zqmӯ$?R NnG%r]5;^(ok{sgv4(wlW{w>Z7z+vm/Cޝ]+s* ~B!x"lPV%|^0xj EcI ) BBpS\\c3zjix@˫zkZGj?8ثinS ?Q_p} o{d AoWk-{}= QM2]w}inaB!4I *inrJ~Yj{%vu:VO]/xfNk4ȚǟrX7=Sϻ$beۏQo }+֫Mű+h~uw}v^؆rbȽх9im50LW[ܽk}_m{E9_ ]=au{άM=΃B!L`}ŕv[nGFcڲaŊN4W:̀ZfY; Xs]tRGzwY 04+݇dc{'J=B!qKv=O&{ҽeVO_=go,1`p9΂P>_ rϷ4lj_gkR7*B!rU&Md\k\ӗ{?Z?GN~/b }![3%-l]y9FLaΙ&v5Up-/e#:z|hqyѩM_`zIm{pGVǭ6[gQS3a(`U[khy>nq-Znx>M/h'B!WcTpy ':?z~7[WvlHHE)5Uyڄ>_l"$.){st7z]#FoHGyc|>UXM \upCE% < )@PlFw&x|>}uO0@[6ﮯ߽{OyRRnB!r&O\x< kf宩ù,r,76l 7Pͮs_"ZlBj%ٹ/;l<0Lۏ,*0?=\[ #jK[o2XP}3Lv͇_Z|&o|]r<jݹٹ|yľKr=he-_b3~G ze_{42 [fӛ6!BUa;Mt1$Lt))(hZ)5k)$lU$pklF!&=)Њ$}!pfQ !IQnyc:OC!BnnoEl&a4a1Li fUCYS0shY;9" # kwB!+u37snt!B&ј B!B_ 6!B! B!BȄ`B!2!( B!L 6!B! B!BȄ`B!2!( B!L 6!B! B!BȄ`B!2!( B!L 6!B! B!BȄ`B!2!( B!L%ؐ)ז"%3L(][%!Bwsv%>_7ˋjB!|Q GTyj<`keoo:gAooo],b⯇]YԴB!B&lla͙q7q֯/>k10 VұegwkZ/~#lq8dV#''jF_Ջg1 ,XۻaO S-^T(J+0 Z\(Fk}n,aH݇V,~?*[:6miٶaԇmV/fOVz} ۖ8c{GVB!+uuo=_j5}ʹGv|[`.veRk_Z%H6;M /sMu~+]N}̑wY8F} r3%/Nð_O!Bn>7KaJXһc:4/'p//b_1e-{Yh9 E#B!,-lejº-YkkP3^]o*=f{qsJ+o4N7.Ksܛ֣駞Z ?e&p6/ s3 b3߸u?ztyG}Oh)VUA$G,cp<@X7%#B!>",e O]!=u{[BuunYXG67U/>="dxOHP6xPG~pDe9M?!AA_Xu?UX MBR'ݿGVB!krs|FacK7ózcZレ6v'B?q3fǼglv>їycjo|TUER-u ԕ|>:{M Xh" mf gA̟βu[w-rtkfd-5{g /xxs+n}F s~|f\Vٹ׺cvmq}[BO/]yz!B&]{絺.F.*)yk"wXPB;83-r=yqmYC ,\ !y5LXWZPbR(Eױ8B!rtwws܄?)bL&0iii 0 c20 @4UUUUY9pV##աپ7zqpQ'}7[}K+:blu,B!эኖy{餠B!t!xbYE] B!Bnj B!L 6!B! B!BȄ`B!2!( B!L 6!g}8012E:TaFʠؠ0 i+zX)c`\4[6Oؽ=#_Olz6GRP*L\ӳSvalY!T3,>'F} }jG6aC>Wx P‘5|#Nl 0LЄvEyrj0ru2habn^A:x XUY:u ;kCS>6'XʎW258%M g)9l"\ꅩMhn"jȂQA}ŊCJGl 8tHlJa72 랝]  BlWy.QU9!!KfXO4OUu obQUT܉\>PVUUBN`XbQ{^e8Nr`Su.=3's)kK pb`FףA)9yS|*O, 5Ie2x,hԮLj덦\:HNU`x"M֦M%\MEh"IoT$c?pZ'z'U1/WBx.@5 pNtu2#xzZDKԉ) !˔ҒF^GnH,[I3D,YWX. k,M ^x%t,ib~"&9bF&=}!.hU-i*36,WkAvX( .`~~:_yR믿$曊([o[曒$jRXofDl `CՅD)āʪ06`R|,x\[#FRi5v^3ˋ @lyY΅+kLY~ŤvPU5q^òTT)Dues\Ho mG4dy1ȁ fTU-BWY6M +:(9%ZVUU]i ͭ\PhAb8LՔ VUTTk[_*p+R:M59V["fVs!+V9х)V9&D.Kۼ|:E̔b'௳^뽯Pn]ْda?8.]ҝB;61*`)\88fSZ @Nεp];N@3޵$ډػVGpu2W '+C.0B;?EN ~ &b++ӣ#1@z>$x>yWX$?}*Ovw#v%c; )e=xC9YT2;_ KEq0]0hG@W;R?$(M֖MZQ5~+\EWJbKߛYP䷾.IR~oxtg9?uɮRqn  3-ȖA-mnՆP' (..၇H􂢈3W C9IG]J S'?Ѧv'Ezzhl|x_ߎϮRq6xN"Z\^čޣ1c4 BP(ȭqφaAUKgUsmSUUy10CB,.$hfPR|vmlv> xyTˑ?.M]NxpI)u5Icw򱚑4 ,{yH^Unઉ6`=eW``9ޔtzp" ϲy1w|%_RհxDKy1pfV5efEP( ?_6#I{6Q$,䤎$XD XM;E zj"esYnvv9~$QDKE˝.s34X@eNk E(AM3]MH}5UpE aXE 6h .^iesBlWђt~_zS﷤/t;uԍW<sM {1[;P N'.?şO%EO/{Rpo&-|ZM΁;ݝפDl\Hb5F1KuXt tmM_wwoԿ&Ru06?ヲtn,@FB&߻s1$8n?u;0: c8afVjT+I,?qԩ#{<1cwQM5f6%l#:LSסiG\OnwrrA1If¿=| a09o_kX/L:aX` 0 cUJS>;0q*ibp`lff0@qv0 csLU3&]z`XRveggjղ890 c NF5¯d6a{xvJ6Ĝ4180t0 ]i+zX)c`\4AZ`EنI$JvХsxlazac>4:sk6'\aj4(+AaogInLNtV9n>hܠ10ܸa1R)>~zI>] ־ͫ.a؀a(]6a\Cf OZqźX6aV ީ@* u \][UH ?3??s(*B'aጨs !'˪Z. !l%! Ra cO(.h8/%!E|R༩\>'UUB|`i:!X. 𙒪jl8!pz5EH",,Yp uYw0 &U{6'R9`Dsp|.ϗ&Jhg^4g9\. Y53x*i̥<w:9!p嬷pI xPr2Z Alp&iHL+|͂u㉄,CV1h٭~kn}pʹ0hjYc1K-C7qUZy)ā Up. X&OƞUQ_6M l]wh I Lk I61*cgܸscO}G9vvHA+k%<p]ݻ]؆#} ݻv}vs?1Wa*i㊐/*gA>xE׿P;ڣ,544'XG3޵$_:NviA2`)\88f~q(m};f{w պ#;h;+'\oO^ eX frm%ˋz0; N\'uh26Oqvecrr@#H 6,C4/9s3:.\=$=I vg?Gg{+`v!&GQ<Pd3,ӆ*X \ 6UAE%v@gX)p?_V7i|%2Fbp^^^{&7(#﨟_;{}$gϞ{* ^zsEYUP[6-l\3>gN +O`e!LfN;W+uILsq {."iO IDAT;#f'2-Ug=40߶oHF:q!_={&nwl8N!隆4Z]˿{*cZN 4/Df ?AFt7z Qcw<RO:Ƣ΃.IRf[+5ZjCڬBR(>]p' Kxbieawߙ$ !gR'X\0{! $?]G>F2 ] ϊH ސtF2q$Pli@{@R'C$?^{(/e: HoX]9x>?DM ǓGĘ&+O~5&f$Y-'eies5ކa:VM.?}Տ$_xp?F#ʻ 뮍Ɖ)W|b[i@[Cw#y{3 R8;IWN9iFOrŅ=[Nuֶ_+.4 ~deeiiiEj7Z'O =UER4$LF3M+$sl6:ۼ<@=9/vdwV~ݽsGQAg*`籯 Qǻ-e8|ȍ3\6Nݴn=OΟen,w`-ﱟOgx`b:SbFN?4\,5Ns۽~}zD$&\ {J ๧z-wXM ow^8۲9x?O{"9gWOy8?:nGΊ|1^p ⇨8pQct}7˧{8w3"hi1uxUSF->X;ܮ_vb IBWeDzx3/aZ][UH-BW׶?8IQna=ڏ/vmooӨ];w6b麡9])-w4/='p,xA.kEΖҾ]m6l0uGW<ړúdݶ,=<;ҕ+I8C"Վ$gm` 5;mFitXķ wf6Zfj43tv=eBoɻlw'fM/mA'<;6\tahֻCs~igECger$co]T1C#o6n!q)ba`'{}to%싅ؑ\7Mi@Y˕qs+5 ĥ¢\HCn{و/nnJhlBP( .Fl*7S( BP( l4A OJ 녩_W$)H SB&D6-;rmvkbT~LP\mo6ta(irC_sDm"Wɕk[?9^L7XBx]-f%67qs݆[XNGe þӿSdYl0-ti\Gf KO]xJ{8;l\XTwW_xB e@>aXk[…~uO>aX(nx!~ps{7;;;gDU7[M fn+Dd\QKS-XИ@ҧ-G/]: Z \8c7ե-&%N}I瑝7EiG-,ף2˿¹J 1EzH^X.LA&tm|r܋>crAvG`=ܛI 4oɃ}(8f4B&߻Ic><ۺa=ME'N|@&-|Hh&(:H#3muInplPw/ٶ}%¦"d *y"pc{ 9[ڷwwkϬq]]ڸ:bŎ $8n?umN 37[N[qMyn>7:lzfG{`wnZiY>h*-gw{ql6Aa7=W*L\ӳSvalY-e07YKgg 06TĠ7:>20FM1 c='Xbfgw /Yʁ^ M$ѳL{bp`,;;6`g18)JaaDQĨaobYʎ 06̒ԨxOǐ|͊PVC.07Z9MAзQۖN::\.O/}X5Q_ZZo ¥GvߘV_ՇJvacgW̍iq33.ae3Q`[ K#}lQAcxJ|i5>'6D`ZLN91%Z^\|篕ak5lK!#qzgca&Vc/L:l ؇FG}(ؽ1cg4Iަ( ' MUH .Ha*(&mpjk4{%*l'k..鮟w]Kܪ WV&+nhޘt0 o>2aw͊-K/ Ie=A`s/iZg>C @vaz0 cz*Y{(o% % b,X h΍7D`Mf1e5ϗ( C.WJ9Tav0c8`P)7Wyd5FSB.g$jIic?p钪^| '>"GgN༩\>'UUBN`Xb\'JZT!x|\du>V) TN6n 6Z qyh@#^\Y'y9|0g0eBʋ}&eY-o8%BNM[Nly5 6K$RhRE j*D.S(*G3$sX| j)MSe0PsQ?2A'KZiqμ?OB@ ɥ,H/D`r7Kv$o3lps?Ud㕪ZYg5VNxٚR gԬd,iԬpI xA .ՊYO$O{Ysq}"HQ࢙ESFUKaТ7F(nV ,,AfW AelcfޢVYY-D1HݖsaP<{9]W h,)MG^CLl]l1p+o@X)2\,28I5+ ǣA;ffLLA^LiuXۿ+R_7$IZ[[{7EQ孷z뭷Ey7%IE6dy1ȁs@0bIՖŪZqtSx.k9Vmlh.Td'OJ fZF+߹0M%!X@aTU V$!U.VUUp3 ﯌,'e3U0.(]Luիc@ֽ%UU+ePjWKd)7ذQkㆴшa=IR֋r [^-漨DFer 17YTմdl4c-_TmMF>#!psW˔h^_%kslfC-9{}bPe/O-ULc>ѩI.7 .^>7#b aK6 QŸ@(^747VVU-@(9p!AU[i 2uiTYY Loe Yd0mϚ$~ŀu%YW+akI,6sL0@Mfz貕06)!Het!|fL$}A V3/ٸEQUO>+o ^?~2}c7;wzCqU8[wq}_5_sIȬ]; \=zH`{xx4p|_mOH k݊C.0BS}ԩ{u_o +;s]{L;X Ɲ…=݌chh<"P]B,h,kmkkW8Qp]ݻ]XFE3r .\|2N8qyfn.ޟ`nHyVkJ|V`[bRI(_/vy7M1RR5c%<Oŀ+R U6S87S׋¿~|g^U6 ˋ:(N; `Saw8tw:{ͫ(ye&۰=sN>:qp];6C^Anu!\ìnf RFS}mŤ"XRLrJhnb1M4I~¬ڤ]]wWF'̘52Sn@nœxrCݪĻҊ%=yop--W~2h # bg)B7.$OWе RUPYw c9좬?M0֫dSO+'VLs~[ \Xw! x"pUy㪝>RէhG4Ⱦ~x<?)޽{?5r8auѵJ6>_~4̱?/?2*o( [2V6szeq/+$\X?r F$ ߱ﱑKBЙ=͂qRb6JP_CԥQuUSӖhnp\^|9K EB;W+2a7\7'h6fcve #\Vy,f]YiPb'@L{"rrKl6J ?7ZZ$K+ $]ׯ{"h=xC9YT2;S>-⋥{>Ÿ7ySY{~H>/IQJr"H^̂'uNx؋L/(8s_xHJQE+>++ӣ#1@zC"oP(2ʞC_]$ii.[(*8=11Rm Gn$/5Y2?}7anQjЮeu:>HBQĥlpd]M n {"/Ē>t$Nv}F7xf]F-#_r#Q* {O]8H|ã>ˡf3b+6$8sbW1~{7A7irwYR\6\[zODׇI~ü.sviij @7~ ߚYXf}lBkzqЧa |F EIWf/)N$ 05>5ds}64. eq%/uiTYdž+ϵ[Ͳzhl|x_&|IAzsNĞta0=}LW~h}\ةk \YڡF$ġ+gL> Xm=Hf9r^ &zVUUE̬'+/5$jufi!/=GKBBIUKxʋ mCIJC嬭^;: 5CE5! Dv ճԙUUU|[ML>Y(L/j76m6"ֲ.DuT(-ARٳbdwvQ^oPNŊKJob Su7ȳL&I=YLwφCQ?W4I_:y-Na7]gecp ϙ_ k)3cv0oxNbӗ5[eGl 8t.e]vM7_+4 mOYnuhWAk`Qp U<>kVU& J?VJxb^Ys Ikk/ V,80K9,G6aC>W89Ƴs:[5Ӑ2&YV {>3ߟ@{߷Ӻs={hO /k*Ύ9쮪Mɡ>-m!0vlQ`̮(>al>0=`ajnº=M `fj묌 8|T91I fJY5\%ZDܸ09Ԣ,M*PA!U*YrT&k/Ύ9lSpmOM-m'fF^G~VAl}fgw /hjp{߈Vcm^Xc4&zI.-Zeĩ^)f ĭR>Xw0H/CspBȥ<wr>ӹ\"ԪV ˪yΛS/3r~`=D<[VR4AĢ | p?Μr TleHWL$xD㑀'L~$eN UsX T+KRXW z)!r3Sr:`,H. ǣA;&jIpc\&䮩\ RS~frB*MB^$bԉ`X']Dk><[DŒ=rz]c9!!Kǯ6X},y !r)?q$l`Yw4ò|(ϥcLUR37I$6[\^\\\\6 hjdu5_Wyc_DA{B2c^Nl-L.f@zY5eݞcesW<) ? x\]k%qN22X3~To| 1JA&8 dlU`i͐a&YTZ g|jfe]匚U9>MpI xxkSUU(| ,jņ'˧,יPZ嚧c1 WGu9 +̠&e85릜)77o͆Z3(bGl1Q+BZ\L`Rs\TifBXo\ S]jsQJ!\ (| xU5V1y5,•MG>oo*"%AldT3&꘸Z@eUUXmɫ!ˋAl0j98a- T(xϔdm]^鬜w4_t ]'b Ti,l6#FDeU]|X JS-,tF2%ӘKa`ERVmżcxV${R& R8l.J[R5~d_TU˂dOΧy8pŚ}*]WVq|q/nyC,DZk!LI-#BY]B$99'r41>9HLgRE ?3 r\.De96PqsXoBU?>sЩS0lT{( !ƂMPaTZ̈́ݵe }Yh,71]4{>$*y 2[Rpjh[ /fQ;Sy_/}p}.C[0T)7d<#˵T{JHvSG'wV ɫdL:Հ7f1̌-p<~AuRб̤`DBh5И7MFD؏H9sh̥F:(*gM P68~.[`q\CV2%Iv67ݳeǀ&4%?jT`_5pߖ2X uB&鏹{hhQ/s|E.˩wt_/eKmS ehL\T0Q3ػfOo{cI&V~p<ު;ykqhc4kxmJFsbaUWY# o e4WٙtqGA?D[ɪ;ksþ9B9FS9:Kۀ[V{BԠ/ mT_D7$IEq:01`_;1>ωҕf޳A;}*v+YJ;Zv&} -2<4+~'L &)A6 I̢&>~ɧOO2;w|os= 0酥Ozd~4ûئ(fU WUE& :T$ҲPYWojhB]8F`pߋXpOowA8 U74[`5PA_ܰ[҅n4*+J/iLXrQziDl=h'vCO};8yi'> heNy;>Þ{ˈF[*yۅh$IR/VdU:CG $Фͥlďm;T|3ZϏ_|Xo,*@]ё'|]&2S:@oś{^!&|w;}k˲\l8TyxB*\g{C HS46hhCȾ kZ|=ɤrd4Uw$*uP={֫9aVfjs^. Frԃ`5:M`IJ!'<*/o$⦹3U mEcbaլ/od+7I;e0tKI7oM+[`a}rJ10eLzx =9/|roo 9¡قy|#W,~Nyb< XcE0 ]ewAߘǞbZƦĒUɳvυs>>Za5\?Ba\8F{gzI`߽b\wmEF,&?/|s6W,?;nZ\GS0LV}],{v`rjqhiO L & ~m@?\ nBcu^ȥBO{r#J/s_հVffS]ex.Ɍj\2W |_VW|,{N8~~z:^s/:螬VQNb]cc6fi֠2;\GGX2S3A:4g]İ }֝hAo/gPKp>p# K\ݺWwk]&|aܹ0dJ$IY/:^_A+8}|ȭHz/F,pϖ1uA!([A3$m{J{,$zhy?Yr$I+Z=e1|epɰ䮵⥃]>I\:w =Ѭ,yk\\4W8 Gj7=5&4Xʚ101Ölֹ:_T4LT>*.iבʦ@YKtl@\hgj%266R6ň\V&Wʓ@T$`Lɻed rb '/Y}!%;~7S6s^pɧ}XLK.'}ek> x"˒$#7:kBL$e#tE|c3zl詮U84["p٠잇3fgϞ:_ެ'Z (򹢭P ;ws4}X[p5ut. s[s `k:Zp~1ǣUǢxE/UZЊD$'V7pF{N D 頋j(.Vgۏ(xn eOR25͊Ӎl$+kIUiDNGG.glE!Nj6[ĹYjWx.UgM>VR2T!Aİ"HT@|ZzS/tlp𹽚1Un[kS *<|Gn'T6JQAĖܖ^WlJsdP,[ZZ:\wu( Iһ^Cih}I\Al$SCO vlo-YU>8 l9w. Dp5ujҿZfRg /dVxЊT Vln4l?YusbpĕFf-mPBE4H\ KD 6ر"@ 5[[+",E"&kU4 ZS7' EMҠ&dO8lO^]zZ]4K [gcC 6ӽ^Wl7ˤ7o[ !Ef߽';G˅jf7 YJեWӽWե=לեm`X4.ܸ -\Ҿٰ,X` ,J[l\3o` ,X`兵AQ.tV/:2;>܊EYٱjc{%@X_( so(t+B lYԨkn~\6s BҬ퉠?eaer18yCn_(],.n`ꀵA)mh!W /^zp)%pF__S6-׾9M c%@<șg}I/Sc6:p%poPP\v0E: #}~ɧl6nEa黇<}^)-թ'/\|H6 zz[~yuܾPh\mkC\8&H6PaAu6҄r&56tS}qm;fشp&QSB3&(ʑyZcqŒߤ=+Kܫ bY7üeSL3@w"f hy}sǎ8qp[lHQ6]\4%EEP l:@aד1;tĉ7;ΩC=\mՀkd!LL&SE9gxaiizrAQ;\'SRaGTF`div\QuijppR2;58tW|r\fAQ]٪xZ c]9ΣLVf:]]]]]]ÊX/EQTGtsc_)0bt xXpPEQIe+pWlae0gvtNg)lªu'g:(Cד]'m*ZĒe`-ok\ ÝEQ1YjفN:+דÎ~N񗦇'')x*uPEu8g* ၩdfjAQc`b ̗o'E~~b(^'ZZxiK6iDRle$`5<=8c'GMr(qf o7K*p>Z3r'\-@q~|kpZPjr``2%d6LbXptP9:11;e:N}ӣ!r'3;5AQT`*/L PNьM^VX- s`ltwxZs$1UOz@ګO^@1¸klzrK\$4(GGC) r XON mQIQu=˞8:)$eDJTG:.M;˚ 3]aLAjN0XWjG{JHRS,\56n`܁HH"#A7` IR:th_^|as6iUXt1 >򝝒1͚ 7 n#>'JWoEr--RV1=D"s9 Т$Is^艦#^xM$}k_}NE5QGp"=o|MĐpt4Y.HR>aFҚyN_8N{AHl@'ca1rt Z&%~$AUݡ9.13TNwAOne7Os!_=\L‘)G\t`sŹ+@"!w4kB<h_8Kt ʥ!;ze2JGrHP{ DB>@OXTqljrkT{ qX^e7_0g$1݃r,d݀;su IDATD;}tz/̗q}<Jphn٬eQX 'A|OxM>*9$Ib: X$PIlE&E_[ [Hiḃ@RkQ>rg ntc}FDDb`$I\k&֘Tzx$N[颠S,q-6mTpg: v||̎8A..?74;XWp~iln ]ڕ??嚑;?Cy"RG0Rpjתּ7ykO`fU6WTb ѲGi8%~- K/b߁Iyί ѽSEO|%Css#9Z zhl`d )UX@MWzԟ(B`y$Vx@H:Ѫ g4N--;u~vߘ&@KZެAs)ڤa4_lǎhy9eU+ &c|φ^\?t{B˄Z=pb%a5txMۓ9IS,\VQ5I!< 'IbwO0Sp/' ksKP̮UԒ'͋gUFqeGp/N*,E3X^49fW uI49$.'BLJCq4l;-p? U$O󧲢$ ^2/Y%}]bA fDw~<*U\d @۠7T176miA;l7!U[)Y>~xA枂Al@jT1A!칞;ZY'L"-[Sl=GOb}^APZ#lRs*hh5uRDdOzI[ k_'Gן(Jd˛$1S1u4Gc_ly.yky'Eq鞒N mR:C@<$adyʪ(#2co'OFln}ǿtj `P`5بQp_O2Y|aܹ0jb۹M7:/RuRv /+F?t~1#,f>] wg{vџ}+IBNb]cc5!$٩Ԫm׾#wuP{DOUKUM*p3:956~7QƊIn.9у0\; ֯JMTfjs0uт"3MD D|("*vy= \ňVT9K6d\AS>RZ9~76B:P>}Nӭ1`.Vv!ـ_[nL9b I{ق$Ib6*8m_9O/p$IJƭQ$IAYTAC-7OJx*&@=̢`k1U>xfWڙ yG9m {^.m\`}c̣LjW f&Klx[h/vUlrE!Nj6IQ 5:ȶk$bތ"lQY:yz,򹂭9^ E Iw/<86k^,^̎[n!ȯ@19o2 9(Ū., &sSʦsKL&+|fr*3lɹE%g^3׏-zAU uAȭUqY9S[)aer18mq+\ !yf/e̳'lO+兑W b֋|)6? F; #}^+TcKOd&y&` ؔ~vY3@#ՊEKonNQ>9ֲqGcTW<;lŹ) O-\p6I|rf7xRBU u`evl;v?.{繧l`əJ>9֩vl+\ @'Kc\Y76;e=%[͛mlB0:RtQKH]aq6oh[--F'^6+m}SޠN2+O Gm.EZ [c \6X X^K$݈ @,β3[+ 5Syccmm#_2=э;t٘mIp7H`ѴɧT\U F YsdDD4ڞkŔU8DZ3[\Jx=C':;O{SAa%5kV{aG-kvR1M`HjwaEQ1d.uQ:{gWr ctIQî#;qp)P3,zŸjg-۟ΌbMgJ2R.LT>fXwPEu~3*n5z *;VѶgWNa#'t*I&9AQTj.3(rϮ('G:(:'`iz10^rhdF%Lc8#t8RGI\(GR H) ͭv|Zvj+l1յS.CbPH?ŕѮ.% !LNʿקG{t1!;|fkzI J[E.ehH!˟ '906:;T Ć$Iks %\84l vuc>^ mDΉI VU:ٌHˮ< IsZ&9ߐJ6gAZE_*)Q>8}t:) du$9kHp&!ՑH7 YM))ܚX(FN5vm2Xx|oF>~o p…wyX,;#5ؐt헃MN[5IlX`xpI^ʂ})H!q݉l2D1c@+k?T)*HSzij3iE|Qe)Q( b ^oPz`c(DAZl $zkMo5)_$I"@/!8' (,izR!ޒ{G\$Ie5}G*/[#eDp/ႤXO^Hh3`s ,*&Epbv8? IXeJDԥ*TT=2yhwp+F;J6^['TOh IR&sYOTtih3_rY޻BT$0qMR" o$ 8QZ҇txY,BKr-2W5ְK9Z|AmZMTTdm\'hA]!Q5s@m2 D*ҝ9h;,\6*# y#S]w|K*dGl{(ҶzzY]H폕/FPThB|M~}ʎMQy콵];y(^cJ{ J;Zvh<_4‹/}N'ݬ^il4Q/9E1>|됢H؋R^U׿rM:2pNƖleC;X$jopnoiq,7yEG?4;-69Qw 8T.3}6c (/Bhj0ӥN9{{5ơ򩒖Juo. ޮ4M A%N H{3MR]Eَض=pb%a5txMӚ酥Ozd~4û̍})Z)n03uM>@w͍T*Ħp}##'U-5Saw(lj7s.c-S`6:&h8bIAZMT6%6pF6T!fPUUS*2!\J)]K%jbfک kz@S?Ok=l{D{Σ r$-jO{^!&|w)G3ł$AF=V+;lͥÛďOO[C"|Amۭ)6՞#s']>ww}oU]#^™~ > SW֫>U[Iazvl۹xmYSX`WRlO{Hi*tԝE)ihc,;s C6F#oɺliO=Z7by p+S+~i|3 kp iTV_`t8N yUc/=t>p{ ҃qu9ٔ5srUJ$ŵ m9X*VK^=" IDAT7q݃*4*FA;Yojpn)HǚI*y|թI$Av_o(f0oA]+_sSR'3-OVE)5ڑ 5hljA,Z'Pk6ǧ /݋([apy|#1ά@<]z (&s2o;p4݄'R뼐K>#gw nZ4\/RjI?5wg{vџYu NN~i$ח+uԹgVVS3ct; ę'Ɠ뼐ˌ>ͬ^A }Gľ\q)F7WfyY6~=LU.*~̃ع_[_-fse[:q_ҪOabK]0qP=$*{3N@јH[/Qt-mWS+1~Gl[A; a@x[]w>у0\; hs 8H8{p`+IV*|n}=[ ?ƛ}ȍXs+"p_u5Q*P%:POf Z0WRZ ΂M8!֟Qya\!O($ 6WU+ IY ;WG/P0aN ;AgytO4[u!m{;=n@OpQӯ$IZPYiπv*0O`*^~1f~ ybH|9+gBu7FŰk /ﴖ=QH}{a1\ғu9 [V* `ܺca/K`\h.BH;@XOظ]:#%prIR+sedcrΊ/ _*XUH逳lL.дo⠚,b_WQ[mXs+|!^+zK[=h֢͢n-U0e|4=E4ڛI32~Y8Y];M{̆$J=QV-)b 1HP "oΘ5MpUe*v>'I=N`=5(Im&]+NńOiGœb_T?05/9+1Q͌$PC*UGuzi6kbD c+Wڙ yG9m {x6{" |NPsE[\(R Df7tQs[sNWؚmc-8WxljW։Wy(;ws4}XinQͦo}S0ӽJ /46nS!+Uj$GE \l?jfU*R|h5Zr2x#\,|nk6x)~jTk-r1 :~W_nKLfyԸ;.y\.-gY@2Ownl]s^S7+C>5BG‹\v;92F2dn ]?A0qyJGEBE/гyk5$*RՑ ' gjX~,\XYYinj"b뮣(}{>뮻EQ$Izw}]kaA ~jM|@sԎ`Ζs`?{Ĵ:M4' GV({ٳ 鱋8O]Q@qe;o:>w䒅rh/~"w_H:bpG' O#i$G;q@?>qtyxׂ+b•jf7?N+#Ef߽';lԫKٵ߽{/ו[Dq5.ܸrs;ݾBa=/[>~W1LzmKVmL+ü,\,X` ,X\ikmT,X` ,X>XM(]~WجJ5Iqkx }lTV^_]d2K+U$( F(P2p&nc\*mf7{F݂29Q֛"/jU!95䊊%3M )ԗvu6NӔ:X`jS6mhYȼN=̣36ۧ8!6pF_),TiY<șgm+f{U*iK]?{dwݍ&l'd K6mdV)[?PQ_>v$qE5.қemf7PBCoziV5~vC򪃰C>EpWظ cJu57RbbC 1N|Ld?Нs.Y_}Gp?|e31~"_ڏ^D-vMݤܦdLT#ycPfC\b2\H u J#v{)`~'܈ @'K\/ oB5&&TXjho})b kHqi3̋硺ד1;tĉhl>4scG'jK7UUG>w=yb nI#h&ӵ=XgѢ޼QOʽŽ2U[ V\+zrq gaikx>5;IQTT+ӏw=sp2%tPc<)d6L"AKr 0`=o1_"Ǟ ;GQLG^zS+WgUXKG'(Oʧy2ڦfԤ1̕,]*X(1>>=5815>@QZXw|];VQO^ؿ'@P [0=<00EQ|qek*|2=;aB-*Y 1d'È}1EQTq@q=9:%gpb8!V:ez77t8z{{{{e}V'EQQ~U*ʺNvP9 Eu8EuaF ÝEQޱ / O8SjrJQkxaU(h*vP0n)4˞8:uLYvs:QדSrTwN%וVr.%|&34o/OLOR$BLoEQ ̔(1>"7>r aG9 :{b>k;f:~fWtF*F C`od+ԗv̰)u)d%m 2MʨՉ\ lLP5,aukB2ǘ½ޮ o^6__,XpᚸA\L/4 ݁p*y]hd.V. "l&4(Nx80ވLD I{uT@8+JRއ hNP:O5Ic+H<JphN띍9tgmMD$%|4!Q\t`sŹ_NvlA6 J Xt#hOp`|$-(HkPVn^/!Fwk-CFU[ |fTr|)3 { $)ut/-t ʥ!; !;O~'@okCNpD{BD"\$# R< 'st-2iYW8Rǖq#AtxNAU#A;4ǥ!pӒT\ GsiQ%,NnV|D"!w4bňr qU!SΚtCO0N'|N%%68HE\`mxJ&AT$eZzC@rb_8sIDЀ[l8*!_@\nK=b˖n&&WL0(lTOYR1HVҦ hB̠p"!',_'>AՒS%Q\I DI݌!cJł JAXl@@}sk$IpylHXb˫ayrR>'V:&=~n9pg%I=Xl$7jWn$qkP7.I.+$Z>hi^q~'ag"2ֲ1@ LvS/6WA4H0kX'QoC$`U$, q/ID(]]hw"[fLX../Iq6i7멡ZlT 0e%] W)H(f}Z`k1.K ->BkoLxE ?g$qA:vt}%s%|X $$BI d.,KZlh=Dt@tʳbD fe} +h([:&JDMH0 ~=Jl"OU[N5=++IqYΰzMRm/,XWbF$ҲPYWo*_ w;>̔TXzq_>AA_bwۨ޺ߚbw^sxb1~;H{mRIFhW=t=77R y_tr2?9OA)P]UѲcc@jTeSk*6L;$ԯ c ZĬjd\8*@^4 j#g{3 g2HsG)ddX`*õ]$Q yL YIxB*\g{oNtM h?|3ܣ߶ 떼Ϗ_|Xo,iћ?_z$}ȝ2:㤛K#Ti*ׁ x0 wWv}]}l@<̹}gs/aշEbId@xe1䠺u8_"d^ߐj wn4*+J/{^!&|wYX׃guKVF%ϟ9,$I 2+mihSm>൒o8ܴz.%\ByAN@i]-^™^(QmPROQI%].|ԩoO8UEK(lu"VG&V& OJ@sdz6cF7<3WN5yh<?rq'O: GwCLł WņGqwL[ JiȪ$GRAk>S[.ԖmJj^NHc:y]`:`bMܾ!mB@ꡏDLMFD1ּ R$ {{g.8G(,͆>Ϊo0"zkVA@161ZB{QRaqÁmG6`gWh]֯&\!ٸ~랃pGx<|ttY|7hѿFUYI\VO߶J@#f&Lv _Cί {&$ _%ALDO=[['RKS>@#majt4ڱwMjKtIC>5*-{O (ģQǫri@;T< ~p@FUKt=lGwb$\UY*Tg 5p ,~{>13O~'OKљPjМD$I[/eǃ'8|p>PUri`OuFg(#hB*ŝP&XWlU.;oN ڽHS ;t &ftӿ0r>vJ+X4 4mB 87o=jf޸=X|p6j$Glw7w : 9t?#4܇_i+|U2gS>mWe;@} @=sP`8 ngQ^ i_ps_ %zGGowkm{ L]J\{^ݮVߙ\<=P@rV^Дpvptuص6~3O?m+L{-.?d?kp30 slsMl5555D4|v۳ B+e\l缹Ui} %AHׯc $$iw^Ӷ'OQRA4*};dUɶZjN)ŽT\OSٕA:NYFcU)ΪhMuH6BHbNWAHd47m0M7ݤ|M0 @˗/_|yCm6!d,Muo;_HY%!WQBɧ^"]ڣd#d#O!a;Vۄ %!eB!R B!m6!B!5A B!BHMfB!R B!m6!B!5A B!BHMfB!R B!m6!B!5A B!BHMfB!Rk!cH$ :16쏊/O9ƢGK(JDu u ]ZbjP"1al7ur*?#j]uiY$R妔ZVE{]}P0=o$"cVdR-BkBn8f!:-Ʀ[۷oF:2/s/҅/B / ^ =]pkT5zVO ?kzT]37^9T HDxzXC W:?q+_W U*WL3B群?pW4 IDATgp%V^ҿ|~V^V,}?ja{5!7 ZZb_eYN{[}ͣkT}-=F^ nxf(x5:`eLkTflC]UNJCV7EX^}6WkxZGp5U)JxU`lf]K>_}yV5ƢG@CuN8OF@W_Go[U%^zo 8cZ;Wa4]M: )?O^[@no@͎>0Hi)ݬ֯Paj0 0GDxh0 cj zG2eHGRjbմDPJ uZv b0Ls)Rhjb buvw6wD EF X;M1 X:zmݣK6kv1:ڜ9>ӝYQ kk)lb19 ֈt7NH Mw6;2o~i4FE@ G^{?'16TVch}mVaV7&m}ϗHPOxbufl\|saa=R4ۦ]HD+#E]ܐ 65`eaC!eX#ıfH0d&H'XCe?8MRjb"͍&cjd>Z*Oɜ.V 8۲+IalZY!KWbt{bjY.-P0 cm~jT<5;љKV>ajabazG-"x[@(bj훈^UKUֺ?}iu6D>hoony*i/5ūV5j~+0Z23+/)5>#pkJ̲alb0PŎmhɮxWHŕ)j`5xkgyIki`˲{8msΛe9=i; teN:<~ϥ$`sᠧLr2heYe9\eY'Ҽ.,wPe91䜃A;pJ986u'm9ܭfl?`_eyw=`, tRe9~N )&p;{l;` e9nE;`_PŖtp)yy ]qQ-4l2Cdxnr9P0L.3`~7p\"+̍۔^L+voɭpq a~7l%>_H49ĬM0;b6>UjM<%Ku=ln8<=llaYNM{;OZ%%|%KmeVV0u8UrE*5A˙7iYNyYj]74gֲ;Y,)r{a@/5k =fXjJNL]@ ;(W)H@gxr||U=*MZcm,_į"Xq !Uw|xҥ>H$I>?X>H z8_^NYx*S4唳 rۚ[3RA`Ž,˩hS~XYF.SUWLavO˲,yyWԴ;d ㋅U})Y 5 tjˤ9\X9Zi8[|ZZd\:fNUW={KV}725ctOYK]=$pρVqKo{4klloaN?)>3}xg/fci؉3Gw+z:&OR!Cv7GMTMV(U=w][`EQm7ûLl [!zOMڷmqmO<+KX_oa`n[M(MߩÛ>PWjM8%Uc]17[hVE*W׊-ۜ#s{ة6"E _Œ^1pC=;_W;=nH]pluŠW5,]fqj䟅x;cRήn GdtR:r,7<,*3;Kx53өҳ,O~q*UѱZ';_}*XU!׃ o|g\u{gtE_<~, ϫ6JeV Y;ݬyDLZŏVȝV'61d;A^9oJ"} awU #E_o|Om7t;(f_kl V_8žN/e8(Xͧ9_7[>t;->g}]z.G9!. C;ogwybK[YQAܮnc'y-r[6ِ2gI,v';qgz=.8J_` 36 >#lPfM(7%KJ.˔[yէqX0G쑻ZrKSgOR4g U+JKV"o/g`e[6آ,~tQ'O>l-8Qqūƪvl@7N>R>GMxE]x{9SjZss^sMS %'B“]_u* !lߎwMDEIf{O,C / 6?&DQX \e`@**K3a;;$aw.?Ox<8|Qlzgo4qs*Ni7 Yasw 43vA]Eb m8zF:MR="oƢ3Rj5AZWV~ ~XxsW{wmed{订M'9Ro? 7$09hFKHX.;mr--GXt>?ώOtKdEno}ML[GyR|d-]!{n=C3( l4^g~%ˏ!20Yy F(c \8l\x$MHք*ugs"s\[~yV+/L$$)6ݖN}KGw@*zd?`v .Yj٧CK{2{I9[x}yt;tw~Zm]e O=+i)@{a >8zHYhv%AL?w'Lrmk á{oTPaUuQ'|J̲J'neי5jOj`uB Yey粭4>{8p^Éז99o]y/ 4=ieUJMzV4y:bgi {srZja{.{sv .߸S-QrCzaic9sqg%:v۳-JzmUǼ]١s9T3qYeyfSSs>3_*/$Uy 9V`+|ȿ̑핊$u%$qYéWPoh7NT:7n!]o0 B(P (E|"kFV2Ve6_^~`# Ѩ4!z[`6W( X!^&TQQX IcoSܙ8Y4.^~#SK'RH uٲDADuQ% B0 yZjm+-zMƞNx&fi{.B?WE Yf6'e#lk ,2g~ wU]H|O5w^댫Y!bls}]*OJDMwjrmKF#ģI7V ~>xi2Bm6!B!5qm66ķQB!B>l\Dob_cOU[$ZoSB! m67^i#u k?*k=럭 !BY(2*\P Wp`pX}{s-WV#!B!( i)lbl(Ǧ 0c($bls'lA!F- 0xnkct:ںGb[_1sDk2 ØQJ&ksaK o\ ~"H!BHml͆mMÏf@Wف[| Oz_=yٙ%b{ώ2.mӁ電5 0Gۧ<{k.Ew>~?]/E;7o MrG佧~|0fXy]B!B4m͆zKOܻO=fep~Mzy倽r޽[Uehk}/{g;hG![o;,ma/ȿIg:b~޽Lܻw׽&`{;3$iՌ~G0b;[|gvH} o&˗^B!rݠlpܵ6ΜyEkc= tt8>بd8MF;(ߊB! 6I٣,H>. 0;=Nײo P^8򢜞}gl2xB!B_(|]`'^>;؊?ajt4ڱwMt 'M֡P(B!Bn(]?ފCGwolN;upwj 0bgCG~~_ .{g;c{WtK!B@>V>ʈcǎZײ~$!7!!ߒb*VFB!B,tZ"Ihܴi07t0 lڴ 0dY|˗bXSѠau fB!Bg!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!v IDAT!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&n͆(RR$X_XLwMjD1{16f1Ѳo!NsEuW"H+Wzi)&QJzʢ?Eqĥx,DcKgV9*VsS~C#Vdi]^R-l'Ɣ}I $(^3z[g]pzHA=nɮO?T~녞_^0NH+駒xVN׫J37zΞ%=e82t}vߺ:&>ѥ4{՜x[`b?.isƺ+.Os^SA<R'cKxm"^HodP Pf2o NPuuXװ-Ij|l%YKlCoJvDʥۮ}ZiYl) XSLjtl4Y4Z<˩,Ag~V(:Dg.jͩTNy+X]ZG9rر;^fCs^e('r8I'7aLͣUSą?_Bt;~iXThs`)/is( i(vEb#⨣m(Rxv7;L XFHKf)>f}4omIcCVkKdzh~jQG!BS0 ;Z|kvGrev7;F Ø,maBV0͎Eilhjb@lj0 8BQ\x)юw25HwsPgl.ԄHs)yc!N-KCVH$/1mU*ȁR㮖:;SrcO;a8T,MYaL #P&DocDΗfa*2m}ϗEH3C0/"hNhM35$BVkoTҎDݝC33} cfiiaa:,C#&a,ل030Z퓩XaSL<2 X'bp-͍^n/Sp6 Q)hÆ@?wLqi4ì]=;ia=~J0;d:B\nV#!b̢sG3j+݇b5XvJcNchW{/1XhwH(2me9J(ͳ8Kq GB?|ڛk&=7 `x^N=@mPӃ8oXI .)Yn,˼ ֓,]~w餜oebz倠7eY^dwp9{ܾp8i wpYyx }N3yS,:c0<7mg:nla(pMk&Iޣ{p?kw=`,k%'iU ɹq>^L˓.R`xz:*`3+2lwvk:l8wP986u'P: ',^,+}7jႿӲ"׊;`_P7fS0&ӥi8r&y2d|~9PjW: ʚ7vcx?Vl~f O& UA7 tg3Xj.[.3`~7pʢN'p+fbHy9>.D&=fzn.N:p0O'*ʚڔƛ> uxzm\ӏ4ZN^̎jq24@ ymf]ȭJMz`]r'+X49iyD1s,/ ΥJE^3̮ɢΐv\>ە.3YVl(k3s)YSEFu?<-UG+V&v.vQ{bT4^Udn^X+\3U֤7KP>E;x`:h^VduFC6V3aaB٥j.1XIݗtpM+cۚ5BmqFx_}U{'0C]: m|o6n޶hhL_p)ǽ6t'/pGCxk8=s#,5hג=$$d4n>rx=> Q\cQychJ9ФjJ\2̽yO/V I2!׷ZfV(.jYеof}=[W>^I{QOxeq_^y˰Mv{J>RhҬ87S#,ytcoFt6fɛ:!4@v7#0~͙m0g!^PO$GfӨoj`gqXpDςuݥ[m.zw_ox[Sw3y:Vv@S[Vɬ!%&4ԗ[Z5'vhz)3`G'g{\x24\;8lWsV9s%iU6L4Ogw=C۳VSL5378V=6Vэ4dzV\lԿ2X.?1teZi{ѻ4F!63=d xpVl|  6|mwl{jaq)n/߲x+XJعf_sU: `|P~7JHj]eQۡm蚅(K@e_`eV@dOECC-eYj 5>,8bkE4kfk{ч`Gl,wm~;(~z 8+=݁o>}ur@sK3w:/P^,=yh_ -|2!IGRj-u4uh.%Asv Fnáӧ\cȵPLk"^דh9j9 @VRhC--T횓]3UJϸ]g~Sihվ.pM۶mʢfT?^+ 2Piwoi8k4::h-Ƞx.\6ˬ`Vu&-ۇ%Xy–.D+\f 'W^/^ĴN7fMC@ݭE \8l\x$M`ʢ&:N*[CGݍt=lGwb$\Ai/Y\"koy4!lh6!h||>+֊whĝ|Yہ?RQx݄$6}?$H_)G~KcyDތEg>d?s;fߨ7~8FK=06d>Sf=Ȃ=34#F \>+@Gǽ궲>hp @?I=TR|d-uL*hǃ'8|p>(asw 43vƆ@譢Ku4#o%$,͎Eϝ6\P<>{h`DvL}Ͽ0w[8y-0rEO3X{*}w@*zd?,O:v(y>,9xO/R .4HaKudNRӷ梚x_c=cmZ1IʶxήKO.3 4~S{Zx<|ttY|#ջ`X3:fTC&V1aZZ&K } ,΄JzW`ng6d9$qG\c̓3 ގ?lzdʜWAˡ1T؛}6Yq;2g7iUtVϰ\|:9[MW=<fŇ/^tXs\\Zu-z[Y@v{îl^,e7= >`vd^juvyxYNgQp9ڛ,\qGcȮdcTu*.ؕ)~9@/z1@+ ,Ei{E3JZrnΙp[5&^2#[U^.s䜾LL,}٥WpRO'fz &K(1Az|\}3gQc(ءQ# B 0 enu6?~+{j^NIRi :UHX.R$!!No0 BAYj֟p#NW$ )4R4`0$$zC~ ңɉǐHJ!ƼD!7 I9 HbBHZ_ZD/ ?ڻ^5m{"8d޽jZ91y)Ç ٞ+h򊂀~RQ&ڄ]lWCL$R&HB"o4BBg{`Yh\ꑼ4#/߇EB*}Cn)Pۋ&{T)?nFUjxeꝧޫLU˅Y~V8^`+3Bj%tysM$I26mb榛nRzӦM ˗/_7?k-Oq޿,xjNVq)Pgh,(\agx3x`(DW tygJ]QrH@cѯ+ZNudR=}kĈ6ϹEx49-x]+i)qm6 >]/R}% tɘ~FC3VT`C_p@Ӛvjg(f*Z \MrRn֯)ef5᪨XV*z:_xuUr"@٨-) e[e镑xBC|kx Bzzf.9^aB<:ۻ?H#?̿GA ҫBJDMwZAG)㭻Jʶt5~MSe!zm6!BA\oۨ!B!lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bj6B!!B!&hA!B lB!Bjlu8&&U.UDob=x4I&SUVP+ƮG*Me%;Jk;Wךsmڭ kvBQZL&K. 5X/(l՞ʫ>'KuO%ݽ,Ɵo\Y c$FD&~rCl6 ZJo=t gzTͼK=ojf?kz֜%rzN ]3[V^}I'-WTG(ړsmڭ kvb٩)1wU49cݵ jP:&z\99D՟ʫ.*K_νVיHcoO2^=TO3?Y&~rCl6_N././..ZZ 7o N+mXYK4he?Ǹhi`mRta%rD?V{!+c-E}Z^U%:j|p^,YIΊ WU,yᚰƪKچWSd=ȑ#ǎݹl7gk:6a3<#SSyikຜWc}Pn5oZ)!.J@Rlhhڸu:&awtI1:=61dekWʯBcm&aL3Dd0 c퟈I6KDoxAJ<xQ p0 ceި>h3QOw d[*ORYTj_|Q58^E〢E{(iaG;GB\j/ffV*HвR 8 (sEc+qSMT/&:ւ&ݞ[zV] 4}yqZq<4}|?} _iWx{? ш_ΤDd۪F (~+0ߺPy8~OW{r`vS,O,'yjzA]FWanm\p+`bynf6Vi`N9Lɠ7eY^dwpyq a~7<9/  IDATrwgKK ¾=f,'y/=`Rspn`v zce_(ݫZ%I7 ̀5+G[8`.3`~7pkǵuwH8R $];Ňn^v#fF<ݦHtO^.L^-p6WL5M`Zۉ)eZdK %h)t9q$Џ#!l8zj㺮s_}󲴑 4Io`m~UAm'Iw4EA9<#HlE9Fb^{k&cHNn6lW&(qWZ68b|^Nhv@L** uO5WXZe?t66鴚5wz} zIjjfylW(ƫ5>hfjHiOW:V\i5LvۢHNZc6&HR ۄP1 R|F.I!$ m81.q{09cNjf6Q=!WxgsKm5tgE/Rʏ$q?ŦD7lϧ{tEcr Q7Gv 7ǐB9IɼD g`j\#U$)J=#Ly“IFuaܝ <'%c#Ͼ=1>?UI\-ȧ<j>㮉ٳG[G9Io:7g|W%IZ,_suȦ4Zc>W }x..lzLNN~s{/,,)Yb*=(j4c W`#A_={$ 7?w i%W·TgWKgޕ?*G&-3갘0#d6귰I F}؛qɇϭlleT6K$B_n^3{ Bn݉e.?`xLj=\ h3M`mD U Qg ?fBh| sOUP3u6MΆzhEۃ@I~h%cY y}*B-wVHfl( dnC ` QZq%I+nOsQg2JM Ƣ+W MYQmc=$זT9+?%CYjTUYoΟR vg^xa^_A١Ok_q*'f89{%+}Xm 97>L1! QotL@cu6c_g'6P4Ę6kBؘa_6#]Bs9y;9L 6/ԜRe3yB$D+VTt$4NȠP~{M%&Og|~UݣRlV19F EîXUnK@DW-?@%gBGL<;YQbinx){5߀n[͖=x2=QwXĩ_uOۮ8yt8Quy5T5V?Cɕ! mMT*wcKxo[d|Ո';1~:;tj>$d3)C|o|D 0~wlr+y6z74KpVkzљ@5햻spQ'eAWBν̢W:^)[urɀ2v3e3ǿ70%Pwہ'ڥ3F8Iͅs:@K}Q1VU.򮸂I ;c]JOYt;Ȣ)hYxɉb|#bvDS:?DG*Ydj~^#j h#Pg{f9=:4q|L=#oGJ p}crWx=OJXU6վGj0<PRRw!d} e)Gp`1GwV˄ёdO3o#qƤ_"r%蹺{Lh=3[#xq%﫧Qc{p}kFZCKp8Pabu*x8=^#u<H~84% C =}iC/WRƎ 'Dߪ7$ ٕkzkjք KsLy~&`:r+W4& UΞ;{JɊ{=bU{* iRQ){|NvU]H7?}cنYQcpxwᶻ2⍓/5?K(8{٪W~CW%lp`=1P/q_LqSJ]}s+|Pw˝wKljոk fywնU1K^0kCY|NZz V*3YFQ USJL$I\z;7ӱKV#m~J9ͽmZU6l_Hl%{$m6Nؽ q[~cieop|EIi`.9E3TwπzUX;-嵛pyY_]j+0+Kn:CvO&n)k"5EAxwvZ)+ԝMaWG{"[*d1U!Ix6LNH$!R^m&D5V c$LwXL,`tCoƘYtix.|xN$a?xKH0V ,vGq|􄨲W+WqT0NP~&StVLF6e*ЬHbFƏ n^ " 3戕>VثmF8(Ҟx{FV &UVlm#^C)x$YyF?ɼ$IZ/_^_}pVgkg[|_JM|59Zf\ $|WTj5 dtNʻ4l0O_i:|a᪏֡^|wCVo5vS^u-r6[8')G[LK94J<n@[9rDC(g4g}D^TiT<)XѪq%'!ZZOs黸;R(xǧeUs\jzE3{(k}'-79V[AjCH1U&i=W8UwʵG;X~cšGUYUq|HOq/?{:zI0>S6KFk~dJ*JYSAP0lZ@zD*M^{f^!DsЛþ{q'ؚF^߽ ^k\@Ԥ+ Piy6yRg۱*G) EQק1 .a$`0]66I'WYu}eZmO\z3LN- &CO~kK_yh_g毟PCMbLZeN'v~vx:~'F0{k礧?Gl(L5Xo{&p]X7mR^b_z{BG=n_1{[ޝ~[c_{fl>l(   b]Ղ qAAA0ؘk9Q6S:PSL؉Id)M={|5f"  60ظ Uob|GJ)wZwVOIH`)WM={[UuLAA#|W2?:NLmlݗøkNw9'w2;S; ARi鈔+= 6m]  ⷕfc|KUeF9zlE ÔVv_]Kaj?\=㲷\x vtE}e5fZNgGj[5DDwyy}O߅21i,sVEyFΕ^#5RV$ň=]^^?0SY0LQː3U0 S7k. T1 SV2] X7ⷉe-UaSFzeX/c" -d#=}x^,h5? U΢_mIw>ŷzZ&E`5awD)Q]@2.._Z`Ё]pִxOo|Ϗ~qw >;T/)ufo6َxyv4-km:;* $LJL-swTn ~ttwfcgnC~{f[ӱ_7"AAn {Ź*P1=Ghh?= `qۗr7O>X5h'oszO>v+ Npz̆=U x̼~k#?F%isz>|:}'9Xt3JPxO6[h[ɛDBVc˻lg?1 -yz7& lj9ypE?AAĆg#!O/ {rfٰ@ϔ$|*wsC~PS7?jkj(TaQ@{xDtZ4&͇biqLP|0~@@@&5wI>p QILulƶ*ejL+W @n-u 5AAlq%EM09i՚^t&Lwlr+y6fs@3e9S0 :@|q(.A|Ո';w>-f۪[|o4>X)*ԡiπI~B `X-F۱]F}*-frA{-t0AAQ+pG\sY_=_@~uks.Q)Mç x|{ &Z&<<͎(5}vLO>Iu~gdٟ) @!܆gGg93\;ҬhJWO>5}6Ypay=1:q˜LAA2lIu=mcNaۗ=eԙJ 4=q\Lh6>Skv0nqhGq;:څ@ uN˱ qv㋻u,:(}[^`j`q5TEh(av^+[c[c-ӿu_;{`_&"oZFEA[¸7|4)O"AӭZ9NPA_SƎqc<hJpgh%x8dg'Z#k@=j5k1AANLMMi47<[ Qz}ZZ0"0 @`0 7Qѥ;FKTU$#5ƪ\wd""ZPiݛ  ('_o]/kM. nsAAq`Fszs '  QN"      6  X(   b]`   u    6  X(   b]`   u    6  X(   b]`   uAa afWDMDUZaa ymlM/ڭ|R =46Tg,R算aItBu*jA>0?GG5ϊWpVUn} ơ`f!ʉam^ ̠wsV6U1ѳA`Z#\x"༌-[Vtvg`L5oz8] xٗ޿7%}#*n_EZlN [=WgNwpm_<ge`dM#{≁^ 8SꩿT(I} BjX6m[DSo rΦ]^nΨyJ3;F ( W IDATHDp|zݲOtvRTkoX%D?]q냿VU p2&~+Hr9EܦpF/`k?a_btˤٳ:$JrSHN֍Il|¾PphkhhP+uj K5f cnT ֊Gm7gƶ67߻$ml"Kc7`gK>'6-7\~ @\X; _uGWOGy[&œ~ϕ@Chk6O ǼGaVYkڛpdDwǑشF jڛ.~.8BW ƥ&Cp[]bkN~Ko%g=MӶRe i:_=QfجJV^㓁٫2-RK SGNVe0#=[ż0 A^Diraj.ݡSS"=41+Ou iX~^u {fh+XsZѝwGM'ʅ:i‚:V%KO\`N` eK삔=cwaQHd9f3xxQ. 􌔕&P?$d!%;&z{n `186,iszўȵjwg쌨E9b~O`zN*]5P"[!ޱUkpN#!bcЄ+=9)Ph,u&4~r3zcpn͉ek9:F^oKOVC\ߦ[(7+xS^$Y xLޯMIWH(@gDN|u1#W];]V=ii `(Ldw ]Pyű1͚crO=St<-h֟ܓ&~hᐯRF ;03ɿcRľ{0U.g iD,:'=?;bhI 'E|;k>@v"֒z/g>rs\5C şN0dn._=f F0;/͑ǧq@]~(B UVـ̱۟, <6I['?399)'N/0e0fÿHl\I9I|~CDF$ltiyquz Ջ>)h3_h=)$ Od^ApJd$0%ry]:J$Csg+Iu-Ff1ۄxȺ-x$eyQvٓD^1ҁ6pu+I#f j'gL+Y$%jĿ%IopK (fc(ueyoP 15kQ.9W93(IO0jk.?`9$s+9/o;s/YY6c-B^k'M]Y*:VJf$Iʝ71㍏fl^7aW(Ԧ`m.3!q k ,6FK$Ix"J ګ=ѺWjOWsF<\$n졶3ߔ>i G"LNN~s{/,,('v!۶3~mpoH is 60i$kW4VjXPqO<{3_;Y3,{z5tj?~r3Kq/G.> {aq >eLEW bU w)Pu?Pb|7U _:1Q&lZ;uPat6h}E.yT(]7tHV FvrǏ~lB> M5I1X_q·h5kOۺ P5WKPܳUnV7gh$LQV5z8ߺgdJ[e%D =h`^AlD6BYcfci :=vO4B|;DhOTfz_@߭*OZ *]my++D<;~0j Np%ZX 4CH+}0?vYE8e+ͪpHcA=ie% *ks&m,iѷ*yVtݪpdux`Μ'AFg#8"nG~/nlKRJo͛$i8kxC^q"gh&L .IBp$To%Q.s煮mئ%)ҥ͒ksyru%W$lIï㏶}e5蓝۪6 8-]e9\an&lU Lh?pf 3% -N%Ht7 s @CQN24[*գi-|KnsM|o]JPI|#MKi&o::P2Zsi=uTiShZl&Ti<*JxdMБ,+1y}J ͡B,%@guSٰb41i^dY 3HPCl9)hŴ|LPbT(vs9Ho蠭Ԝ3.Z:T$IY>HgR iT$$o<ɴfʙHO\8oڮs&JvD-J^{'MI( C*MBYMrNIpXI[7A86.#_&m<^A?_j&JsF_֨&x-t~}q% =*Y.>R1Sfh9B,* [Jjڅ*~/QsLxJyӥ/C'm D^N9%GRit)X?O)w>֜ejFs.[oΔzkis'~Sk7b_; '-9y麛8 q /ͺ5WsJ T9}AܒLMMi4>TIOOga4 $) l0r u$0AV 66i}aS|"M @fgf){xzڭ>TxUa)#p0Cܠ[i<|b_AA)lAA[Ƈ   G`5G QqOu@h&_‘gLx{̽yg܈ G^]TdkNDn{vas#_^Y3$InvjFٞ u=vu!#g\eDnvjF(ܞ 3 gqvj]Qс_^T[{ AAq!l?+˱,ԏy)s: NW;]FvXۿ]ifruoV_tf*ٽo;ɿ"֧^~bWGFoyHZZۿ]#O{vh6sZcGʇN_/ab%  H lzhxu,]YԔ <ω?v{B=)j=|}_J[Z.J_0bE{ut4cq~땉bjb--gzͶNWNfrirUI[]33,,t|E^_Í3`WX! ܠh~ `rTj x +ZPVk[ reџVrsC2pqvkrX )ܹSj- |ņj ݬYw~xS6Tw|wl s*FhT=_⨶ܓ `AD*;No~Ɩ:?b5~ !  dl` SgQ>|//_u6ؚs¥'^=n$IOdԡnӒ4mw8~?(ta_}ߗ\\יھRcr#儂rdbيYs Ö?9>=YsJa:^>C{\)oVQ   }e&xtZkp{Ԕ _enw ^i|4,'EL}gJKt3m{t~v,a%O^))u݀@}R,GLu?SoRitٹ{9f 7NAw(JRAA 6(⾯Z- _:?|~ݜHaF #MxTqa{f;E57Ν?7݁̏n{̨'OP+^Jp-a^N6U=4ٺ-sNj*]'~$  ne4uIOOga4 $) ߆2*Nt%|H!.=~GR~)NEF/:`_  .~'e    g   [ 6 QP IDAT X(   b]`   u    6  X(   b]`   u    6  X(   b]`   u    6  X(   b] 3=g{uXTO}ˀWԔwO|¥ wX_K}ԍ4rعSgfSkeONմL܈S+KNu$ދ"/t)+-**-!(0rl B"?Ju b?rX}edل^߷."kA{Mj/:w?W]tC.U7^_bkXI^hum}. ,\y/3ɾb> h~OOҫnnWxꍵ#7az⼂vڱ5O]z}#A`C4ʘ,27y8qaFoS'p'ľGٓʘҳ7A<RpŤfu6%-=Y lQ߀GlcÇncWE0>ɽ]^ S ?U~<$IQϣAl` C 0E5ѳ~lphY 2,Lvח1 SVs.9[0 ST=&/k. -ggGˋ)l_!Ύ)/b):un ܈}S˿wq#]eE 0]#셚S]'X}Yy"^)HWCngT90 \0]_~\WK ÔpډCKa*ώq??wlUjn&Py>ⶎp*Kj83za/jԅc.Q/Ut*w;0LQieL ++b̹s+e; S':3 2=},wfcW"Cٓya2S[Jn3UilRVZv!v \/}\qFƺ"X&[f`a13!7}wl'J87(@+QjhRJkΞ9UY%k5nܩ"aLZ(咗e8[YV2]nFµA'?399)a#`h$qW>NuY [kwHlEwZFu$k5b x5Vw,Pmb6 \VE]0vIP)H0buM앤^ eM۫+h6f5l`F 'Wxn$i5kNsu͝N`0u$7V 6{ol#\@EsgKP2> ~xWV0um`lIw$oL@amvٰlx5% lw-FhVWq2%hN'E,{fl6 ^iUi;[:hPm;m>;NTJ-[fà7g-su'^ `>fQ(xE/Zl[ku.q7ĮN#N_⒝V6!TyEAZ%AA띟_XXy/--(b (.--< o`^ń6#Xk8Z `6$IqP9{oq~&BBw@VҊvI$GRa"K7WJ2}ڻ$uJ$/Z-t-|*P)L,w }! X> a`b2"+&W&#"XCXDӟ?Tۿ[./NG9mefeB@HfAOq'@k>kXe6U5iU W0@2~?i)9-.@U5OxZ_X[!J|dymȥ''fdZ&'[x.wu+UU +J[l/z2r:\Ԣȕį :w0 Q[4&z,#{;^ӠP(?P湍 xu Gw2c>O d\DVdEVu3jamGZ.f"30dPb_{f*r!Lܸ<15m{=&\ 0cmql=п 6]XNo%c;E׿5CMwg)ӻe6[C[hʫަO(r?jpGu=9 ]s.\ϿXMq Wxv_^*uߠ@ZF^B>Ԩ?Kc)<3 C+GVz?fÞ=4 LT8d\SucRtxHC ʼnQƖ_/Ju ~B_GU~vl{?FP(M&ln׉Uy6"!-k' p h=02G Ei=/UWx0N;X~<Μ;w쀔rkXHPUu:X\mG7ϝ; >N")}[rmR[\"SǺI[x-Ωc&z:~sXɿwl/TU6B75j \ikȒBVu|ɟ [+X[l 7fG`bahig|w[=7S l*YDR\^S9j L]]^w}VDpȟ{)n Bl͆8><~"BP(,̆AAU!wc'6o:!1maUXYcIUUtԳB`lg6֓7ˣx-eUU0 =U5nqߙrUTn,j?Fp{v:Rt`o>]-PһDUu1ah$n'(_N} }Awoj:'wS"=<˂kB,dKmΥ$t *$ knΗΙd-Q6*A xiMX@bFl3x& _^䋘m ]+jBE#ZC>7 Px55)8od?ZgrBP(;OHk׮nEC,;u$QQW>IsòQcw8Jl$QmGQYBn:#۷:jŮ=r~kg[ۻ)Es2l:bfY("fr@̈$Qj5b]{I̕|}+ѧ[uovSHg7WU۰|/JHRM]$1֭VwtU ٘F`{Rw"9<ΒomjEkZPfs/CI(| B277WWWPts= { PU͛7o܄xW yez#{տ|;0^;|jGH69/p, rj:F"BP(ffą٬~X7ąyy]&ߜ_|= 7g37CGwҍBP6;mAw{\R={W[nסûm5MMc硣;( &{BP( BS BP( !BP( BfBP( BltAP( BP6٠P( BP(lP( BP( n6( BP(ʆ@7 BP( eC BP( !BP( BfBP( BltAP( BP6Ͱِ$I)G1OF;F%u$_(Psj7Cۨ)H3}thn|-~ߨH}` U2S#Ʀj@$`~TĺjF;;gRCƦᙍVZ3e1wFf@ j͆49K_On\+mSqvw:_]xu(~a"1ȿ~/^>}"IVX7JrZ΁ oԥ/ڏ\*gyu'ںj Ñ~fC]{ƣ3v+P"nliwlI&O@+pks ֌wZX،efa\OoO^nWz8k/Z[UW *?}%9rk#=/۩V j'N4'"*Z [-]kF+yĩSwA7V2Fz !9ݲCj +oqkPls ]F'Ys?{#} 4O}$e̍a)ۋ]M 0 9%+ᮎfid&Z] 03]͝kjnbilX ,TWs#0ޮ殡)s73S͚SRCFa֡RY?#}ZSbRSC].a\s M}co i;ru f?(,*0ѕXdII֚\)||M}]y 0LkR~0LSK$OYi vC=HkJqrLe@nv(pcvXhndiin[-t5u'2[jjdq%, 94UR $43گcH`\ 4 t6M֡!z\oŁKO7$$ 77w՟*5]mA3IEj62 9':itueaut!LjQJ|ZMw) " +_YVݜ R[sRg<ȖSf2Af t2 Z*ʹ$*^Q6ւo႟ᮎ`G0Qkndw8S@J/E w0+%b1L u64]]>/FxjaԐa?ˠTk#0.mKe,FX˨7Bttp.90Lcאthy!);O3;;nt@xV;rL*w(q̩jV\"*8f H2=dĢ@{ *p0<#W/vw,==UU<ɰ ?N'|d@ _"=8RwS+>4LE#yUUa/K~7Y?rIYKOG{x-VzѰ76D`hl,-fWNPw@N"ٜ9Dkg*l0&pe:stz8p:x9UͥN'"6 n4zȡ!zB屐%Ҳ43Chbެ(bX_t1T…hHKd༑hlOBC1vm^hnrɬ*/OP$_Y2J1XbQ'vrd+WUUDNsF\q\"K.ލ)",Jf6::-ڼ/ yҹz UxC|NXh4E-n*&ő5HгODL\(~"%: FB>S:2gH `mogw}ƍ${({$m͆*O{Odz:76 ԣA^8:^zҋ`ICC$1kʵKf~jp,Z9-PU5OxZo$\+n6&Iر!m6ˋtӭXo$ rEI.8!?ZEA`ydkuW>w0tHclGs㘏59tgeW9'jvhyLUolNJ9%7+ʍU]h[s^i+D e"]-Yc-cg*KUe-$-2By6W7,TVx5'{8pIUU~L:QK($gɠ`nU:"83+..K]Eꜭh|mbԴ]E]z )BE7MM U͋Ly^%]څZ9K>X)r: y A!|2k5&#le֛q3\ZC5%GV7aa*`5ϐJ)wfcF_{h? '{Yp9@^Vs osn; %Lhj4݊M|:\'?/bQ]~ukŷ[=?"$04! C w.Lh_gۏK #;ۜ4u6f?T?48Wdɓs,ȑ+z/-^f\@ N4J [R^/:5n_zhNFa';Ẇ+xRd7VkvN$-;A{^40`-csa__ogZ.Dm+ƎFg* P,9IBQ<ׯBSLb7YkV,j.6_O#9v2k[r2l>M_V'$[$[M% DKVJreJ*[P>myZNZ'2*bl%KBU.}F*,CԚ]y'NC݃.8|O[[rY*ʍU}m 8 07:4:`߱#.X(!int$K{Co~4?bHgp̥F`15c$Uh| ~Ƈ8_}|R'k屿H4"^*'o-3H%JԈK?Sȋn%]E\EwRheYԛs3 XePm\V`fX[񻕥%b.kCj@۳}K pgԕCueSMpylP6ffW@;g;9@,V7 l=u9۝ { .5_~n6{zj\9L'Cprgy/l=de؎ cm6籖 8<:8sCXˆn4q/i{yYg='|.bH^Ԝxn>؞pȏΙ䟘P^8~RL ;ދS WVam՝!:sztH+et;;7=r|o@7bTZB v7H:p[yG* }48ǽ<bBǟqHox*ʍՙ籯z9w[Ño]clو/ҺǸ9QB#vsi=gz/³Bj. 6йjeӭ]2f䮽uE+]8I_Fׯ VMk9_Ȗ8Хٽvg={mMa +ell>E7hh8@*(Qc mm;yz(w|Yr$w0PMǁ=ٽ~/ :DYH".J2? @86T1KV"!NB׳`; }։ˤl֢7gQay6 X3$r|?ōnF]vmt+"eDnՑǴ$j޶DE6I̕-[R@E85ovXtN>_ŜͶ5I(bFo-o$9o|I +W"I9Y[4QSDơ}*( 6 {T$bt$QYb&8Za-^2QkCEpA\ї(,/bY7V"fr@̈Z3eo iHJVHg7W5Nj(!^fJb٣ZenضMrOYD'+bFDX@PdU%IdWO!PzUUp)W)m*bFobY5ڸXRiщ+g[SqI6kW,+eH E\]:bkEq:s0{t{0TUy͛7fcӐev;\ǿEToDzqAp'WZ3wlʕ8܁d?'x)غp_VY }z#{տ;Rrq.wQ08e} FY;o?p݅(B͙ |d+ѝw u47O=g,P6ea*ss<^qaf^޶W5Pi4y_iy.BP( B&9 NP( BP47h_gȌU֞['uiS$q ͍vvϔ+FVZ]"whV5w&rX?Ħ X%g*s [tyn\*S׻y[lwG0xqyDA֏*F5/ˇFِz%Gd޽tKrv}_t_Uvyp&r;^x?zuw]_ o7 "7!60znܚ,T&Rh?rܼE?Y.楻y&5Ql[~Wc:q򒱉>o=YGLѽ]^/um- WZQ@Clj9W_b}ﰸlw&[ r-230ɪ?nҠiꛬ\H\Mٞ |+駷`VUuw Z-#[l}ӵ&Vòi'|,v Yoܖ8pbd- B"e}E8`k'suW-92]5?.fSP7Y|"Hc:}&tmh(39&MmX |5ն.rhμcL v552 øZf'G;iJibVR]Fa;~7jB5W2}.aFWh̙΁q,6_:RSC].a\o㵏,&:#M v-,ѳL,:50LcBfj0 >L:'R øt߉/a&$]ቾfiOyD9)BYJv472 4v俭jkduxJ$tP+B诚]m}/DkʯfF]Z L84ȠD0FQ蠋a\)s.as0U6s;&:a5pW`jraW׈(572 ;u^YJu5kퟓ,62r\|[Dg}D%&x^ttwr:_[Ir憿pIWS`-.ѓp52 ;0:ʉ80LcSk_$[֌2,\%OhaR47Ꚓ@t:nv(msp*N*#*%R5O\oss!pgkGٕVbPkkias/7: jr N•S1dRCPuyKILhLiM}]å-Z> ɪj/ՆEXjWPm$ZBb @i-dif%L)~5FLJt=hma9eO^xfgg d9DŽt"PZUլ: FB>YUUgDZdy6 Ӳvw,==UUn`Hl>!gѓ\VU5+ e4lY vGlaρ/z?,&`4x"f ?N'|2/Mgcn/Ģ`";*x;8Lѻl:azXl,wC .UUUaO,=S՜| 6ND"ɜY2(y{=dz,=EQr t:~q D thGk!/P:ɒm^hj.QUUI?x²A͇y" 6iy tk=drʦ}cѱF<폦c>yS>/׹\f#Â/nLUBc^7~UVIY$ vИ76j,lKfU5WL$W^c> 'X$<6vl'򣣘٢( WNAֱ(yFYFN I$ b)("c>VS ,WQ>zjnڰ:ȣ|ഹ"?XfXZDV)M_fzܫ!#'ִ<&f7$IZYYyEQEy$I<< #J-G4g@qYN7VXpl~OƄ~Qn7SUU9=B>)9KN,SLZBVUU9Ω3d,2!;R6lS1F;d+VkU=T3ZzmXMX*.L deo%c;E׿ N~h dos#0jYp9YcGWߐ--"gosn{2Ԣ/~3H2 HKui~Ĺ 9X'Q}ɸz'3 ,ȑ|@׋4s@CGqmp:i 暈]TB#r%To}͖{M@]|'@}>\@kH-yܼo(Ncր p>i=pԳjnq,~MV`,B]7>R՗[*ueUMyw©ڻ6Oz/gV~yoqܹO} fsD*lyKoK}k.o-ȞfH/U#2F/ }5?ǂvגfy'p=_:.{4eY?cQzn$`irWx-:LV3Qoc-){? _IQZlU=7$J W~@]ݣOx?9E'| 3-GF3 %wZ{WNkxrALF47:Z|:F *U'ں'DIf&gLFᩥܮmP8~ݍD)3vOTA <3I&S'P;/DNsqxj1Pr$ߖ'VD`>*ZzGsBy_%9wv:'}eUU0tg-"B0جRϛb?AjBD91>onrcXTHϲ# ^;mWk(~rM Oh(/|@XSjH$kG6۵s[ĬMټz^4#n1HyIʎs+D!@V#Ϊ]r<:lJ W*¢/lc9X N-نYy>s Ez'ZSs,o҆E[H^oT04|n*Zy'gJl!|έ0׶<wڙ ǟ/b$i׮]ق"fr@̈E%QaV']v=+eNE6nCNF% -%b_Wr3 "J(C$ᨃ"9MY(j-Q0FV.EʈN"I9Y6Y, 8U>_oñ61hNe$bXIERjkF:N̽z"fDcR+jBBٶXCIDAT~)(bif0]F:Dh~4(HbN6_vɬit&)r,}!f1իtI UD&o4l?pS VͭmT4I __>)q$eZyɕ|f3BY6 N;٨{ɕ,(DnoP8{a{W{=a޼y͛fq[(s#߸x~[9 q.wXPHlʕ8܁$ ij.^ 2eSm\i4ݹUS(w,3ヌ>uP&q!|>}NEolѣTz;٠P( BP( OP( BP(w ?U _IENDB`elog-3.1.3-1/doc/hierarchy.gif0000755000175000017500000002403212556103750015042 0ustar rogerrogerGIF89a,!)11!9!!)9!B!B)J)!!!!!)!!1!R1!Z1!Z9!c9)))))1))9)c9)k9)kB)sB11111911B1{J1J1R1R99999B99J9ZB9R9R9Z9Z9cBBBBBJBBRBBZB{ZBcBcBkBkJJJJJZJJcJkJkJsJsRRRRRcRRkR{R{RքRބZZZZZkZZsZބZZZccccc{ccccckkkkkskk{kkkksssss{sssss{s{{{{{{{ƥΥ֭֭޵޵罽罽,, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXõׯ`ÊKٳhӂb(T.+ܸww_ ࿇ #6L !QKyʊ1c<ٲfϝ3>1dkҬco^=[v_ͦO;h׽GH}seP]_ ^%zTbuQ|D(t! r%A|NP@7}Ux]z` v^l 9 S 9 }T䣐%u  *ġ*.aE @kXESBrw\gK,e̾b ~Ѕ\ rq" DuQ@h!y6/Oeblx| υ-~rAܕ#S6@H3]$Mq&S2.K[ȥ ؇ثQ F wAU8`[+ 0 -.?'0e^j($ HhEӝ76a( A0rka rqʱUDV1 zmN|^qw0x%G ay]` F P"(XM'yqh@"8A? zop1h ڋX@ l`4a`EJQ`~بY y&=YW ffLd!(u[*,ĉRD8I=Qɗ/.>7̔q'Ac;jcۘ?$m  IJNd&-INZғdOJ -(&/i )7yYƒl# P r pD!B!v̑   3!v94yfC, AnAD/g`& 1|-&>"l>P: 8Ĕ̡m!Є٠ HB#Qz!5!3#"+H!@<4<DLPP8!#9!T)*a՜IjEPUx4R=!ڰ4ѪE_ Ub 7Z/BW X ˔kUA*2p^3Ϳu a? $$FWQF6)Pe_Wd#jYkJNۑ6/mmNMX%3)XDZQ mqo* nHVu0 mLϺD"+W?@G!"$CS_9ľ%!D61|S&iO`c@ 7'xS`{!]| DER3ҡ1NlA cjC>OK:V;'D~L[np0=3x''1Aډ/6n@7J1)Ia5*ȴ7N{ә&( RZ~FĩW}<հtcMl/ݒP Mu-bNe;V6rFζmmwg8|rK{ƶm%DŽ yC-oywo}.o-P p}3\ m;Aq|Gy ao&xA~r9UNsۼ(=qq_|;="IɃNt@wz_~pGXzѱs?ͻt=gO}9yAdqkxϺXn{t'&K.ţ\s~w_}' sgaGAzc4Ag:"yΧ6hr.o{lo)_>8<銿G/A ߂XyX h띃P||PPw{6~o?~x6̧yazMP& ~֧cxvkgroեoorzp[O{;w@7q w7yڇo sopM`FKw׃}otvPFzjopNFgr:8o)s\f_`3s.hoG7LGZ&H_`-pfFos}MPQhw7y JgT`vIhZ hX-#XsxTNeÙ6y~WH17PjeT:Fy6ajX3:]?%oH1tv6qgv|HZ@oppvos@yZH:q< dro;kd6iB{ YpKI,jvmV;k:Bv qKxǷ%}'yq[{宅 庸}˃˕h;Ȅ@BOpY%lBJLNPRH@/ ԁp̚:,~!@pc1.0~#(/}rq81]Sxד_^}r빎CQ|O溎haTW:S)U.M<nչ%rGp;- h`24]3ܣp( Pwl@q\l->P~` OoB3q9U"{#U3&~떱" ]s~3 B송 Pk#.ޠ;*@P @9l6OL8q>hRpC9L'v.~(Pͳ84/ 'Ȟ<~~qxFEїtc!Q!? sx` Fhȟo/THsuF͜o| F 蹌dˆ\|tGݜ@,ɿOFyt?oʭ֯oU_ok&||lc3 B @T%8Y`!E,I)UdK1eΤYM,h? lddA#pNZUY,婍<.,qfѥ- %MiǨ[Υ[ݕ]KY1 -V*p ecƆi@Yhe. -&A&]镆ecj6УQ;/Z˄y&mlnRhѥO_Np`g.;s`k$կ/m`v3h8 Ɠr0Bp@ &[&80 +';>c⮅tp?CÏ.Tqŕ2Û>>fR@Q {0"8;,aFi:G*Rȡ0*mZI'?$4+RKK&kI0P3 H ΐst?%㬊N0hO=>/޼KIeBT5&@K SXYDG%L=P0ŊUWcYkʹ Un| \ #v;B{ƈ[+r1oHñ2򫏮Wok"cr;D-!KWCkck9:{ރR{too ޓ]*qvژL ~3 0a167|W/^2Ĉ}2x}%s  #nETȃ@F`ȿ =XA뽄 $? 4! @̡ - @ma1  KtB"1(}${yxQQgnLoSE B`6W%m'-&D l[ $pC[h4p8nSp&$$6Gv AM)@DCvP?UIHՊ5<ُ:6 \S2bǡ`DLJ(؋/Vqhm FMZ` \Mcva6x`GϚM"G,#}v.)щQA#^T53vЖd]/v$G$OG61l J6JGg/5v #ڿ6 ?ڑۉE`֘pt5J\A*D`ylӛZC|AoʩеM#D꼸ಎn9GBP۵c2?6mػQ:Hn˼ݮ ]GlCLf!)l!{^fﲗ(xuo-iq\H~F?^I;v <0dA?Ԕ~%UˮZ{ȁ;jJU?Df@5iAJ߂l8ʿ|y!?ޙ T/:.+#ܻk c@NR8'"ŋD\8@4ܳ˭Cd$)KA DkB͂$ #:##%(t!w; ŠBJNB2.28/B1ܪ47ܯcA-F!er&@t,D?r ?(?LDCDH$]RDK>d"DDINLOQV 1;Ra&`va^a /`a.N>a`".b#>b$Nb$JCPb'~$b$~H!b*b+b,b-b.b/b0c1c2;elog-3.1.3-1/doc/tabs.gif0000755000175000017500000001047712556103750014025 0ustar rogerrogerGIF89a#!!))11BBJcRRRkRkZZZkZscccscsc{kkk{sss{{{{{ƥƥέεֽ,#H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟeJѣH*]ʴ)BPJJիXjʵ+TK,٧^Ӫ]˶-V` 1 wݫo޿| Lxp]nH̸qTq퐔2k,bhY}]]Uu\!GWeBo˛ڿs՝hqܾ~.-h]nqףGl6ߑ-xޅ uٙ+s^e%mʚy yG R'r)X|bZxRXҁm7Ye ^x8\%(!?FD2[eWb8np 7eo R[r{UiǤ8ᘱomĊh^ XWb`p<)hA*bhIfJFfZ$v*By㖺)J}T)hUvd5\HI}jzދ޹`FhjJzգZ)oQ])v%#;I׉H쎘)*z*y{鸹-yo)]Mۣio~:-7pvq blpŬjݐJ,FZaF|-b8Q!{4r4T'f1P=wP'Ls tdhotڸn rMvK!ķSwjniWTQ~/nxXxI=Չ7dxos7G]ԘgPeyz@G.;#{'7_<#gp} j:O^︁}֕1/^t겄n;+W)-3N.!h=p ղj?`cD'A,Ik)B 0_"^2K<'aIHjfLAx 9tr<ֈ 2hd2 SaLNrR'*U;Df?.򖴱Չ l'Hސ%l%+{S+KɆS;tICj0U^DvћعlAs)Fk5b]G%rB_,u&f&>/)"SJrumI*UG;*O 6Vm,H)G"fYYyֶN$5\CLM҅O)K] o^ ح ox\4]^ W?Lb(NWLu,gLci@ w/L"9p&M8αL*Jx RǼd!dNw, vps$,x#>-psXLBg%5  uѐ6 ,H[릣L^[lPH2?e9QtNUr؉,2jd"+7 hT6n8Z^%V׶To6JiΪLu:-S*wM\,V (U[losSeI~nm"ZI7=o3-o׊'ײ|/C>rIBﵺnKIKNssm4S_ۆՍmY9ԯQ$zuDlS_5o:꺝8m(v6qƹ?J[ST'vwm%rz&7~̯N<ܝ$xsJ,图^6]*`5g_zXe3-Ug=Áo}ќ G?z_p}&k?kd=DzY}VxwGEnkt~.7uqvhx)fdlc Pg9>k~V{H%|&&c-,XT'Y6K+ȃ@c/ Fc!G3"}L'DXRxfy4\85PV؅b8>cxAl/mjAmk(v8wVa`|P{8+PV؈(x6@R8kH2:AWX:sEXxhȊx舯3(Xxh @SHA;$2st8O`%6`:'j+iiUQ5T 5%X֑UC4X4GyP9Au Ai0>@:;:(Պ@4'TCs3?Ai<(pbYamT 9e%uF4w'">ВFԖR waI$>0aYRiyY`gƘ !A@i^cTM%9m雯 fEBeҹYxAJz)ˈbIu!Xv[Y_KH-퉒gIHE] >#A> P E&*H;Ts@Aa9=9{%>` 1>IYfiBEtO;yf)Uw(h/qȓɉG1c9)':aSX\*`)5IBYF|ڧ&KW}:@ԙEْ6y7`z)5Bt5K7u*ÊtzQᄶx5`  5: XU9&  #8jP=`W7-P &2,zVK"{7&+{*,2.02;4[6{8:<۳>@B;D[F{HJ;elog-3.1.3-1/doc/elog.gif0000644000175000017500000033541412556103750014020 0ustar rogerrogerGIF89a!19=GR%Vs).O?../Bi.Ke92QJPEY R7 .[J%UL!)1191BG9C=J)ARJXZVZDNRROZ5=)k-{1*6/25[=ii5mJcBJcJkJkJktsIXV[igv7Ń(jlǩͤľ֭߬fgg)iu# Luф۟ՊŹν֥ܹ 1?H=:(Jd)#.2JoIx-Ve҇ƄsƜƔΔȟΜΥAɻֵ޽ޭ11BV!,+2h(TX#JHŋ3jȱǏ CIɓ(S\ɲˍ cʜI͛8sɳϟ@ JEH*Ѣi-T իXjʵׯ`N-(F 2hӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǍɈQab`˘3k\`fBMӨS^ͺװc6Mv(rͻwX*dȓ+_μУKNسkνËOӫFi`Q/(h& 6F(4%RaA*tHԇ߈$bV*C0(4h8<@)DʄԅÆzh“&%ViV':9D PPA~T@,f9ANrSCŴr4`YU^Vё6 O1D+H!Iw!D58jd05P  Q cLD(!thC G=D"W5L(@ H2oܳƀela-0*d LpS8  @xE).NTĊd*FjqDDAJA`Cࡆ̲YbpX^ʲ*J @)P $HDS6%2KYmҲ!\e+HrL:-d|(w "\my`tz< Ȃ,d ID RP" GD6pUJIMYHHX>$5;{cPh3P6UiCKDæ C)jm|@CCt;1ͤIQmlXӛ& @MZ1mZֶp 5њ=ϻ`jg1DӁ-`A jbUh6`H&!*LLY%bG}nrjUUaVl2.zࠋ$uluPNbv`G1>7`mӱ֙SU`y4@m] V ԅkFKMz|Kͯ~o^IVڄGF1 NvD`W!lF2ֱ lBS ʲpTa@qnظ6%slE1,9G^9KB$yHf-Ӑ7͡rpd ؘyqD9@VLd*_[>πMBЈNF;ѐ'MJ[Ҙδ2鍕5% KN?@ckX^ }oٳ_n1x{='NG\ϸ7q| !JHnڪ ! v hk"b6kƮLqmm5CI=1mp ^K1 47<졆zC^wIhm/~Φ ;PF=6v~~v[ϼ7{A]!Wnݖa:y Z bA0݄@md*fGUd{X \ oG*XS pK <@濡NoYZ1؟Yvgn0X?zlhWpwpVUlw~؁ q,2RF `-&ws8{{bBG|D*|?榄`Ruő_C (LX`얅bP_YQ:P:%nh\q&i A4Ѕ~$}fhjȆrHP8Xxh<`=.2Q؉`62S0:6;ȃ䃹6:0&k~a،8xh'!!rhPט؍hQh` H` 苺$A9Q9Yyi] I^;p{:x:h!]Bq$Y&y(*,ْ.029f70^^s>ّ={ !bJxFLٔNPTYVyXZ\ٕ^`b9dYfyd21GzjkG(GA)HQa|ٗ~9Yy٘9Yyٙa{':ٚ9Yy)찛i ™ɜ)Yyؙڹٝ9Y ܐ虞¹9Yyٟ Z ڠz J Zڡ,)*,ڢɛɜ9Yٛ ͹˹i@B:DZFz剞ꩤ NPR:TZ))\ڥ^`* fz *$ZWrpr 09ɚ?*<ʣ>H:ZzLzj:[zܠ ʥdZ*h:z jZlڦ"o:uʧ2~68:;:ZjڬXZz*Zh\p:㦱ZڜxZzzvګ٧ZzjʺگzZJzۥ  fJ.z JIz(*ڤ0: {8۠ ۰*ۡ[(KN<ٜ)U4ڣ &0_ad[ݹ)jl+k9; >K#F;" 0K۴.J뚇k,[븓˚Kv:[KXK[۵9㩸k띤fi۶[Ko+tۻj*Z[%kq L˸y;* +_c+Ii^۾(y;ˡ{悴ܴ˫J+ ̼): \뽷?ڽ̽˽"ƒ۹a2̽(̾Թ/\4ܺ9̝í=D|JKʻڿ:j:˸ܸ{  LfK**!:NެGNJhlZ-0tޠsnw.Pt~>^~^~^ NPa`Id~Yj>ꤎn~Np x{|^~> N _ᦛ.WK^^ܙ؞ Πy>^~>^>Ǟ.?_ ?_ ^Xb$(*,.V2,%n<@>LN0on\e-bD_ >Sߡ^np_ P/^z/O>~(mٍmms_Yx_, o_Ρpvoa_,m?YzZۺm:Z* lO`WYhu u``os0E@ DPB >QD-RKƍ=~Rȇqn  SL5męS "NaNPEN9RM‘TDAæ6lf CɩJZum\stK5N}X`… FXbƍg9YY8^UMfΚdy3[)S6}rc֭]$}Z8YLJIke׽#Gx9p*W-랮^{'+hK.>nו\Aǟ_~ 3}Ų3ʹf+VbPR%,887K/Es9mTJ7FJ:[>޳%% 2J)-J+2K-A @ A d("(PM5)ܲ0<.c ImMD8/NDFXĥReJmT?vtխ+Ҋ, $j ր5VYe}P[o5W]+K-vTSc0݃S(e\ ߹{,M Q,)4@:r'L2#BؗEs ԁqG=(~3¡+C.Í<򐃕u]z)m8\#p4m=~z㺱`nk*9E-sqj"j=_/<+Rg7ЀQf0% [#AP`2 ⤃aM.w 2U2FBô G(op}pOXc;p .n¦ J:)^QJt=/k`ĢRtыasȧ\ X$\m|ʽҋn9Me=\\0Wj]`] XB$b!L AMf&d_>ARb %_Tdd%Y u' W!-5PU*&8Hմ"8‹]tEsĸt0BC7҅@]i4&Oz$7VK.+cڛRl`Ld:ғ6Mjs dVBRʅr2FЂ)@O)fe"#3<䱏|K͢CYFYw s 'n5$Z;SXx6Yl-S]E҇)qFa TC.\:¢xTTBd+?DƁU;) hI5T ,OY2-nO(\- ]X*׹OsJZ;ix;Vre9] UĽn_w^HAVESಧFUQJ@H7ʏLf5ObKc†&T@`slaX"LtwlY5f9"e턧a Į) CYr Ne2 %m%jz-r(PȚOWG[`]I[7)r Sʒd&fnv~e_]A9Mo=w{^4m^ dbi9(w&S-XQ RQZi)z",jy<~EgJPcl06-$3tl7n͂inxN|_Fu *:O`*q2e\LŲMIV%`C(A;R"CC 9|4=9+S竾3C;DXDTE\DXcDD( #89xaB1D\ux|}=X5KTL@t;t;CZPDT\|D._Za7C1 m#0+H<-fiLjt .l.xBCBm@ "qC25 $pB"$=p@p'.G#BBC.4s4H#rKb"J4CTr4j/: ҩ抯IR/5Dȗ.C$MTFEDJKDqPux70T̀D0ʵC6 ZTQX_|DUŻQP`L b@<ӫ)KkAt Kn#lKH< 8ܱGnydLцǪȜ,[ +IHbMHZ余3 ĸ49 tJG|D2DJfi ucބ6Z-Q`+ ,`{TJAb)s)L4L7ȷĴ7GmL*O~<2ΤA*Բ M,M_@̓<Ѝr/ڴ$+MdQM|UN,N&J~iN% kQd$Rɶ,-Tcc WXOhu rOԆ5O7O\!$=P|2uPzHP<)0> ?KN}D DFu8KDT "-R.N%ϵ쫶445ҩ0LTqARqR LÔ1U4*ӭXR4U)ck7*9йZ=gV L%RXUVL l"ehKV"8%& zt]݆zm@fWyw}~}W׀}',AY4Ձ57ewyR(RTq؏ %ْ5Yt,PٖuRX,8٦CٳOv}WxuqWآU0ڤWK%H$X%q}Ƭeگ[X ۲5۳E۴U۵e۶u۷UYۺۉԶ2WǡYeت%\(90í{Uʵ\5ڍpW_X_%%]/@]M]i]p^ (5 Yܽ ]ݓPY[xE-Ye5Ya+ٛO=FU߿u_z]5=ս ==^u> ^`&^N f^`%EVן]_eVՆ X!6]6>bE^b$eb 6^) `-`-bb&263^Xf6vۢv<=>a\/ ]"&B]$>$vb(ԕbX +,b10~^NFPQ.c5.7FTvccWXeE]}eE\ew^_`a&f%5H&dVeffvg.`#55fiUlfs2bpq&r6sFg(Yavvwxge6aSm|}~MY_6EVc4ԅfv臆艦芶i.ב&6FVfv闆V&Cۛ&F[%C٥fv꧆ n@j]Rj!H?Ѓ>5p\'v뷆븖k k?q&k=(;Xk!j&6F=kpV &6v"Hfvk=6llm%m@6m~F~&5޶ 6noen?nl>Flvo癀'I~Vnکn̎pno nsBp=XkfR  p m "OmWp&0uEqPq _q_r%h>̆q rooxr-r+r/273~7PZr{p(Or'oq'&;Ў?s-qA'-BC7,qxq4HtPnʙs;GrNtu:>n@OtoCrEp@_D'IYĨt6 !ttuP͞rXGt/7UscguBZiv6uj]o~'_o>Qbvg?VWuc?WOwGyjp7hnousO'QxUWuT_wggxoG2mω6jgkX'xPsOx.ovh_y1s^v,OO/iPe?NwGwWzyw?]M?z Onz/6kz+gkxx\X|ݶF7Gw &\pGP/'}|ɟ|nV._٧}ӟ|GikN|`͇ P _Džp¿}ݧ'7k'r7~?~_?x̗ 8IY]~Lʾ$( ƒ2lHʿ'Rh"ƌ7r#Ȑ"G,i$ʔ*Wl%̘2g|=:uͿ5/X0PP "N!RIjDWrJԬ[ŊZ Tm[q;[yBD/.l0Ċs>-xRN#B+_GkliGuډk5Xvݸma‡/n8ʗw 9dBe3sӧA+Ԯ&Uj짴7{ n>EAtU ImOgZyzi}GJzu^{疈u![!"-"1'NRF)݄]XQhBZ䈸8o)#UZy%Yj)c *ɛ\t>dQB}' SI}vYAzhiIiTqaBHbo!J}*ze6N6+–C ;, **,嬉iZ{-,;.%={hR-ʻб{/zOTve;0|0 +0 ;0K<1[|1*A{1!<2%|%Nj࿚15|393=3Aڱ}I#M3QC=QwrDN 5a=6e}6iGk6q=7u}7vKhݓ]j~8+8;n=9[}] ,㡋>:饛~:F~9뭻nw|Xy;;;<+<;|Ȟuy,<{=y姁R>>o>ǚW_;g?(,1 H #( R`6;]V(&] @>|xa [C><6!/AKW8^MDc"0"I$WJ<O()Rk!B 4hGkъErh7D_0A ;|//wCw24#FL;RG+^Ǟ )8ao"'HA\$(yd F$IEJ` /<%*QنE XdTz{h9/v/̇ U1xe2#-:P8O* RfTXP2, nb>9':})፛ \ d%Y&gM"mO9CUJՂ*RO}TS_S&" UrUPr%$|"S4dũǤ. Tr `RtlRP^tKlJ%pw=VF-a 2,#1ګ%m?\ WZ[򚂅Y8^#:XÜ0[QN~rWAQ PS#m)G"rȁ ^,K/}}Ak 9lt\9Xy^򎡬EOIUC/1kD3POZ>`SXwr1+U干tu\YIcF$i~I6h'Ɇ%dG:^vҼRig3/jZq Љ:Q!LU 7S=Z~4j4aY<k4lQ4dۚJwmCnt4N7j)n.no}lsγyC{u5!Lk0:؟B5J3T0  jǡ$vG6<--G[z/b0h\Il2rd\`2E9dsO]s(ѓ?9S= XӾSy,m=9:֚?grt~ >/}->G:k}#]zz/OVr[ӿus? _߭>RNV ^f nK)] Ơ ֠   !!&.!6qP^!fJ ]0H)!a!B)0!!!ڡaa΋"."#6#>"$F$N"%V%^"&f&n"'v%v͙MD)H*"++",+ D)b,."/,bA"*"1/z"2&2.#363>#4F4N#52(f换D۹c-7>78b8v`c5;#<<#=֣=r5N! Va)H@92H8dr dAd` =6C>$DFDN$E2c>&,?AzHH$III=,6 GJJƤL$MJK~dMLdKV$PP%QQ:E2>jdpd),@=DTN%UVU^%VfeT҃9d0BVXXrWSZYQ%\ƥ\%]RdR_FjcDeV`_e=V6@U `N`.&UabZbecf%de]n&gvg~&h6]>)%=j&&=k&l&ml9hCdemk&ppgn&jҦop*glf&tFtN'u h_iigr's'k֦spm'zjWj~w'{V'}֧}'~*u",?<{gx k¦ h6g/g*ohF(&Ҧ94sh|l.Nhj('( ~j]n'wN("(:(hw"(9hwFm&j^)fgh662i i陲阚7h{rei )陮)鞊蝺`jjsjN*V=riy^.).**&6)Zr*`)*j ꡞqv~* vF+r".^jj*y*Ҫf*z*"*Ff+#b>))jnRW+b+,Jf6Ê+VŮ楦*kj F붖6­굂>l*.))ˆ,lflviv-N+l6"6,->-:jԢ+fnmh mἲ66ٞ-ڦڮ-ۦ+66---b-.Qz2`"FN.V^+(pt(@b.膮肮r>nn".F. f́..r+-&./.b#6/.Fofn4.?ndB/b1P.//"//&z/SR GBV:c&TFpUi/cBpT+fopoB ;λg>m! 0 0D@ װ 00011'/17?1GO1W_1go0=.n!111DZ1Kip1  2!!2"[Jn?n"G$O2%W%_2&g&W"W#oSV&2))2**rw.}G0p--Wq. .p//0 r02+32'2/32rG s1?5۲r6C0&w33399;>-N45_8<;0 &3:?3@s3='4 p. C .C4D? 244TtCS47/ g40CD083=H2GwEHDsG_tKK;@N4O3A+A1B[4K4Q=gGuQWL/QtQ35R3uM4t54QcCwuT'X_UWW4ZZ$4i~AFWTuXRsYQ';4U{T]6XX?`'Uc?6dG5,'_?5N6E3V74GotGge4M{a4vjav_F6XK6m׶m߶Sk']7hr^/^a3`_6q7.6_#6_Oucu_7v_nC3oKl]{sOrb7Wsvt/7K7qy;x{;uv7~ZowʰwgwGKwu^vgkjWw8FCw5#MeDo6]6_~w8:w?83898n,O18׸ck,8?xP?29'y& 9\ \+9GOy3o9w99999ǹ9׹9繞99pGs G/:;2.M:W_:go:w,%::z+:::ú:z?:(p::_:{z,G;7?O;8'+go;K{;go,;;{{G8*`ۻ绾ذ aSx[x+8Gg4^ϵ54o;wk:s;ϸz?;?O~[?>ٟ=}҃w>/?z=}C8@K?OO>{?@8`A&TaC C6",N1Ƌ9X#ƒ&OTeK!U9f̙#_dgO?:hQG&UiSOF:W{k7_[WѦUm[oƕ at%/~wA7VXp߽!G<2%wu#g}ubqI=jݻyV^xX{w_xyxyѧW}{,/Vz/[,u:/LP"/!P ) ͛ˏ9 QD*QLQraQiF4<й  d#Ll'RJr9}O-\ S1![r3LS5|J.˫BS=? TA -CMT>G!TI)K1TM9OA UQI-SQMUUYmWa wPN-u;u_'`-cMVemg]Z2Zcy%Sm6 Wq-YuplW5{W_ U[%y .XzMXn~݀148AYI.QNYYnaYiqΙd'8']3㣑Vhcn駡:4x]+# `$[쁖N[mB;ĵCx/.￷lzn!\rͫ\Rϵ{QO=-opT=-%.2@@[&JG{]؏W< ug-_frvvêܳ_|{=7ҿG>Z<-yvLc3&0_GiZBv/xaQB 樵EkE 0s9΄#,^oMtBGa ,C*FA7"IT ˸D(mSm54bX.PaJFLl!977rV(A<>0~ysH2"Qe4"JQ"o#E;hj %K2g\.1S(LӕҔAix"·~y3V" ;an$f1sso`93n&8o#NSwD=-Mm ;9yOvA!O.h@2P=:Q}D!YN4FQRuԣd8ς5>uS=OT%k)p@ZzҦMŽUNIURԦB]W-gdZ)WV\b'Y(Εu]FŊ}_X5aR4kVLe KeUVլ[&[AZѲIhgщѮPiSwZZSmq@ƪy5-MgkэYn\6n1ml\b,UuQNŪtPZ75oQ`:قWMd:[!W!y_lw}.{S+maÁ/ >JzO^gϽC; M' gRK\ KnY\.s b l4KFOrBzZVb$ iXjc){XǓޏ"'YA:r e0mBrP(O KJZp0t&،ܤMS=^LqE:4q17G.:,=g2SC/A?rɄ.'IXs2bN߀k(_:ӽ!iWҒu@ _gLamCZ)ה}MT*g hnt>*,Nzմ薝jezْ>]68D\s_ݕ an{w i\ڬx-˵|>&QڄֿM7 nbKE8S JYG;yX-$!StЮܹ-o')4ϳIg[8$.{х\͘ɖVtv.ԭxq}0OUo-_흍m{~O_Xx cxKasռΟ W0p ƏB*0X(k!0ml/5)&0;7pI(:*CYSP3fcu_NfhvaSϞ09/BPp OGD "`ru,"F0d ͯ ϰqy. 0kPF0~m&Ɯl .!QŐ>PSl-Lm1IQFSp4;8Q<&L}LK1)jI$ %|̙֍1h=q$+u.'@pU>-Jpmzdqq/ ˊ^No/D0 M羬WL.!RЇCц~"Qr W/B E" K1"]2=1Ģ j# 1.$-Ѣ躢!G%&"U&&)[ $%n'G(`Xr(CZ/S2sƐO<0!St0 s1=J.[P<0CO/?5/ 0Ks Ӊ569M6gjn7R77{3Ile98GĎSE9B"Q129E:S{I3<Ӆqx3>s>E >3?s???)e1UD$N4OLų14C?!hMNˈ4FO4RY(Gb)i2R;*tQQl U%TO*M CɒUqS+SA.wM2nɢ . QpD%WQI)>Ot4qȂ#gV'5Z,چ.'n\KY7JUUK1[h#؎tS՞mW*u]Ŏ6~U+yr^VrM˳)#Q*imU v\ѵba1eO]#˜Ysu\6fb^ ___5Ꮃdaeŕ NbiYV8.!?qXrQqvuUwLA6hqrFV+lMRȞniiߵi6_1ZA-*IQzlG R7+uaKю~Xrn!n=g/g3[mCt+_5Jb5uGsRT;;MkPBuφu]wvM/o{6 vVv:ulxw7:<1vqMQ@Y]a8e?s7~q8uX|DX}k@8)z8P_8'~c8")b8otaɸ 8xٸ8x鸎8x9y 9yyxzx"19/ 4=A9EyIMQ9UyY]a9eyimq9uyyI9 9y9}GXvVGy9yٕ@3?/ Yy""{89y鹞yǙΜْEY9z!:=yGYC9/ 9 HKE:MzIS}٥Ya<t_Z}ka:mz:z+Z9.;Y؀Cyc ::C9 Ԙݹz:zWWڧzٺ?y {/YE9 1ٯ=X//z/? d`;e;-p;uʡڌ;Y! {;kϺ[Cګz=9 aKz[Z{?y!`: {Ek3-ۿY[ ۧ31e> ={:;{!'{OOĩ۵/Y{ 2<-^k;])Ĺ#[s5 -;ݧUYa1y ]>1]G?){ʥۤ{'=]M<%[MyUe 9Չ[}Sޣs]׾? ؀ ɭ]ʳ!e?ޣ=5sJB0B$xPD!ԻܬpDh1#FrHFo)Xrn39LIL(=r4Р8Sl^,g5}NFZ%G0GY<,QnRUӫٹ|…(0nB'0WI8~ ɓI2qc7kaRA;=jnyk^`@ݼ{ <ąWq"ݧ'Vpό.qymB7)'N\֨ '%e ϜU{DZT >3}hWxJYyCM?=_PhhoIg%4aJ+ O:BmuT_%ZF E4fţSUR1EEm&`FgG"HHy\aU)_WI"MȣYyGfabrΩPiͥZk&mV\Jhr5\tő"M#JܭXl!祇z|%D_}Q_Qjn)Nkd"NE ] ҙL:Td1(l?!T>a?Ɂmښ4+_'ⴘN/J,hmf5PTf )ŔSL Uwuiлh-ɮ)E_(vXL X',1e gff! #\ƚk{ms>r̝eAIҥoG~x^j+|aj^:#a-; z1FAXl=OTaAsCcف0%~Kr6r%G fvX"•RpYGu 3Q$MŒPNfQsY:9WI;O{sm: }jBgtGE){MwV$+K+愿vl!K, cϪh AKZڛ-lHp~#L wl,22w!e AB+_b$u- CnH3J,d"ޜbG&r,^22˘lL@2*)e[񐧼y(( 9QIyRG5P}Dl`G7E?&;QhD&1 nbBL9oJPL+ɠ_yBk^DHfDMDF<"F9nR1kyڡ("XDD3w]~jjN_(,goԸ(qJ/_Bؽ| S5W2(;dX4CH,XjG-AF#cpY&HOj$/3 c@t.C.%a:%GesCQdOFZҜ  >1/gR,qYEjln<\(nfiޟvfΰSIIalmZuqk`J@G)&WN+G%))39ZPП<' ZQx< ӳV,&j5y*ܢ1kFJf6@lbZCh*Vl"d%C=*ɢeµOwNnwYrü NKkS΅ukنѶ$#Xu `zN Q .d&C[_om_x<ox,n_ x0lgsĖI d!Ghdcq$+yLnPSo3 cb#gd.ό4Rv-ˬy_o\4y| @3& .hB9 szWLkzӜF;Ǒ.&FKCӬn_ Xo:ͣMi˻;h~, 8Ĉ.xl[-: .}JzDɅ(^s7\ 9laYB,)YG `~[KH#}ȠD-J E/jыUpA X@yCϾpw ~  ^J<_2clJmt[#]EuU H">`> zbز 8qe{jX$q{ǁ62G|`|f|rP)(A 0 3g0Fb$A~Xw?3'xx ;=RoC88zm!#騎υE49dc!L]BĈdb)@-A>2_ q)sYGﴌPxmqU;os2DCC0"!٘5'9\R"0I93G7Y(F]ixKM z(0r{ȏ_t шfW9JjɖЖSqYQ86cToS+uz ɘZB8Fp!Ӡ1ZzxsR:C)TF!Ä7H=a(œ蔄z[Yɇɥ5Iصpvd'$h|9Y1)Xvw#u\+ś-XMJء~"ꨋY*%r}g-: nzI021YI|w !I*djLrBq%< _n\^S/TQ^ُGB g'hžn 3 X V) 0}89,   J@HyZn01BT Y`Q`,KJ˱ˣy[ɫ`ƍ2 Q$Jzlbz+کZ7|H:E"8ǣإX9rRei⪦ҦYp w: c; W $x0ʠ} z';kfoTn+ ɗG:kjاaYQ`/;X9@:?ipJd͹$KBgbzRKX3մ(*JCAi1C}@[ٟ4/O}y%jK|;{oz:+ 2ø仑~s,@zԸ_.[qTCC+ӳe\ X?$ҪBHB|h[KLs'Ԁp̷8՟|G4ÀW- p*zÑxY E%4E :;,1CD+;e%d|{50T1 xȗ0XԀ ]p `S0L{3p,|b/aYwfMmΆ fm ]m' nlbbM%m')kLͧ+3M5m7f,.WLJ8 A-CMӺGKMO Q-SMUmWY[]_ a-cMemgikmo}GM<xVwy{} ؁-؃M؅m؇؉؋؍؏ ّ-ٓMٕmٗٙوі7cHuPmڧکګڼ*נMvڳM۵m۷۹m(ڲۿ -Mܪ۪ŭ3ǝ m׍=ҍͫ-Mm -ݧ݃ .]ߦ}E ]}+x a =]p #69r?ngۣ][b(+~%TnR4"8I=;^Nbgb%IM{L64;]NV* '. On.Un((>6/wᚾx۷]7Nz!Kx^B~瘞.i.kNNhηŞ焮>*n^^zksN^~NkNn ݞOO  N~ ,.瞞7/9Al#&*_~~>Ҿ^N!@~``.N.~2H7;oi0'?3kL.~SO>lY(o?/n O攟wO^8خپO.54c^h//g?2kٲsڌE^snI}Tn/O_d#8 A`0C XQ@̸Å!=h1Dž9R2G lIRM9uOA%ZQI.e)P)Mۿ7zn}5/X0PًDCTh4]w_&<˺"V̓qcȑ%O\ȇ­Jj֭Z~ ;lfԲuu-\mܶ>Ybo'^q}njkhѣ߀KVymN}hnŏyկg=sh}G/ޚp@ 4@TpA8s4opC;CC8: ;mD.t; YqFkF+Rn]4H$TrIv9=r&ԏF!"I.K0<ԃ 4$GPL8sN:rM ktsK;tPB O3T++ tRJ+qg~zk8Y3 k>{?洅n|ԑOzOzˏnY\ivz- ƭDqIj@lw`3C].{L:A P2 쇻It,b]Df2x N}C PA$)?&Q)F--QSbOx["H,Fc$=oQkt5݀p1 `P&hGǵ|ccFQwzLx:iF> i Y<nsYsظ6!N+Gƒ8/x@Gry9㐷 dƔe3H'bGDEq[f)vMp:/ltXLt1OP +wZPIS7a>nSd: *?hɰ ]w+~9>5o|h+%4D LBy2kh^m`2)AXT )H=,;%#{N&!TCIZĦ[T;DUUv*:VKeYVrk\zW͕Au]O6ՀYfatkc(Kge›~bL48Mq%-q $)ўN*Y1-WFxU *3zBMencNeś1W8:Ty5p7m4hJӚ%gw^&L9A-pxlNt|ߑ 8r{-a,)o_,:2<` Nv7UzEIȘ 7;{^ c͡JҠ|h@TSqhDGZ2&rULcՈt5iO{Du%}j^}%iKZֳ> diZZpk`u}hsݧi~c}Xϑpk[ JjyhA{s#Ycmo[ިtlFARLLg\;+sxf.IM_;꒸]+u?=UM_e$e L,udf̗cTY3LUjzu ؐw!N2aoFy,i\aw݊8>pe.e#|9m'R/MhO2o`uv++>\탿9^i.>p_ s`dN+h#Ne_Yf\܂lOO^GFܲ}oؾl@. Og~l~q'oHS_;~?v.~8O~Y>u/>`7?Rz @%><."@|>l%R3@g?J$ @urv:[@ @% ̸>,A> lA 6?՛Aľ;a(C@c:#, TdHx6*׻&\':J㷙iU41d c9?l:Ql<~D=;ĻF><6CZDA ,B7«̞$K i ͬB=8MCɯJHMT>ܼLs؃MM pͶNS?S@ TATB-TC=T?%1P/ 1%2ETKTLTMTNTOTP UQ5%S@/HWupXUY͂ɂbY WUJSЁTc=VdMVe]VfmVg}VhViVjVkVlVmVnVoVp WqWr-fQXQ QXPtpz-`( (}LU_[ _3 P5}XXXXXXX؉RP)UX -Y UeTpxeՋȂWpU)HȂDٞ_W+7(y(ypW75(,%~0Z [퐂=[ZP۴YYXP}R[[[[ \\-\=\M\[)aUX0ْMY\XY` `۰Zy-@ BHDYٰeU7`+X~][~]]y"ee|][Pۃ }Y[`X *_-_=_M_]_m_}_]\___%\@t 4^ՉHΉ4@\,7Y^BB8B @HHP]]EaW)]\Y+)^ר0^v~=ZWV]q%p]X @$hI(_0c1c2.c3>c4N"EqP4x1c904P6vc88c;4FhF`D8dD04ܕ  CC) &]N-ب_Ee ݵUeYW] Ε]׎]V^U`m%n]%@XPPD@4&IQfnfofpgqgr.gs>gtNgu^gvnP4Q=~c4c0@)}x=ctFxA6C@p`pHvY\ >CaONaPSfzm!nWu&^}Ua\5Ab&Fޏb>f)XGPDЀX6 hG Q Ep pP)E?fER5MtRt)u߷ngkkll†@}8 |Fl6gh΄Lx>ހ@0xhPdNB8 6^_]|ݗ=Zޤ][$ni`Vn&ޠjcYEXEn*H7N @jF%^R{HX]44{hpȁ=)Ճpf' 8p5^H__?x¾p p ppt>lxgzndž(`&&p`汢unLmL>&]((ݘ%]ߍ ZVTV]^5XiuDnԅf$xc xI55;_ U섧&1gD0@dZ?uڣX_hPFDBPaϝ"`qrWD@w  GtUotzwJQg;Eko5oĿFtwHxky_5Xp?_O|=w4xXDy}؏}ٟ}7S7y9yADoHRzW]zh`gmhhBc](䮿G :k?szF bG*^xs' g]xza _8j\H~& tp0䂖CAcbŋaҴFjT *t(ѢFHt)ӦNB*u*ժVbͪu+׮^ +v1hΞ 0=KZ쿴j}oǎ(xah"DcQ0ά9 Eԏ?zC. ڇ- x.T(K$77#O|9ΟC.}:֯O'  tH$^ 2IR1yc7QXg_| xCBi?j@|Q0=,}=H:AA}.P`!Y# ~sH3X7☣;أ?CYGYY[pѧuUx=K `^ D  =dUeuF!R!Vk{lgiݦhprvYNp tr4ȢHa<#&fZ*룻]w@$P{^ﱸ{@Bn-EϺn?,/9$EOHm!DôߊAFb 3ܰCS\cs0>]9 *CWn,%]9 ` e`cIYoVڝqjZj{Z~FZֶnFH"bwqڦ!vrtZ7lFށ x^8u$ K88REdba8ݦCabSOop,f &d,!Ki$$o>oㇷ®أF=t۱S_cs߽k\@M~6ܥs Aіaf,i)lh5?QOFJPѨD k!8r:0"`iЄ)UZBU-J ƜW-p{{C*. ~-DDY4s-vdD15 b!HE̘!nYjEPsR#s@; $d[$#GB2> 2pܵKO !BL~4mM8Z#'($ t < 'ِ maDD8q~Plys>:mR 3*bQr[o7N'ح< o#~1b,CMvG? 9&"GVY#,O~l9XtB'a:9#*ᾆmvr S8w ?_>C?_>;w`79W~oO???o?w?7_ `"`*2`:B`/R jr`z`~--l`  ` -!M٠fVr`A>Pa!a `ȁ@4!mQa =ira:]a_aaJab`i`!"b"*/ `$J" ʠ]a&j&rb'ya((*u_a*aa,b--_b//c0 rC!Z""c2.b#B#$B ]"y5bc6jc6")z7J)!b".c;;ơ.0c==c2?v 3># 6c4p$N W nCBdDJdu#8ZE"8b9!:,_+cII<">dKd1#@$@ $ޤA$B&d$.% yMPeQ`T$F*%)j9*nd+:IUbeVJdWzW`L1dY&M23$"Ne[O\C Q]&&eR&__%+ʢT&UU#c$dڢdj$+zɒk';2)c2k+++" m&`xJlvXZm@ 4r{2* fNJl؊^h"j깞g%&k*,*-m)v(u6*mv2,>-Jm$VZ#vN؂nɞm)k*NߦmF:iΪitΩi髊2u")vkiJ$^r A%[j[nn2d}֥oʥfjan)nݲb$Y6N&(B-/Z/ pZf$'jn-%2@趚Ctn~#`3fߞ8!i.o&pIJ00_$*0Kjq7o?s{0ߺ_p Ka >gf-iVk qf.TBGsR/bqC` wp{&4cb+rR2&bqSr%ϡc VC'Wvr/')r**r+) *Dz'r-r'r5- f" s6?>$2[r3;ser4O&''_3+ss7{7-Ȳ83.s 29w.2"03sao3>۟4sR54Ac6sB+B9CW:'!sF˳^G{GtHHtIItJJtKKtLLtMMtNNtOt;_40kP/Q#uR+u/RC#3$/3>W%VVs?{5r65B/Y5+7tD3[CDC:E;u"G]e,q^u?[We K365[9+jc;cdEr>5^[Qu_kfo`#a{q 6)'6 26dvkd_lCdfsmkgqhv?r1iӤa5bb+k#wr&luesomtonuO`Xwh rx s;Cwz_tSw{;ucw|oo?vi6x7y7y7e۵z 8Jid6ZEA9_9Ձ`xcxlAןOxߴ| xtGjx{#648A48AoY6A•c+…k~xxA x>L|-;կ 6=6l v|SxO}ѳǟx;<ϗ"w{{|W6ϳ}(˜[A# ӫA뻾V={<'@><0$=|C}[| 6tA$=]l~;Gɏ~!=ϻBD:Rzc88λ_9+WpEus"^WXҥ(qt%Km渕4i2?Xl&4oTִygN;yhPC5zg[6uVM9ejYuZ-W0A`PЎ_[_oi^zֽ[`ly#_uޢE  *8FMhIthѣI6}J˘#BfKyre#[ֻy; xpr1nWoٰyӼ94k4UDnSrnXĨ(-^yʕR&n`/U2'QS&4ԎPB*24 -‹Z,* oIu|Í{Dl jkð^~%-5ޘㅽinjͪ)4M5 ^tӕ∛y$]54 7)/P40 UeJ!sm G,lS+#NUuU\u4-ָ"m;/Ż 碩gV-:BYH֯zX~y'k]vUw( ,÷wq '1uߝ}yL?4)-Yz:i#8z ފX;Ey W.ŔV4ڈ8B$^a&"趷dX ۯv j ),nzc'hA:B$d,B B/ 'h!ZZH%;@H70Ǐc>uE&sWA.g Cl֒h$IiJqXG;yG? YHC1x)Fv!oyҢ7=9/~\rB }QC%yxC pAj4^pz!#(Xё9p8P0' TۯhAM+\ ub+ kvoҜݶMnuCRN91P-YZAfɌ0zJz-fQ 0 >!CF߸fQYK;FQv4b-,0hZv41kRC9NyS4|7-!&Z4)җγ>a (+UhP@=a.y#{؃eъCс]]iGIZR,d r"T2UTUz+H>Zg4S 2e9KǑef8%n0޵yG8~)K@>0#| :& R Se ńV,WWmMKON ` D!ց p* 8@JnP'B#nGGv e34Q@ $њBF+'X1l !`9"n@  Ml`b ʯQ P(hűFx7HCKp!PA' (qB#=2 'fyp8sJ s `  @%W'Y\rbRB'yrJB)r3^*h* (SQ")k1wj@2+G-qR_Ma |1a ;T#ԑ00M27R2'3 p-!1!M3?4C3 2#?R5G <$+.R(s%y pj3^ zR87Q%()7R~ s):*)9U9o+u+%\ -LJ),3-+,jT.c. : r ֠$0T ڑ  #OB#TRs5 n.C-TlB+D=4%(>4D]tMd3%671Y%gFm%mTFFHp837A8{Gq'4$ 7H*JH*tK)JsJtIkFt(i6sـ®AOO% ;c ` O? aQ ОAD3US}ǶdR?T)E T TOUA75)U_VS\$EetrWSWisW{XYrM |3@ ITIyUZ5rSJY4KJ*[u95;}5IIwZٴMMNو*Qۄ _U5 N\a_ a3Qaaa4&b+b/c3Vc7c;c?dCVdGdKd-TKdWe[e7vViEVՔGѵWoWgwg(&Ba'iTG۵It])&7V)K[r[Ukh@jy+iZ5]]Mm^۶m``^{ANcjį5pVp)Y:YWqqo7r!7+) ./8s.<7tA7GI163흷{yy]7?چOiz9]Ϟ$UbMǍ^%?\ ڭc~91?q۫S_ b&*ö&\Ӟ~3Y5{_I{y>q!!ß|ӟ9;߿7cW_?Z o  Tq"AHsi񟄊 LI$ɑ(K e˒/a8Sʓ.c mN>oПCy*]ʴE6r(͜8c4ժЮ5$C]oڶJ}˶\n,襯 ~2xp`,Ǐ #K˘3k̹ϠCMӨS^ͺװ]s77&\aDn\]SmˑT5`kwt/o(@L`d>.PR B //@х$E=V(La>/wfH8̡w@ HDRrH۵Z寃 O'E*0u&T7v{!3V2hL׈#&EZ|,b+.]  /΅!F:=t#'9ڇ>qUxI-k.d&G\1Q )UJ=J ,AX:@%f0& 0IJIf:ϴ䕰I-Ŏu|W&RM]2;)QINWq<9INR. ɾ<c)>GG, MB* ZPv?%X(VqN%e'Va8'@>6pL:?Mȳ>π=h "L FNLҔr~r10d;ԨNWV9R@g-Zֶ5w^׾u DNl!e!Eٛnv1CmCjvMrovMzBc/WLYJӈ s Bm =ncθ7{\GN淿 L.%<6v6S+oo?NHxO;M7S|Ȭ/>c.# m9^NW;=vŻ='p箿KxϻԋMz2Ժ9eK]TMxRW}9өzSxvb;'p;GOқ~!O@=KagOdz ҫxy%=TO-;Џ# ,X i("p{O{T1x;rY|L.}6|7X?y}o4Ԁ 8C }Wxw~~UbZuhe.c*,؂+(SP29Vs\:25x@oח} hM0kkx}xkz'x}oguc{"eD%H.\0jl؆nDnPMDG$QB|?yR No ORXƈ(>xNC(8x\ȁdbADe(. PGk9x=h}xȈRF،PN٨HшX!'[rd8 V# ؏ȏ'::8JLI@Vu\N4Q.ŨT%u%ɸ4f r Lkk׀XF|r(i o&8]聾'ehnOII="PiQ8HPssTi%9dyf:9ߗ9Yn(ooY8L(xryiXЀ(}ng @ɊD9C-QCFICDHCypĔ>a陜$TtI%5%Vl5Xɑy"y&x (xGk@}ɛ) ()|ogŹ{酱r6ԘCtl䝌92ÙJy+)Q깞VY\2y%W)ه} |674 h6gɓz&X  >xlƘ)F !Z"iXC ICIiLyąAI_IETIY٤gQh|@||F{x֠P} xCиgԉ)^mڡ (ڦYp#J<1C3 .Y4n0QY26ڞ)Hj쩟@QGښ2 QAꤠ| $YriHj!r9Q*x'$y $w Ț܉κz:}zC1Lں-P4P*j|ҋɨ9BZYԙyY+I*w Xn|I+hu:hIʄy+9z٩J-:՚<R಑@ѭjVV:  RгP@=+A;RYP;Y Jn ;;C+F.qO W?:\ʩq%\*9z**zϗgJPkn$)&{{;~{/۷.Ii:: ۱o|*v{.>V>WKU۴KP.8 YVQV 0ɛ:ʋɋR _\-bnsil\\d{MJCx[kj!.Y،19éùی(ȩ"*Xǹgn "k[̪۬13R@pE ˽ P2^\6`b>jMa8D> y?t>ϳ]6\].;M[刞vfN>^1k?Nh>[&.}Ir^ Q>m? A^#>^~Ȟʾ>^~؞.=ȟ~`5\zץꬽ!mFmrtN@B?D_FHJ?b x7|HnRPZ 焰 -0O3O5?7oDz|~?M?OQuS4k5\崫zN~I._P l q₮u?_~t=ើٺC׵]_ ktOeup.>5-Rࢭ去7|ۑ_AСC H.ID hGH#A NGLh"NO?\=}TPEETRM>UTU^ŚUV]Uۿ7z̞}V5/X0PP&ΝśW^}X`… F7??~BV] ( Ν ,P4g( 6xHJ'FGtlvyv2gxnΝ_G\r͝?}5RIT՟b}MXc˞5vm۷qM_|~%C + B( BP0B, ĊHb&P@ )8^^*ID+N:_1FgFR J5v\JX.p#;TCB/=`-争8K/30#L3D dOMXF;3O=S%}$R)!,H$\'CoJ*ٻN4SM7lL4?5TQG-RB U U\\mU`JVh9WqE֦x% X| Da"c3A 54BHrKɞ\#GC*Ò8E7]uTw߅7LjY*{i*}2{^z=~2Zx}z"&jxcdBb4~ޢ:VG C)E l}R\Ju&ڕ7i&^˜d)5)|yO6 堤׋"I.lJ`j[6e6弔۞Ҡ5:qGigi95s)|͑\~uOr)W妝QorUwԁKv77tۅwݝٻ&Jul+w-.WKg}t<~GvZkߕso[~ ? 5u=w'NZs'VOGXK֗/ 7\>̵%ш_$_ϊ;UVBr_` O~ӟ\:PԠEhBP6ԡ(AGEhbi4+qkf蔴,LvU2 S`%chz (͒6Mo%Cf:!t&>z 'DŪWjVUvի_kX:Vru1eEkZպVZu6(MkU5]8W1ԥcszRly=_:FrT+_YR/K(\uG7Žϒ{LMNI}dleKOҶJê+Q)f<񯱺ekyRַ e!K+-Jo ]6-׻)vKQ3.lgnZxiO{nkPuk/ am J'zypW" tx)O oZpU3)mPT׿\37mmc5X«zQ#\)D.򌐌%gxJHLߣ(2@K~#Xg, g֤f6osXnŅ=GA2MC^'Qe/Ǹ3&͌fNOmuE=ԧFuUPR"YoLu8@KӆtC# FvRφv=;̕37ln'wGljFwmki*&mzwJuxonޯ-sb[xW-pWxTpSۙ{G'3r8MU" m~{zGޥt7Ozԥ>uWWzֵuw_{>vgG{վ٦ssN6Gz?xG|x+׎mA|m_B߼9zፇ=W- }u{~?|G~|7χ~?}W_w'E`<_* ?9A C@./ ,+<>+8DLP˼C\HÄ+M9O=FO$E+sDU :݃F|RS=\lSLE5_,B617yC\?)+,ћы+N@ALFJD=o̽Z+EL >q=ut>nDvGDyc+0z.}==sEQlICә\EeۣM_ʄ(U ?c dD[\e\Vdȓ}dMμLVbds؆ST&a6fIFhdefUeKDcfLWeY6ZsFtVuN^ξKpf3zgz{g}|ggsІkgzNhfvh}&he~hpq&g1gv&Vgw>@udy^^hVfhNegv蚎ߖi^6v꧆jCiSiԉꀶgj|jgsij}gkk.볶ifkk꺾n. .TjڲVQfv揩fFkvkk&jhFkN~kЎil{6Ѿkμn{~m쨐9<@c n .k v ~nnnn~n1f+.3~V9voso~o^osz׎~ppopV^6< nVFn^nPnn/nq!_aF pGr%_F(&_# ~r>r$o+.p O ?NC=m;f7@u7x?CWiwvqk/l!"o g-Wrzrm/Vo2')ϼoop'vo|7};tY$G`P TÈ'Rh"ƌ)G@BcdH"GT ɘ%EY3&˙(WDΕ/}Ҧ̤J2m)ԨRRj*֬Z>+o-K 7ּ`@A''\/9\K1ȒFq޽7G2ТG.m4ԪWKgӮm6n^E7‡/n8ƽklZlu].^X$,;Ǔ//ڹo=ͥ_>>~?glx *PG9 J8!Z8!s5sgV[o5u_6"-hN=18#5Ψۄ=ȢA y#}9?9$1( QJ9jڵ Yj% ^%a9&eQ>݇ӉhWJ9'uy'y'}' :(z( gl\:(yy&Zz)sб)]Ցz***:+z+2): ;, )ԍZJ;-Z{-fhz-;.{.骻.3;/{/// <0|0 +0ڥl,Z|1k1ǴV1!ڑ%|2)2˴&9j5|3'+=/A =4Er,*9;4QߺU[]Fk5]1tR}6gS}5mWq=7c?[v}7k=p}8+z0ހK>9 N8V8{yq奛~:g;,>lb9{T4k oI QOͮL=;S{޻<}9Ƌ%ORJSu>NCo=񓉽ң= C?Co|^PW'#]~D(:Y®%͓CxI{ Sx3! H "21pCDCL$8=%z7A"NL V 94> lu4ctc(D >v$I5]~X\F3q(;9JD1B93I;r҉y (QV+ JD"q.a 5q4b"sJ6vMHaSW'H qы,nvI7<_5K%rӖ})Nӄ,&:ӹ*Sp%| 0v\b,/H{ВB 1yInБ'BN-{ :#*Q͔'F]Ѝrv|(] ґ&=)JSҕ.})Lc*әҴ6)Ns ӊ^ԙ5F;*ԡ{AT9N}*TڅRV*Vխr^*X*ֱf=+Z V!*\8+J׽򵯫k+Qu(la92},d#+Rs`[3ˠhʵ5]j-m/Rղx-lc+Ҷ-ns򶷾-p+Ŭf32 udnPֵi]_p.x+񒷼=/zӫ}/|+j~zW,>0/x[̩v3]_0C,&>x8ыl.~1a]Q8Es (1,!Mn[%3K3KErc 8<-s^񑑜Q6fvaУF27le_3g/YT-h4qk&[L],!ut ,Җ4g?̃4 f5VެhUso.-kXGXss]czӜ!C-a;к3uUYֲI;k^Sm@OG}%{٬vuc=*K6-ooIFO&J6#lѢhٴv6׭GoÍ+S83^q-"l8KW x]h? G8Zb8s_ 2@w EH9N4(2rpyɳQ'Mԁ^Z}n83Lð/:Qt@$w8xPs(Ş־<#۸cdʼ|;cd~=>^Xֲc/Ӿ=s=/DL۽\ :JkA~- o_!??ӯ?/ӿ?ȏ:_*=_]MS8Y= xV^ f_!v~  `rY^CEv g),i6A jJ &.!6>!F`YA =Q]=R%!!5Tʑ!ơ!֡!!fm@E"r[&bGC]$F$N"%B \"&f&n"'v'~"(("))"**"+) l$], ""81@ "00/1#2&2.#363>#4F4N#5V5^#6f6n#7V#,b".#'"\4:#;;#<ƣ<#=֣=#>>#??#@7c9dc(C6C>$DFDN$EVE^$FfFn$GvG~$HHG$AA"Jގ9ƘK}L$Md~N$OO$P!!u$R%S>%TSKF%UIdJnB5ǃUXzUbh%Wye[%\NY%Ze [ƥ_\ҥ%^fL_&`n a&d b*&e ,!6&mRmə&jjwՠ y]1_ssNP=U&QRE#{b]=P{T' Չub\qpQw6(˄''!}fgQ?MQ.Q5vngw: u]`zN(v6Iz(!!)Ig.Np&(Vi`zZ -M>Uʧzih$Q9&΋&Zr (6]<-']n()8S !itiTt)"n$f hz*a&))Jҫ^SYjQ%- Ԩnʐ,fB? ~UeИ)%>#mӛ穞aZ 0&~lp&װ'eXJK`b Z&kɁ+Լ[+–ƨ6kކ&,Bjf,@lk~\삮Ɩ:MʮlP,Ƭ,,¼+Ú(,Bϲe-"-6mD--Ĵ%:*-^-2l6Q0-ٖٞ-ڦڮ-۶۾-ܒέlWҭmŭ-ත.mmlW@nH.lPnHJ-t~.l.`.. mξ?0ފFAn0n-./nLj2nJ.bo.Vn o>oFlo/jobLvъo:nWRJ\Zo߮S؊.~.C(/op0~ / o p 3oװo.n^0CpB1pnq.pkp[?1O.O1wk'M"/+#1kq1{p 0 p 1 0pr "ʱ$Gq$q%qo2J%go&O/)o*_2'cr)۱ɞ1or+.+2/1) 12"/2 13'4Cs3G34r(_'1G&17q'ws0o6G0::*82.3/sr.n-)0˳70g0cn{2'JD3H !/r#/1GC0bs;3H1GWno.3@(2;I4@w1ڳe>r,*94óJ{K S#?2 2j K4134UtI[r?5+{rJsYs?RHJQ/,OO7>SrRw߲ʇ%ɞbj88xb8x8^xT8x^x9R89 #9S9?*yBy8_Jyc99-997rә9ꚇx9tӛ9{3hҟ:+栯xѡ7:G9_zbN:У/viW/:gzIoj:[ۊn3?s:k~o:Y4{W0ywꢷ_M_g_@WK4m;tzgr8/6.w]:h2N1z:7Q8\{+{{(+gk3;|:koJG=/=;?ODgȲk~>e>`> p[=v>rxq)[ V\G>QjN,a [>$/y~zW׿BJ9=kN3KPδj/B 1+@6PIlPoAaQF89Q;1;1> ,HjTr<'Q;(+LC@D/I0,3Ӓ*.qD2|S9s,5ì&8 TA )dBP tjHRŠ0SM{S_ē@ECD5 >j#-t&M(%̵l}XU]c.Vģ/jUO򛵽DUҷ v$qE7ۛk#f.YV_*Zvm/j]M7oaI +*ji^aBUI\\ߑ;W6INfsc;XٷyiWxt׭Yd*5ivmZXnXJ}5Zfaaٶ''7ӣ0\6%NK;B7e=l{9g^#\˓z=1<)nCock+5}5[y>WczXIҐ-!:ri#J {<+9Hn󒈌B% Kft 7Qd3e1u*g utT#Y(qW`9 PfnkR#[@ ToEE=R3"HMRԍ*}J]t2=LmST/MK{TfiQ/&LGeSQTZɩSz'UU]SVZ$ekYw4V}[Uu5YZ"敯3k_5WvD%{XVȰUPbM:%,K'STX=k MgVٱK- kQ5񫦢OQJpx{83oh6AZ=;,DSC'%ˆ`e<"s+.%kRS^Ӆ.x5Eun˼yxRqa}K^{W$gWǹ!vVI=8/p²f9PJ+<9U`Yh0ONX$vAʐc"峵e^zC0m|G8Ƽ%9Bwx4pjYYL]LKM^yT#39t. fǞ1\d ãr'[Rn21|LegRL䲎N rHcp:Qle#9\a#,*W#ڗb5wPձ"ֳWm1Czq(o&uo[܂3cpvwLlNX5jEk:>[m"'-jƎdH[%'ve`zk3yɁ0z`[ݗᵳvMoŎq Q:E[yyhi`*-, Jօ?hcsO1N&{4)a7huH]ej]m/ Ki]ugnǰ]}"ro7B'?|ߙG|~y V2yлGMoҧ,g}We^Im G->('>q5 ocD? Tʂ$4Ѿ| Ynx By\د>N_?޷(% #! sb $  +P 2 P @ @ No5a$ ypl};^0~u"`,0Z  ! p!   jT o ! O !! !ڏ w#00!P <j M   0!"a ! P 0 0dE"B OH0q8pDv!KP?~@A ,qP 0Ǒʱ !ʐ k i"PHb8y  !!01C!DQ "K#L  o$Ұe;R'!bR +&yB~0C 2!AA   0$#OQ0? Y7#/%w=r!b*'kr !4Cz'Q(l : Z**b$Y,YDO,R&)4r3-"`.''Q .60 :12"7{b4g%,Q&S=2:8%yR0ez42:V!)  0xs,D lRғ!2!SHP'}R!r! ?_ 3), گ=%(94! ?AS!S5r5-! ?oAeAMB!7T55F F2(\553)tB5H&vB"JT!'A5KbL4MєLS1PD7?+Q;BsF3?FrC- :]T Fo4'IU!! aGwQR14 L;5b³TMTٴMM SRuO2.S3;O{H !H{"5JT5tYUIRBRZ""Z@K>&´SGT\5]UUt=MU  4;]-QGO3B?R;`_ 8s l@A`'oY$H!"& )SU\C\Ó]EU"AatNgB).WR +gI Aa "F˳bYctcBc'U"B&KdCkVLU6 LSeU-g]۔^11D_wn5;U3B hVK!'?iHKjjRS"kk6sŶLOUv1\=Q[/_NFwV(BB+k7 U`T`qquxroB[ K75Tu\OV!H5<\s#]^A(Vt^O7@g(]Co 7~65 b'iOCZcxXR5!,!F K6eQvzzP{=#tӓ~5TaJrn@@W vh7 RY)6j Q}Br=jxyu[0{mBz5qlLw]%,(X9.Sa` 3AXc!cv0b'v'Uwv'UQ߸ S1؁Es;8WڈNF܀Y'vjYx'Y'Zuy! ]XX!85H9L3Q9uj7]٦`-eycqYt'yMi29딵N 9y9y9yɹ9yٹn) ¡Nt͎ 'ڀ&`&@!:!"Z!%z¢!: 4Z@ HMZ ր :8Ů3=ziie+Z;{a表ᨏV: Y٥:!`BZmz:іqڡ:z:y 暮)Z}Z'::z˺YG ۮkzڧz'[ګ:A'4]9[Z-e۪=:m?w"0cqaZa ;O{)๣;{{A{m7 ;׺2[ mM 6E_.9\»׾N™2:D7U;E īB*=\W e~ >~mei`Xq='* u o]MQ>U~Y]a>e~imk~빾>~ɾ>~ٞ=u~MM*? ;'~u`u`("@v\|9=A?EIMQ?UYa?eimߠ؁!?at \n mc<@%" P* ?ɿ?`o@E: @b <0… :|1ĉ+Z1ƍ;z2ȑ$K:լ[~ ;ٴk۾;ݼ{ au8H"DXHЄ#TPf &__~" &hؙܭN:cϐE A?BeRNIeV^eZJ|RA]ufyuhd&k1?h|^vcQ?i]MO -I&Ǟ{ wKNJi^in\Jʠ_:1"(*I2R-fO:*,=iȀ=Avh@Cgzd)i䶩1nn oK/kvHoD8ƙh|/vĉu~${RGXy0(#_2z|~ r"sEDD1z)~Jj̀B A"&YH%#Zkzto52\9E']t^ vaÎ %b-V Gs=v^iPwzw~XoNx!,n.a}P@'i9)& `C?T#m :@#ߎ^D!DOQ)˙| 2L%~3pH Ћt5h/eq]t~ ?oM£lU`݁hhǎ7M}%ȿbC 7ip?p$, Op+@,'` K*Wذ 86&PFKNguV<؝]Jwd,@)ax;ިHՀyJ4 T@pj`<ܚ!5V0-i`A2q0= FJ+Hd'uҐ ,_&t=2}\@)#Yw# d*sl3 hJst0ЈP48r!=# "cP"Mv2Q EԵ)b)wEpG0)5^|!G9F`Y" ҆+W|LZ- Ј{nV8}4;#d0ST|KpSmFJse˻ 5Ѹj F V2ִulm[ ׸ut]׼u|_ vq0PFI97|QEw aγ)$iWq]`ɕEQEoGV Q G()Z (F8 @@7(*LBwp[` g^-|My. |;_Wm?{PsC[K]}6`(`cZe x$.Ox,n_ x4ox,ކ6o`<1<[shK{ZdB)CkLH?춷m3(#E0.cSWQ>w(u'@yw{FZwg, 5t-I{wBiOZG7i0?xRG( iW [WiQ}GGz3ŠkS~lk{ p{.ύt;7 sEt"paʨꨏ *Jj5ǩjʩکjzڪꪱ*Jjʫ꫿ z@J`Ǫʬ *Jj׊٪ۺڭJj犮骮ʮzRW){E|t¹ ˰:`[s  !+#K%jŚʲ-/ 1+0+Z9;˳ *@Kb GIK˴MO Q+L;+ ˵] 젪 5'5ڱ& q+sKu۫(kx3˷} 5;䊳=;R@k˹빘Yk_kcklnv˻z++KK7˫뭏g2$j׋٫˽ ׋髾 +egi[kKʱۻ˿$*Klk˼ >;)BF~62> !,#\y),훪dk{+9;+A,C L6I)L;kvCY['|`*۱7\kprltwz D}jHą̼:ŷ l)ŏ ɑ3^`ɖ`, {ɘ̨ ɠbg\i<ưvDQG=P W][{ڮc͸Еrրfm}<ɝ|'Mז*t )M(z ӛN]{3 k?-TN=ٵL@E-ٜMVգ Zէ|bM֯ĿEh-͠2n۹Mp]}w=ҟLu|}sM!.m22mӲ4ӎ]Ԑԛ}UIݜᝫ|٤#kک 񽭫Ѓ\߰ߐrM)Xۼ]yͰȝ` ʙ2 5}<Ìrnݔٚm,)-;*7Ϊ`=Qї֎,GN ^L.]L>i,mM!bN+`n">\"~3.Z{w.;> Fi/sHn٬Ҁw-%=&V@,1͍ҥӟnٓ*J=ԡm٭}=~jx^ַBMk}J>ώšި[<˸.i^տ.O ȋ]^I >.HCNN#[ '?N*LM+5o Ȍ\^ۗ"$o(^XLO7oWﻺn;C?f~glnK0S4L4R<lrN:S'OJ>RK@tPB1UtQFK+3MH# #$6%NN;6!/iڍjm=6g8lFmF^"鶢[ ~x-xWޠd6("(z!||$doc )N~s ΩNQv%=tlXgש-v{cjW. #a5dPAP,x"LxB>i^W!6E yc;}ᛡb>8K.g!~Fd 袅#h ^ "8z"V: lݶ.pQdvaP $d! A*&R4#A Q"D%+G#npzA>!? = bLF,VŨ%SQ_301dF!Z;(9bk9H^У甝bxqq`.x3d)LTD&JXR$'cyMǑ^qhx? D%u'ЬH ̃9BQJ9mDtE6ӝNa2FQjL}L5h[k鼉9hK4{mp B;qׅ<>g: sO*z_Wv +R &%6e,DL>& E4y<;ũrB4F>@u|?{P% /.6Z`OT-Et M AUfԴ Tӥ4-/AC%Uƍ1.G{qh0<QbElG8FXB   w`  lbfذQ:L\䱗&L@6i!׫[]'C C>ehKŶ-6ܰ%fpip8W%`X4r'MЍ zAjeպ Ƞ^Cfh {7cfB0G{ A9e]/~m [q@2 V^:dG$YҍJ`r\?Zl(:\͋1%ka ?h<-?~Cȇ?A#x)K䲀<@Pe wXqRL/6jb[0p#P5&CaF7mp3y!H ^)+_VBko*WK'4"29U7aw<:΅s5zO?_5;M ?~Y¦RеzkF(ؠselK@Bm~>,<FXx#^hﶃfH6\SGX\oz߈5 - \aNpA tѦѰ%/䀋 .Vpz!!(Xa9p[^ '\.uq~?J~X®w?ɏ#v"TJX_c[9])|ZZ$K(3#dZ5 Y00 yp6ho̷|%A[#<T(A97pm ,.@@$)"+w 9(6-j;aУBa#3ÀS=Ӗ>;$x==l/C8 8RR +c[P[[X>VA< CjNk03?s{D9?Q󹩛?;H?kBSE0(NLB.#DA\" AV`A4P2y<؃=ЃYY[AAFh<$,x<9,,$d%2SD)-jzk+z-|$rm9p Xؓڥ5< .&: ɘ6C@>?$@#3CL7>'I'4aDODHlDI|?QlD?GKIG"Qu&TEPTLX'>) 01MITNӥ:NdNTΦS:AU> ?$QEREdTFx I$edOTt\0o `5X~yx0 ,$5c[UUnS UQP , UQ&Tԇ))igVVn,U;; !nHi٭' :RoIz 'mR_0=.+M3 NKKkXcX5IRCLDUΔubFMFYNUCO<ASZUb% uU^6Z9y:ϑP'e Q]+2:Ȃ | ȄWx3o>WxHu!-W̐3TWiזr^ mڈ={ѠM3[#ؓ-ӄ AZC"$U+ XX 0KܽzZ%YeWT5LЫ ݗUP~2 YO Z`<Pݑ+,eVu܄Zs"̩ڽM i5%[r\ݦm5px>~[) ( %Eµ_^_PЭ E`=LU_X]mV5549 L|pU^YR^[ɼ[Ӡz8߾%.*p_3M b"6 a]Ϣ]$N&b!^`)]Zu+b0.35cD1҄Mc5^c3fc8cߜ2,Y4pa9Ncûߤyh/ @ 8b/ndGՙAdIdJb8ZLdN@F&,FQe\dit"v0.3F|3dYn7e[pL%H$=,*eߜ-fe*Hfe^ffVEhfifjfjdLfmfeT&bUN1RVҌea`Ng[m.oYgvD3Akg}fg~gߜ 89;_HhHVpH@@ hh [dViJh~iiiiidRiiijo`/V>jsvrhnj~jjꁆ>h~hfڐh^NNlNlR!6jrVrgsjʮl˾ll6hm j;갆&k^hkviFi k~km6iln&l TlNn^lNe0g^jͮnn4l^m&VmFm^h덆kݶkڞ~~&nonpOlƞ`nOp_pLM8Nh ߜp p p pp qq/q?q6<wq]箈qqqq r Gq#?r$Or%_Vqir)r*r+$$d-pu-s3?s4Os5_s6os7s8s9s:s;ss?s@tAtB/tC?tDOCu n~tHtItJtKtLt`tuGtPuQpnttRouWuX'QJ?p\PueXu`vag$q?HZ[^Z_vjvkv,=8dgvfphX_7s?wtGw" %hwwwxwywzw{v}w~_mWvo6wg-t?xu{oxx|wxozng&5 xE5y_xxOyoyyx?oonp6 'y7xWyOzgyzxoyy'Qߩ 'zGz/wzO{_z=ww zOwwo7{wg{?|çzׁ{"z/{}gxw'}%?G}W?oG|گ7xǏȟ||z?~{~g}o׏~~Տٷ}~}vzWW*~П}~~kx#p k^`¿B(q"Ŋ/b̨qC'pe$ $G2aJ%_yC%6oi3=Թ3N8ԧϛ 8B*u*ժVbͪu+׮^ +v,ٲfr׆ [m "T,ݺ=y2ɔ)+$MBta#vxS/cάy3Ξ?-jpm 6Ѯ p/_}/BJNT8)|z9ΟC.}:u^lÍc˒ʖ[QDg~bL}?fqcNk v 6hFbao֡*''+X7d\-jF?-6?b4YG"si Z/)y:b%U94~٥cYi18~f~e#.A)ҧZ"a:B).YbYZoѩmaw^egz*骬 +⚫Qmꩯoyl|NڬJbindo)'Dn醀(NgF+(;Z-0~ \ $˯J ?o#\tw 5,1[͆fq8ΊܲG#GTc$Q({qFnGq׸oga|1lvh7^mngI^\-IG ~K)6b [-y.tyo9w:޺k㞻V%nH7d/ḏS/N6oo={o=_Ζ,ol6{uC/_+soy8= 0|K jp [jV9JX=uT KE%u;W n!viF ^cD0C[ Q+ DP 1a28 "+~E|\Q6Ðo[b"ߖ8Z!sE@qztlGꊌob1^Bs&P$)K$IRp2٬d(kHS2IB*;O|e@)KDҖ.Ҍ-[e0ikb3&7o3'9idI \';w3'=)8$R/4(A jЃ"4T,Ї:4D+JыZ4ݨF3N|6Dk_^8Г4*])K[җ´ajjӛ4:)O{ӟT ?D> 41]*SԧB5R]i,ԫb5ZPjT!NS+Yjֳ5MVַ C?8-qbU+_׿6QUd"6],cXY`!W ,g;ςvHiPOvJ]-k[6MW){WIlh{7`\,JPqB7ҝ.uQڵ`ɨ)7oK {ի՝/}KN6)HYID6N7"R|e5Ѓ9F f6GHQFwӼMz-Є> m|Nxt#kⷨ./KE?' X9b 38?L0TM/_~0Ϝ|Alv 6߹Xs# 58tv,57ъVtv_;wO*{Aiiy+sgAw坝8خj-q|y*g #Gok񾇲!>lX^z#]q_9:l=]eQM%ݞa-A\[u5 Mߒ` vQ BօAS_Aӟ@]<;ҭ`R!ېv"Y>5XMNͮ}>`` zٛ FFD! 6_ :r *5\]Y! ]6Ӎ,a\"=!a;X;u;ub(2WzAa:UVI]XQ܍ B Rr_j^~0 Kb 2!3 ZZI\i9c͡9UU;j D<cd9׶B]Uw,n_>bU `Af22a!Rd1JCbDb-j_3R_2nAvdDZ-~!.@aMѤ=\!aM>Nޡգ %M^@aa"!r1bMJTjJ] #U$`|t]zdT`K4eUΞme.BADTLQ^a]ΡM&``J`2L&c fDU%&d+qWxW\_eĭfiFaXEX!ɑi\T2fqeS&m 'sn|k^! *[ad՜%ٟM<&gd]'Qaj+ d,'`EFꚁ"@ElL96 HMZɧ`^D=$H~h"6R*SvUb]ݎy}s19z) . nRnZJ"ڬθM@n|8. д.SL bo ,r-٤n.ҮB/HKJBϴ. obbh-R ".onoMQ!p..JN/zo/-raz rp wRQ p\p= w N, L Sq 0*O  ;nwo31 w2C1۱r!`q"o´Qݠ1OL/1 _N%)[2 *N!r-2$2@b$1?q(VrH6'2(1,K35r' 7s8I.˨&oR-gss$ +s42;31120+O;#q_3s{:B3t2rr'Gs6M;s1OH62GPH3$/,1$W3nLF+3ErH7Nt>rJt-5B?KRkM CTS5tF SruWS㋴0WuZu^9ZG-Tw׵\^m[k1 `"5#+2vc;65 6uerbvfkfpgvhÖgviiG*6:[kr`Jx6vܶvPvppn[nInSrr3r;wsCsKwtSt[wu+7T7.l" L# wyowH/A?l7zK>AN%v|wbw/|HI8v5D~ՉE>:H::׶narxx聚I+r8n>:hxɉ?Ă~ osku#yE4s;U49W7gBDx8AwgyTD9&lyVSvYuܮrS?WDTcNR8G4cyfH8:8o8F@:E|:*6/,$n-3Jt7q49?45 {ˊa0{3@dADdwC T:W 4A4AApo;GYP:C\7Alz~kDk9{?rO˛Vn1(@n p@߰[L o<xǗAP;㻹CD''ucxKE7AͿI͇;;w;:r|<ЧD㼍γɼyCd;jn= 3,|͎;Ysr|xG}zTpڗ:3 2+o[0N4N#>߬4 |c>|<[ԣ|Ї~?_Cdοvz>՛~Տ~~+}g:ԻJ8գ;d}:w7K:m%4#3KH_*S3 ׿ B Dw4\ 6L"D)N(1"/j#H!=yĊSF|&3gibv\icKGvX#ɠCԸf\?4ggMΝ= 5ɔʥ]ɖ5{mZfu9!ܷ.']\wݫ׮^Tۿ0߼o  TX{sf͛9q"nC.0DԸV ]ۻwuݷ{ܭ`䰕wlջk;:peN}:iص 7!ThkڼH34 %0S +Bj*M+0 sƿV(1 2("O% ~T`FYOUӡ5hD)"SZ*{^!HI7$)E)E,B 5-)/37.mGU~Tp+XgM V.q\ZWy^:ȶUғUbX8u_cEʅʯf4Xm4ePZӞUjYZ׾li[[ny[խeE$Ur\*G:Vvv3xZ+L,g^~|:5/ho^7 |ef][`_t _ְH aR^H+0h<?3qCv,Oyr̐" $V2I; b (瀂MELKMy+M>ӳ3 9 9Kg9Yo56ej[8ՐuGj?3ѻVy$ViC4/ݯ?fu}d{ksj[ޫ^y͌A١hz{ͦ4Y'mh[w}P+FutWkˊ!8I'BqMͦ%8z K ]so[E9qz59Ӻc^zMkVQ\mvthB:KQo-w=#25uWKP5~_x6{ onGӼs1(DqĝvǬ묻@.w݋<_~{gٛs(c:fgue|5"~} #?ʏqHo }1ƭ'[iP c+hOl*3P*+6b"BHV[_cPgkosPw{P;  P cpyĄ4J’  P ǐ T>С P 0YЪNL $"QP ,ܰ P p3T0AQ7;?1K퐰L(3,)OkosP=%g1QS1Ϋ]ᑄwb~MQQjq* '@q_]0qWc0Oq  #Ñ!bA"!4@^kqQil#=rqE MS9a%%$Yp&{P#%iK&'') R'23,A*"q'7r%&a2&$ R'+$R2,m'2(Q.k-a,2s$fR) !R2#"/rnp//%2-9'.,53(73K4..IS#{4USc'5Y2'{R-?-{335S,Y/u7r&77&i%Y4i7C-S:1s5q857rsD0r-,qˑ"12:W9;:9S8I8wS4:T%/3;9T8S@}?/@@s@'tA5'T?ݒ>-83AB()7M69Ur?0ʸ0ɓF4AA>4;H?@>gBAsHD2KCRH:KT@tJ4IS?״@;}L.kOFKqG_PuIהHI%M?SL8;4'CմM4N-:5T73I3RMTST,sEKCKS7CU6QJD35f4Ow;S}>>)|XK}ש[q>ؙ1{]Ë^qדŻ[ii[~ؿȹ;e;yd "(Yiwx9g9񑙳'9հAs@b-ɸ?Sy)u-f-F[K{_ج1QX; ___ǟ__3Ei@s_ H*\ȰÇ#JH f@ǁ5 ȱɓ%Q\ɲ˗0U=8s|Ϳ5/X0PP!ѣH 8FAhPJJիXjʵׯ`ÊKٳhӪ]˶Eb̸cȺ#;1߿ Lam+^̸ԙkOB&̹ҦOvLӨS^ͺ5׷'ݙ3׸s dʕ{ :h~v:P.Fسkν;[&i| \ᙍO_<.z(xUydM'f5!r5 !l4,ֆ1cx.ڄQ $(ڨL6?$#FQ2mXX(O~3p#H'ݚG&\~E'UwNv{B'cZhu%)$V" FXjz4Q!)Ef "p"*[jRji'bՠF%j9lV嫞*[UyB*%㔴m;WPz`ya @ 4 kgoXǎ5⺯i,,7LSVUqZ1ÂV+Hٶ!]t|ҕ^V\̕:)@/nMWofz Z1,p6<>]`oRk0]vY?ݴZopclrKm7_sL8a۝x}4z <I%ɬ̠#|:nϔqaаmb CߺՉC}¿-K;۸'p7u*|t=n󃞿86xa(+)/-wEOA^UJu|Lr:.vS#$ LTOғ7y׋FC5/n{pxB.}J3L(8!6ĞvDA1nwӃ g­*had_H2@.t\HF.dN\' .Th"0IY+BQ{/r5,$D{~k!}dI52|k>J֍}d;9Jec[\ M2/ f^qs\Jgr OsA;ah)eT<FM 8VpX!(yr| ]YJqґᄧ:(Ӡ"nɹ\.,fr?LUZ2Bԣ  ~ɂu&а=c86Ty EkJ+R,NqJ4<&Ϟl_TzPBn%C ]җۢ-ύNP. XfE-VR96˪ZK# f"~6`'GDt2|`1YЋRyZ-ǚ{QA.(QcUbGQ6h͙Z-4Mխkn1;Ep]c-of.t-JL׶[nMzަ+ ~↖IuK޴LS&B햎[yC/t_w]f'L [ΰ7{ GL(NW9J,8αwc@L"9Ax&;=(3QXβcOգrL2g6Yǖ*@R&5dfπMBЈNF;ѐ'MJ[Ҙδ'eD(E)@RԨNVհgkO,εw^92egNf;Ўv;Oζm`Mrv׾ 6=b7η~C(4#lj[ϸ5U&JظG;#0g{|< w^y~xʏrHSҗ?K aG!wIs z:;G/hO#Q|[g4=@wsyͪL=xl7Q؆5OŻ~ҠW):[^ЀD?+үt!wKϧ^]{}G ~ҟ1Э}u|ѳJ7#zen(늏>] )ExUмG'{~=^?{?77h~P?|`?#'}o'M}ɡ}vUyy'wzz~{}7pW~uG{,8.8)gy/~ŇG{.&{G8|)ȂA,::x{2zK>((xFm_3 pv/rI ɑ7G눏8!5d鏫8@^6If98?iyLN ;P)iTIV7m`ɄX6qnّwFi3"i eicɚy9ɗ؛-T9(H#ERx(nٖ)(xϙYَ-(h5x2i҉G( 陚؝P(҈ cH%IȉYf(7@Brl׹oڠvC' eǞ&6"or.gZT/z8\*y-90a&TpHJLڤNPR:TZVzXZ\ڥ^`b:\Z 9dlڦnp&2JYIvzq呦§:rDC:&_ ڨ]1@-#zRm訚sZ'WZ58:GjBګ:PhZj؋ƚʨ j B&ѱؚK |]0Mڪ]zx!R$%r" !:h:u .b@$*"ïa8Q[LA Mʬ ʁ&kB4ob/0;%^(e@23;/[ڡz&0*2*;D?VC\L+EvCVTPąXXs57ŵQ6X;Z){ZU+ qG䲳uڳ14#/R/"+J #T& ;TU^R[ZUELSW4U[iKpx˶ⶇ !EsdSHak>{76jcZY6J&dTt8' UxS8^hck5[TJPO{TLŷKuʹ˱Sk4KT];JtNdNM>-D3XeI=$D_{„<̽Σǡ\E;K5Ol æqa4g:5͊z)Rk;\ԣÆR"}qЅ^-_*#"r+2%}A*=.ʔd@B=D]F}H@]3JR=T-a=A?\\vzZ%c&p lQ6}nhYu| x=P ^A>o M>>HioNӴ=h>:|N먞g8nn+~N𮔠^^ P(x.0N1 ޞunNo(ɉ &޹H &JJ.WOaoHLN.ΛE>}ک{?e,/Gn= X=5]ݐO??_ߤ_x?_fMO=:П/ڿ/Zԑ0-Ab_5 Z$ h DPB >QD-^ĘQF=~RH%MDRJC5o  TxTPEETRM q"F֭ dVXe͞EVZmݾU̚6q'Ч}pԩU̘q̈!+ e* ˙2\0tѠխ]1gsΞ?\8STZuBk @@*抪IcVt=SQ7/:C}5jo`EP; N@dAhh t˫70C *0h;#JϽT/ˢ/RQj ŋx<@Ҳn4dH%}I3M7*kC/s1HćTo;s(ߤs4 lI;K2SP]<>OE4;s8><rS!%U<S% TTU\5IrE2'W_Ϻ򶺴ҷ0EV1_9gHMuVksZ?eOUnq[D 7@VA\]Mq_lWp]uVTy,W-}^;նֆ!TsbM VBby2Y?kd*5U_@}Vc P"wve~^YpY$zUNzf^fV{!4ll;8K 96d6J0 B@啃螿oOVz+R9;Au4VC.o?;]97WkVqϦvvmچw6#eٟk-[ϗi'j+YM'|Vr=y:u'`ӷ~ݮw?^R7H}~c=ѯp}b'=ufd璗8[e =^@:ms;\w/~7ah? S""\RhMgpbyP>I{E tKLP@W3>pyYLх*L4l(;1{L47А|PP"͇@ !r$kK%qdINvғl'FҔ$\JAO-H Y9KZҖ%-tJ^'f09Lb3RH^6S0"B49MjVӚf6Mnvӛg89NrӜDg:չNvӝg<9OzӞD'28ҙ:PԠEhBP6wTЧtLTdhF5QvԣiHE:RԤ'EiJURԥ/iLe:SԦ7iNuSԧ.\ÉvOT6OjT:UVժWjVA$)Q*DgEkZպVխok\*R.R XԖ:Bsl`;XְEZU.Өzu&_Y&ֲlf5Yvv(JQ6S+#YֵֶmleVJԱ+-/O;ET·uD"ZR!)rij\*Wo}nF麴Jl]5cs{݊@oz h`|Xʝf7u+7o|7x50#]&=0 .غ/}ka s#abT&b~`%xMYސ5q^68*/J w BuH>2I e>?Ʋeje-Ϸ(|%K83Ŭ,͊.<<>R@1H[>ͬb#{u 5]4mGfFkю.a%43jLw9ɕuYj*C"I+7`^G}e^Kx6vmhf']󛥭҄+Z /ng ot̓.6lvؿ>[]M?ִmkwG;n1Z-evCnww ^o.O@˿kS9ܓ=:H/4c>5@[= ;YS5BAA:W;B8K4S; ,:6,,T;K/7-<-\BIC$4ܩK;zSC<=D9/yYAB>;4EdDC?@AlJKLTG+@*Q$R4SDTTUdVtWXYZ[\]^_`a$b4cD\NO|rhijk$(N'OF0DMpq$ǍڒfF/rtwxs-n\GT}~GGG|?9̩DŽTȅd>* AG{H iGBȆȌȍ tÉ|ܩ 44ɓDIH/ p71 h)*I JJ*ʏ*ʣ<)+LɧLǼrIeə:B"KKDʝ4KJ˰ɌjK*ܨDK"˼$ʶtK\ˑ K:ʽ(KL ̾˺\̜tȄʫJ߀IZ1K˻<˛*̺,TLl)|*M\ʿ͞,l͕LTT4NܬM$NL\mT̫=3>3ޤj ]!3lm^oR碎*ll6醦iyYlfHlW~mw^`o!knnЖmFp~.p4r"Yqlmg?cҒ6鸞`a#tt)9".r12^ I`>q,G"/<&e_f&h3ONk\ΣAz:G=,r]*rf?GΆdCWE@/*&#gﵘILGp?tMPQOg*.>m &|bVwWXYZ[\]צe>ia'b7v _HuguAtUhlJvjO mq'wGv w-vwwgs`bwx|w$kO)'xxwT7vQLr=s[)0B)E$у'jw1J*ċ+=4t35c l0];D/A22Z\2+CAZ5sW{OfkA zy`7f?)tw+4'dAO;C~|Q75EK 4w{T?g;T?}?3©}'G*>ҷ@;@G~{l;|sy_|o;AsHӾԯ D>[=#^{ڍkNc[fX[GZj 7`|6aI|!9YUH^q)"-҄??\s)ŔS#=nXPM-heIGz]([~U$Ft~MF9dH 8m("Z-G^&Ov&KXmpHmwzH.'Q#R7F#:]XA$hvehA|.tbZ}:YiV'{l*ᬭ饩^Z+(zQz,Jr4=:-B$wנvzjl|jfmDYin+WFWg&߿aolnYbK+ٽyq|[q G*{2)C! 8~8+8;8K>9[~9k9{9衋>:饛~:CU(G߱;Ͷ~;;; U̕_;髏Kk{3_VL??ڗhů #.1| #(A x+`8$ C(:CApp$|! c(̈́Fy] Um XDYm]7Z\#&1O)ؖH)Rq"6s~#IP#@l&ȠLSx/GPt"6)1dL|6d#"X^1QYԢXI\BTx%+\ TH8vk*ckL)'nRG/1XbS.mɱ* Sz؛Ze'`Nk,H@LfrS;9P WH{],0xI_ɘt@\ٙm]WHaH'>M"OnՓd`u6. BCy.Hb̕9MTjf+CjO:qD6-N Gu5ه B<勖ߺ%/u?r%bh%ʦ |5ӈ%2E着Y[Ǒ)@ A3iN(q g* #)TI?RcLT 'Y`Nev5N^$fuA*.;'|抣l*fZq"8n}i.JW1ϸOˈ`r:+nS~U.Lyc8b45W,U^^);vE5۶A W8S;6n27PpZ.IԘ\&V|ln2!<&XҨ|X̎yyڜY[Qcz~涣u.oKx4ng$޸F/m|aqm-ag&e;iCDBcm X\}.0[WE*'cL *g ɆNHȜöJ.9#ˌ={Qy~2iieIZrz1-j>5Qv=N߰S-kΒE5sUӯ]u-auu.u~6-iS־6msۋӴXE.-7ӭu~erÛf66} JK6m{?8‰#VgYu#.S|enݸ}j\ED8C. .<8:"g.9WrKRqļ>9f>RsZD%tG$ ;ڭu^}3}QVj]F}-:Ѕ-tWQ8/#+"@ZP,#;ϷHz:uBzH-|>:TSI8Ѝ~gþG5/;~Ҷ+۟>N{=XÉoaw/ZZie{ka k~&co8}$0 ֿ_^O gÿ́QEZV'\vtm 7`oGhVT5`=I )_}^)]9.d MIeاT2ݪV bJ ] ^ VMWU Ձr)Yv)L+V}Uj\j%Y% EVn%؎J R^GaIAPa2fY!m5eV}rV2\9lމؖ׋`V}iba;)5`}2&YhA_] L!-"#rޕ\="vD␴^Pa '*J 0N؄̢( X)^スRXIdpiX8~: nlE- 0VY#"u( X!E!!!4Q~!.INyX;tA<5_##?V?JLY! !: #Ş5T6-d !rcSZRYPa=VB]`W>٣$KnY֝ݽ%]LJn% %%_f%^HK &b&baf?EdN&eVe^&fffn&g6ZJ:揜hi&jjB|&&W 4&m֦m&nn&oo&pp'qq'r&r.'s6s>'tFtN'uVu^'vfgr&>ۼ%lNl& hy'zz'{{'|Ƨ|'}'w4u1V'4('(&.(6>ho'`Z#~Ez&96p (((ڦg1Vhh^?'lCOCn))*hnZZR6tifn)v~.i>擮Mf(hpl)qp)z)⦜"*hgjr*&⩉.*>ꑊwzx>)Ap2*4)[fg橪nupjujZf))m*{*瞎*cH$*6>kNR+^R)eOh舚яB@`)jΩfkBiJBg*ڦ>gj:BR,^l몶Rg'+.xg2k*fJ+jj+,Jk쵶jR+,϶lll,:l,2N,ZmVnmn"-؂,kҫ׮*֭-^쫾-rmZlrl,Ǻm6nV跊썒,A)x,."-㞮~.*nnn⪮nk.l..:.". ../R-jjNo.~.j..f6)Q.Afjp..-oJlBo..jn//.0'pߺFkWn/*BZ0pmĶb߮t/hRT(Qk)¬N; 7p3p"q,+n_Gcxxo˂/Ϯ0n,ߒ:/-qw:qqv1R11+)h$,q_opCm1'gqJ?1 {/۲pr q* 31 ۫22'r""ok~ZzjhL-?0* 8n3333z23(302s/s0=袲?.3>0W0,r #=1&?t434Sj5ۨHC@Dx,!JDZ-q4oLq2NJCc+Cô  cm-O 5OKm=suL2 C5L'usMmP?4>m>5Qtƭ5\/uF7g#Ss^G׵r/_7?#,5an~>2$ai!7v6{R6d_6fKswz^g6hh6i]st^' kk6lǶl6m׶m6nn6oo6pp7qq7r'r/7s7s?7tGt7Z^z6i*vo7wwwKŪYwga.y7zDxu[7|Ƿ|UuxiyϷ~7]7^o2~8k6MLb7܁'b"ރ_8#\v\z`YĞm(?#8^B]fpe&]_͆7~+Kک]ۅQҚ5!Qd9.QD}wYyסG"U^Q2MeP߷5zw$^ B&mG-͞q(ݍIҞ&c;ߥآ^5x9D]e>X$SR)& R&_V`S@tR=UM&W乺_6Sߛ {K!`++"HV%q&i:xć/3:YOXuu$`zOqXJ7bXSɡ`BydS ! ޻ I9*rAxY!y}wxMtC3${| v" bǡW_<=^:>+ŸOAeV;Xd]z :-ݷ7"e^ýVa ;w9|<|33sN ~g> 5|![zFc=L;.7=?cŋ{;w^k LQKM<ļ=d+D~A JB. <ԢX!_E<ٷodMVd9~=RQvx L=?@4k@ 0p‚2XaŃ<"E5~DMl!Ȗ#V%ƙ&?2"ː/[$ISeP7{ QaјdӟWQ^9Ԭ_jqȒ`ztiծl…k֦K]iջo_L_anQXoy 1gּsgϟA=t"NёYpkׯaǖ=pTڷqֽ[S޷m>xqǑ'W>aČ?,eױg׾};jլ?~x<Ǘ?>ṡCo8ʗ P ͻ0Ol!P )O: Q;03*PLQYlaQiqQy R!,#LR%l{nC<+R3/ S1,3LS5l!O?*;C0h? TA -CMTEmG!TI)K1TM9OA UTItNN:KP9sV@BakVmo)V)Uf%? iWy{ͷ^qE xu Wn!X[~O_;θ3*8aI.QNY|)PTX-SCpς`FV8[7h?hK8镙niD[&CeϝYf=]4`^ѣA4m@f!}PV>3lNPkF|pG٩͵raEmJɞYƛKOKMgoq'ZwhVswx%-Z@g]pM۱p7~i\'w ^w׹iNX㭐[ɳ0ּ`YƳD- &4! }B Ox!AXD o|!T!RE0bBwB C!Vdbؾ0*m`KA ΈAdc5rb`Cl]sphC |WtWAюj$I/걍lb*V2u<,#)SO%+ޑ$)cIG9͋+K#SAyFrPk@6R[̟+M.uI 2&<%8;X*vo'Mmӓ7 LL"4&r bNX|Ŗt=ːL !`MNbT hթpzR$g:YJZKAgzKYo9%LS*Ei7}z= OJPRh 3fc]uMZtbYYlRRDb>]);ӖGjY%Eݵ, N%DحhkT\654?YeXV[E0l 9VMќM\0ͱ xWmR u>O'2P-MN3b2%m&!m%c% %q'2(RB/N' Ud)))2*r***2+RFRX)Ur,ɲ,,KtR:|r(2.r.d--r//R<24/ 031"0l0=0s2)2?17172?-3A34O 353/M0#\O4Y5]H4 D/g#TLnj$m7q^s8#$6e48/ƭZ/V=|5ގjn9:o06.e9q a#:!r= 1"S-Ҍ86eDnJn538ڣ 61@=qA<@uP 3q wS>9#9.پ*?=!'!.,R*|4=)qGIrHN !+ 2J5 .qCqC-DEfNv69C[Ay4t@#G7/mPK U@MYLs+ۏ-t3S7;oNT嬑M F T=pO=C50Ps?Tt/ ON}B?K=-tS D30=PN}UoU 0_u:; !+0OZ5XU+5QY=u[3/eL Tb[kXXtˆSpzX78W뉍 %x8'lXmFǸ@ǩ獙xvqbʏ mvxb(;8ÊX"emҦ*qev8;R9nP0Qj~XxI ,{g(ˁGɶ 8mJKII$晩YKiTjvk+s{y)Y-h l+ꭌDIn{EwؑX`YY }(K RIP(d+(ɖ!cQIVɦ(p ܊ร EzU#`Z!Kiڬ@ZL ^ jzǔjyzڠ KK+e-Z'㚏z3uڇwz3ɭ {Jj7zǠP"{Kˎ4Oj8}:'@ڨvu{̘ʲi:i3gzs*.zJئNu*:{*n;h=[c[ZzZ;횽+ZWɽ;W %:= {m۽93ǻˉ[\e F/<ޛ cy*e[;;| /9ʚ˹˗r+Bz(Ṩِ| hI˷ 9ˑk"ǜ(kq{Y YgJ\iؙٝtwwi٭ڱ/8iZ=}8=g$sٽ-mܫ=2s=/tGRmh砍BGA߳Qߧ㘳9-ECᥓys4>mBAau+B%7OV_rK>rk\gRusMp~R!5$ש<3NA[>BUsIWYEgV$-0S^[#tcP= d6~5D EAYeVGH 'BQw(I5E#;y^HH IۢJw64_U>UngtgvnuVB{\kuc/XOCoiu^mVwokOw4 1vKjUXv?!UydekKXQ-W~օ~OECX>6j6X\UUu_KeVYf,8„ xȐAN(!ĈfرHr$bȆ&StHiƴ(ʚ 7 4СD=4ҥL*/*oX&k^`BԱd˚=6ڵlۺ} atuJБ#EH["ܘΐ'yS$`;ov,YqN21f=5ܼ'h'ԊO.ƖQulҞ-x0þ= ,06cë:=ԝBJ*VYz+6˛/?]{F>\8<۴ 4g5}!W ~B\3 jXa|Wۅy8Vb*b]TUiwuUXc>]c ~9I8h[nEdv&"٤jQ eYdA%sG2\Fb%Z IHc g#!zg)(#5]hz|X%pəieiYfVpyz*ꨠۨfjhainZ+\%d+g衚V,F *b쭴j*Nk n;hh[VC.|ދƹo p3j:p>*+qٷ/oq Q 0:rAO. s2Ls6w<2>T MtFtJ/tN? uROMuV_uZou^ vbMvfvZ)|(rvߍwzw~ xlLxkKONy_yoyz袏Nz馟zꪯz뮿{N{~s,@1'Jo~ܼkAшLg ING? RkPIOR5|d?LI3Ħ%SV6K<&ӞRtK=ENf4TU:cj=U-=K ?2^rݪlQSQ% pҠLEΤU!SZG>Īc ȲN\*X1V#uH9JvmkGٮ㲘kYYyuho]9Ӟt}q.kfokeJujww w-yϋwm{ w}we9*[>w[ +x n a]8? 8ܘp-l[]^_Ace Yl| P;1BaM1CyT[r!cy,Y񖁜4q\4yo[I9t,f.9g2g<̃.vM%~1 '-s/wXƳgN{Є.2OL^vNubj\a6t^|ƱKdz*zhZ'ʮy hd ̿Lp{3aAںv cMB2;C7f=;Wdw +| ϵY 3k|qr21|,OF>Fyo A̗K]k/ϏoCiA @P  u8 u 8ȁ!(#H%h')+Ȃ-/1(3H5h79;ȃ3pxiA IMȄQ(S8`WY[ȅ]_a(cHehgikȆmoq(sHuhwy{?(h$怈 舏N( Xȉ艟(HhȊ芯(Hhȋ?h8iIh8HW(Hh׈٨ȍ([DSh(FhɘHpਏȏ)IQ  Sx hhIEX !)#I%i'x+YB萟yhi ΈA)CIEiG8+E 87mࣈ7?\?MȑIigikɖX MْB/SىUt'Ÿ 9 9Lp)>ّ@ٖ )ImBpPs I \Vn} ;EYɛxG qY@vf\9yceٛɝɝДũvɩYgH0i 8ɇ *Jj ʠ ʆ)` yX_(IgA2 C$Z dٟ+ʢ-/ 1*3J L9 ]X0 9 gʓGj ^'ڤ5 Q*SJUjW,L[A ̉>HTmo q*sJS*+iZYQѧf\WTHPhʨꨏ *Jjʩ꩟ *Jjz^Jk$c j ʫ꫿ *JjNJɪʬ *Jj׊٪ʭګ?ȫ `fA€ * Hjʯ +Kk ˰  +Kk[?H9 fA#K%k')+˲-/ 1+ XqОgzq3=? A+CKEkGIK˴MO Q+SKUkWY[˵VK [esugik/o q+sKukwy{˷} +Kk˸븏 +Kgw89 +,`˺뺯 +Kk˻뻿 +KkNjɫ˼u @k׋٫˽ +Kk狾髾˾ +Kk ѫl ,Ll  ,Ll !,#L%l'ѥl-/ 1,3L5l79;=? A,CLElGIKMO Q,SÓgWYv;elog-3.1.3-1/doc/contrib.html0000755000175000017500000000260012556103750014720 0ustar rogerroger ELOG FAQ
  ELOG Wishlist  

ELOG contributions can now be found in a separate logbook


elog-3.1.3-1/doc/config.html0000755000175000017500000042570513020302340014523 0ustar rogerroger ELOG - Syntax of elogd.cfg
ELOG - Syntax of elogd.cfg



Global and individual logbook options for an ELOG server

The configuration file elogd.cfg contains entries which define the structure of logbooks and the behaviour of elogd. The file has a simple ASCII format. Each logbook is defined by a [<name>] section where <name> is the name of the logbook. The [global] section is used for settings common to all logbooks. Each line contains a setting name, followed by an equal sign and the value for this setting. Lines starting with ";" are treated as comments.

Here is a simple example, which define two logbooks, "Linux" and "PC":

  • [global]
    SMTP host = mailsend.your.domain
    
    [Linux]
    Theme = default
    Comment = General linux tips and tricks
    Attributes = Author, Type, Category, Subject
    Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other
    Options Category = General, Hardware, Software, Network, Account, Other
    Options Author = Stefan, Linus, unknown
    Required Attributes = Author
    
    [PC]
    Comment = Database PC installations
    Attributes = Location, OS, Owner
    Options Location = Building1, Building2
    Options OS = Linux, Windows ME, Windows 2000
    Required Attributes = Location, Owner
    Email All = name@address, othername@otheraddress
    Use Mail Subject = Location
    


Global options

The notation of the following options is such that items enclosed by "<" and ">" should be replaced by a specific string. If a value contains blanks (like a complete sentence), it should not be enclosed in quotation marks.

If a setting has a number of possible options, they are shown in the form option1|option2|..., meaning that one of the options (without any vertical bar) should be used. The following options are specific to the [global] section:

  • Port = <port>
    Specifies the TCP port under which the server is listening. Default is 80. Can be superseeded via the '-p' command line flag.
  • SSL = <0 | 1>
    Turn on Secure Socket Layer transport. If SSL is on, one can connect via https://... to the elogd daemon. If the URL = directive is used, make sure to use https://... instead of http://... there. The ELOG distribution contains a simple self-signed certificate in the ssl subdirectory. One can replace this certificate and key with a real ceritficate to avoid browser pop-up windows warning about the self-signed certificate. The default for this option is 0.
  • Resource dir = <directory>
    Specifies the root directory for ELOG resources like help files, themes and icons. Can be overwritten with the -s flag when starting elogd. If not specified, use the directory where the configuration file elogd.cfg resides. Changing this option requires a restart of the elogd server.
  • Logbook dir = <directory>
    Specifies the root directory for logbooks. Can be overwritten with the -d flag when starting elogd. If not specified, use the directory where the configuration file elogd.cfg resides. Each logbook data is stored in a separate directory under this root directory specified by the Subdir option. Changing this option requires a restart of the elogd server. This directory also contains any password file and user HTML file.
  • Language = <name>
    The language setting determines the language of the elogd output. Not affected by this setting are the configuration file options and the commands specified with the optional Menu commands and List menu commands, which have to be specified in English and are translated automatically by elogd. The attribute names are unaffected by the language setting and have to be translated manually.

    If a language name is given (currently "german", "french", "spanish", "dutch", "brazilian" are supported out-of-the-box), the system searches for a file named eloglang.<name> containing string translations from English into that language. If you create a new translation file, please send it back to the author to be included in future distributions.

    The online help for elogd is contained in the file eloghelp_xx.html where xx are the first two letters of the language (like "en", "ge" and "fr"). For new languages, a new file of that type must be created as well.
  • charset = <name>
    Specifies the charset of the pages produced by elogd. Can be used to switch to Russian or Asian fonts.
  • Logbook Tabs = [0|1]
    This flag controls the display of "tabs" on top of the logbook page which allow to quickly switch between logbooks. Default is 1
  • Main Tab = <string>
    If this option is present, an additional first tab is displayed which takes you back to the main logbook selection page. The string is used for the contents of the tab.
  • Main Tab URL = <string>
    Normally the main tab brings one back to the logbook selection page. In case one wants to specify a different destination, such as a special web page outside of elog, one can use this statement to specify a full URL.
  • Welcome Title = <html code>
    This optional HTML code gets displayed in the title of the logbook selection page. It can contain images via <img src="welcome.gif">. These images must be stored in the resource directory or in the theme directory.

    The following line is an example Welcome Title:

    Welcome title = <img src="welcome.jpg"><p><font size=5 color=white>Welcome to our Elog</font>
    

    This displays an image and a text below.
  • Page title = <string>
    The string specified here is used for the title of individual logbook pages. It is also used by most browsers for bookmark names. <string> can contain substitutions like $<attribute> where <attribute> gets replaced by the attribute string from each message. The option Page title in the [global] section is used for the logbook selection page.
  • List page title = <string>
    The same for the summary or find result page. This may include substitutions as well, although attribute substitutions make no sense here, since the summary page may contain many messages with different attributes.
  • Selection page = <file>
    When this option is present, a user defined file is displayed instead of the logbook selection page. This file must be stored in the resource directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).

    It can be completely customized in order to contain logos etc. As a template, the standard selection page produced by elogd can be used.
  • Guest Selection page = <file>
    The same for installations which have a global password file. This means that the logbook selection page is also password protected. It might be however that some logbooks have guest access, in which case guest access to the selection page should be allowed as well (maybe with only a subset of the available logbooks). In that case this options can be used, to show a list of logbooks with guest access.
  • Protect Selection page = 0 | 1
    Normally, one can see the logbook selection page without having to log in. If one wants to require a login for the selection page, this switch can be set to 1. Default is 0. It is necessary to put the Password file = ... into the [global] section of the config file for this to work.
  • Expand Selection page = 0 | 1
    If this option is not present or set to one, the logbook selection page is expanded (all logbooks are shown if groups of logbooks are present). If this option is zero, only the group names are displayed. If one clicks on a group, its logbooks are shown. Using this option set to zero only makes sense if one has a large number of logbooks which would not fit on a single browser window, so collapsing makes sense. Default is 1.
  • SMTP host = <host.domain>
    This defines the SMTP host needed to send automatic email notifications. The host name you can get from your email program or your local system administrator.
  • SMTP username = <username>
    Some SMTP server require username/passowrd authentication. This option specifies the SMTP user name, while the option SMTP password can be created or modified via the -t switch when starting elogd. This is necessary since the password is encrypted. To set your SMPT password, enter on the command line:
    elogd -t <your password>
    
  • Logfile = <file>
    This option specifies a filename which logs all login/logout activities and successful user connections for logbooks with user level access. The the logging level (see below) is larger than 1, also read and write accesses can be logged.
  • Logging level = 1 | 2 | 3
    Specifies the logging level. The higher this value, the more information is logged. Default is 2:
    • 1: Log only logins and logouts
    • 2: Log also write accesses
    • 3: Log also read accesses
  • URL = <http[s]://host.domain[:port]/[subdir/]>
    If one of the three cases is true:
    • elogd runs with SSL enabled
    • elogd runs under a proxy
    • The automatic email notifications contains the wrong URL

    then the URL under which elogd is running has to be specified manually with this statement. The URL has to contain the port number if not the standard port 80 is used or 433 for SSL, and it has to contain the directory if used under a proxy like

    URL = http://host.domain:8080/ if running on port 8080
    URL = https://host.domain/ if SSL is enabled (SSL = 1)
    URL = http://host.domain/subdir/ if running under a proxy
    This URL is then used for any redirection. For example if one submits a new entry, the URL in the browser reads ...<logbook>/?cmd=Submit&..., containing all the attributes etc. After the submit this page gets redirected to ...<logbook>/<ID>, where <ID> is the ID of the new entry. For the redirection via the HTTP "Location:" statement, an absolute URL is required. Since elogd cannot figure out the complete URL under which it is running when accessed through an Apache proxy, this statement is necessary to tell elogd the complete URL.
  • Relative redirection = 0|1
    Under some circumstances, absolute redirection via a complete URL may not work. If you access elogd through two different ways simulataneously, for example directly and via a stunnel connection, a single absolute URL cannot be used, because one connection starts with http://, and the other with https://. Another case is when the elogd server has a dynamic IP address, which changes from time to time. Setting Relative redirection = 1, relative redirection is used. This uses the current URL from the browser, whatever it is, and only specifies the last part of the URL. It should noted however that relative redirections are not allowed in the HTTP standard, but most browsers support it anyhow. Problems have been reported with the Safari browser. So this option should only be used when it is really needed.
  • Usr = <name>
  • Grp = <name>
    The user and group to run the elogd daemon under when started by root.
  • Resolve host names = 0|1
    Resolve remote host names if set to 1. If set to 0, which is the default, only IP numbers are stored in any log file. If the hosts allow/deny options are used with host names, this setting must be set to 1. If turned on, the DNS server is contacted on each HTTP request to elog, which can slow down the server considerably for slow DNS servers.


Groups of logbooks

If installations have very many logbooks, it can be hard to navigate between them. To make things more structured, it is possible to build a hierarchy of logbooks. A logbook group can contain any number of logbooks as well as other logbook groups. The hierarchy is defined with the the option

Group <group name> = <Logbook1>, <Logbook2>, <other group>

in the [global] section of the configuration file.

To define following logbook hierarchy:

one would use following statements:

  • [global]
    Group Linux PCs = Red Hat, Debian, Mandrake
    Group Windows PCs = 98, ME, NT, XP, CE
    Group CE = 1.0, 2.0
    
The logbook tabs would then look like this:

Where the selected group or logbook becomes blue. The lower groups/logbooks change according to the selected upper group. Please note that a logbook can be contained in more than one group, but then it should not be the first logbook in those groups. The colors of the tabs and the title bar can be specified in the CSS file.


Top groups

Sometimes groups of logbooks should be completely separate. Imagine two groups of logbooks, one for the engineering department and one for the administration department. These groups should have different administrators, and the logbook tabs at the top of the screen should not show the logbooks from the other department. Prior to ELOG version 2.4.1, one had to run two elogd servers in parallel, listening under different ports. Since 2.4.1, one can achieve the same behaviour using Top groups. The configuration could look like this:

  • Group Linux PCs = Red Hat, Debian, Mandrake
    Group Windows PCs = 98, ME, NT, XP, CE
    Group CE = 1.0, 2.UL
    
    Top group engineering = Linux PCs, Windows PCs
    Top group administration = Employees, Purchases
    
    [global engineering]
    Password file = engineers.pwd
    Admin user = stefan
    
    [global administration]
    Password file = admin.pwd
    Admin user = bill
    
Note that there can be a [global] section for each top level group of logbooks. The rule is that a configuration setting in an individual logbook section overrides a setting in the [global <top group>] setting, which by itsel overrides a setting in the [global] section. This way one can define settings for all top level groups (such as the SMTP host) in the [global] section, and define different password files and administrators in the individual top level group sections.

If top groups are used, the root of the elogd server is not accessible any more. Presume that elogd is accessible normally under http://your.host:8080/, this URL becomes invalid for top groups, to avoid the case that one group can "see" the logbooks of the other groups. Instead, one has to append the top group name to the URL, such as http://your.host:8080/engineering or http://your.host:8080/administration. If someone does not know the top group name, one cannot see the list of logbooks there, so the groups become completely independent of each other. If this feature is not wanted, it can be disabled by setting Show top groups = 1.
Individual logbook options

For each logbook, there is a section with the logbook name in square brackets, so that each logbook can have different options. If an option is not present in a logbook section, then the system tries to locate that option in the [global] section. Thus if the following options are placed in the [global] section, they are defaults for all logbooks. If they are present in the [global] and in the logbook section, the logbook option is used.

Here are the available options, by broad categories:


General options
  • Data dir = <directory>
    This option is obsolete from version 2.2.5 on and should not be used. Use Subdir = ... instead.
  • Subdir = <directory>
    Each logbook has a separate directory where the logbook entries are stored, which is controlled by this statement. If the directory does not exist, it is created autmatically by the elogd program. The subdirectory is relative to the logbook root directory specified with the Logbook dir = ... option. So if Logbook dir = /usr/local/elog/logbooks and Subdir = Demo then the logbook data is stored in /user/local/elog/logbooks/Demo. If the Logbook dir = ... option is not specified, then logbooks is used. If the subdirectory starts with a "/" ("\" under Windows), then it is used as an absolute path independent of the logbook dir. To see which directories are used, start elogd with the "-v" flag.
  • Comment = <comment>
    The comment is displayed on the logbook selection list. The selection list is displayed if more than one logbook is defined on a host and no logbook is explicitly specified in the URL.
  • Theme = <theme>
    A theme determines which layout and colors are used for a logbook, similar to skins in other programs. The theme option points to a subdirectory under the "themes" directory which resides in the resource directory. It contains all files for that theme. The format of these files is described under the Themes section.
  • CSS = <filename>
    A given theme can contain several Cascading Style Sheets (CSS). This can be usefule if several logbooks use the same images and icons, but differnt colors. By default, the CSS elog.css is used. This statement adds an additional CSS, which can overwrite settings from elog.css. If different CSS'es should be used for different output media, this can be accomplished with a comma- separated list in the form CSS = <file1>&<media1>,<file2>&<media2>. This will then be translated into separate style sheet statements for the different media. For example a statement CSS = default.css&screen,print.css&print will result in the HTML statements:
      <link rel="stylesheet" type="text/css" href="default.css" media="screen">
      <link rel="stylesheet" type="text/css" href="print.css" media="print">
            
  • Title image = <string>
    HTML code for the icon in the upper right corner. By default, following code is used:

    <img border=0 src="elog.gif" alt="ELOG logo">

    This code can be replaced by <string> to display a different icon file, or to display some text. The icon image has to be present in the theme directory, which is usually <elog root>/themes/default.

  • Title image URL = <URL>
    The ELOG icon at the right upper corner usually points to the ELOG home page. This URL can be changed to point to a corporate page for example with this option. The icon can be changed by replacing the elog.gif icon in the theme directory. This option should only be used if the Title image option is not used.
  • Time format = <string>
    This option determines how the date and time of a logbook entry is displayed. The format of the string is the same as the C function strftime, so a string of %A, %B %d, %Y, %H:%M yields in a display of Thursday, November 15, 2001, 12:35 for example.
  • Time format <attribute> = <string>
    Same, but just for an individual attribute.
  • Date format = <string>
    This option determines how the date is displayed from attributes which are of type "date". The format of the string is the same as the C function strftime, so a string of %A, %B %d, %Y yields in a display of Thursday, November 15, 2001 for example.
  • Date format <attribute> = <string>
    Same, but just for an individual attribute.
  • Welcome Page = <file>
    By default, the list with the last twenty entries of a logbook is displayed when the logbook is selected. This can be overridden with this option, which causes a HTML file to be shown instead of the message list. This file can contain further links for new logbook messages of for logbook queries. Here is a simple example of such a file:
    <h1>Welcome to the test logbook</h1>
    <ul>
    <li><a href="?cmd=new">Enter</a> a new message
    <li><a href="?cmd=find">Search</a> the logbook
    </ul>
    
    The file must be present in the resource directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).
  • Start page = <command>
    This option can be used to display a different start page. command can be either 0?cmd=Last to display the last message, or any other ELog menu command in the form ?cmd=xxx. To start with the search page, one uses
    Start page = ?cmd=Find
    
    Please note that if another language than English is selected via the Language = xxx option, the commands have to be in that language as well (like "Start page = 0?cmd=Letzter" for German).
  • Submit Page = <file>
    This optional page can be displayed when a new message was submitted in a logbook. Here is an example:
    <h1>You successfully submitted a message</h1>
    <a href="?cmd=Back">Back</a> to the logbook<p>
    <a href="?cmd=New">Enter</a> another message
    
    The file must be present in the logbook directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).
  • Message comment = <comment>
    This optional comment is displayed on top of the text entry field when submitting a new message. It can contain a sentence like "Please enter your message here:".
  • Reply comment = <comment>
    This optional comment is displayed on top of the text entry field when replying to an exiting entry. It can contain a sentence like "Please enter your reply here:".
  • Attachment comment = <comment>
    This optional comment is displayed on top of the attachment sumbission section when entering a new message. It can contain a sentence like "Please upload your attachments here:".
  • Menu commands = <list>
    This option specifies the menu commands displayed on top of a single logbook page. For certain installations, it can be useful to disable some commands. Following commands are possible:
    • New - Enter new logbook entry
    • Edit - Edit current logbook entry
    • Delete - Delete current logbook entry
    • Reply - Submit a reply to current entry
    • Duplicate - Duplicate the current entry with the possibility to change some values
    • Download - Download a message in ASCII format
    • Find - Search entries in logbooks
    • Last day - Display entries from last day
    • Move to - Move entry to other logbook
    • Copy to - Copy entry to other logbook
    • Config - Edit elogd.cfg (if no "Password file" is given)
    • Config - Modify/Add user accounts (if "Password file" is given)
    • Admin - Edit elogd.cfg (if "Password file" is given)
    • Login - Login with user name and password (if "Password file" is given)
    • Import - Show CSV (comma-separated-values) import page
    • Logout - Logout current user (if "Password file" is given)
    • Help - General help


    The commands are always in English, independent of the language = ... setting, and are automatically translated into the specified language.

    If this option is not present, following default is used:
    Menu commands = List, New, Edit, Delete, Reply, Duplicate, Find, Config, Help
    

  • Copy to = <logbook list>
  • Move to = <logbook list>
    The commands Copy to and Move to make it possible to copy or move a logbook entry from one logbook to another. By default, all logbooks except the current logbook are shown as a possible destination. With the configurations options Copy to = <logbook list> and Move to = <logbook list> it is possible to specify a list of destination logbooks, separated by commata. This can make sense if only certain logbooks make sense as destinations. The flag Preserve IDs can be used to keep the entry ID in the destination logbook.
  • List Menu commands = <list>
    This option specifies the menu commands displayed on top of the listing page. Although all commands from a above are possible, only the commands New, Find, Select, Import, Config, Admin, Change password, Logout and Help make sense. The command Select can be used to select multiple messages for deletion or for moving to other logbooks. Once the Select command is clicked, check boxes appear in front of all entries which let the user select one or more entries. A new menu bar shows up with a Delete and optionally a Coyp to ... and Move to ... button, if these commands are present in the Menu commands list. Pressing one of these buttons deletes, copies or moves all selected logbook entries.
  • Guest Menu commands = <list>
    This option specifies the menu commands for guest logins. A guest login happens if a password file is used, but someone accesses the logbook for the first time, which means that no username/password is given. In that case the commands from the guest menu are displayed, which usually contain a subset of the normal commands. A typical scenario is a logbook which only has commands to read the logbook on the guest menu, but no commands to write/edit entries. Instead, the login command is given in the guest menu, with which one can login as a real user (username and password have to match those from the password file), which then allowes full access via the "Menu commands" list. A typical example for the menu settings for this scenario are:
    Menu commands = List, New, Edit, Reply, Duplicate, Find, Config, Logout, Help
    Guest menu commands = List, Find, Login, Help
    

    Note that the presence of this option opens user access also to the find result or elog listing page, which usually contains some config command. So it is useful to combine the Guest menu commands option with the following Guest List Menu commands option to restrict the access to the find result page as well.

  • Guest List Menu commands = <list>
    Same as Guest Menu commands but for the find result page.
  • Menu text = <file>
    If this option is present, and additional menu row above the message gets displayed with the contents of <file>. This file can contain arbitrary text, images or links. One example would be following text to go back to the listing page and display the next Routine entry and all Routine entries:
    <small>
    &nbsp;<a href="?cmd=next&type=Routine">Next Routine entry</a>&nbsp;|
    &nbsp;<a href="../?Type=Routine">All Routine entries</a>
    </small>
    
  • List Menu text = <file>
    The same for the list page. One example would be following text to switch between the different display modi:
  • Filter Menu text = <file>
    The same for the filter line in the list page.
  • Guest Display = <list>
    This option specifies which attributes are displayed on guest access. It is possible to display only a subset of all attributes for guest access, but the full list if someone is logged in (using the option "Password file"). The list consists of comma separated attributes, including the word text, if one wants to display the entry body text for guests.
    <small>
    &nbsp;<a href="?mode=summary">Summary</a>&nbsp;|
    &nbsp;<a href="?mode=full">Full</a>&nbsp;|
    &nbsp;<a href="?mode=threaded">Threaded</a>&nbsp;|
    </small>
    
  • Top text = <file> | <string>
    The text of this option gets displayed at the top of every Elog page. It can be a string or a filename which gets displayed. Might be useful to display company logos etc. If a file is specified, it must be present in the logbook directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).
  • Bottom text = <file> | <string>
    The text of this option gets displayed at the bottom of every Elog page instead of the little Elog home page link. It can be a string or a file. It can contain for example a link back to the main logbook selection page like:
    <center><a href="/">Main page</a></center>
    
    Or it can contain other useful links. If a file is specified, it must be present in the logbook directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).
  • Bottom text login = <file> | <string>
    The same as Bottom text but for the login page. This allows to display a different text at the bottom of the login page. It can also be used to execute some JavaScript.
  • Help URL = <URL>
    This URL is used for the Help button. By default, the file eloghelp_xx.html is returned with the contents of the help page. Edit this file directly to add site-specific help for all logbooks. Alternatively, use the Help URL option to specify different help pages for different logbooks. It can point to a site-specific help page via http://... or to a local file like file://c:/tmp/config.html, or to the name of an HTML file which must be present in the resource directory.
  • Message Width = <number>
    This value sets the number of characters per line of the main message entry field. The default value is 76 (78 for replies), and can be increased for installations which need a larger window size (like pasting log files etc.). If both Message Width and Message Height are not given, some JavaScript code is used which automatically resizes the message window dynamically to fit optimally into the browser window.
  • Message Height = <number>
    This value sets the number of lines of the main message entry field. The default value is 20, and can be changed for installations which need a different window size. If both Message Width and Message Height are not given, some JavaScript code is used which automatically resizes the message window dynamically to fit optimally into the browser window.
  • Admin textarea = <cols>,<rows>
    This defines the textarea size for the admin page. Default is 80,40.
  • Display mode = [full|summary|threaded]
    Default mode for search display. On the find entry form, the checkboxes are set accordingly. The "Last xxx" page uses this setting directly.
  • Entries per page = <number>
    Number of logbook entries displayed per page in a search result. The default is 20.
  • Restrict edit time = <hours>
    If this option is set, a new message can only be edited a certain number of hours after its creation. This can be useful if one wants to ensure that old entries cannot be modified. Hours can also be fractional, like 0.5 for 30 min.
  • Max content length = <bytes>
    This option restricts the size of attachments. When very large (>100MB) attachments are uploaded, the elogd server can be busy with this upload for a longer time and not respond to other requests during that time. To avoid this, the maximum size of attachments can be restricted. The server will then refuse to accept larger attachments. The default is 10485760 (= 10 MB). This option has to be placed into the [global] section and the elogd server has to be restarted after a change.
  • Fonts = <list>
    List of fonts (comma separated) to be shown in the font drop-down box of the entry edit form. Default is

    Fonts = Arial, Comic Sans MS, Courier New, Tahoma, Times New Roman, Verdana

    On Unix systems some of these fonts might not be installed, in which case they can be replaced by others like Serif, Sans-serif, Helvetica.
  • All display limit = <n>
    If a logbook contains many entries, the list gets divided into pages, with some page navigation for the next, previous, a specific page and all pages. If the logbook contains a large number of entries (>500), the display of all thes entries can take very long and might slow down the elogd server, especially if the entries are not displayed in "summary" mode but in "full" mode. Therefore the "All" link should not be used in the page navigation for large logbooks. The number of entries from when on the "All" link gets hidden can be specified with this number, the default value is 500.
  • Thumbnail size = <size>
    This option determines the default thumbnail size. To make the automatic generation of thumbnails working, the ImageMagick package has to be installed. Refer to the admin guide for installation instructions. The thumbnail size size gets passed to the -thumbnail option of the conversion. A value of 300 converts all pictures to thumbnails 300 pixels wide. A value of 300> converts all pictures to thumbnails 300 pixels wide if they are larger than 300 pixels initially, and leaves them untouched if they are smaller. A value of 10% converts all pictures to 10% of their original size. If the thumbnail size option is missing, the thumbnails will be created with the original image size, and can then be resized and rotated interactively with the image manipulation buttons:

    Setting Thumbnail size = 0 turns off the thumbnail creation.

  • Thumbnail options = <options>
    With this option one can pass additional parameters to the ImageMagick package. They are passes 1:1 to the convert program. Commonly used is the -density option to increase the image quality when converting from PDF or EPS files.


Attributes
  • Attributes = <list>
    Define a number of attributes for the logbook, separated by commata. A maximum of 100 attributes can be defined. Typical values are "Author", "Subject" or "Type". Following values are not allowed:

    • Text
    • Date
    • Encoding
    • Reply to
    • In reply to
    • Locked by
    • Attachment

    since these are used internally by elog.

  • Options <attribute> = <list>
    Usually, an text field is used for an attribute, where the user can fill in text of up to 100 characters. If instead a drop-down box with preset items is better for a given attribute, these items can be defined with this statement. Up to 100 items can be defined, separated by commas. To add an option including a comma, encose it in quotations marks like
    Options town = San Francisco, "Paris, Texas", "Paris, France"
    
  • Extendable options = <list>
    When using the Options <attribute> to specify a list of possible options, this list is fixed. Sometimes it is desirable to extend the list when a new entry in a logbook is made and a certain option is missing on the list. By adding the attribute name to the Extandable options list, a button appears next to the attribute in the message entry form which lets you add new options to the list. The elogd.cfg configuration file is then automatically updated. When a new logbook entry gets made, the new option automatically appears in the drop-down box for that attribute.
  • ROptions <attribute> = <list>
    Same as Options above, but using radio buttons instead of a drop-down box.
  • MOptions <attribute> = <list>
    This list allows for "Multiple Options", meaning that an attribute can have several values simultaneously. When entering an entry with MOptions, each value from the list is represented by a checkbox. Unlike with normal options, multiple checkboxes can be checked for an entry. The attribue value then becomes
    <value1> | <value2> | ...
    
    In the "find" page only one of these values can be specified, which is then treated as a substring in the search filter.
  • IOptions <attribute> = <list>
    This list specifies a set of icons for an attribute. Some icons are contained in the themes/default/icons directory which can be used here like
    Attributes = Author, Icon, Subject...
    IOptions Icon = icon1.gif, icon2.gif, icon3.gif, ...
    
    New icons are welcome and should be sent back to the author to be incorporated in the next version.
  • Comment <attribute> = <comment>
    Optional comment which is displayed below the attribute name in the entry form. Can be used to explain the attribute somehow.
  • Tooltip <attribute> = <comment>
    Same as Comment <attribute>, except that the comment gets displayed as a tooltip (tiny pup-up window) when the user moves the mouse cursor over the attribute name in the entry form.
  • Tooltip <attribute> <attribute option> = <comment>
    Same as Tooltip <attribute>, but for option values of a MOptions attribute. Using this option, a different tooltip can be shown above each check box of an optional value for an attribute. Please note that attributes or options with spaces should not be enclosed with quotes.
  • Icon comment <icon> = <comment>
    Icons may contain a comment, which is then used in email notifications instead of the icon file name. One has to add a separate icon comment for each icon file.
  • Options <attribute> = boolean
    If an attribute is marked "boolean" this way, a checkbox is displayed for this attribute.
  • Preset <attribute> = <string>
    This option uses a preset string for an attribute. The string can contain subsitutions like the ones described under the "Subst <attribute>" command. One possible application is to use the login name for the author field like:
    Preset Author = $long_name
    
    If the attribute should be locked at the Web submission, use the "Locked Attributes = ..." option. If a preset value is given for an attribute which has an options list, the preset value is selected in the drop down box by default.

  • Preset text = <string> or <file>
    This preset value is used for the main body text. It can be a string or a file, which must be present in the logbook directory. Alternatively, an absolute path can be used if the file name starts with a "/" (Unix) or "\" or "x:" (Windows).
  • Preset on edit <attribute> = <string>
    Same as Preset <attribute>, but evaluated when editing existing entries.
  • Preset on reply <attribute> = <string>
    Same as Preset <attribute>, but evaluated for replies.
  • Preset on first reply <attribute> = <string>
    While Preset on reply <attribute>, is evaluated for any replies, this one is only executed for the first reply to an entry. It can be useful for example to so do something like this:
    Preset on first reply Subject = Re: $Subject
    
    So the "Re:" only gets added once, and you don't get long chains of "Re: Re: Re: ....".
  • Preset on duplicate <attribute> = <string>
    Same as Preset <attribute>, but evaluated for duplicted entries.
  • Locked Attributes = <list>
    The attributes specified here cannot be modified when a new entry is submitted. This makes only sense for preset attributes.
  • Fixed Attributes Edit = <list>
    The attributes specified here cannot be modified when an existing entry is modified via the Edit button. This feature can be useful to preserve the original author of the message, when using the Preset Author = $long_name option as described above.
  • Fixed Attributes Reply = <list>
    The attributes specified here cannot be modified when an existing entry is replied on via the Reply button. This feature can be useful to preserve the original subject of a message for example.
  • Required Attributes = <list>
    The attributes specified here are required when a new entry is submitted. The attribute names are marked with * on the entry form.
  • Show Attributes = <list>
    Attributes present in this list are shown in the single entry page. Omitting attributes can make sense for attributes which are automatically derived from other attributes via the Change <attribute> command.
  • Show Attributes Edit = <list>
    The same as Show Attributes, but for the entry form.
  • Propagate Attributes = <list>
    With this option, changed in an attribute are autmatically propagated to all entries of a thread. This can be useful if one has an attribute "problem status" for example with the options "open", "under investigation", "fixed". A thread related to a specific problem can then have several replies. If the problem gets fixed, a new reply can be made with the attribute "problem status" being "fixed", and then the propagation causes all entries of this thread to become "fixed".
  • Page title = <string>
    The string specified here is used for the title of the web page. It is also used by most browsers for bookmark names. The string can contain substitutions as described unter the "Subst <attribute>" option.
  • Edit Page title = <string>
    The string specified here is used for the title of the entry form. It is also used by most browsers for bookmark names. The string can contain substitutions as described unter the "Subst <attribute>" option.
  • List display = <list>
    Specified the display and order of items in a message listing page or a search result page. In addition to all attributes, following items can be specified:

    • ID for the entry ID
    • Date for the entry date/time
    • Edit to display a column with an edit icon to directly edit and entry
    • Delete to display a column with a delete icon to directly delete and entry

    The restriction to certain attributes can be helpful if many attributes are defined in a logbook, which usually makes the table too big to fit in the browser. The default is
    List display = ID, Date, <all attributs>
    
    Which displays the message number, date, and all attributes. The display of the message body is controlled by the Display mode and Summary lines options. If a search goes over "all logbooks", an additional colums with the logbook name of each entry is added in front.
  • Guest List display = <list>
    Same as List display, but for guest access (user level access with password, but not logged in). Please see also Guest display. In addition to List display, one can optionally specify Text as an attribute here. Without that attribute, the summary text of the entry body is not shown. This makes it possible to show the text for registered users and hide it for guest access.
  • Link display = <list>
    Normally, each column in the display list contains a link to the individual entry. If this is not desired, the list of attributes with links can be restricted to only a subset with this option.
  • Thread display = <string>
    Optional way to specify the line contents in the threaded search result. Following substitutions are possible:
    • $<attribute>: The value of the attribute
    • $logbook: The name of the current logbook
    • $entry time: The message date and time, formatted via "Time format"
    • $message id: The message ID

    A typical example would be
    Thread display = $subject, posted by $author on $entry time
    
  • Thread icon = <attribute>
    If a logbook uses some icons for an attribute, these icons can be displayed in the search result page instead of the default icons contained in the themes directory.
  • RSS Title = <string>
    ELOG supports so-called RSS feeds. Once can subscribe to new logbook entries with RSS readers such as Mozilla Firefox. Once new entries are submitted to the logbook, the become visible in the subscripition. By default, all attributes of the last 15 logbook entries are used as the RSS title. With this option once can changed this behaviour. Following substitutions are possible:
    • $<attribute>: The value of the attribute
    • $logbook: The name of the current logbook
    • $entry time: The message date and time, formatted via "Time format"
    • $message id: The message ID

    A typical example would be
    RSS Title = $subject, posted by $author on $entry time
    
  • RSS Entries = <n>
    Number of entries to be shown in the RSS feed. Default is 15.
  • Subst <attribute> = <string>
    When submitting logbook entries, attribute values can be substituted by some text. This text can contain arbitrary fixed text and following values:
    • $<attribute>: The entered value of the attribute itself
    • $host: The host name where elogd is running
    • $remote_host: The host name of the host from with the entry was submitted
    • $short_name: The login name (if password file is present)
    • $long_name: The full name from the password file for the current user
    • $user_email: The email address from the password file for the current user
    • $logbook: The name of the current logbook
    • $date: The current date, formatted via "Date format"
    • $utcdate: The current UTC date (GMT) and time, formatted via "Date format"
    • $version: The version of the ELOG server in the form x.y.z
    • $revision: The Subversion reversion of the ELOG server as an integer number
    • $shell(<command>): <command> gets passed to the operating system shell and the result is taken for substitution.

    Following example use this feature to add the remote host name to the author:
    Subst Author = $author from $remote_host
    

    Following example substitutes an attribute with the contents of a file:
      Subst Info = $shell(cat /tmp/filename)              (Unix)
      Subst Info = $shell(type c:\tmp\filename)           (Windows)
    

    A special option are automatically generated tags, which are automatically incremented for each new message. This is achieved by putting #'s into the substitution string, which is used as a placeholder for the incrementing index. Each "#" stands for one digit, thus the statement
    Subst Number = XYZ-#####
    
    results in automatically created attributes "Number" of the form
    XYZ-00001
    XYZ-00002
    XYZ-00003
    
    and so on. In addition to the #'s one may specify format specifiers which are passed to the strftime function. This allows to create tags wich contain the current year, month and so on. Once the date part of the attribute changes, the index restarts from one. The statement
    Subst Number = XYZ-%Y-%b-###
    
    results in automatically created attributes "Number" of the form
    XYZ-2005-Oct-001
    XYZ-2005-Oct-002
    XYZ-2005-Oct-003

    and
    XYZ-2005-Nov-001
    XYZ-2005-Nov-002

    on the next month.
  • Remove on reply = <list>
    This option clears one or more (separated by commata) attribute values from a logbook entry when creating a reply to that entry. This can make sense for example for the author, since the author of a reply can be different from the original author.
  • Quote on reply = 0 | 1
    This flag controls if the original text is quoted in a reply. Default is 1
  • Reply string = <string>
    String used to mark original message lines. Default is "> ". Can be empty string ("") if no message marking is desired.
  • Subst on reply <attribute > = <string>
    Substitution of attributes for replies. This option can be used to replace the current subject with a "Re: <old subject>":
    Subst on reply subject = Re: $subject
    
    Note that this option works only for the first reply. So a reply-to-a-reply would still have Re: <old subject> and not Re: Re: <old subject>. If you want the substitution for all replies, please use Preset on reply instead.
  • Subst on edit <attribute > = <string>
    Substitution of attributes for edited messages. This option can be used to replace the author by the current author for example:
    Subst on edit author = $full_name
    
  • Quick filter = <list>
    Specifies list of comma separated attributes for which a drop-down filter is displayed in the search result page. By selecting a value from that drop-down box, only entries with that value are displayed. In addition to all attributes defined in the Attributes = list, the attribute Date and the option Subtext can be listed here. Using the Date filter, the last day, week, month and so on can be displayed. The Subtext filter works on the entry body text.
  • Format <attribute> = <flags>,<css_class_name>,<css_class_value>,<width>,<size>
    Optional formatting parameters for attributes. Following items can be defined in the comma-separated list:

    Values used for single message display page:

    • <flags> Sum of following flags:
      • 1: Display attribute in same line as previous attribute
      • 2: Display radio buttons or check boxes in separate lines (if applicable)
    • <css_class_name>,<css_class_value> Cascading Style Sheet class names used for cells containing attribute name or value, respectively. The classes must be defined in the style sheet file (usually themes/default/default.css).

    Values used for new message entry form:

    • <width> Width of the text entry field in characters
    • <size> Maximum number of characters allowed.

    Default is "0, attribname, attribvalue, 80, 500". Trailing parameters can be ommitted, so specifying for example only the flags is possible.

  • Type <attribute> = date | datetime | numeric | userlist | useremail | muserlist | museremail
    A normal attribute can contain strings of any type. With this option, attributes can be forced to be numeric or to be a date/time, or to consist of a list of all users from the password file. When new logbook entries are made, numeric attributes are checked to contain only digits. Note that JavaScript has to be enabled to do this.

    Attributes of type date are treated as a date. Their format for display can be controlled by the Date format option. Upon entry, drop-down boxes are displayed which let the user select the day, month and year. Alternatively, a pop-up date picker using a calendar can be displayed if JavaScript is enabled. Date attributes are saved internally as seconds since 1.1.1970, and can therefore be sorted propoerly by clicking on the header of a logbook entry list. On the find page, dates can be searched for via a start and end date. If date attributes are used in a quick filter (see above), a drop-down quick filter box is displayed which lets the user select "last day", "last week", "next week", and so on. The datetime type combines a date and time in HH:MM. The output of this combination is controlled by the Time format option.

    If the attribute type is userlist, a drop-down box is displayed which contains all user names from the current password file. This can be useful for example in a bug tracking system, where a new entry gets assigned to an individual. The type useremail is similar, just a list of email addresses of all registered users. This can be used to send email notification to assigned people by using this attribute in an Email all = <attribute> statement. The type muserlist and museremail are the same that userlist and useremail, except that several user names or user emails can be selected at once using check boxes.
  • Style <attribute> <value> = <style>
    Optional formatting of logbok entries in list mode. For some logbooks it might be useful to display different entries in a different color for example. To achieve this, a CSS style sheet can be attached to an entry based on the value of an attribute. If you have an attribute called importnace and you want to highlight all entries where importnace is severe for example, you can specify following style:
    Style importance severe = background-color:red
            
    For possible formattings, please refer to some CSS documentation. You can change the colors, font styles and sizes. The style is then valid for the whole row of that entry.

    For empty attributes one can specify "", such as
    Style importance "" = background-color:red
            
  • Cell Style <attribute> <value> = <style>
    Same as above, but only for a specific cell containing <attribute>. Following options
    Attributes = Author, Status
    Options Status = Fixed, Under Process, Not Fixed
    Cell Style Status Fixed  = background-color:green
    Cell Style Status Not Fixed  = background-color:red
    Cell Style Status Under Process  = background-color:yellow
            
    for example produce following listing:

  • Change <attribute> = <string>
    Instead of subsituting an attribute, the original attribute can be kept and just the output formatting can be changed. This can be very handy for constructing HTML links out of attributes. Presume that a company has a telephone book reachable under

    http://any.company.com/telbook.cgi?search=<name>
    
    where <name> has to be replaced by a search string. Now one can construct an automatic telephonebook lookup with following options:
    Attributes = Name, Telephone, ...
    Display Telephone = <a href="http://any.company.com/telbook.cgi?search=$Name">$Name's telephone number</a>
    
    The attribute Telephone is now automatically constructed from the attribute Name and consists of a link to the company's telephonebook. The advantage of this system is if the URL of the telephonebook changes one day, only one statement in the config file has to be changed, while otherways (like with the Subst Telephone = ... option) all entries would have to be changed manually.
  • List Change <attribute> = <string>
    Same option for the list display.
  • Execute new | edit | delete = <command>
    It is possible to execute a shell command on the server side after a new message has been submitted, edited or deleted. This feature has been used in the past for SMS notifications over a telephone system and for synchrnonization of the ELOG database with an external SQL database. The <command> can contain substitutions similar to the Subst command. In addition the list of all attachments can be referred to via $<attachments>. The text body of the entry can be referred to with $text. It should be noted that only the first 1500 characters of the text can be used, in order not to exceed the limits of the shell. Following (Unix) command writes a notification into some file:
    Execute new = echo "New message wiht ID $message id of type $type from $long_name on $remote_host" >> /tmp/elog.log
    

    It should be noted that this feature can impose a security problem. If someone can edit the elogd.cfg through the Config command of elogd, that person can put malicious code into elogd.cfg and execute it. This is even more severe if elogd runs with root privileges. To avoid such problems, the execute facility is disabled in elogd by default and has to be enabled explicitly with the "-x" command line flag. The administrator has to ensure then of course that only trusted people can edit elogd.cfg.
  • Last submission = <string>
    This option determines what gets displayed on the logbook selection page in the Last submission colum. The default string is $entry time by $author. If a logbook does not contain an author attribute, another string can be chosen.
  • ID display = <string>
    This option determines the display of the entry ID. In some applications, the entry ID can be used as a tag, containing more than just the ID number. For example
    • ID display = TAG-$message id
      
    would display the entry ID as "TAG-1","TAG-2", ... and so on.
  • Prepend on reply = <string>
    With this option a string can be placed on top of a reply. Using string substition, this can be useful for adding the author and the date of a reply, like

    Prepend on reply = Added $date by $long_name\n\n

    where "\n" causes a line break.
  • Append on reply = <string>
    Same as before, but gets added after the previous entry.
  • Prepend on edit = <string>
  • Append on edit = <string>
    Same as before, but for editing entries.
  • Sort Attributes = <list>
    For the list display, the entries are normally sorted by their ID. Alternatively, one can specify one or more (separated by commata) attributes, which are used for sorting. The first attribute in the list has the highest priority. Only if two entries have the same value in the first sort attribute, they are sorted according to the second sort attribute and so on. To the list of attributes one can add ID, Date and logbook, although ID makes only sense together with other attributes, since it is sorted as the primary key anyhow.

Conditional attributes

When entering data into a elog form, it might be helpful to change the options of the attributes depending on the value of other attributes. Let's assume you have a logbook containing entries for different computers with different operating systems. Your elogd.cfg file starts like that:

  • Attributes = PC Name, Operating System, Version
    Options Operating System = Linux, Windows
    

For the operating system version, you would like a list, but this list has to be different for Linux and Windows. This can be achieved with conditional attributes. Simply write following configuration:

  • Attributes = PC Name, Operating System, Version
    Options Operating System = Linux{1}, Windows{2}
    {1} Options Version = 2.2, 2.4, 2.6
    {2} Options Version = ME, 2k, NT, XP
    

If you enter a new entry into that logbook, the drop-down list for Version changes automatically depending on the Operating System. Note that you have to enable Java Script for this to work. Without Java Script, a separate button appears in the line of the Operating System which has to be pressed to make the Version list change.

The number {1} and {2} in the configuration file are called conditions. Depending on these conditions, certain other lines can be activated. So if the Operating System Linux is selected, condition {1} is true, which selects the line starting with {1} to select the options 2.2, 2.4, 2.6.

This technique offers various other possibilities, since any configuration option can be made conditional by adding a {<n>} in front of that line where <n> is an arbitrary number. One often used possibility is the definition of forms. Depending on an attribute, the configuration option Preset text = ... can be used to copy some pre-defined forms into the message body, which can then be filled out. Consider following example:

  • Attributes = Author, Type
    Options Type = Network check{1}, System check{2}
    
    {1} Preset text = network.txt
    {2} Preset text = system.txt
    

This causes two text files network.txt and system.txt to be copied into the message body when a new entry is made. The file network.txt could look like:

  • Routers checked:  [ ]
    DHCP checked:     [ ]
    Comment: ...
    

This works like a pre-defined form, the user puts X's between the "[ ]" when that item has been checked. Other possibilities are pre-defined shift sheets in environments where elog is uses as a shift logbook. The shift sheet could contain the names of the shift crew, some check-list for standard tasks etc.

Another use of conditional attributes is in conjunction with the option Message comment. Depending on some attribute values, different message comments can be displayed to tell the user what to enter exactly in the message body for that attribute value.

Show Attributes Edit = <list>
When using conditional attributes, it might be necessary to omit certain attributes under certain conditions, to make the input mask shorter and maybe change the order of the attributes. With this option, a subset of all attributes can be specified which get displayed on the single entry page in the same order as they are specified here. This option mainly makes sense when used with conditions, such as:

  • Attributes = PC Name, Operating System, Version, Distribution
    Options Operating System = Linux{1}, Windows{2}
    {1} Show Attributes Edit = Operating System, Distribution, PC Name
    {2} Show Attributes Edit = Operating System, PC Name, Version
    

The above statements cause the atrribute Version to be only visible when "Windows" is selected, and Distribution to be only visible when "Linux" is selected. If "Windows" is selected, the PC name is shown before the version.

Multiple conditions

It is possible to define conditions in more than one options list. The only requiremnt is that conditions are uniquie, meaning that a condition in one option list cannot be used in another list. This can easily be avoided by using numbers for one condition and letters for the other condition, like in the following example:

  • Attributes = PC Name, Operating System, Version, Location, Floor
    Options Operating System = Linux{1}, Windows{2}
    Options Location = Main Building{a}, New Building{b}, Old Building{c}
    {1} Options Version = 2.2, 2.4, 2.6
    {2} Options Version = ME, 2k, NT, XP
    {a} Options Floor = Ground, First, Second
    {b,c} Options Floor = Ground, First
    

It is possible to specify an OR of several conditions like in the case {b,c}. This is also possible over several conditions, like {1,a} would mean "The PC has Linux or is in the Main Building". To specify a AND between conditions, a "&" is used. The condition

  • {1&a} ...
    

specifies for example the condition "Linux AND Main Building". If several lines with condition combinations are true, the upper one is used.

Conditions in the list display

Conditional attributes are usually only used for change items in the entry form. It might however be desirable to have conditional attibutes also working in the list display (the page where several entries are shown on a single page). The value of one attribute can then for example change which other attributes gets displayed via the list display option. To enable the evaluation of conditional attributes for the list display, on uses the option

  • List conditions = 1

It should be noted that this option can cause a significant performance degradation if many conditional attributes are defines, so it should only be turned on when it is really needed.


Access control

Note: Starting with version 2.9.0, the password level access using the options Read password, Write password and Admin password is not supported any more. Please use the user level access as described below.

Password file

Access control is done on a user level with a password file. When a user logs in, a session ID is created and placed as a "cookie" in the browser. Using this cookie, the user can workin on the logbook until the cookie expires. For this it is necessary that cookies are enabled in the browser.

Following options can be used to control the behavior:

  • Password file = <file>
  • Login expiration = <hours>
  • Admin user = <user list>
  • Login user = <user list>

This file contains user names and passwords in XML format, such as

    
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!-- created by MXML on Tue Nov 07 08:15:51 2006 -->
    <list>
      <user>
        <name>stefan</name>
        <password encoding="SHA256">Ebx/a.9tFFQ/iUW3mU8GbnPpCVk74jFt56CmiJXVwdm</password>
        <full_name>Stefan Ritt</full_name>
        <last_logout>Tue Oct 17 12:59:47 2006</last_logout>
        <last_activity>Tue Nov 07 08:15:51 2006</last_activity>
        <email>stefan.ritt@psi.ch</email>
        <email_notify>
          <logbook>demo</logbook>
        </email_notify>
      </user>
      <user>
        <name>midas</name>
        <password encoding="SHA256">t56CmiJXVwdmEbx/a.9tFFQ/iUW3mU8GbnPpCVk74jF</password>
        <full_name>Midas User</full_name>
        <last_logout>0</last_logout>
        <last_activity>0</last_activity>
        <email>midas@psi.ch</email>
        <email_notify>
          <logbook>demo</logbook>
        </email_notify>
      </user>
    </list>
    
    

The passwords are encoded. New users can either be created by hitting Register as new user on the login page if Self register = 1 in the configuration file, or by the admin user in the Config page by pressing New user. The password file resides in the same directory as the logbooks. When a user is logged it, the entry for this user can be modified via the Config command.

To start a new password file, follow these steps:

  • Specify a password file name with Password file = <file> in the configuration file
  • Connect to the logbook. You will be presented with the new user page. Enter the user login name, full name, email and password, then click on the "Save" button.
  • Add Admin user = <user> into the configuration file, using your login name from above
  • If you now enter the "Config" page, you can add other users
  • Remove the self registration option if you like

The presence of a password file requires all users to "log in" using their name and password, except when a guest login is allowed via the "Guest menu commands" option. An additional advantage of this method is that the user name can be used as an attribute value for creating logbook entries. For example, the following line could be added to the configuration file to fill in the Author and the Email attributes with the current user name and email:

  • Attributes = Author, Email, ...
  • Subst Author = $long_name from $remote_host
    Subst Email = $user_email

Thus the author name is not user-input anymore, ensuring the entry always contains the actual user name. For a full listing of substitutions, see the "Subst <attrib>" option.

The user name and password are stored as cookies on the user side. The expiration is controlled by the Remember me checkbox during the login. If unchecked, the cookies expire after the current browser session. If checked, they expire after 31 days by default, which can be changed with the Login expiration option, giving the expiration time in hours. Setting this to 24 for example, makes the password expire after one day. If presistent cookies are not desired, the Login expiration option can be set to zero, in which case the Remember me checkbox is not displayed.

The Admin user = <user list> is a list of one or more user names, which have admin rights. They see a button Change elogd.cfg on the config page by which they can edit elogd.cfg through the web. They can also modify other users on the Config page, change their passwords or remove them. In addition, the admin user(s) can delete or edit entries from other users if Restrict edit = 1.

The Login user = <user list> is a list of users who can log in to a specific logbook. This option can be used with a global password file. If a Password file is present under the [global] section, the registered users in that password file can log in to all logbooks. It might be required that only certain users can log in to certain logbooks. This can be achieved with the Login user option, places in each individual logbook section in the configuration file. Only those users listed in this statement can log in to the logbook where the statement is defined. This method has the advantage over the option of definining individual password files for individual logbooks that only one central password file exists. So if a user changes her/his password, this becomes then valid for all logbooks. If there would be individual logbook password files, one would have to change the password in all logbooks individually.

  • Self register = 0|1|2|3|4

With this option it is possible for new users to self-register an user account. At the login page, a link is displayed "Register as a new user" which leads the user to a configuration page where one can enter the account name, full name and email address. A flag allows for automatic email notification on new entries on the logbook. These settings can later be changed with the Config menu command.

Setting this option to 0 disables self registration. With option 1, users can silently register, while setting it to 2 causes elogd to send an email notification to the admin user(s). The option 3 is used to only send an email notification to the admin users(s), which then can validate the account and commit it by hitting the URL given in the email notification. Setting this to 4 causes and email notification to be sent to the user, which then can validate the account herself/himself proving to have a working email account.

  • Allow password change = 0|1

Enables or disabled the ability for users to change their password. If disabled, the "Forgot password? link in the login page is ommitted as well. The admin user(s) can always change passwords.

  • Allow <command> = <user list>

Commands can be restricted to certain login names (separated by commas). For each command in the list defined with the "Menu commands" option, a list of user names can be specified, which are allowed to execute that command. If the allow option is not present, all users may execute that command by default.

  • Deny <command> = <user list>

Used to deny a certain command to a list of users. This can be used to deny a guest user to enter new messages or modify a message.

  • Hosts allow = <list>
  • Hosts deny = <list>

These two settings can be used to restrict the access to the logbook to certain computers. It is similar to the UNIX hosts.allow and hosts.deny files. The list can consist of individual host names or IP numbers, subnet masks like 123.213. (note the trailing '.') or .mit.edu, or the word All. The following rules are applied:

  • Access will be granted when a host matches a pattern in "hosts allow".
  • Otherwise, access will be denied when a host matches a pattern in "hosts deny".
  • Otherwise, access will be granted.

These rules are applied before any password is checked. To debug problems, start elogd with the "-v" flag, in which case the rule checking is printed on the screen.

The global option Logfile = <filename> can be specified to log all user login/logout activities plus all successful user connections.

If any of the password statements are in the [global] area of the configuration files, they are used for all logbooks. If one logs in at one logbook, access is automaticlly granted to all logbooks. If the password statements are in the individual logbook sections, one has to log in to each logbook separately.

Kerberos authentication

Starting from version 2.9.0, site authentication has been implemented in elog using the Kerberos authentication scheme. This widely used system is also used in MS Windows Domain Controllers, and can be used for site logins, meaning that the same credentials can be used on all computers of a site.

To use that authetication, Kerberos has to be installed on the server running the elogd daemon. Please read the Kerberos documentation how to do this or talk to your site administrator. There are packages for Linux, Windows and Mac OSX. If you compile the elogd program yourself, make sure to have the flag HAVE_KRB5> defined in the compilation process. To configure elogd to use Kerberos, use following options:

  • Authentication = <method(s)>
  • Kerberos Realm = <realm>

where <method(s)> can be File or Kerberos or both such as in Kerberos, File. If the authentication option contains Kerberos, the user credentials are authenticated using the default Kerberos Realm. This is typically obtained from the file c:\windows\krb5.ini (Windows) or /etc/krb5.conf (Linux). If another than the default realm should be used, this can be overwritten with the Kerberos Realm option.

When Kerberos authentication is used, the password file is still used to store additional user information such as the full name and the email address, but the authentication is done via the Kerberos server.

If both authentications Kerberos, File are enabled, the credentials are first authenticated via the Kerberos server, and - if not successful - via the password file. This allows combined elog installations with centralized and local elog accounts. If the Kerberos authentication was successful, the password in the password file is overwritten with the encrypted Kerberos password. This allows the system to work even if the Kerberos server is temporarily not accessible.

If the password is changed via the "Change Password" button on the config page, the system tries to change the password in the Kerberos database. On some installation it has been found that this does not work, in which case you have to change your password by other means (such as via the Windows login if you use a Windows Domain).

Beside the Kerberos authentication, elogd version 3.0 and higher can be configured to accept a authentication done by the webserver.

  • Authentication = Webserver

You can also combine it with other authentication methods as shown for Kerberos.

Elogd is then accepting the username set in the Request-Header "X-Forwarded-User" as already logged in.
To make this work, you need to configure the webserver correctly, as describe in the adminguide.

LDAP authentication

LDAP (lightweight Directory Access Protocol) has been implemented by vykozlov in a separate branch at https://github.com/vykozlov/elog-ldap. The code has been merged into this distribution on an as-is basis. Following info has copied from the link above:

To use LDAP authentication, do the following:

  • Enable LDAP authentication in the Makefile by setting USE_LDAP = 1
  • Change elogd.cf to contain LDAP authentication:
    • Authentication = LDAP
    • LDAP server = ldap://example.org:389
    • LDAP userbase = ou=People;dc=example,dc=org
    • LDAP login attribute = uid
    • LDAP register = 1
    The login attribute is from the DN (distinguished name), e.g. uid=user,ou=People,dc=example,dc=org. The register flag determines if LDAP users are automatically stored in the local password file, which is necessary for email notifications.

Please note that it is not possible to change a password in the LDAP database from within ELOG.


EMail notification
  • Email <attribute> <value> = <list>
  • Use Email Subject = <string>
  • Use Email Subject Edit = <string>
  • Use Email From = <string>
  • Default Email From = <string>
  • Use Email Heading = <string>
  • Use Email Heading Edit = <string>
  • Omit Email To = 0|1
  • Suppress Email to users = 0|1
  • Email attributes = <list>
  • Use Email URL = <URL>

To send email automatically when new entries are created in a logbook, a SMTP host = entry must be present in the [global] section of the configuration file. To submit an email based on an attribute value, use the statement Email <attribute> <value> = <list>. Whenever an entry is submitted where attribute is equal to value, an email notification is sent to the email addresses in list. Several mail addresses may be supplied, separated by commas. The mail addresses can contain attributes via the "$" substitution. If a logbook contains for example an attribute name which contains email names, then one can put $name@domain to form a valid email address.

Multiple Email xxx statements may occur in a configuration file. If either the attribute or the value contains one or more blanks the string must be enclosed with quotation marks, as in:

  • Email type "Normal routine" = ...

The statement Email All = <list> sends an email notification independent of the type and category. The Use Email Subject = <string> statement specifies which text is used as the email subject. The text can contain $<attribute> statements which are substituted with the current value of that attribute. For a full list of possible substitutions, see the "Subst <attribute>" option. The Use Email Heading = <string> specifies the text for the email heading line. Default is "A new entry has been submitted on [host]". The option Use Email Heading Edit = <string> works the same way for updated (edited) entries.

The option Use Email From = <string> is used for the "From:" field in the email. Since more and more email servers do not accept invalid "From:" addresses in order to reduce spam mail, it might be important that a "real" email address is used in the "From:" field. If Use Email From is present, it is always used. If not, the email address of the currently logged in user is used for the "From:" field. If no user is logged in, or the current user has not specified a email address in the password database, the setting of the option Default Email From is used for the "From:" field. Only if this option is not specified, a generic address ELOG@<hostname> is used, which might be rejected by the SMTP server however.

If the flag Omit Email To is set to 1, the To: field in the email is left empty instead set to the real email address of the recipients. This can be useful if one recipient should not see the email addresses of the other recipients.

The flag Suppress Email to users can be set to "1" if email should only be sent to the recipients of the Email <attribute> <value> = <list> statements but not to the users who have registerd for automatic email notification.

If one wants to send only some attributes but not all in an email notification, one can use the option Email attributes = <list>, where a subset of the attributes can be specified as well as their order.

The option Use Email URL = <URL> can be used to set the URL of the ELOG logbook used in email notifications. This can be useful if no URL = ... statement is used form some reason.


Flags
  • Show text = 0|1
    This flag controls if logbook entries contain a body text. If an installation only requires attributes, this flag can be set to 0. Default is 1.
  • Enable attachments = 0|1
    This flag controls the attachment submission at the bottom of a message entry page. If this flag is 0, the attachment section is not displayed. This might be useful for logbooks where attachments are not used. Default is 1.
  • Show attachments = 0|1
    This flag controls the display of attachments such as images on normal logbook pages. For logbooks with large images, this flag can be turned off, so that attachments are only displayed when they are clicked on. Default is 1.
  • Preview attachments = 0|1
    This flag controls the display of attachments in the edit form. If one one uploads an attachment, but has not yet submitted the entry, the uploaded attachments are shown at the bottom if this flag is 1. Only ASCII files and images are shown of course. Default is 1.
  • Summary lines = x
    This specifies the number of text lines displayed in a summary page. Zero displays no text at all. The default is 3.
  • Summary line length = x
    This specifies the number of charactes of the summary lines. After this number of charactes, a line break is inserted in long lines to keep the column width not too wide. The default is 40.
  • Attachment lines = x
    This specifies the number of text lines displayed for ASCII attachments. For long ASCII attachments, it can be useful to only display the first few lines not to make the HTML page too long. The default is 300.
  • Reverse sort = 0|1
    If this flag is 1, all listing pages (the default page view, the result of a search query and the result of the "Last day" query) is sorted in reverse order (newest entry down to oldest). The checkbox Sort in reverse order on the search form gets checked by default, too. Sorting in reverse order can make sense if there are many pages of entries, but the ones entered last should be displayed on the first page. Default is 0.
  • Search all logbooks = 0|1|2
    If this flag is 1 or 2, the search form displays the button "Search all logbooks". If the flag is 2, the button is checked by default. Setting this flag to 0 hides this button. It might be necessary to do this for public logbooks if there are also protected logbooks. Otherwise the search result would also display entries from the protected logbooks. The default is 1.
  • Enable browsing = 0|1
    If this flag is 1, browsing (hitting the next/previous button) is enabled. For some rare occasions it might be necessary to disable browsing. Default is 1.
  • Filtered browsing = 0|1
    If this flag is 1, browsing (hitting the next/previous button) can be filtered by individual attributes. If the checkbox next to an attribute is checked, only messages with the same attribute value are displayed. Default is 0.
  • Default encoding = 0|1|2
    This specifies the default encoding for new entries. For installations where entries are normally submitted as plain text, the default can be set to 1. Set to 0 for ELCode encoding, to 2 for HTML encoding. The default is 2, which activates the built in FCKeditor automatically for new installations. If this editor is not wanted or people are concerned about cross site scripting, the default encoding should be set to 0 or 1.
  • Allowed encoding = <n>
    Allowed encoding options. <n> can be the sum of following flags:
    • 1 : Plain
    • 2 : ELCode encoding
    • 4 : HTML encoding
    To allow plain and HTML encoding for example, set <n> to 5. Default is 7. Note that allowing HTML encoding may cause some security risk, since an elog entry may contain malicious scripting code. It should therefor only be allowed for installations where it is really needed and with no public write access.
  • Allow HTML = 0|1
    This flag allows or denys the usage of HTML in attributes. Note that allowing HTML encoding may cause some security risk, since an elog entry may contain malicious scripting code. It should therefor only be allowed for installations where it is really needed and with no public write access. The default value is 0.
  • Suppress default = 0|1|2|3
    This specifies the default state of the "Suppress Email notification" button on the new message entry form. For installations where normally an email notification is not necessary, the default can be set to 1. If an important entry is entered, users can then uncheck the suppress box. If this value is set to 2 , the suppress box is not displayed at all, so that an email notification is always produced. If this value is set to 3, the email notification is always suppressed. The default is 0.
  • Suppress Email on edit = 0|1|2|3
    This is the same as Suppress default, but just for edited entries. The default is 0.
  • Resubmit default = 0|1|2
    This specifies the default state of the "Resubmit as new entry" button on the edit message entry from. If this button is checked, the current message is removed from its current position in the database and submitted as a new message. This can for example be useful for applications where users want to see which records have been updated recently. If this value is set to 2, the resubmit box is not displayed at all. The default is 0.
  • Resubmit replies = 0|1
    If this flag is set to 1 and an entry is resubmited as a new entry and this entry has replies, all replies of this entry are resubmittes as new entries as well. The default is 0.
  • Display Email recipients = 0|1
    If this flag is 1, the email recipients are displayed when a logbook entry is entered which produces an email notification. Setting this flag to 0 suppresses this display, in case users need not see that email is being sent and to whom. The default is 1.
  • Email Format = <n>
    Specifies what is sent in an email notification. <n> is the sum of following flags:
    • 1 : Send heading line "A new entry has been submitted..."
    • 2 : Send attributes
    • 4 : Send URL of logbook entry
    • 8 : Send message body
    • 16: Send optional attachments as email attachments
    • 32: Send logbook name
    • 64: Send names of optional attachments
    So to send for example only the attributes and the URL, set <n> to 6. Default is 63 (send everything).
  • Email Encoding = <n>
    Specifies in which encoding an email is sent. <n> is the sum of following flags:
    • 1 : Plain text
    • 2 : HTML in the form of the plain text, but with ELCode interpreted
    • 4 : Full HTML page as shown in elog
    So to send email in plain text and full HTML, set <n> to 5. Some email clients have the possibility then to switch from one view to the other. Default is 2.
  • Back to main = 0|1
    If this flag is 1, the "List" button takes you back to the logbook selection page instead to the last entry of the current logbook. The default is 0.
  • Logout to main = 0|1
    If this flag is 1, the "Logout" operation takes you back to the logbook selection page instead to the login page. The default is 0.
  • Logout to URL = <URL>
    If this URL is set, the "Logout" operation takes you to a specific web page specified in the URL.
  • List after submit = 0|1
    If this flag is 1, the list page is shown after the submission of a new entry. If this flag is 0, the entry just submitted is shown. The default is 0.
  • Restrict edit = 0|1
    If this flag is 1, users can only edit their own messages. The system checks automatically if the currently logged in user matches the user supplied in an author attribute via the "Preset xxxx" option. The default is 0.
  • Expand default = 0|1|2|3
    This setting determines how messages are displayed in threaded mode. Following options are possible:
    • 0: Only message heads are displayed, no replies. A "+" indicates which message has one or more replies.
    • 1: Messages and replies are displayed, but no message body.
    • 2: Messages and replies are displayed together with the first few lines of the message body. The number of lines is controlled by the Summary lines option.
    • 3: Messages and replies are displayed together with the full message body.
    The default is 1.
  • Hidden = 0|1
    If this flag is 1, the logbook is not displayed in the initial logbook selection page and in the logbook tabs. This can be useful for logbooks which are only accessed for backup or archiving and would clutter up the logbook list for the normal user. To access hidden logbooks, one has to enter the logbook URL directly, or from a bookmark list. Default is 0.
  • Hide Comments = 0|1
    If this flag is 1, the logbook "Comment" is not displayed in the logbook selection page. Default is 0.
  • Use Lock = 0|1
    If this flag is 1, a logbook entry is locked when someone edits it (clicking the Edit command). A locked message gets displayed with a little red sign indicating that the message is currently edited by someone and should not be touched. This can be helpful in installations where several people can edit messages. Without locking, the second submission of an edited message overwrites the first submission without notice. Although the sign gets displayed, the message can still be edited (the lock can be "stolen"), but it's the user's response to avoid any conflict.

    Since elog cannot determine if someone keeps a message very long for editing or if only the browser got closed, the locking can show up even if the message is not kept for editing any more. In that case, the message has to be edited again and submitted, to remove the origial lock.

    Note that logbooks accessible from the internet usually get scanned by search engines. This can lead to situations where the Edit link of each message is "followed" by a bot, resulting in all messages being locked. In those cases locking has to be turned off.

    Since release 2.5.4, some Javascript code has been added to avoid unwanted locks. If someone edits an entry, but then goes away from that page or closes the browser without submitting the changes, a pop-up window appears asking the user to submit the changed entry. Although this works for most browsers in most cases, it could be that Javascript has been turned off in a browser, in which case the stale locks still might appear.

    Default for "Use Lock" is 0.
  • Show top groups = 0|1
    When using top groups, the root of the elogd server is not accessible any more, to avoid cases where one group can "see" the logbooks of the other groups. If this feature is unwanted, the flag Show top groups can be set to 1, in which case a list of available top groups is shown.
  • Fix text = 0|1
    With this options the main text body can be fixed, so that it cannot be changed via the Edit button later. This feature can be useful for set-ups where some attributed must be changed later, but the text body should be preserved. The default is 0.
  • Case sensitive search = 0|1
    This switch has two meanings. First, it defines the default state of the Case sensitive check box in the "Find" page. Second, it determines if the quick filters are case sensitive or not. The default is 0.
  • Mode commands = 0|1
    If this flag is missing or set to 1, the links "Full", "Summary" and "Threaded" are shown on the top of the listing page. If this flag is set to 0, these commands are hidden. This might be useful in logbooks where only one mode makes sense for example.
  • Suppress execute default = 0|1
    External scripts can be called with the Execute new/edit/delete options. If these options are enabled, a checkbox appears which lets the user suppress execution of the external script. The setting of this flag determines the default state of this checkbox. In logbooks where a script should only be ocasionally executed, it could make sense to set this flag to 1.
  • Preserve IDs = 0|1
    When a logbook entry is copied or moved to another logbook, it obtains a new entry ID in the destination logbook. This can cause problems if the logbook entries reference each other with their IDs. To keep the same ID in the destination logbook, this setting can be set to 1. If an entry with the same ID in the destination logbook exists already, it gets overwritten. Default for this setting is 0.
  • Collapse to last = 0|1
    In threaded view, the list of replies can be collapsed into a single entry. If this flag is 1, then the last entry of each thread is shown, otherwise the first thread is displayed. Default for this setting is 1.
  • Sort Attribute Options <attribute> = 0|1
    If this option is 1, the options for this attribute are sorted alphanumerically. This can be handy when locating options from long lists in drop-down boxes in quick filters for example. Default for this setting is 0.
  • Allow branching = 0|1
    With this option one can probihit "branching", which is that an entry gets more than one reply. When branching is prohibited, only linear threads are possible, which is one head entry, one reply to it, then one reply to the reply and so on. Default for this setting is 1.
  • Enable Smileys = 0|1
    When encoding an entry with ELCode, certain sequenes such as :-) get automatically converted into small "smiley" images. If this behavior is not wanted, it can be turned off with this option. The default for this setting is 1.
  • Refresh = <seconds>
    The elog listing page can be refreshed periodically with this option. If it is given, the page automatically reloads after <seconds>. This can be useful for logbooks where other people often post entries or where some entries are posted automatically (via the elog utility) and one wants to keep an eye on what's new. The default for this setting is 0 meaning no refresh.
  • Show last default = <days>
    In large logbooks, search operations can take quite long, blocking other users from accessing ELOG. On the Find page, one can restrict the search operation to a certain time period, like last day, last week, etc, which makes searching much faster, but restricts it to a certain time in the past. If one forgets however to enter anyting in the Show last drop-down box, then the search again can take quite long. This option pre-selects an option in the Show last drop-down box, so that the user does not have to think about selecting a certain time period. Following options are possible: 0, 1, 3, 7, 31, 92, 182, 364. "0" means an unrestricted search default.
  • Save drafts = 0|1
    Starting with version 3.1, ELOG supports auto saving. When text for a new entry is entered in the browser, it might get lost if the browser windows is closed before the entry has been submitted. In order to avoid this, entries can be saved as drafts, to be finished and submitted later. This can be achieved by clicking on the Save button or by the autosave feature (see next option). The Save drafts option turns this feature on or off. Default is 1.
  • Autosave = <seconds>
    Drafts can be sent to the server regularly after some editing (see previous option). This option determines the interval this is done. The default is 10 seconds after the last edit. Setting Autosave to zero disables the autosave functionality.
  • List drafts = 0|1
    By default, draft entries are shown in the list display in another browser when the entries are currently edited. This can be confusing to other users since the draft entries are frequently updated. To avoid this, this flag can be se to 0, which hides all draft entries in the list view. If they are hidden, the only way to come back to them is to hit the New menu item, in which case the system presents to the user a list of open draft messages to be continued.


Themes

Themes are layout and color schemes which determine the look and feel of a logbook (sometimes called "skins"). A theme consists of a set of images, which are used for the title banner and browse buttons, and a Cascading Style Scheet (CSS), which defines the colors, fonts and spacing of the ELOG pages.

Each theme resides in a separate subdirectory and is specified with the theme = <dir> option in the configuration file. Each theme can contain several CSSs, which can be selected with the CSS = <filename> option.

A default theme is contained in the distribution. If new themes are developed by users, they can be sent back to the author, to be included in future releases.

To change colors and fonts, the source of a ELOG page can be examined. All elements use CSS classes which are specified in the class="<name>" statements. These classes can be found in the .../themes/default/default.css file and changed accordingly. For a description of all options, please consult for example the W3C consortium.

If the CSS file is edited, most browsers require a "reload" to refresh the modified file. The elogd daemon does not have to be restarted after a change in the CSS file.

These two images display the same logbook entry using different themes:


Mirroring

Sometimes it can be useful to have the same ELOG logbook on two different computers. This might be the case if you travel with your laptop, but want to keep the logbooks from your desktop computer on the laptop. The problem is that if you add an entry on your laptop, the logbooks on the laptop and the desktop get out of sync. Merging only the ELOG database files does not help, since two entries could be made at the same day on the laptop and the desktop, which would lead to a conflict in that day's database file.

To solve this problem, mirroring was introduced from Version 2.5.0 on. This technology allows to synchronize one ELOG server with a number of other servers on a per-entry basis. No additional software is needed, only two elogd daemons talking to each other. The synchronization can be executed manually or periodically. If entries are changed/added/deleted on both sides, they get merged properly during synchronization. In order to minimize network traffic, each ELOG server calculates a MD5 checksum for each message, which gets exchanged during synchronization. Only when the MD5 checksum differs, entries are transferred.

To set-up mirroring, install two elogd servers on two machines (for testing purpose that also works on one machine with two elogd servers running on different ports). This can be done in two ways:

  1. Automatic configuration

    A complete elog server can be transferred to a secondary server using the clone command. Assume the existing server resides at http://master.your.domain/, and you want to mirror this server to a new location at http://slave.your.domain/. You do that by installing the elog package at the slave machine, and then executing on the slave:

    elogd -C http://master.your.domain
    
    or
    elogd -C https://master.your.domain
    
    for a remote server running under the SSL protocol.

    This command tells elogd to retrieve the configuration file, and optionally all logbook entries and password files from the master machine. Note that both servers must be version 2.5.4 or later. In case of trouble, you can turn on verbose messaging:

    elogd -v -C http://master.your.domain
    

    which could give some hints. If a logbook on the master server uses restricted access, you have to specify the admin user name and password. After everything has been transferred, you can start elogd in the normal way.

  2. Manual configuration

    First, copy the elogd.cfg file from the master to the slave server. Make sure that the files are identical (except the port setting if you run two servers on the same machine). Then, add the following configuration options. They should be put into the [global] section of the cofiguration file:

    • Mirror server = <URL-list>

      This statement specifies one or more mirror servers. Each URL must contain the host, port and possible subdirectory of the remote server, as if you would access it through your browser. A typical statement looks like:

      Mirror server = myhost.mydomain.org:8080, http://another.server.org/elog/, https://yet.another.org

      The URL should not contain any logbook name, this gets added automatically. The second example contains a subdirectory, which is typically used if the elogd daemon runs under an Apache proxy. The third example shows a server running under the SSL protocol.
    • Mirror config = 0 | 1

      Normally, only the logbook entries are mirrored. One can also mirror the contents of the elogd.cfg configuration file for individual logbooks. This can be turned on by setting this option to 1. Default is 0. Only the individual logbook section is mirrored, not the [global] section. Settings which are specific to one server, for example the URL = statement, should then be kept in the [global] section, so that they are not mirrored between different servers.

    • Mirror cron = Minute Hour Day Month Weekday

      This statement turns on periodic mirroring. The format is similar to the UNIX cron command. Each of the five values can either be an asterisk, which means all possible values, a comma-separated list or a range. It can be explained most easily with examples:

      Mirror cron= meaning
      0 3 * * * Every night at 3:00
      30 7 1,15 * * At 7:30 every 1st and 15th of a month
      0 12 10 10 * Once a year at 12:00 on my birthday
      0 7-18 * * 1-5 Once every hour from 7:00 to 18:00 from Monday to Friday


      Valid ranges for each value are:

      Minute 0-59
      Hour 0-23
      Day 1-31
      Month 1-12
      Weekday 0-6 with 0=Sunday, 1=Monday, etc.


      If mirroring is turned on, it is advisable to use the Logfile = option to turn on logging, so that one can inspect the logfile to see if the mirroring works correctly.

    • Mirror user = <name>

      If periodic mirroring is used via the Mirror cron = statement and the remote logbook uses user-level access, this statement specifies the user name which is used to log in to the remote logbook. The password is taken from the local password file and has to match the password in the remote password file, otherwise the access is not allowed. The user name is typical the login name of the administrator.

    • Mirror simulate = 0 | 1

      If one wants to try out mirroring without causing any harm, one can turn on this flag. During synchronization, entries are compared and necessary transfers are displayed, but not executed. Default is 0.

    • Mirror exclude = 0 | 1

      By default, all logbooks are mirrored. Individual logbooks might be excluded from mirroring by putting Mirror exclude = 1 in their individual logbook section of the configuration file (Not the [global] section). Default is 0.

If the statement Mirror server is present in the configuration file, a new menu option "Synchronize" appears on the elog page. Clicking on this menu options starts the synchronization:



On the left side one sees the entry ID's. Entries which are equal locally and remotely are not displayed. Here are the rules for synchronization:

  • If an entry has been modified locally but not remotely, it is submitted to the remote server.
  • If an entry has been modified remotely but not locally, it is retrieved from the remote server and saved locally.
  • If an entry has been modified remotely and locally since the last synchronization, an error is shown that the entries are conflicting. In that case one has to merge the entries manually and delete it on one side.
  • If an entry has been deleted locally, it is deleted remotely.
  • If an entry has been deleted remotely, it is deleted locally.
  • If a new entry exists locally, it is submitted.
  • If a new entry exists remotely, it is retrieved from the remote server and saved locally.
  • If new entries exist locally and remotely having the same entry ID, the local entries are changed to have higher entry IDs, then the remote ones are retrieved. Care should be taken if external links (such as elog:123) to the local entries are used, since they will point afterwards to the wrong entry.

By starting the synchronization on one elogd server, this server becomes the client and the other one becomes the server. This means that the local server actively compares the local and the remote messages, and updates one or the other if necessary. The other (remote) server does not need to have any mirror option in its configuration file, since the local server simulates a web browser to send and retrieve messages to the remote server. It is however allowed that the remote server also contains some mirror settings in the configuration file, this way the synchronization can be started from both servers.


elog-3.1.3-1/doc/ChangeLog0000755000175000017500000017571413076344200014160 0ustar rogerrogerVersion 3.1.3, released April 21st, 2017 ======================================== - Implemented rudimentary certificate check - Fixed crash with certain set of parameters and no password file is present - Added German UTF-8 file - Fixed missing CKEditor bug with "language = english" - Fixed crash when no username and email are supplied in the login page - Implemented “Restore” button - Implemented “List drafts” flag Version 3.1.2, released September 5th, 2016 =========================================== - Fixed two vulnerabilities - Added "sort by email" to config page - Updates some translations Version 3.1.1, released August 4th, 2015 ======================================== - Updated CKEditor to version 4.5.1 - Implemented "Date/Time format = ..." - Implemented "Use Email Subject Edit = ..." - Replaced "Back" by "Delete" button - Fixed many issues with Draft Messages - CSS file is now in *addition* to the default file elog.css - Added LDAP documentation - Added "Logout to URL = ..." option - Added description of Apacher server authentication Version 3.1.0, released April 1st, 2015 ======================================= - No fools' day joke! - Added Indonesian help files - Implemented "Show last default" option - Allow subsitutions like $short_name in "Title image" option - Implemented draft messages and auto save feature - Reworked Drag & Drop to work also for ELCode and plain encoding - Resize editor window with browser window Version 3.0.0, released December 23th, 2014 =========================================== - Replaced insecure SSLv23 with TLSv1 method - Switched from SVN to GIT versioning - Implemented service under Mac OSC via launchd - Added LDAP support for authentication - Don’t overwrite certificated, themes and init scrip during ‘make install’ - Create one logbook subdirectory pear year - Switched to CKEditor with Drag & Drop Upload (courtesy of Dario Milicic) Version 2.9.2, released April 27th 2012 ======================================= - Implemented "Preset on edit" - Increased stack size under Windows - Increased MAX_PARAM - Added escape char possibility to "\#" for substitutions - Abort program start if password file is write protected - Show error "File not found" on "Preset text" only if text is a file - Fixed problem with CSV import and first two lines disappearing - Added RSS description - Remove cookies when user is logged out - Delete always messages together with their replies - Strip html only for html encoded entries - Added nowrap to all date/time attributes - Apply reverse sorting to all attribute types - Added "login user" check (needed for global password files) Version 2.9.1, released February 20th 2012 ========================================== - Improved broken link handling - Fixed problem with record splitting introduced in FF 10 - Added Xcode project file - Added extra parameter to mxml_parse_buffer - Do a "rsort" on date attributes if "Reverse sort = 1" - Fixed date substitution - Check for locked attributes on chkform() - Fixed missing user login time - Removed old -a -r -w password options from documentation - Set 'ufnm' cookie with full name, needed for shift check list - Fixed crash reported in elog forum entry #67084 - Changed printchar() to putchar(), since the first is not available under native OSX - Check for inactive account during login - Added check for content-length to be non-negative - Print warning if "preset text" file cannot be found - Implemented SVG previews through ImageMagick - Show Kerberos error number - added rmoval of pid file - Switched off Kerberos support by default - Use logged in user's email address for From: in email notifications - Fixed bug with "." replacements in emails - Fixed mangeling of <> in email notification - Fixed missing error if user name exists already Version 2.9.0, released March 9th 2011 ====================================== - Fixed corrupted config file under Ubuntu - Automatically log in after self registration - Fixed XSS issue with mail0 - Fixed problem if an attribute contains "?" - Fixed bug with SSL connection shutdown - Added 'Toggle all' for MOptions - Moved 'Search text also in attributes' to the right - Added find by time from Andreas Luedeke - Implemented Kerberos authentication - Implemented "Propagate attributes = ..." - fixed bug with importing CSV containing a text body - fixed preview of import data - added confirmation for "Back" button - Added vcxproj files - Fixed bug with "B" attribute and quick filter - Made two logoobks with same subdir work (again) - Implemented mirroring through SSL - Fixed bug with moving attachments with thumbnails Version 2.8.1, released December 23th 2010 ========================================== - Fixed bug with regexec, thanks to Christian Richter - Fixed bug with $attachments in shell command - Fixed problem with hitting "submit" twice - Fixed submission_locked in
\n"); /* Encoding radio buttons */ if (allowed_encoding < 1 || allowed_encoding > 7) { rsprintf ("

Invalid \"Allowed encoding\" in configuration file, value must be between 1 and 7

\n"); rsprintf("\n"); show_bottom_text(lbs); rsprintf("\r\n"); return; } if (allowed_encoding == 1) rsprintf("\n"); else if (allowed_encoding == 2) rsprintf("\n"); else if (allowed_encoding == 4) rsprintf("\n"); else { if (allowed_encoding == 4) rsprintf("\n"); else if (allowed_encoding == 2) rsprintf("\n"); else if (allowed_encoding == 1) rsprintf("\n"); else { rsprintf("%s: ", loc("Encoding")); if (allowed_encoding & 4) { if (enc_selected == 2) rsprintf (""); else rsprintf (""); rsprintf("\n"); } if (allowed_encoding & 2) { if (enc_selected == 0) rsprintf(""); else rsprintf (""); rsprintf ("\n"); } if (allowed_encoding & 1) { if (enc_selected == 1) rsprintf(""); else rsprintf (""); rsprintf("\n"); } } } rsprintf("
\n"); } } /* Suppress email check box */ if (message_id && bedit) getcfg(lbs->name, "Suppress Email on edit", str, sizeof(str)); else getcfg(lbs->name, "Suppress default", str, sizeof(str)); if (atoi(str) == 0) { rsprintf(""); rsprintf("\n", loc("Suppress Email notification")); } else if (atoi(str) == 1) { rsprintf(""); rsprintf("\n", loc("Suppress Email notification")); } else if (atoi(str) == 2) { rsprintf(""); } else if (atoi(str) == 3) { rsprintf(""); } /* Suppress execute shell check box */ if (!bedit && getcfg(lbs->name, "Execute new", str, sizeof(str))) { if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) { if (atoi(str) == 0) { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Suppress shell execution")); } else if (atoi(str) == 1) { rsprintf("        \n"); rsprintf (""); rsprintf("\n", loc("Suppress shell execution")); } } else { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Suppress shell execution")); } } if (bedit && getcfg(lbs->name, "Execute edit", str, sizeof(str))) { if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) { if (atoi(str) == 0) { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Suppress shell execution")); } else if (atoi(str) == 1) { rsprintf("        \n"); rsprintf (""); rsprintf("\n", loc("Suppress shell execution")); } } else { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Suppress shell execution")); } } /* Resubmit check box */ if (bedit && message_id) { if (getcfg(lbs->name, "Resubmit default", str, sizeof(str))) { if (atoi(str) == 0) { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Resubmit as new entry")); } else if (atoi(str) == 1) { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Resubmit as new entry")); } } else { rsprintf("        \n"); rsprintf(""); rsprintf("\n", loc("Resubmit as new entry")); } } rsprintf("\n"); for (i = 0; i < MAX_ATTACHMENTS; i++) if (!att[i][0]) { /* put first free attachment for show_uploader_finished() */ rsprintf("\n", i + 1); break; } index = 0; if (!getcfg(lbs->name, "Enable attachments", str, sizeof(str)) || atoi(str) > 0) { if (bedit || bduplicate || isparam("fa")) { /* show existing attachments */ for (index = 0; index < MAX_ATTACHMENTS; index++) if (att[index][0]) { rsprintf("%s %d:\n", loc("Attachment"), index + 1); sprintf(str, "attachment%d", index); rsprintf("\n"); thumb_ref[0] = 0; if (strlen(att[index]) < 14 || att[index][6] != '_' || att[index][13] != '_') { strencode2(str, att[index], sizeof(str)); rsprintf("Error: Invalid attachment \"%s\"
", str); } else { strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(att[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, att[index], sizeof(file_name)); display_inline = is_image(file_name) || is_ascii(file_name); if (chkext(file_name, ".ps") || chkext(file_name, ".pdf") || chkext(file_name, ".eps") || chkext(file_name, ".svg")) display_inline = 0; if ((chkext(file_name, ".htm") || chkext(file_name, ".html")) && is_full_html(file_name)) display_inline = 0; thumb_status = create_thumbnail(lbs, file_name); if (thumb_status) display_inline = 1; if (getcfg(lbs->name, "Preview attachments", str, sizeof(str)) && atoi(str) == 0) display_inline = 0; if (thumb_status && display_inline) { get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0); if (strrchr(thumb_name, DIR_SEPARATOR)) strlcpy(str, strrchr(thumb_name, DIR_SEPARATOR) + 1, sizeof(str)); else strlcpy(str, thumb_name, sizeof(str)); strlcpy(thumb_name, str, sizeof(thumb_name)); if (thumb_status == 2) strsubst(thumb_name, sizeof(thumb_name), "-0.png", ""); rsprintf("\n"); rsprintf("
\n"); sprintf(str, "im('att'+'%d','%s','%s','smaller');", index, thumb_name, att[index]); ricon("smaller", loc("Make smaller"), str); sprintf(str, "im('att'+'%d','%s','%s','original');", index, thumb_name, att[index]); ricon("original", loc("Original size"), str); sprintf(str, "im('att'+'%d','%s','%s','larger');", index, thumb_name, att[index]); ricon("larger", loc("Make larger"), str); rsprintf(" \n"); sprintf(str, "im('att'+'%d','%s','%s','rotleft');", index, thumb_name, att[index]); ricon("rotleft", loc("Rotate left"), str); sprintf(str, "im('att'+'%d','%s','%s','rotright');", index, thumb_name, att[index]); ricon("rotright", loc("Rotate right"), str); rsprintf(" \n"); sprintf(str, "deleteAtt('%d')", index); ricon("delatt", loc("Delete attachment"), str); rsprintf("  \n"); /* ImageMagick available, so get image size */ strencode2(str, att[index], sizeof(str)); rsprintf("%s \n", str + 14); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) sprintf(cmd, "%s -format '%%wx%%h' '%s[0]'", _identify_cmd, file_name); else sprintf(cmd, "%s -format '%%wx%%h' '%s'", _identify_cmd, file_name); #ifdef OS_WINNT for (i = 0; i < (int) strlen(cmd); i++) if (cmd[i] == '\'') cmd[i] = '\"'; #endif my_shell(cmd, str, sizeof(str)); if (atoi(str) > 0) rsprintf("%s: %s\n", loc("Original size"), str); rsprintf("
"); } else { rsprintf("%s\n", att[index] + 14); rsprintf("  "); rsprintf("
\n"); } if (display_inline) { if (is_image(att[index]) || thumb_status) { if (thumb_status == 1) { get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0); strlcpy(str, att[index], sizeof(str)); str[13] = 0; if (strrchr(thumb_name, DIR_SEPARATOR)) strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14, sizeof(file_enc)); else strlcpy(file_enc, thumb_name + 14, sizeof(file_enc)); url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */ sprintf(ref, "%s/%s?thumb=1", str, file_enc); rsprintf("\"%s\"\n", ref, att[index] + 14, att[index] + 14, index); strlcpy(thumb_ref, ref, sizeof(thumb_ref)); } else if (thumb_status == 2) { for (i = 0;; i++) { get_thumb_name(file_name, thumb_name, sizeof(thumb_name), i); if (thumb_name[0]) { strlcpy(str, att[index], sizeof(str)); str[13] = 0; if (strrchr(thumb_name, DIR_SEPARATOR)) strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14, sizeof(file_enc)); else strlcpy(file_enc, thumb_name + 14, sizeof(file_enc)); url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */ sprintf(ref, "%s/%s?thumb=1", str, file_enc); rsprintf("\"%s\"\n", ref, att[index] + 14, att[index] + 14, index, i); strlcpy(thumb_ref, ref, sizeof(thumb_ref)); } else break; } } else { strlcpy(str, att[index], sizeof(str)); str[13] = 0; strcpy(file_enc, att[index] + 14); url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */ sprintf(ref, "%s/%s", str, file_enc); rsprintf("\"%s\"\n", ref, att[index] + 14, att[index] + 14, index); strlcpy(thumb_ref, ref, sizeof(thumb_ref)); } } else { if (is_ascii(file_name)) { if (!chkext(att[index], ".HTML")) rsprintf("
");

                           f = fopen(file_name, "rt");
                           n_lines = 0;
                           if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
                              max_n_lines = atoi(str);
                           else
                              max_n_lines = 300;

                           if (f != NULL) {
                              while (!feof(f)) {
                                 str[0] = 0;
                                 fgets(str, sizeof(str), f);

                                 if (n_lines < max_n_lines) {
                                    if (!chkext(att[index], ".HTML"))
                                       rsputs2(lbs, FALSE, str);
                                    else
                                       rsputs(str);
                                 }
                                 n_lines++;
                              }
                              fclose(f);
                           }

                           if (!chkext(att[index], ".HTML"))
                              rsprintf("
"); rsprintf("\n"); if (max_n_lines == 0) rsprintf("%d lines\n", n_lines); else if (n_lines > max_n_lines) rsprintf("... %d more lines ...\n", n_lines - max_n_lines); } } } if (thumb_status && display_inline) rsprintf("
\n"); } strencode2(str, att[index], sizeof(str)); if (thumb_ref[0]) rsprintf("\n", index, thumb_ref, str); else rsprintf("\n", index, str); rsprintf("\n"); } else break; } /* optional attachment comment */ if (getcfg(lbs->name, "Attachment comment", comment, sizeof(comment))) { rsprintf("\n"); rsputs(comment); rsputs("\n"); } /* field for add attachment */ if (att[MAX_ATTACHMENTS - 1][0]) { rsprintf("%s\n", loc("Maximum number of attachments reached")); rsprintf("\n"); } else { rsprintf("%s %d:\n", loc("Attachment"), index + 1); rsprintf("\n"); rsprintf("  \n", loc("Upload")); rsprintf("\n"); // print the holder for dropping attachments rsprintf("\n"); rsprintf("\n"); rsprintf("
%s
", loc("Drop attachments here...")); rsprintf(""); } } rsprintf("\n"); /*---- menu buttons again ----*/ rsprintf("\n"); rsprintf("\n", loc("Submit")); if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1) rsprintf("\n", loc("Save")); if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) rsprintf("\n", loc("Preview")); if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1) rsprintf("\n", loc("Delete")); else rsprintf("\n", loc("Back")); rsprintf("  %s 00:00:00", loc("Draft saved at")); rsprintf("\n\n"); rsprintf("\n"); show_bottom_text(lbs); rsprintf("\r\n"); /* rescan unconditional attributes */ if (_condition[0]) scan_attributes(lbs->name); xfree(text); } /*------------------------------------------------------------------*/ void show_find_form(LOGBOOK * lbs) { int i, j, year, month, day, flag; char str[NAME_LENGTH], mode[NAME_LENGTH], comment[NAME_LENGTH], option[NAME_LENGTH], login_name[256], full_name[256], user_email[256], enc_attr[NAME_LENGTH], whole_attr[NAME_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH]; /*---- header ----*/ show_standard_header(lbs, FALSE, loc("ELOG find"), NULL, FALSE, NULL, NULL, 0); /*---- title ----*/ show_standard_title(lbs, "", 0); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Search")); rsprintf("\n", loc("Reset Form")); rsprintf("\n", loc("Back")); rsprintf("\n"); rsprintf("\n\n"); /*---- evaluate conditional attributes ----*/ for (i = 0; i < lbs->n_attr; i++) attrib[i][0] = 0; /* get attributes from parameters */ attrib_from_param(lbs->n_attr, attrib); evaluate_conditions(lbs, attrib); /*---- entry form ----*/ rsprintf("\n"); rsprintf("
\n"); rsprintf("%s:
", loc("Mode")); if (!getcfg(lbs->name, "Display mode", mode, sizeof(mode))) strcpy(mode, "Full"); if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) { if (strieq(mode, "Full")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("Display full entries")); if (strieq(mode, "Summary")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("Summary only")); } else { if (strieq(mode, "Full") || strieq(mode, "Summary")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("Summary")); } if (strieq(mode, "Threaded")) rsprintf(""); else rsprintf(""); rsprintf("\n", loc("Display threads")); rsprintf("
%s:
", loc("Export to")); if (strieq(mode, "CSV1")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("CSV (\",\" separated)")); if (strieq(mode, "CSV2")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("CSV (\";\" separated)")); if (strieq(mode, "CSV3")) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("CSV (\";\" separated) + Text")); if (strieq(mode, "XML")) rsprintf(""); else rsprintf(""); rsprintf("
\n"); if (strieq(mode, "Raw")) rsprintf(""); else rsprintf(""); rsprintf("
\n"); rsprintf("
%s:
", loc("Options")); rsprintf(""); rsprintf("\n", loc("Show attachments")); rsprintf(""); rsprintf("\n", loc("Printable output")); /* put hidden reverse=0, which gets used if the reverse checkbox is unchecked and "reverse sort=1" is in elogd.cfg */ rsprintf("\n"); if (getcfg(lbs->name, "Reverse sort", str, sizeof(str)) && atoi(str) == 1) rsprintf(""); else rsprintf(""); rsprintf("\n", loc("Sort in reverse order")); /* count logbooks */ for (i = 0;; i++) { if (!enumgrp(i, str)) break; if (is_logbook(str)) continue; } if (i > 2) { if (!getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) || atoi(str) == 1 || atoi(str) == 2) { if (atoi(str) == 2) rsprintf(""); else rsprintf(""); rsprintf("
\n", loc("Search all logbooks")); } } rsprintf(loc("Display")); if (!getcfg(lbs->name, "Entries per page", str, sizeof(str))) strcpy(str, "20"); rsprintf(" ", str); rsprintf(loc("entries per page")); rsprintf("
\n"); rsprintf("%s:", loc("Filters")); sprintf(str, ""); strcat(str, loc("regular expressions")); strcat(str, ""); rsprintf("  ("); rsprintf(loc("Text fields are treated as %s"), str); rsprintf(")
"); /* table for two-column items */ rsprintf("\n"); rsprintf("", loc("Entry date")); rsprintf("\n"); for (i = 0; i < lbs->n_attr; i++) { rsprintf("", attr_list[i]); rsprintf("\n"); } rsprintf("", loc("Text")); rsprintf("
%s:\n"); rsprintf("\n"); rsprintf("
%s:", loc("Start")); year = month = day = 0; sprintf(str, "ya"); if (isparam(str)) year = atoi(getparam(str)); sprintf(str, "ma"); if (isparam(str)) month = atoi(getparam(str)); sprintf(str, "da"); if (isparam(str)) day = atoi(getparam(str)); show_date_selector(day, month, year, "a"); rsprintf("  "); show_time_selector(-1, -1, -1, "a"); rsprintf("  /  %s: ", loc("Show last")); if (getcfg(lbs->name, "Show last default", str, sizeof(str))) i = atoi(str); else i = 0; rsprintf(" \n"); rsprintf("
%s:", loc("End")); year = month = day = 0; sprintf(str, "yb"); if (isparam(str)) year = atoi(getparam(str)); sprintf(str, "mb"); if (isparam(str)) month = atoi(getparam(str)); sprintf(str, "db"); if (isparam(str)) day = atoi(getparam(str)); show_date_selector(day, month, year, "b"); rsprintf("  "); show_time_selector(-1, -1, -1, "b"); rsprintf("
%s:"); if (attr_options[i][0][0] == 0) { if (attr_flags[i] & (AF_DATE | AF_DATETIME)) { rsprintf("\n"); rsprintf("\n"); rsprintf("
%s:", loc("Start")); year = month = day = 0; sprintf(str, "y%da", i); if (isparam(str)) year = atoi(getparam(str)); sprintf(str, "m%da", i); if (isparam(str)) month = atoi(getparam(str)); sprintf(str, "d%da", i); if (isparam(str)) day = atoi(getparam(str)); sprintf(str, "%da", i); show_date_selector(day, month, year, str); if (attr_flags[i] & AF_DATETIME) { rsprintf("  "); show_time_selector(-1, -1, -1, str); } rsprintf("
%s:", loc("End")); year = month = day = 0; sprintf(str, "y%db", i); if (isparam(str)) year = atoi(getparam(str)); sprintf(str, "m%db", i); if (isparam(str)) month = atoi(getparam(str)); sprintf(str, "d%db", i); if (isparam(str)) day = atoi(getparam(str)); sprintf(str, "%db", i); show_date_selector(day, month, year, str); if (attr_flags[i] & AF_DATETIME) { rsprintf("  "); show_time_selector(-1, -1, -1, str); } rsprintf("
\n"); } else if (attr_flags[i] & AF_MUSERLIST) { for (j = 0;; j++) { if (!enum_user_line(lbs, j, login_name, sizeof(login_name))) break; get_user_line(lbs, login_name, NULL, full_name, NULL, NULL, NULL, NULL); sprintf(str, "%s_%d", attr_list[i], j); rsprintf("\n", str, str, full_name); rsprintf("\n", str, full_name); } } else if (attr_flags[i] & AF_MUSEREMAIL) { for (j = 0;; j++) { if (!enum_user_line(lbs, j, login_name, sizeof(login_name))) break; get_user_line(lbs, login_name, NULL, NULL, user_email, NULL, NULL, NULL); sprintf(str, "%s_%d", attr_list[i], j); rsprintf("\n", str, str, user_email); rsprintf("\n", str, user_email); } } else { rsprintf("\n", attr_list[i], attrib[i]); } } else { if (strieq(attr_options[i][0], "boolean")) { if (isparam(attr_list[i]) && *getparam(attr_list[i])) flag = atoi(getparam(attr_list[i])); else flag = -1; sprintf(str, "%s_0", attr_list[i]); rsprintf("\n"); if (flag == 0) rsprintf("\n", str, attr_list[i]); else rsprintf("\n", str, attr_list[i]); rsprintf("\n", str); rsprintf("\n"); sprintf(str, "%s_1", attr_list[i]); rsprintf("\n"); if (flag == 1) rsprintf("\n", str, attr_list[i]); else rsprintf("\n", str, attr_list[i]); rsprintf("\n", str); rsprintf("\n"); sprintf(str, "%s_2", attr_list[i]); rsprintf("\n"); if (flag == -1) rsprintf("\n", str, attr_list[i]); else rsprintf("\n", str, attr_list[i]); rsprintf("\n", str, loc("unspecified")); rsprintf("\n"); } /* display image for icon */ else if (attr_flags[i] & AF_ICON) { for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) { strcpy(option, attr_options[i][j]); if (strchr(option, '{')) *strchr(option, '{') = 0; sprintf(str, "Icon comment %s", option); getcfg(lbs->name, str, comment, sizeof(comment)); if (comment[0] == 0) strcpy(comment, option); rsprintf("", attr_list[i], comment); rsprintf("\"%s\"\n", option, comment, comment); } } /* display check boxes (or'ed) */ else if (attr_flags[i] & AF_MULTI) { for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) rsprintf("\n", str, str, attr_options[i][j]); else rsprintf("\n", str, str, attr_options[i][j]); rsprintf("\n", str, attr_options[i][j]); } } else { rsprintf("\n"); } } rsprintf("
%s:\n"); if (isparam("subtext")) strlcpy(str, getparam("subtext"), sizeof(str)); else str[0] = 0; rsprintf("\n", str); rsprintf("\n"); rsprintf("\n", loc("Search text also in attributes")); rsprintf("
\n"); if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str)) rsprintf("\n"); else rsprintf("\n"); rsprintf("\n", loc("Case sensitive")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ const char *find_section(const char *buf, const char *name) { const char *pstart; char *pstr, str[80]; do { if (*buf == '[') { pstart = buf; buf++; pstr = str; while (*buf && *buf != ']' && *buf != '\n' && *buf != '\r') *pstr++ = *buf++; *pstr = 0; if (strieq(str, name)) return pstart; } if (buf) buf = strchr(buf, '\n'); if (buf) buf++; if (buf && *buf == '\r') buf++; } while (buf); return NULL; } /*------------------------------------------------------------------*/ const char *find_next_section(const char *buf) { do { if (*buf == '[') return buf; if (buf) buf = strchr(buf, '\n'); if (buf) buf++; } while (buf); return NULL; } /*------------------------------------------------------------------*/ void load_config_section(char *section, char **buffer, char *error) { int fh, length; char *p; error[0] = 0; *buffer = NULL; fh = open(config_file, O_RDONLY | O_BINARY); if (fh < 0) { sprintf(error, "Cannot read configuration file \"%s\": %s", config_file, strerror(errno)); return; } length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); *buffer = xmalloc(length + 1); read(fh, *buffer, length); (*buffer)[length] = 0; close(fh); if (section == NULL) return; if ((p = (char *) find_section(*buffer, section)) != NULL) { if (strchr(p, ']')) { p = strchr(p, ']') + 1; while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++; } memmove(*buffer, p, length); if ((p = (char *) find_next_section(*buffer + 1)) != NULL) { *p = 0; /* strip trailing newlines */ if (p) { p--; while (p > *buffer && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) *p-- = 0; } } else { p = *buffer + strlen(*buffer) - 1; while (p > *buffer && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) *p-- = 0; } } } /*------------------------------------------------------------------*/ void show_admin_page(LOGBOOK * lbs, char *top_group) { int rows, cols; char *buffer, error_str[256]; char section[NAME_LENGTH], str[NAME_LENGTH], grp[NAME_LENGTH]; /*---- header ----*/ sprintf(str, "ELOG %s", loc("Admin")); show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, FALSE, 0); rsprintf("
\n"); /*---- title ----*/ show_standard_title(lbs, "", 0); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Save")); rsprintf("\n", loc("Cancel")); rsprintf("\n"); if (lbs->top_group[0] && (!top_group || strieq(top_group, "global"))) { if (is_admin_user(NULL, getparam("unm"))) { if (lbs->top_group[0]) { sprintf(str, "global %s", lbs->top_group); if (is_group(str)) { sprintf(grp, "[global %s]", lbs->top_group); sprintf(str, loc("Change %s"), grp); rsprintf("\n", str); } } } } if (is_group("global") && !strieq(top_group, "global")) { if (is_admin_user_global(getparam("unm"))) { sprintf(str, loc("Change %s"), "[global]"); rsprintf("\n", str); } } if (top_group) { if (strieq(top_group, "global")) { rsprintf("\n"); strcpy(str, "[global]"); } else { rsprintf("\n", top_group); sprintf(str, "[global %s]", top_group); } rsprintf("
%s
", str); } if (is_group("global") && !strieq(top_group, "global")) { if (is_admin_user(NULL, getparam("unm"))) { rsprintf("\n", loc("Delete this logbook")); rsprintf("\n", loc("Rename this logbook")); rsprintf("\n", loc("Create new logbook")); } } rsprintf("
\n\n"); /*---- entry form ----*/ rsprintf("\n"); /* extract section of current logbook */ if (top_group) { if (strieq(top_group, "global")) strcpy(section, "global"); else sprintf(section, "global %s", top_group); } else strcpy(section, lbs->name); load_config_section(section, &buffer, error_str); if (error_str[0]) { rsprintf("

%s

\n", error_str); rsprintf("\n"); rsprintf("\r\n"); return; } if (getcfg(section, "Admin textarea", str, sizeof(str)) && strchr(str, ',') != NULL) { cols = atoi(str); rows = atoi(strchr(str, ',') + 1); } else { cols = 120; rows = 30; } rsprintf("\n"); /* put link for config page */ rsprintf("
Syntax Help"); rsprintf("\n"); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Save")); rsprintf("\n", loc("Cancel")); rsprintf("\n\n"); rsprintf("\n\n"); show_bottom_text(lbs); rsprintf("
\r\n"); } /*------------------------------------------------------------------*/ void remove_crlf(char *buffer) { char *p; /* convert \r\n -> \n */ p = buffer; while ((p = strstr(p, "\r\n")) != NULL) { memmove(p, p + 1, strlen(p + 1) + 1); } } /*------------------------------------------------------------------*/ void adjust_crlf(char *buffer, int bufsize) { char *p; #ifdef OS_UNIX /* convert \r\n -> \n */ assert(bufsize); // avoid compiler warning about unused bufsize p = buffer; while ((p = strstr(p, "\r\n")) != NULL) { memmove(p, p + 1, strlen(p + 1) + 1); // strcpy() gives error under Ubuntu } #else char *tmpbuf; assert(bufsize); tmpbuf = xmalloc(bufsize); /* convert \n -> \r\n */ p = buffer; while ((p = strstr(p, "\n")) != NULL) { if (p > buffer && *(p - 1) == '\r') { p++; continue; } if ((int) strlen(buffer) + 2 >= bufsize) { xfree(tmpbuf); return; } strlcpy(tmpbuf, p, bufsize); *(p++) = '\r'; strlcpy(p, tmpbuf, bufsize - (p - buffer)); p++; } xfree(tmpbuf); #endif } /*------------------------------------------------------------------*/ int save_admin_config(char *section, char *buffer, char *error) { int fh, i, length; char *buf, *buf2, *p1, *p2; error[0] = 0; fh = open(config_file, O_RDWR | O_BINARY, 644); if (fh < 0) { sprintf(error, loc("Cannot open file %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); return 0; } /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = xmalloc(length + strlen(buffer) + 10); read(fh, buf, length); buf[length] = 0; /* find previous logbook config */ p1 = (char *) find_section(buf, section); p2 = (char *) find_next_section(p1 + 1); /* save tail */ buf2 = NULL; if (p2) buf2 = xstrdup(p2); /* combine old and new config */ sprintf(p1, "[%s]\r\n", section); strcat(p1, buffer); strcat(p1, "\r\n\r\n"); if (p2) { strlcat(p1, buf2, length + strlen(buffer) + 1); xfree(buf2); } adjust_crlf(buf, length + strlen(buffer) + 10); lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(error, loc("Cannot write to %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); return 1; } /*------------------------------------------------------------------*/ int change_config_line(LOGBOOK * lbs, char *option, char *old_value, char *new_value) { int fh, i, j, n, length, bufsize; char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3; char list[MAX_N_LIST][NAME_LENGTH], line[NAME_LENGTH]; fh = open(config_file, O_RDWR | O_BINARY, 644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return 0; } /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); bufsize = 2 * (length + strlen(new_value) + 10); buf = xmalloc(bufsize); read(fh, buf, length); buf[length] = 0; /* find location of option */ p1 = (char *) find_param(buf, lbs->name, option); if (p1 == NULL) return 0; p2 = strchr(p1, '='); if (p2 == 0) return 0; p2++; while (*p2 == ' ' || *p2 == '\t') p2++; strlcpy(line, p2, sizeof(line)); if (strchr(line, '\r')) *strchr(line, '\r') = 0; if (strchr(line, '\n')) *strchr(line, '\n') = 0; n = strbreak(line, list, MAX_N_LIST, ",", FALSE); /* save tail */ p3 = strchr(p2, '\n'); if (p3 && *(p3 - 1) == '\r') p3--; buf2 = NULL; if (p3) buf2 = xstrdup(p3); if (old_value[0]) { for (i = 0; i < n; i++) { if (strieq(old_value, list[i])) { if (new_value[0]) { /* rename value */ strcpy(list[i], new_value); } else { /* delete value */ for (j = i; j < n - 1; j++) strcpy(list[j], list[j + 1]); n--; } break; } } } else { if (n < MAX_N_LIST) strcpy(list[n++], new_value); } /* write new option list */ for (i = 0; i < n; i++) { strcpy(p2, list[i]); if (i < n - 1) strcat(p2, ", "); p2 += strlen(p2); } /* append tail */ if (buf2) { strlcat(p2, buf2, length + strlen(new_value) + 10); xfree(buf2); } adjust_crlf(buf, bufsize); lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); return 1; } /*------------------------------------------------------------------*/ int delete_logbook(LOGBOOK * lbs, char *error) { int fh, i, length; char *buf, *p1, *p2; error[0] = 0; fh = open(config_file, O_RDWR | O_BINARY, 644); if (fh < 0) { sprintf(error, loc("Cannot open file %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); return 0; } /* remove logbook name in groups */ change_logbook_in_group(lbs, ""); /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = xmalloc(length + 1); read(fh, buf, length); buf[length] = 0; /* find logbook config */ p1 = (char *) find_section(buf, lbs->name); p2 = (char *) find_next_section(p1 + 1); if (p2) { i = strlen(p2)+1; strlcpy(p1, p2, i); } else *p1 = 0; lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(error, loc("Cannot write to %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); el_index_logbooks(); return 1; } /*------------------------------------------------------------------*/ int rename_logbook(LOGBOOK * lbs, char *new_name) { int fh, i, length, bufsize; char *buf, *buf2, *p1, *p2; char str[256], lb_dir[256], old_dir[256], new_dir[256]; fh = open(config_file, O_RDWR | O_BINARY, 644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return 0; } /* rename logbook file */ if (!getcfg(lbs->name, "Subdir", str, sizeof(str))) { strlcpy(lb_dir, logbook_dir, sizeof(lb_dir)); if (lb_dir[strlen(lb_dir) - 1] != DIR_SEPARATOR) strlcat(lb_dir, DIR_SEPARATOR_STR, sizeof(lb_dir)); sprintf(old_dir, "%s%s", lb_dir, lbs->name); sprintf(new_dir, "%s%s", lb_dir, new_name); rename(old_dir, new_dir); } /* change logbook name in groups */ change_logbook_in_group(lbs, new_name); /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); bufsize = 2 * (length + strlen(new_name) + 10); buf = xmalloc(bufsize); read(fh, buf, length); buf[length] = 0; /* find logbook config */ p1 = (char *) find_section(buf, lbs->name); p2 = strchr(p1, ']'); if (p2 == NULL) { close(fh); xfree(buf); show_error(loc("Syntax error in config file")); return 0; } p2++; /* save tail */ buf2 = xstrdup(p2); /* replace logbook name */ sprintf(p1, "[%s]", new_name); strlcat(p1, buf2, length + strlen(new_name) + 1); xfree(buf2); adjust_crlf(buf, bufsize); lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); el_index_logbooks(); return 1; } /*------------------------------------------------------------------*/ int create_logbook(LOGBOOK * oldlbs, char *logbook, char *templ) { int fh, i, length, bufsize, templ_length; char *buf, *p1, *p2, str[256]; fh = open(config_file, O_RDWR | O_BINARY, 644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return 0; } /* add logbook to current group */ add_logbook_to_group(oldlbs, logbook); /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); bufsize = 2 * (2 * length + 1); buf = (char *) xmalloc(bufsize); read(fh, buf, length); buf[length] = 0; templ_length = 0; p2 = NULL; /* find template logbook */ if (templ[0]) { p1 = (char *) find_section(buf, templ); p2 = (char *) find_next_section(p1 + 1); } else p1 = NULL; if (p1) { p1 = strchr(p1, ']'); if (p1) while (*p1 == ']' || *p1 == '\r' || *p1 == '\n') p1++; if (p2) templ_length = p2 - p1; else templ_length = strlen(p1); } /* insert single blank line after last logbook */ p2 = buf + strlen(buf) - 1; while (p2 > buf && (*p2 == '\r' || *p2 == '\n' || *p2 == ' ' || *p2 == '\t')) { *p2 = 0; p2--; } if (p2 > buf) p2++; strcat(p2, "\r\n\r\n["); strcat(p2, logbook); strcat(p2, "]\r\n"); if (p1) { p2 = buf + strlen(buf); strncpy(p2, p1, templ_length); p2[templ_length] = 0; } adjust_crlf(buf, bufsize); lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); el_index_logbooks(); return 1; } /*------------------------------------------------------------------*/ int save_config(char *buffer, char *error) { int fh, i; char *buf; error[0] = 0; fh = open(config_file, O_RDWR | O_BINARY | O_CREAT, 0644); if (fh < 0) { sprintf(error, loc("Cannot open file %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); return 0; } buf = (char *) xmalloc(strlen(buffer) * 2); strlcpy(buf, buffer, strlen(buffer) * 2); adjust_crlf(buf, strlen(buffer) * 2); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(error, loc("Cannot write to %s"), config_file); strcat(error, ": "); strcat(error, strerror(errno)); close(fh); return 0; } TRUNCATE(fh); close(fh); /* force re-read of config file */ check_config_file(TRUE); return 1; } /*------------------------------------------------------------------*/ int save_user_config(LOGBOOK * lbs, char *user, BOOL new_user) { char file_name[256], str[256], *pl, user_enc[256], new_pwd[80], new_pwd2[80], smtp_host[256], email_addr[256], mail_from[256], mail_from_name[256], subject[256], mail_text[2000], str2[256], admin_user[80], url[256], error[2000], sid[32]; int i, self_register, code, first_user; PMXML_NODE node, subnode, npwd; /* if we outsourced the authentication, use external username */ getcfg(lbs->name, "Authentication", str, sizeof(str)); if (stristr(str, "Webserver")) { /* do not allow HTML in user name */ strencode2(user_enc, http_user, sizeof(user_enc)); } else { strencode2(user_enc, user, sizeof(user_enc)); } /* check for user name */ if (!isparam("new_user_name") || *getparam("new_user_name") == 0) { sprintf(str, loc("Please enter \"%s\""), loc("Login name")); show_error(str); return 0; } /* check for full name */ if (!isparam("new_full_name") || *getparam("new_full_name") == 0) { sprintf(str, loc("Please enter \"%s\""), loc("Full name")); show_error(str); return 0; } /* check for valid email address */ if (!isparam("new_user_email") || *getparam("new_user_email") == 0) { show_error(loc("Please specify a valid email address")); return 0; } strlcpy(str, getparam("new_user_email"), sizeof(str)); if (strchr(str, '@') == NULL || strchr(str, '.') == NULL) { show_error(loc("Please specify a valid email address")); return 0; } /* check for blank character in user name */ strlcpy(str, getparam("new_user_name"), sizeof(str)); if (strchr(str, ' ')) { show_error(loc("User name may not contain blanks")); return 0; } /* check for blank password if not external authentication */ if (isparam("newpwd")) { strlcpy(str, getparam("newpwd"), sizeof(str)); if (str[0] == 0) { show_error(loc("Empty password not allowed")); return 0; } if (strchr(str, ' ')) { show_error(loc("Password may not contain blanks")); return 0; } } /* check self register flag */ self_register = 0; if (getcfg(lbs->name, "Self register", str, sizeof(str))) self_register = atoi(str); /* check if passwords match */ new_pwd[0] = 0; if (isparam("newpwd") && isparam("newpwd2")) { strlcpy(new_pwd, getparam("newpwd"), sizeof(new_pwd)); strlcpy(new_pwd2, getparam("newpwd2"), sizeof(new_pwd2)); if (strcmp(new_pwd, new_pwd2) != 0) { show_error(loc("New passwords do not match, please retype")); return 0; } } /* check if first user */ first_user = !enum_user_line(lbs, 0, str, sizeof(str)); /* check if user exists */ if (new_user) { if (get_user_line(lbs, user_enc, NULL, NULL, NULL, NULL, NULL, NULL) == 1) { sprintf(str, "%s \"%s\" %s", loc("Login name"), user_enc, loc("exists already")); show_error(str); return 0; } } /* if register through selection page, use first logbook with same password file */ if (lbs == NULL) { getcfg(NULL, "password file", file_name, sizeof(file_name)); for (i = 0; lb_list[i].name[0]; i++) { getcfg(lb_list[i].name, "password file", str, sizeof(str)); if (strieq(file_name, str)) break; } if (lb_list[i].name[0] == 0) lbs = &lb_list[0]; else lbs = &lb_list[i]; } getcfg(lbs->name, "Password file", str, sizeof(str)); if (lbs->pwd_xml_tree == NULL) return 0; sprintf(str, "/list/user[name=%s]", user_enc); node = mxml_find_node(lbs->pwd_xml_tree, str); code = 0; if (new_user) { node = mxml_find_node(lbs->pwd_xml_tree, "/list"); if (!node) { show_error(loc("Error accessing password file")); return 0; } node = mxml_add_node(node, "user", NULL); if (isparam("new_user_name")) { strencode2(str, getparam("new_user_name"), sizeof(str)); mxml_add_node(node, "name", str); } do_crypt(new_pwd, str, sizeof(str)); npwd = mxml_add_node(node, "password", str); if (npwd) mxml_add_attribute(npwd, "encoding", "SHA256"); if (isparam("new_full_name")) { strencode2(str, getparam("new_full_name"), sizeof(str)); mxml_add_node(node, "full_name", str); } mxml_add_node(node, "last_logout", "0"); mxml_add_node(node, "last_activity", "0"); if (isparam("new_user_email")) mxml_add_node(node, "email", getparam("new_user_email")); if (!first_user && (self_register == 3 || self_register == 4)) { code = rand() + (rand() << 16); sprintf(str, "%d", code); mxml_add_node(node, "inactive", str); } else mxml_add_node(node, "inactive", "0"); } else { /* replace record */ if (isparam("new_user_name")) { strencode2(str, getparam("new_user_name"), sizeof(str)); mxml_replace_subvalue(node, "name", str); } if (new_pwd[0]) mxml_replace_subvalue(node, "password", new_pwd); if (isparam("new_full_name")) { strencode2(str, getparam("new_full_name"), sizeof(str)); mxml_replace_subvalue(node, "full_name", str); } if (isparam("new_user_email")) mxml_replace_subvalue(node, "email", getparam("new_user_email")); if (isparam("active")) mxml_replace_subvalue(node, "inactive", "0"); else mxml_replace_subvalue(node, "inactive", "1"); } subnode = mxml_find_node(node, "email_notify"); if (subnode) mxml_delete_node(subnode); mxml_add_node(node, "email_notify", NULL); subnode = mxml_find_node(node, "email_notify"); for (i = 0; lb_list[i].name[0]; i++) { sprintf(str, "sub_lb%d", i); if (isparam(str) && getparam(str) && atoi(getparam(str))) mxml_add_node(subnode, "logbook", lb_list[i].name); } if (get_password_file(lbs, file_name, sizeof(file_name))) mxml_write_tree(file_name, lbs->pwd_xml_tree); /* if requested, send notification email to user or admin user */ if (new_user && !first_user && (self_register == 2 || self_register == 3 || self_register == 4) && !isparam("admin")) { if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) { show_error(loc("No SMTP host defined in [global] section of configuration file")); return 0; } /* try to get URL from referer */ if (!getcfg("global", "URL", url, sizeof(url))) { if (referer[0]) strcpy(url, referer); else { if (elog_tcp_port == 80) { if (_ssl_flag) sprintf(url, "https://%s/", http_host); else sprintf(url, "http://%s/", http_host); } else { if (_ssl_flag) sprintf(url, "https://%s:%d/", http_host, elog_tcp_port); else sprintf(url, "http://%s:%d/", http_host, elog_tcp_port); } if (lbs) { strlcat(url, lbs->name_enc, sizeof(url)); strlcat(url, "/", sizeof(url)); } } } else { if (url[strlen(url) - 1] != '/') strlcat(url, "/", sizeof(url)); if (lbs) { strlcat(url, lbs->name_enc, sizeof(url)); strlcat(url, "/", sizeof(url)); } } retrieve_email_from(lbs, mail_from, mail_from_name, NULL); /* send email to new user */ if (self_register == 4) { if (lbs) sprintf(subject, loc("Account activation for ELOG logbook \"%s\""), lbs->name); else sprintf(subject, loc("Account activation for ELOG on host \"%s\""), host_name); if (!isparam("new_user_email")) { show_error("A valid email address necessary for this ELOG account"); return 0; } strlcpy(email_addr, getparam("new_user_email"), sizeof(email_addr)); mail_text[0] = 0; compose_email_header(lbs, subject, mail_from_name, email_addr, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0); sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n\r\n", loc("Please click the URL below to activate following ELOG account")); if (lbs) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"), lbs->name); else sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Host"), host_name); if (isparam("new_user_name")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Login name"), getparam("new_user_name")); if (isparam("new_full_name")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Full name"), getparam("new_full_name")); if (isparam("new_user_email")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Email"), getparam("new_user_email")); sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n", loc("Activation URL")); sprintf(mail_text + strlen(mail_text), "\r\nURL : %s", url); if (isparam("new_user_name")) sprintf(mail_text + strlen(mail_text), "?cmd=%s", loc("Activate")); sprintf(mail_text + strlen(mail_text), "&code=%d&unm=%s\r\n", code, getparam("new_user_name")); if (sendmail(lbs, smtp_host, mail_from, email_addr, mail_text, error, sizeof(error)) == -1) { sprintf(str, loc("Cannot send email notification to \"%s\""), getparam("new_user_email")); strlcat(str, " : ", sizeof(str)); strlcat(str, error, sizeof(str)); strencode2(str2, str, sizeof(str2)); show_error(str2); return 0; }; show_standard_header(lbs, FALSE, loc("ELOG registration"), "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("
\n"); rsprintf(loc("An email has been sent to <%s>"), getparam("new_user_email")); rsprintf(".
\n"); rsprintf(loc("Use that email to activate your account")); rsprintf(".
\n"); show_bottom_text(lbs); rsprintf("\n"); return 0; } /* send email to admin user(s) */ if (self_register == 2 || self_register == 3) { if (getcfg(lbs->name, "Admin user", admin_user, sizeof(admin_user))) { pl = strtok(admin_user, " ,"); while (pl) { get_user_line(lbs, pl, NULL, NULL, email_addr, NULL, NULL, NULL); if (email_addr[0]) { /* compose subject */ if (self_register == 3) { if (lbs) sprintf(subject, loc("Registration request for ELOG logbook \"%s\""), lbs->name); else sprintf(subject, loc("Registration request for ELOG on host \"%s\""), host_name); sprintf(str, loc("A new ELOG user wants to register on \"%s\""), host_name); } else { if (isparam("new_user_name")) { if (lbs) sprintf(subject, loc("User \"%s\" registered on logbook \"%s\""), getparam("new_user_name"), lbs->name); else sprintf(subject, loc("User \"%s\" registered on host \"%s\""), getparam("new_user_name"), host_name); } sprintf(str, loc("A new ELOG user has been registered on %s"), host_name); } mail_text[0] = 0; compose_email_header(lbs, subject, mail_from_name, email_addr, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0); sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n\r\n", str); if (lbs) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"), lbs->name); else sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Host"), host_name); if (isparam("new_user_name")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Login name"), getparam("new_user_name")); if (isparam("new_full_name")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Full name"), getparam("new_full_name")); if (isparam("new_user_email")) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Email"), getparam("new_user_email")); if (self_register == 3) { sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n", loc("Hit following URL to activate that account")); sprintf(mail_text + strlen(mail_text), "\r\nURL : %s", url); if (isparam("new_user_name")) sprintf(mail_text + strlen(mail_text), "?cmd=%s&new_user_name=%s", loc("Activate"), getparam("new_user_name")); sprintf(mail_text + strlen(mail_text), "&code=%d&unm=%s\r\n", code, pl); } else { if (isparam("new_user_name")) sprintf(mail_text + strlen(mail_text), "\r\n%s URL : %s?cmd=Config&cfg_user=%s&unm=%s\r\n", loc("Logbook"), url, getparam("new_user_name"), pl); } if (sendmail(lbs, smtp_host, mail_from, email_addr, mail_text, error, sizeof(error)) == -1) { sprintf(str, loc("Cannot send email notification to \"%s\""), getparam("new_user_email")); strlcat(str, " : ", sizeof(str)); strlcat(str, error, sizeof(str)); strencode2(str2, str, sizeof(str2)); show_error(str2); return 0; }; } pl = strtok(NULL, " ,"); } } else { show_error(loc("No admin user has been defined in configuration file")); return 0; } if (self_register == 3) { sprintf(str, "?cmd=%s", loc("Requested")); redirect(lbs, str); return 0; } } } /* log in user automatically */ if (new_user && (self_register == 1 || self_register == 2)) { if (isparam("new_user_name")) { /* get a new session ID */ sid_new(lbs, getparam("new_user_name"), (char *) inet_ntoa(rem_addr), sid); if (lbs) sprintf(str, "../%s/", lbs->name_enc); else sprintf(str, "."); if (isparam("new_user_name")) { sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config")); strlcat(str, getparam("new_user_name"), sizeof(str)); } else if (isparam("cfg_user")) { sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config")); strlcat(str, getparam("cfg_user"), sizeof(str)); } else if (getcfg(lbs->name, "password file", str2, sizeof(str2))) sprintf(str + strlen(str), "?cmd=%s", loc("Config")); setparam("redir", str); /* set SID cookie */ set_sid_cookie(lbs, sid, getparam("new_user_name")); return 0; } } return 1; } /*------------------------------------------------------------------*/ int remove_user(LOGBOOK * lbs, char *user) { char file_name[256], str[1000], str2[1000]; PMXML_NODE node; if (lbs->pwd_xml_tree == NULL) { show_error("No password file loaded"); return FALSE; } sprintf(str, "/list/user[name=%s]", user); node = mxml_find_node(lbs->pwd_xml_tree, str); if (node == NULL) { sprintf(str, loc("User \"%s\" not found in password file"), user); strencode2(str2, str, sizeof(str2)); show_error(str2); return FALSE; } mxml_delete_node(node); if (get_password_file(lbs, file_name, sizeof(file_name))) { if (!mxml_write_tree(file_name, lbs->pwd_xml_tree)) { sprintf(str, loc("Cannot write to file %s"), file_name); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return FALSE; } } return TRUE; } /*------------------------------------------------------------------*/ int ascii_compare(const void *s1, const void *s2) { return stricmp(*(char **) s1, *(char **) s2); } /*------------------------------------------------------------------*/ int ascii_compare2(const void *s1, const void *s2) { return stricmp((char *) s1, (char *) s2); } /*------------------------------------------------------------------*/ void show_config_page(LOGBOOK * lbs) { char str[256], user[80], password[80], full_name[256], user_email[256], logbook[256], auth[32], **user_list; int i, n, inactive; BOOL email_notify[1000], sort_email; if (lbs) strcpy(logbook, lbs->name); else strcpy(logbook, "global"); /* get user */ strcpy(user, isparam("unm") ? getparam("unm") : ""); if (isparam("cfg_user")) strcpy(user, getparam("cfg_user")); /* get sort_email flag */ sort_email = FALSE; if (isparam("sort_email") && atoi(getparam("sort_email")) > 0) sort_email = TRUE; /*---- header ----*/ show_standard_header(lbs, TRUE, loc("ELOG user config"), ".", FALSE, NULL, NULL, 0); /*---- javascript to warn removal of user and of deactivation ----*/ rsprintf("\n\n"); /*---- title ----*/ show_standard_title(lbs, "", 0); /*---- activation notice ----*/ if (isparam("notice")) { rsprintf("\n"); rsprintf(getparam("notice")); rsprintf("\n"); } /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Config")); // for select javascript rsprintf("\n", loc("Save")); rsprintf("\n", loc("Back")); strencode2(str, user, sizeof(str)); rsprintf("\n", str); rsprintf("\n"); // needed for "Save" command rsprintf("\n\n"); /* table for two-column items */ rsprintf(""); rsprintf("\n"); /*---- if admin user, show user list ----*/ if (is_admin_user(lbs, getparam("unm"))) { rsprintf("\n"); rsprintf("\n", loc("Select user")); rsprintf("\n", loc("Active")); if (stricmp(user, getparam("unm")) == 0) rsprintf ("\n", inactive ? "" : "checked"); else rsprintf("\n", inactive ? "" : "checked"); } else { rsprintf("\n", loc("Active")); rsprintf("\n", inactive ? "" : "checked"); } rsprintf("\n", loc("Login name")); getcfg(lbs->name, "Authentication", auth, sizeof(auth)); strencode2(str, user, sizeof(str)); if (stristr(auth, "Kerberos") || stristr(auth, "Webserver")) rsprintf("\n", str); else rsprintf("\n", str); rsprintf("\n", loc("Full name")); rsprintf("\n", full_name); rsprintf("\n"); rsprintf("\n", user_email); for (i = n = 0; lb_list[i].name[0]; i++) { if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) { /* check if user has access */ if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) { /* check if emails are enabled for this logbook */ if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0) n++; } } } if (n > 0) { rsprintf("\n"); rsprintf("
%s:\n"); /* show "update" button only of javascript is not enabled */ rsprintf("\n"); if (sort_email) rsprintf("Sort by email"); else rsprintf("Sort by email"); } /*---- entry form ----*/ if (get_user_line(lbs, user, password, full_name, user_email, email_notify, NULL, &inactive) != 1) sprintf(str, loc("User [%s] has been deleted"), user); else strlcpy(str, user, sizeof(str)); if (is_admin_user(lbs, getparam("unm"))) { rsprintf("
%s:
%s:
%s:
%s:
Email:
%s:\n", loc("Subscribe to logbooks")); rsprintf("
(%s)\n", loc("enable automatic email notifications")); rsprintf("
\n"); for (i = 0; lb_list[i].name[0]; i++) { if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) { /* check if user has access */ if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) { /* check if emails are enabled for this logbook */ if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0) { if (email_notify[i]) rsprintf("\n", i, i); else rsprintf("\n", i, i); rsprintf("
\n", i, lb_list[i].name); } } } } } if (n > 2) { rsprintf("\n"); rsprintf("\n", loc("Set all")); rsprintf("\n", loc("Set none")); } rsprintf("
\n"); rsprintf("\n"); if (is_admin_user(lbs, getparam("unm")) || !getcfg(logbook, "allow password change", str, sizeof(str)) || atoi(str) == 1) rsprintf("\n", loc("Change password")); rsprintf("\n", loc("Remove user")); if (is_admin_user(lbs, getparam("unm"))) { rsprintf("\n", loc("New user")); strlcpy(str, loc("Change config file"), sizeof(str)); rsprintf("\n", str); } rsprintf("\n\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ int activate_user(LOGBOOK * lbs, char *user_name, int code) { int inactive, self_register; char str[256], str2[256], smtp_host[256], url[256], mail_text[2000], error[256], mail_from_name[256], mail_from[256], user_email[256], logbook[256]; if (lbs == NULL) strlcpy(logbook, "global", sizeof(logbook)); else strlcpy(logbook, lbs->name, sizeof(logbook)); get_user_line(lbs, user_name, NULL, NULL, user_email, NULL, NULL, &inactive); if (code != inactive) { show_error(loc("Invalid activation code")); return FALSE; } if (!set_user_inactive(lbs, user_name, 0)) { show_error(loc("Error activating user")); return FALSE; } self_register = 0; if (getcfg(logbook, "Self register", str, sizeof(str))) self_register = atoi(str); if (self_register == 3) { if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) { show_error(loc("No SMTP host defined in [global] section of configuration file")); return FALSE; } /* try to get URL from referer */ if (!getcfg("global", "URL", url, sizeof(url))) { if (referer[0]) strcpy(url, referer); else { if (elog_tcp_port == 80) { if (_ssl_flag) sprintf(url, "https://%s/", http_host); else sprintf(url, "http://%s/", http_host); } else { if (_ssl_flag) sprintf(url, "https://%s:%d/", http_host, elog_tcp_port); else sprintf(url, "http://%s:%d/", http_host, elog_tcp_port); } if (lbs) { strlcat(url, lbs->name_enc, sizeof(url)); strlcat(url, "/", sizeof(url)); } } } else { if (url[strlen(url) - 1] != '/') strlcat(url, "/", sizeof(url)); if (lbs) { strlcat(url, lbs->name_enc, sizeof(url)); strlcat(url, "/", sizeof(url)); } } retrieve_email_from(lbs, mail_from, mail_from_name, NULL); mail_text[0] = 0; compose_email_header(lbs, loc("Your ELOG account has been activated"), mail_from_name, user_email, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0); strlcat(mail_text, "\r\n", sizeof(mail_text)); strlcat(mail_text, loc("Your ELOG account has been activated on host"), sizeof(mail_text)); sprintf(mail_text + strlen(mail_text), " %s", http_host); sprintf(mail_text + strlen(mail_text), ".\r\n\r\n"); sprintf(url + strlen(url), "?unm=%s", user_name); sprintf(mail_text + strlen(mail_text), "%s %s.\r\n\r\n", loc("You can access it at"), url); sprintf(mail_text + strlen(mail_text), "%s.\r\n", loc("To subscribe to any logbook, click on 'Config' in that logbook")); if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) == -1) { sprintf(str, loc("Cannot send email notification to \"%s\""), user_email); strlcat(str, " : ", sizeof(str)); strlcat(str, error, sizeof(str)); strencode2(str2, str, sizeof(str2)); show_error(str2); return FALSE; } } return TRUE; } /*------------------------------------------------------------------*/ void show_forgot_pwd_page(LOGBOOK * lbs) { int i; char str[1000], str2[1000], login_name[256], full_name[256], user_email[256], name[256], pwd[256], redir[256], smtp_host[256], mail_from[256], mail_from_name[256], subject[256], mail_text[1000], url[1000], error[1000]; if (isparam("login_name")) { /* seach in pwd file */ strcpy(name, getparam("login_name")); for (i = 0;; i++) { if (!enum_user_line(lbs, i, login_name, sizeof(login_name))) break; get_user_line(lbs, login_name, NULL, full_name, user_email, NULL, NULL, NULL); if (strieq(name, login_name) || strieq(name, full_name) || strieq(name, user_email)) { if (user_email[0] == 0) { sprintf(str, loc("No Email address registered with user name \"%s\""), name); strencode2(str2, str, sizeof(str2)); show_error(str2); return; } /* create random password */ for (i = 0; i < 16; i++) pwd[i] = 'A' + (rand() % 25); pwd[i] = 0; /* send email with new password */ if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) { show_error(loc("No SMTP host defined in [global] section of configuration file")); return; } /* try to get URL from referer */ if (!getcfg("global", "URL", url, sizeof(url))) { if (referer[0]) strcpy(url, referer); else { if (elog_tcp_port == 80) { if (_ssl_flag) sprintf(url, "https://%s/", http_host); else sprintf(url, "http://%s/", http_host); } else { if (_ssl_flag) sprintf(url, "https://%s:%d/", http_host, elog_tcp_port); else sprintf(url, "http://%s:%d/", http_host, elog_tcp_port); } } } else { if (url[strlen(url) - 1] != '/') strlcat(url, "/", sizeof(url)); if (lbs) { strlcat(url, lbs->name_enc, sizeof(url)); strlcat(url, "/", sizeof(url)); } } url_slash_encode(pwd, sizeof(pwd)); sprintf(redir, "?cmd=%s&oldpwd=%s", loc("Change password"), pwd); url_encode(redir, sizeof(redir)); strencode2(str2, redir, sizeof(str2)); sprintf(str, "?redir=%s&uname=%s&upassword=%s", str2, login_name, pwd); strlcat(url, str, sizeof(url)); retrieve_email_from(lbs, mail_from, mail_from_name, NULL); if (lbs) sprintf(subject, loc("Password recovery for ELOG %s"), lbs->name); else sprintf(subject, loc("Password recovery for ELOG %s"), http_host); mail_text[0] = 0; compose_email_header(lbs, subject, mail_from_name, user_email, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0); strlcat(mail_text, "\r\n", sizeof(mail_text)); sprintf(mail_text + strlen(mail_text), loc("This is an automatically generated account recovery email for host %s"), http_host); strlcat(mail_text, ".\r\n", sizeof(mail_text)); strlcat(mail_text, loc("Please click on following link to recover your account"), sizeof(mail_text)); strlcat(mail_text, ":\r\n\r\n", sizeof(mail_text)); strlcat(mail_text, url, sizeof(mail_text)); strlcat(mail_text, "\r\n\r\n", sizeof(mail_text)); sprintf(mail_text + strlen(mail_text), "ELOG Version %s\r\n", VERSION); if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) != -1) { /* save new password */ auth_change_password(lbs, login_name, NULL, pwd, str, sizeof(str)); /* show notification web page */ show_standard_header(lbs, FALSE, loc("ELOG password recovery"), "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n"); rsprintf("
\n"); rsprintf(loc("Email notification")); rsprintf("
\n"); rsprintf(loc("A password recovery email for user \"%s\" has been sent to %s"), full_name, user_email); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\n"); return; } else { sprintf(str, loc("Error sending Email via \"%s\""), smtp_host); strlcat(str, ": ", sizeof(str)); strlcat(str, error, sizeof(str)); show_error(str); return; } } } if (strchr(name, '@')) sprintf(str, loc("Email address \"%s\" not registered"), name); else sprintf(str, loc("User name \"%s\" not registered"), name); strencode2(str2, str, sizeof(str2)); show_error(str2); return; } else { /*---- header ----*/ getcfg(lbs->name, "Authentication", str, sizeof(str)); if (stristr(str, "Kerberos")|| stristr(str, "Webserver")) { show_error ("This installation of ELOG uses site authentication\nwhere password recovery is not possible"); return; } show_standard_header(lbs, TRUE, loc("ELOG password recovery"), NULL, FALSE, NULL, NULL, 0); rsprintf(""); /*---- entry form ----*/ rsprintf("\n", loc("Enter your user name or email address")); rsprintf("\n"); rsprintf("
%s
\n"); rsprintf("\n", loc("Forgot")); rsprintf("
\n", loc("Submit")); rsprintf("
\n\n"); show_bottom_text(lbs); rsprintf("\r\n"); } } /*------------------------------------------------------------------*/ void show_new_user_page(LOGBOOK * lbs, char *user) { char str[256]; /*---- header ----*/ show_html_header(lbs, TRUE, loc("ELOG new user"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("


\n"); show_top_text(lbs); rsprintf("
\n\n"); /*---- title ----*/ if (lbs) show_standard_title(lbs, "", 1); else show_standard_title(NULL, "", 1); /* table for two-column items */ rsprintf(""); rsprintf("\n"); /*---- entry form ----*/ rsprintf("\n", loc("Login name")); if (user && user[0]) { strencode2(str, user, sizeof(str)); rsprintf("\n", str); rsprintf("\n"); } else { rsprintf("\n"); rsprintf("\n", loc("name may not contain blanks")); } rsprintf("\n", loc("Full name")); rsprintf("\n"); rsprintf("\n"); rsprintf("\n"); getcfg(lbs->name, "Authentication", str, sizeof(str)); if (!stristr(str, "Kerberos") && !stristr(str, "Webserver")) { rsprintf("\n", loc("Password")); rsprintf("\n", loc("Retype password")); rsprintf("
%s: (%s)
%s:
Email:
%s:\n"); rsprintf("
%s:\n"); } rsprintf("
\n"); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Save")); rsprintf("\n", loc("Cancel")); rsprintf("\n\n"); rsprintf("\n\n"); rsprintf("
\r\n"); } /*------------------------------------------------------------------*/ void show_elog_delete(LOGBOOK * lbs, int message_id) { int i, status, reply = 0, next, nsel; char str[256], str2[256], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], owner[256]; char attrib[MAX_N_ATTR][NAME_LENGTH], mode[80]; /* check for editing interval */ if (getcfg(lbs->name, "Restrict edit time", str, sizeof(str))) { for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id == message_id) break; if (i < *lbs->n_el_index && time(NULL) > lbs->el_index[i].file_time + atof(str) * 3600) { sprintf(str, loc("Entry can only be deleted %1.2lg hours after creation"), atof(str)); show_error(str); return; } } /* redirect if confirm = NO */ if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) { if (message_id) { sprintf(str, "%d", message_id); redirect(lbs, str); } else { strlcpy(str, isparam("lastcmd") ? getparam("lastcmd") : "", sizeof(str)); url_decode(str); redirect(lbs, str); } return; } if (isparam("elmode")) strlcpy(mode, getparam("elmode"), sizeof(mode)); if (isparam("confirm")) { if (strcmp(getparam("confirm"), loc("Yes")) == 0) { if (message_id) { /* delete message */ status = el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE); if (status != EL_SUCCESS) { sprintf(str, "%s = %d", loc("Error deleting message: status"), status); show_error(str); return; } else { strlcpy(str, isparam("nextmsg") ? getparam("nextmsg") : "", sizeof(str)); if (atoi(str) == 0) sprintf(str, "%d", el_search_message(lbs, EL_LAST, 0, TRUE)); if (atoi(str) == 0) redirect(lbs, ""); else redirect(lbs, str); return; } } else { if (isparam("nsel")) { for (i = 0; i < atoi(getparam("nsel")); i++) { sprintf(str, "s%d", i); if (isparam(str)) { if (strieq(mode, "threaded")) status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, TRUE); else status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, FALSE); } } } redirect(lbs, isparam("lastcmd") ? getparam("lastcmd") : ""); return; } } } else { /* check if at least one message is selected */ if (!message_id) { nsel = isparam("nsel") ? atoi(getparam("nsel")) : 0; for (i = 0; i < nsel; i++) { sprintf(str, "s%d", i); if (isparam(str)) break; } if (i == nsel) { show_error(loc("No entry selected for deletion")); return; } } /* check for author */ if (getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) { /* get message for reply/edit */ el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (!is_author(lbs, attrib, owner)) { strencode2(str2, owner, sizeof(str2)); sprintf(str, loc("Only user %s can delete this entry"), str2); show_error(str); return; } } /* header */ if (message_id) sprintf(str, "%d", message_id); else str[0] = 0; show_standard_header(lbs, TRUE, "Delete ELog entry", str, FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n", loc("Are you sure to delete these messages?")); rsprintf("\n"); if (reply && strieq(mode, "threaded")) rsprintf("\n", loc("and all their replies")); } else { rsprintf("%s\n", loc("Are you sure to delete this entry?")); /* check for replies */ /* retrieve original message */ el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL, NULL, NULL, NULL); if (reply_to[0]) rsprintf("\n", message_id, loc("and all its replies")); else rsprintf("\n", message_id); /* put link to next message */ next = el_search_message(lbs, EL_NEXT, message_id, TRUE); rsprintf("\n", next); } rsprintf("\n\n"); } rsprintf("
\n"); /* define hidden field for command */ rsprintf("\n", loc("Delete")); if (!message_id) { rsprintf("%s
\n"); if (isparam("nsel")) rsprintf("\n", getparam("nsel")); if (isparam("lastcmd")) { strlcpy(str, getparam("lastcmd"), sizeof(str)); rsprintf("\n", str); } if (isparam("nsel")) { reply = FALSE; for (i = 0; i < atoi(getparam("nsel")); i++) { sprintf(str, "s%d", i); if (isparam(str)) { rsprintf("#%s ", getparam(str)); rsprintf("\n", str, getparam(str)); } if (!reply) { el_retrieve(lbs, isparam(str) ? atoi(getparam(str)) : 0, NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL, NULL, NULL, NULL); if (reply_to[0]) reply = TRUE; } } } rsprintf("
%s
#%d
%s
#%d
\n", loc("Yes")); rsprintf("\n", loc("No")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ void show_logbook_delete(LOGBOOK * lbs) { char str[256]; /* redirect if confirm = NO */ if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) { redirect(lbs, "?cmd=Config"); return; } if (isparam("confirm")) { if (strcmp(getparam("confirm"), loc("Yes")) == 0) { /* delete logbook */ str[0] = 0; delete_logbook(lbs, str); if (str[0]) show_error(str); else redirect(NULL, "../"); return; } } else { strcpy(str, "Delete logbook"); show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n", str); rsprintf("\n\n"); } rsprintf("
\n"); /* define hidden field for command */ rsprintf("\n", loc("Delete this logbook")); sprintf(str, loc("Are you sure to delete logbook \"%s\"?"), lbs->name); rsprintf("%s
"); rsprintf("\n", loc("Yes")); rsprintf("\n", loc("No")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ void show_logbook_rename(LOGBOOK * lbs) { int i; char str[256], lbn[256]; if (isparam("lbname")) { /* check if logbook name exists already */ strcpy(lbn, getparam("lbname")); for (i = 0; lb_list[i].name[0]; i++) if (strieq(lbn, lb_list[i].name)) { sprintf(str, loc("Logbook \"%s\" exists already, please choose different name"), lbn); show_error(str); return; } if (!rename_logbook(lbs, getparam("lbname"))) return; sprintf(str, "../%s/?cmd=Config", getparam("lbname")); redirect(NULL, str); return; } else { strcpy(str, loc("Rename logbook")); show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n", loc("Enter new logbook name")); rsprintf("\n\n"); } rsprintf("
\n"); /* define hidden field for command */ rsprintf("\n", loc("Rename this logbook")); rsprintf("%s
"); rsprintf("

\n"); rsprintf("\n", loc("Rename this logbook")); rsprintf("\n", loc("Cancel")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ void show_logbook_new(LOGBOOK * lbs) { char str[256], lbn[256]; int i; if (isparam("lbname")) { /* check if logbook name exists already */ strcpy(lbn, getparam("lbname")); for (i = 0; lb_list[i].name[0]; i++) if (strieq(lbn, lb_list[i].name)) { sprintf(str, loc("Logbook \"%s\" exists already, please choose different name"), lbn); show_error(str); return; } /* create new logbook */ if (!create_logbook(lbs, getparam("lbname"), getparam("template"))) return; strcpy(lbn, getparam("lbname")); url_encode(lbn, sizeof(lbn)); sprintf(str, "../%s/?cmd=Config", lbn); for (i = 0; lb_list[i].name[0]; i++) if (strieq(lbn, lb_list[i].name)) break; if (lb_list[i].name[0]) redirect(&lb_list[i], str); else redirect(NULL, str); return; } show_standard_header(lbs, TRUE, loc("Create new logbook"), "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n", loc("Create new logbook")); rsprintf("\n"); rsprintf("\n\n"); rsprintf("\n\n"); rsprintf("
\n"); /* define hidden field for command */ rsprintf("\n", loc("Create new logbook")); rsprintf("%s
\n"); rsprintf("%s :  ", loc("Logbook name")); rsprintf("\n"); rsprintf("
\n"); rsprintf("%s : \n", loc("Use existing logbook as template")); rsprintf("\n"); rsprintf("
\n"); rsprintf("\n", loc("Create new logbook")); rsprintf("\n", loc("Cancel")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ int show_download_page(LOGBOOK * lbs, char *path) { char file_name[256], error_str[256]; int index, message_id, fh, i, size, delta; char message[TEXT_SIZE + 1000], *p, *buffer; if (stricmp(path, "gbl") == 0) { /* return complete config file */ load_config_section(NULL, &buffer, error_str); if (error_str[0]) { rsprintf("

%s

", error_str); return EL_FILE_ERROR; } size = strlen(buffer); strlcpy(message, buffer, sizeof(message)); xfree(buffer); } else { message_id = atoi(path); if (message_id == 0) { /* return config */ load_config_section(lbs->name, &buffer, error_str); if (error_str[0]) { rsprintf("

%s

", error_str); return EL_FILE_ERROR; } size = strlen(buffer); strlcpy(message, buffer, sizeof(message)); xfree(buffer); } else { /* return entry */ if (message_id == -1) index = *lbs->n_el_index - 1; // last entry else { for (index = 0; index < *lbs->n_el_index; index++) if (lbs->el_index[index].message_id == message_id) break; } if (index == *lbs->n_el_index) return EL_NO_MSG; sprintf(file_name, "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir, lbs->el_index[index].file_name); fh = open(file_name, O_RDWR | O_BINARY, 0644); if (fh < 0) return EL_FILE_ERROR; lseek(fh, lbs->el_index[index].offset, SEEK_SET); i = my_read(fh, message, sizeof(message) - 1); if (i <= 0) { close(fh); return EL_FILE_ERROR; } message[i] = 0; close(fh); /* decode message size */ p = strstr(message + 8, "$@MID@$:"); if (p == NULL) size = strlen(message); else size = p - message; message[size] = 0; } } show_plain_header(size, "export.txt"); /* increase return buffer size if file too big */ if (size + 1 >= return_buffer_size - (int) strlen(return_buffer)) { delta = size - (return_buffer_size - strlen(return_buffer)) + 1000; return_buffer = (char *) xrealloc(return_buffer, return_buffer_size + delta); memset(return_buffer + return_buffer_size, 0, delta); return_buffer_size += delta; } return_length = strlen(return_buffer) + size; strlcat(return_buffer, message, return_buffer_size); return EL_SUCCESS; } /*------------------------------------------------------------------*/ int download_config() { char error_str[256]; int size, delta; char message[TEXT_SIZE + 1000], *buffer; /* return complete config file */ load_config_section(NULL, &buffer, error_str); if (error_str[0]) { rsprintf("Error loading configuration file: %s", error_str); return EL_FILE_ERROR; } size = strlen(buffer); strlcpy(message, buffer, sizeof(message)); xfree(buffer); show_plain_header(size, "export.txt"); /* increase return buffer size if file too big */ if (size + 1 >= return_buffer_size - (int) strlen(return_buffer)) { delta = size - (return_buffer_size - strlen(return_buffer)) + 1000; return_buffer = (char *) xrealloc(return_buffer, return_buffer_size + delta); memset(return_buffer + return_buffer_size, 0, delta); return_buffer_size += delta; } return_length = strlen(return_buffer) + size; strlcat(return_buffer, message, return_buffer_size); return EL_SUCCESS; } /*------------------------------------------------------------------*/ void show_import_page_csv(LOGBOOK * lbs) { char str[256], str2[256]; /*---- header ----*/ show_html_header(lbs, FALSE, loc("ELOG CSV import"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("
\n"); /*---- title ----*/ show_standard_title(lbs, "", 0); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Cancel")); rsprintf("\n", loc("Import")); rsprintf("\n\n"); /* table for two-column items */ rsprintf(""); rsprintf("\n"); /*---- entry form ----*/ rsprintf("\n", loc("Field separator")); rsprintf("\n"); rsprintf("\n", loc("Options")); rsprintf("\n"); rsprintf("\n", loc("CSV filename")); rsprintf("\n"); rsprintf("
%s:"); str[0] = 0; if (isparam("sep")) strlcpy(str, getparam("sep"), sizeof(str)); if (str[0] == 0) rsprintf(""); else rsprintf(""); rsprintf("\n", loc("Auto detect")); if (str[0] == ',') rsprintf(""); else rsprintf(""); rsprintf("\n", loc("Comma")); if (str[0] == ';') rsprintf(""); else rsprintf(""); rsprintf("\n", loc("Semicolon")); rsprintf("
%s:"); if (isparam("head")) rsprintf("\n"); else rsprintf("\n"); rsprintf("
\n", loc("Derive attributes from CSV file")); if (isparam("notignore")) rsprintf("\n"); else rsprintf("\n"); rsprintf("
\n", loc("Do not ignore first line")); rsprintf("\n"); rsprintf("
\n", loc("Preview import")); if (isparam("filltext")) rsprintf("\n"); else rsprintf("\n"); strcpy(str, loc("text")); sprintf(str2, loc("Column header '%s' must be present in CSV file"), str); rsprintf("
\n", loc("Fill text body"), str2); rsprintf("
%s:"); if (isparam("csvfile")) rsprintf("%s:
\n", loc("Please re-enter filename")); rsprintf("
\n\n"); show_bottom_text(lbs); rsprintf("
\r\n"); } /*------------------------------------------------------------------*/ void show_import_page_xml(LOGBOOK * lbs) { /*---- header ----*/ show_html_header(lbs, FALSE, loc("ELOG XML import"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("
\n"); /*---- title ----*/ show_standard_title(lbs, "", 0); /*---- menu buttons ----*/ rsprintf("\n"); rsprintf("\n", loc("Cancel")); rsprintf("\n", loc("Import")); rsprintf("\n\n"); /* table for two-column items */ rsprintf(""); rsprintf("\n"); /*---- entry form ----*/ rsprintf("\n", loc("Options")); rsprintf("\n"); rsprintf("\n", loc("XML filename")); rsprintf("\n"); rsprintf("
%s:"); if (isparam("head")) rsprintf("\n"); else rsprintf("\n"); rsprintf("
\n", loc("Derive attributes from XML file")); if (isparam("keep")) rsprintf("\n"); else rsprintf("\n"); rsprintf("
\n", loc ("Keep original entry IDs (may overwrite existing entries, but is required if imported entries contain replies)")); rsprintf("\n"); rsprintf("
\n", loc("Preview import")); rsprintf("
%s:"); if (isparam("xmlfile")) rsprintf("%s:
\n", loc("Please re-enter filename")); rsprintf("
\n\n"); show_bottom_text(lbs); rsprintf("
\r\n"); } /*------------------------------------------------------------------*/ void csv_import(LOGBOOK * lbs, const char *csv, const char *csvfile) { const char *p; char *line, *list; char str[256], date[80], sep[80]; int i, j, n, n_attr, iline, n_imported, textcol, datecol, attr_offset; BOOL first, in_quotes, filltext; time_t ltime; list = (char *) xmalloc((MAX_N_ATTR + 2) * NAME_LENGTH); line = (char *) xmalloc(10000); first = TRUE; in_quotes = FALSE; iline = n_imported = 0; filltext = FALSE; textcol = -1; datecol = -1; attr_offset = 0; strcpy(sep, ","); if (isparam("sep")) strcpy(sep, getparam("sep")); if (sep[0] == 0) strcpy(sep, ","); if (strieq(sep, "auto")) { /* count commas */ for (i = 0, p = csv; p; i++) { p = strchr(p, ','); if (p) p++; } n = i; /* count semicolon */ for (i = 0, p = csv; p; i++) { p = strchr(p, ';'); if (p) p++; } strcpy(sep, i > n ? ";" : ","); } n_attr = lbs->n_attr; if (isparam("preview")) { /* title row */ sprintf(str, loc("CSV import preview of %s"), csvfile); show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0); rsprintf("\n"); rsprintf("\n", str, str); /* menu buttons */ rsprintf("\n\n"); rsprintf("
%s
\n"); rsprintf("\n", loc("Cancel")); rsprintf("\n", loc("CSV Import")); /* hidden fields */ rsprintf("\n", sep); if (isparam("head")) rsprintf("\n", getparam("head")); if (isparam("notignore")) rsprintf("\n", getparam("notignore")); if (isparam("filltext")) rsprintf("\n", getparam("filltext")); rsprintf("\n", csvfile); rsprintf("
"); } p = csv; datecol = -1; attr_offset = 0; do { for (i = 0; i < 10000 && *p; i++) { if (!in_quotes && (*p == '\r' || *p == '\n')) break; line[i] = *p++; if (line[i] == '"') in_quotes = !in_quotes; } line[i] = 0; while (*p == '\r' || *p == '\n') p++; if (!*p) break; memset(list, 0, MAX_N_ATTR * NAME_LENGTH); n = strbreak(line, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, sep, FALSE); if (n == MAX_N_ATTR) { strlcpy(str, loc("Too many attributes in CSV file"), sizeof(str)); show_error(str); } /* check if text column is present */ if (first && isparam("filltext") && atoi(getparam("filltext"))) { for (i = 0; i < n; i++) if (strieq(list + i * NAME_LENGTH, loc("text"))) { filltext = TRUE; textcol = i; break; } } /* interprete date entries correctly */ if (!(first && isparam("head"))) { for (i = attr_offset; i < n; i++) { if (attr_flags[i - attr_offset] & AF_DATE) { /* convert to seconds in Unix format */ ltime = convert_date(list + i * NAME_LENGTH); if (ltime == 0) { show_error(loc("Invalid date format")); return; } sprintf(list + i * NAME_LENGTH, "%d", (int) ltime); } if (attr_flags[i - attr_offset] & AF_DATETIME) { /* convert to seconds in Unix format */ ltime = convert_datetime(list + i * NAME_LENGTH); if (ltime == 0) { show_error(loc("Invalid date format")); return; } sprintf(list + i * NAME_LENGTH, "%d", (int) ltime); } } } if (first) { /* check for date column */ for (i = attr_offset = 0; i < n; i++) if (strieq(list + i * NAME_LENGTH, "Date")) datecol = i; /* skip message ID */ for (i = attr_offset = 0; i < n; i++) if (strieq(list + i * NAME_LENGTH, "Message ID") || strieq(list + i * NAME_LENGTH, "Date")) attr_offset++; } /* derive attributes from first line */ if (first && isparam("head")) { if (isparam("preview")) { rsprintf("\n"); for (i = attr_offset; i < n; i++) if (i != textcol) rsprintf("\n", list + i * NAME_LENGTH); if (filltext) rsprintf("\n", loc("text")); rsprintf("\n"); if (filltext) n_attr = n - 1 - attr_offset; else n_attr = n - attr_offset; } else { for (i = j = attr_offset; i < n; i++) if (i != textcol) strlcpy(attr_list[j++ - attr_offset], list + i * NAME_LENGTH, NAME_LENGTH); if (filltext) { if (!set_attributes(lbs, attr_list, n - 1 - attr_offset)) return; lbs->n_attr = n - 1 - attr_offset; } else { if (!set_attributes(lbs, attr_list, n - attr_offset)) return; lbs->n_attr = n - attr_offset; } n_attr = lbs->n_attr; } } else { /* ignore first line */ if (first && !isparam("notignore")) { first = FALSE; continue; } if (isparam("preview")) { rsprintf("\n"); for (i = j = attr_offset; i < n_attr + attr_offset; i++) { if (iline % 2 == 0) rsputs("\n"); j++; } if (filltext) { rsputs("\n"); } rsputs("\n"); iline++; } else { /* get date and check it */ if (datecol != -1) { strlcpy(date, list + datecol * NAME_LENGTH, sizeof(date)); ltime = date_to_ltime(date); if (ltime <= 0) { /* try other date formats */ ltime = convert_datetime(date); if (ltime <= 0) ltime = convert_date(date); if (ltime <= 0) { strcpy(str, loc("Invalid date format")); strlcat(str, ": \"", sizeof(str)); strlcat(str, date, sizeof(str)); strlcat(str, "\"", sizeof(str)); show_error(str); return; } /* convert back ltime to date */ get_rfc2822_date(date, sizeof(date), ltime); } } else date[0] = 0; if (!filltext) { /* submit entry */ if (el_submit (lbs, 0, FALSE, date, attr_list, (char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, "", "", "", "plain", NULL, TRUE, NULL, NULL)) n_imported++; } else { strlcpy(line, list + textcol * NAME_LENGTH, 10000); insert_breaks(line, 78, 10000); for (i = textcol; i < n_attr + attr_offset; i++) strlcpy(list + i * NAME_LENGTH, list + (i + 1) * NAME_LENGTH, NAME_LENGTH); /* submit entry */ if (el_submit (lbs, 0, FALSE, date, attr_list, (char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, line, "", "", "plain", NULL, TRUE, NULL, NULL)) n_imported++; } } } first = FALSE; } while (*p); xfree(line); xfree(list); if (isparam("preview")) { rsprintf("
%s%s
"); else rsputs(""); /* skip text column */ if (i == textcol) j++; if (i >= n || !list[j * NAME_LENGTH]) rsputs(" "); else rsputs(list + j * NAME_LENGTH); rsputs(""); if (list[textcol * NAME_LENGTH]) rsputs(list + textcol * NAME_LENGTH); else rsputs(" "); rsputs("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); return; } sprintf(str, loc("%d entries successfully imported"), n_imported); show_elog_list(lbs, 0, 0, 0, TRUE, str); } /*------------------------------------------------------------------*/ void xml_import(LOGBOOK * lbs, const char *xml, const char *xmlfile) { char str[NAME_LENGTH], date[80], error[256], encoding[256], *list, *p, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64 * MAX_ATTACHMENTS]; int i, j, index, n_attr, iline, n_imported, i_line, line_len, message_id, bedit; time_t ltime; PMXML_NODE root, entry; iline = n_imported = 0; n_attr = lbs->n_attr; root = mxml_parse_buffer(xml, error, sizeof(error), NULL); if (root == NULL) { strencode2(str, error, sizeof(str)); show_error(str); return; } root = mxml_find_node(root, "ELOG_LIST"); if (root == NULL) { sprintf(str, loc("XML file does not contain %s element"), "<ELOG_LIST>"); show_error(str); return; } entry = mxml_subnode(root, 0); if (mxml_find_node(entry, "MID") == NULL) { sprintf(str, loc("XML file does not contain %s element"), "<MID>"); show_error(str); return; } if (mxml_find_node(entry, "DATE") == NULL) { sprintf(str, loc("XML file does not contain %s element"), "<DATE>"); show_error(str); return; } if (mxml_find_node(entry, "ENCODING") == NULL) { sprintf(str, loc("XML file does not contain %s element"), "<ENCODING>"); show_error(str); return; } if (isparam("preview")) { /* title row */ sprintf(str, loc("XML import preview of %s"), xmlfile); show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0); rsprintf("\n"); rsprintf("\n", str, str); /* menu buttons */ rsprintf("\n\n"); rsprintf("
%s
\n"); rsprintf("\n", loc("Cancel")); rsprintf("\n", loc("XML Import")); /* hidden fields */ if (isparam("head")) rsprintf("\n", getparam("head")); if (isparam("keep")) rsprintf("\n", getparam("keep")); rsprintf("\n", xmlfile); rsprintf("
"); } list = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH); /* derive attributes from XML file */ if (isparam("head")) { if (isparam("preview")) { rsprintf("\n"); for (i = 0; i < mxml_get_number_of_children(entry); i++) { strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), sizeof(str)); if (strieq(str, "MID")) strcpy(str, "ID"); if (strieq(str, "DATE")) strcpy(str, loc("Date")); if (strieq(str, "TEXT")) strcpy(str, loc("Text")); if (!strieq(str, "ENCODING") && !strieq(str, "IN_REPLY_TO") && !strieq(str, "REPLY_TO") && !strieq(str, "ATTACHMENT")) rsprintf("\n", str); } rsprintf("\n"); n_attr = i; } else { for (i = j = 0; i < mxml_get_number_of_children(entry); i++) { strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH); if (stricmp(str, "MID") != 0 && stricmp(str, "DATE") != 0 && stricmp(str, "ENCODING") != 0 && stricmp(str, "TEXT") != 0 && stricmp(str, "IN_REPLY_TO") != 0 && stricmp(str, "REPLY_TO") != 0 && stricmp(str, "ATTACHMENT") != 0) strlcpy(attr_list[j++], mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH); } if (!set_attributes(lbs, attr_list, j)) return; lbs->n_attr = n_attr = j; } } else { if (isparam("preview")) { rsprintf("\n"); rsprintf("\n", "ID"); rsprintf("\n", loc("Date")); for (i = 0; i < n_attr; i++) rsprintf("\n", attr_list[i]); rsprintf("\n", loc("Text")); rsprintf("\n"); } } for (index = 0; index < mxml_get_number_of_children(root); index++) { entry = mxml_subnode(root, index); if (isparam("preview")) { rsprintf("\n"); for (i = 0; i < mxml_get_number_of_children(entry); i++) { strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH); if (strieq(str, "ENCODING") || strieq(str, "IN_REPLY_TO") || strieq(str, "REPLY_TO") || strieq(str, "ATTACHMENT")) continue; if (strieq(str, "TEXT")) break; if (iline % 2 == 0) rsputs("\n"); } rsputs("\n"); rsputs("\n"); iline++; } else { message_id = 0; if (isparam("keep")) message_id = atoi(mxml_get_value(mxml_find_node(entry, "MID"))); for (i = 0; i < n_attr; i++) { strlcpy(str, attr_list[i], sizeof(str)); while (strchr(str, ' ')) *strchr(str, ' ') = '_'; if (mxml_find_node(entry, str) == NULL) *(list + (i * NAME_LENGTH)) = 0; else strlcpy(list + i * NAME_LENGTH, mxml_get_value(mxml_find_node(entry, str)), NAME_LENGTH); } /* interprete date entries correctly */ for (i = 0; i < n_attr; i++) { if (attr_flags[i] & AF_DATE) { /* convert to seconds in Unix format */ ltime = convert_date(list + i * NAME_LENGTH); if (ltime == 0) { show_error(loc("Invalid date format")); return; } sprintf(list + i * NAME_LENGTH, "%d", (int) ltime); } if (attr_flags[i] & AF_DATETIME) { /* convert to seconds in Unix format */ ltime = convert_datetime(list + i * NAME_LENGTH); if (ltime == 0) { show_error(loc("Invalid date format")); return; } sprintf(list + i * NAME_LENGTH, "%d", (int) ltime); } } encoding[0] = 0; if (mxml_find_node(entry, "ENCODING")) strlcpy(encoding, mxml_get_value(mxml_find_node(entry, "ENCODING")), sizeof(encoding)); else strcpy(encoding, "plain"); reply_to[0] = 0; if (mxml_find_node(entry, "REPLY_TO")) strlcpy(reply_to, mxml_get_value(mxml_find_node(entry, "REPLY_TO")), sizeof(reply_to)); in_reply_to[0] = 0; if (mxml_find_node(entry, "IN_REPLY_TO")) strlcpy(in_reply_to, mxml_get_value(mxml_find_node(entry, "IN_REPLY_TO")), sizeof(in_reply_to)); date[0] = 0; if (mxml_find_node(entry, "DATE")) strlcpy(date, mxml_get_value(mxml_find_node(entry, "DATE")), sizeof(date)); attachment_all[0] = 0; if (mxml_find_node(entry, "ATTACHMENT")) strlcpy(attachment_all, mxml_get_value(mxml_find_node(entry, "ATTACHMENT")), sizeof(attachment_all)); memset(attachment, 0, sizeof(attachment)); for (i = 0; i < MAX_ATTACHMENTS; i++) { if (i == 0) p = strtok(attachment_all, ","); else p = strtok(NULL, ","); if (p != NULL) strlcpy(attachment[i], p, MAX_PATH_LENGTH); else break; } str[0] = 0; if (mxml_find_node(entry, "TEXT")) p = mxml_get_value(mxml_find_node(entry, "TEXT")); else p = str; bedit = FALSE; if (isparam("keep")) { for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id == message_id) break; if (lbs->el_index[i].message_id == message_id) bedit = TRUE; } /* submit entry */ if (el_submit (lbs, message_id, bedit, date, attr_list, (char (*)[NAME_LENGTH]) list, n_attr, p, in_reply_to, reply_to, encoding, attachment, FALSE, NULL, NULL)) n_imported++; } } xfree(list); if (isparam("preview")) { rsprintf("
%s
%s%s%s%s
"); else rsputs(""); strlcpy(str, mxml_get_value(mxml_subnode(entry, i)), NAME_LENGTH); if (!str[0]) rsputs(" "); else rsputs(str); rsputs(""); if (mxml_find_node(entry, "TEXT")) { strlcpy(str, mxml_get_value(mxml_find_node(entry, "TEXT")), sizeof(str)); if (str[0]) { /* limit output to 3 lines */ for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; i++, line_len++) { if (str[i] == '\n') { i_line++; line_len = 0; } else /* limit line length to 150 characters */ if (line_len > 150 && str[i] == ' ') { str[i] = '\n'; i_line++; line_len = 0; } if (i_line == 3) break; } str[i] = 0; strip_html(str); if (str[0]) strencode(str); else rsputs(" "); } else rsputs(" "); } rsputs("
\n"); show_bottom_text(lbs); rsprintf("\r\n"); return; } sprintf(str, loc("%d entries successfully imported"), n_imported); show_elog_list(lbs, 0, 0, 0, TRUE, str); } /*------------------------------------------------------------------*/ int show_md5_page(LOGBOOK * lbs) { int i, j; char *buffer, error_str[256]; unsigned char digest[16]; /* header */ rsprintf("HTTP/1.1 200 Document follows\r\n"); rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision()); rsprintf("Accept-Ranges: bytes\r\n"); rsprintf("Connection: close\r\n"); rsprintf("Content-Type: text/plain;charset=%s\r\n", DEFAULT_HTTP_CHARSET); rsprintf("Pragma: no-cache\r\n"); rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n\r\n"); /* calculate MD5 for logbook section in config file */ load_config_section(lbs->name, &buffer, error_str); if (error_str[0]) rsprintf("

%s

", error_str); else { rsprintf("ID: %6d MD5:", 0); remove_crlf(buffer); MD5_checksum(buffer, strlen(buffer), digest); for (i = 0; i < 16; i++) rsprintf("%02X", digest[i]); rsprintf("\n"); } xfree(buffer); /* show MD5's of logbook entries */ for (i = 0; i < *lbs->n_el_index; i++) { rsprintf("ID: %6d MD5:", lbs->el_index[i].message_id); for (j = 0; j < 16; j++) rsprintf("%02X", lbs->el_index[i].md5_digest[j]); rsprintf("\n"); } keep_alive = FALSE; return EL_SUCCESS; } /*------------------------------------------------------------------*/ void combine_url(LOGBOOK * lbs, char *url, char *param, char *result, int size, int *ssl) { if (ssl) *ssl = 0; if (strstr(url, "http://")) strlcpy(result, url + 7, size); else if (strstr(url, "https://")) { if (ssl) *ssl = 1; strlcpy(result, url + 8, size); } else strlcpy(result, url, size); url_encode(result, size); if (result[strlen(result) - 1] != '/') strlcat(result, "/", size); if (lbs != NULL) { if (!strstr(result, lbs->name_enc)) { strlcat(result, lbs->name_enc, size); strlcat(result, "/", size); } } if (param) strlcat(result, param, size); } /*------------------------------------------------------------------*/ int retrieve_remote_md5(LOGBOOK * lbs, char *host, MD5_INDEX ** md5_index, char *error_str) { int i, n, id, x, version, ssl; char *text, *p, url[256], str[1000]; *md5_index = NULL; combine_url(lbs, host, "?cmd=GetMD5", url, sizeof(url), &ssl); text = NULL; error_str[0] = 0; if (retrieve_url(lbs, url, ssl, &text) < 0) { sprintf(error_str, loc("Cannot connect to remote server \"%s\""), host); return -1; } p = strstr(text, "ELOG HTTP "); if (!p) { if (isparam("debug")) rsputs(text); strlcpy(error_str, loc("Remote server is not an ELOG server"), 256); xfree(text); return -1; } version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14); if (version < 250) { if (isparam("debug")) rsputs(text); memset(str, 0, sizeof(str)); strncpy(str, p + 10, 5); sprintf(error_str, loc("Incorrect remote ELOG server version %s"), str); xfree(text); return -1; } p = strstr(text, "Location: "); if (p) { if (isparam("debug")) rsputs(text); if (strstr(text, "?fail=")) sprintf(error_str, loc("Invalid user name \"%s\" or password for remote logbook"), isparam("unm") ? getparam("unm") : ""); else { strlcpy(str, p + 9, sizeof(str)); if (strchr(str, '?')) *strchr(str, '?') = 0; strlcpy(error_str, loc("URL is redirected to:"), 256); strlcat(error_str, str, 256); } return -3; } p = strstr(text, "\r\n\r\n"); if (!p) { if (isparam("debug")) rsputs(text); strlcpy(error_str, loc("Invalid HTTP header"), 256); xfree(text); return -1; } for (n = 0;; n++) { p = strstr(p, "ID:"); if (!p) break; p += 3; id = atoi(p); p = strstr(p, "MD5:"); if (!p) break; p += 4; if (n == 0) *md5_index = (MD5_INDEX *) xmalloc(sizeof(MD5_INDEX)); else *md5_index = (MD5_INDEX *) xrealloc(*md5_index, (n + 1) * sizeof(MD5_INDEX)); (*md5_index)[n].message_id = id; for (i = 0; i < 16; i++) { sscanf(p + 2 * i, "%02X", &x); (*md5_index)[n].md5_digest[i] = (unsigned char) x; } } if (n == 0) { if (isparam("debug")) rsputs(text); if (strstr(text, "Login")) { strlcpy(error_str, loc("No user name supplied to access remote logbook"), 256); xfree(text); return -2; } else strlcpy(error_str, loc("Error accessing remote logbook"), 256); } xfree(text); return n; } /*------------------------------------------------------------------*/ int send_tcp(int sock, char *buffer, unsigned int buffer_size, int flags) /******************************************************************** Send network data over TCP port. Break buffer in smaller parts if larger than maximum TCP buffer size (usually 64k). \********************************************************************/ { #ifndef NET_TCP_SIZE #define NET_TCP_SIZE 65536 #endif unsigned int count; int status; /* transfer fragments until complete buffer is transferred */ for (count = 0; count < buffer_size - NET_TCP_SIZE;) { status = send(sock, buffer + count, NET_TCP_SIZE, flags); if (status != -1) count += status; else { return status; } } while (count < buffer_size) { status = send(sock, buffer + count, buffer_size - count, flags); if (status != -1) count += status; else { return status; } } return count; } /*------------------------------------------------------------------*/ int submit_message(LOGBOOK * lbs, char *host, int message_id, char *error_str) { int size, i, n, status, fh, port, sock, content_length, header_length, remote_id, n_attr, ssl; char str[256], file_name[MAX_PATH_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH]; char subdir[256], param[256], remote_host_name[256], url[256], upwd[80]; char date[80], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256], *buffer; char *content, *p, boundary[80], request[10000], response[10000]; #ifdef HAVE_SSL SSL *ssl_con = NULL; #else void *ssl_con = NULL; #endif text = (char *) xmalloc(TEXT_SIZE); error_str[0] = 0; /* get message with attribute list devied from database */ size = TEXT_SIZE; status = el_retrieve(lbs, message_id, date, attr_list, attrib, -1, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) { xfree(text); strcpy(error_str, loc("Cannot read entry from local logbook")); return -1; } /* count attributes */ for (n_attr = 0; attr_list[n_attr][0]; n_attr++); combine_url(lbs, host, "", url, sizeof(url), &ssl); split_url(url, remote_host_name, &port, subdir, param); sock = elog_connect(remote_host_name, port); if (sock == -1) { sprintf(error_str, loc("Cannot connect to host %s, port %d"), remote_host_name, port); return -1; } #ifdef HAVE_SSL if (ssl) if (ssl_connect(sock, &ssl_con) < 0) { strcpy(error_str, "Error initiating SSL connection\n"); return -1; } #endif content_length = 100000; for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment[i][0]) { strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[i], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[i], sizeof(file_name)); fh = open(file_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); size = TELL(fh); close(fh); } else size = 0; content_length += size; } content = (char *) xmalloc(content_length); /* compose content */ sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand()); strcpy(content, boundary); strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSubmit\r\n"); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"mirror_id\"\r\n\r\n%d\r\n", boundary, message_id); if (isparam("unm")) { sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n", boundary, getparam("unm")); if (isparam("upwd")) strlcpy(upwd, getparam("upwd"), sizeof(upwd)); else get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n", boundary, upwd); } if (in_reply_to[0]) sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"in_reply_to\"\r\n\r\n%s\r\n", boundary, in_reply_to); if (reply_to[0]) sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"reply_to\"\r\n\r\n%s\r\n", boundary, reply_to); for (i = 0; i < n_attr; i++) sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, attr_list[i], attrib[i]); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"entry_date\"\r\n\r\n%s\r\n", boundary, date); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"encoding\"\r\n\r\n%s\r\n", boundary, encoding); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"Text\"\r\n\r\n%s\r\n%s\r\n", boundary, text, boundary); content_length = strlen(content); p = content + content_length; /* read attachments */ for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment[i][0]) { strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[i], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[i], sizeof(file_name)); fh = open(file_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); size = TELL(fh); lseek(fh, 0, SEEK_SET); buffer = (char *) xmalloc(size); read(fh, buffer, size); close(fh); /* submit attachment */ sprintf(p, "Content-Disposition: form-data; name=\"attfile%d\"; filename=\"%s\"\r\n\r\n", i + 1, attachment[i]); content_length += strlen(p); p += strlen(p); memcpy(p, buffer, size); p += size; strcpy(p, boundary); strcat(p, "\r\n"); content_length += size + strlen(p); p += strlen(p); xfree(buffer); } } /* compose request */ strcpy(request, "POST "); if (subdir[0]) { if (subdir[0] != '/') strcat(request, "/"); strcat(request, subdir); if (request[strlen(request) - 1] != '/') strcat(request, "/"); } strcat(request, " HTTP/1.0\r\n"); sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary); sprintf(request + strlen(request), "Host: %s\r\n", host_name); sprintf(request + strlen(request), "User-Agent: ELOGD\r\n"); sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length); if (isparam("wpwd")) sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", getparam("wpwd")); strcat(request, "\r\n"); header_length = strlen(request); send_with_timeout(ssl_con, sock, request, header_length); send_with_timeout(ssl_con, sock, content, content_length); #ifdef HAVE_SSL if (ssl) /* receive response */ i = SSL_read(ssl_con, response, 10000); else #endif /* receive response */ i = recv(sock, response, 10000, 0); if (i < 0) { closesocket(sock); xfree(text); strcpy(error_str, "Cannot receive response"); return -1; } /* discard remainder of response */ n = i; while (i > 0) { i = recv(sock, response + n, 10000, 0); if (i > 0) n += i; } response[n] = 0; #ifdef HAVE_SSL if (ssl) { SSL_shutdown(ssl_con); SSL_free(ssl_con); } #endif closesocket(sock); remote_id = -1; /* check response status */ if (strstr(response, "302 Found")) { if (strstr(response, "Location:")) { if (strstr(response, "fail")) sprintf(error_str, "Invalid user name or password\n"); strlcpy(str, strstr(response, "Location:") + 9, sizeof(str)); if (strchr(str, '\n')) *strchr(str, '\n') = 0; if (strchr(str, '?')) *strchr(str, '?') = 0; if (strrchr(str, '/')) remote_id = atoi(strrchr(str, '/') + 1); else remote_id = atoi(str); } } else if (strstr(response, "Logbook Selection")) sprintf(error_str, "No logbook specified\n"); else if (strstr(response, "enter password")) sprintf(error_str, "Missing or invalid password\n"); else if (strstr(response, "form name=form1")) sprintf(error_str, "Missing or invalid user name/password\n"); else if (strstr(response, "Error: Attribute")) { strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str)); if (strchr(str, '<')) *strchr(str, '<') = 0; sprintf(error_str, "Missing required attribute \"%s\"\n", str); } else sprintf(error_str, "Error transmitting message\n"); if (error_str[0] && isparam("debug")) rsputs(response); xfree(text); if (error_str[0]) return -1; return remote_id; } /*------------------------------------------------------------------*/ int receive_message(LOGBOOK * lbs, char *url, int message_id, char *error_str, BOOL bnew) { int i, status, size, n_attr, header_size, ssl; char str[NAME_LENGTH], str2[NAME_LENGTH], *p, *p2, *message, date[80], attrib[MAX_N_ATTR][NAME_LENGTH], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64 * MAX_ATTACHMENTS]; error_str[0] = 0; combine_url(lbs, url, "", str, sizeof(str), &ssl); sprintf(str + strlen(str), "%d?cmd=%s", message_id, loc("Download")); retrieve_url(lbs, str, ssl, &message); if (message == NULL) { sprintf(error_str, loc("Cannot receive \"%s\""), str); return -1; } p = strstr(message, "\r\n\r\n"); if (p == NULL) { if (isparam("debug")) rsputs(message); xfree(message); sprintf(error_str, loc("Cannot receive \"%s\""), str); return -1; } p += 4; /* check for correct ID */ if (atoi(p + 8) != message_id) { if (isparam("debug")) rsputs(message); sprintf(error_str, loc("Received wrong entry id \"%d\""), atoi(p + 8)); xfree(message); return -1; } /* decode entry */ el_decode(p, "Date: ", date, sizeof(date)); el_decode_intlist(p, "Reply to: ", reply_to, sizeof(reply_to)); el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to)); /* derive attribute names from message */ for (i = 0;; i++) { el_enum_attr(p, i, attr_list[i], attrib[i]); if (!attr_list[i][0]) break; } n_attr = i; el_decode(p, "Attachment: ", attachment_all, sizeof(attachment_all)); el_decode(p, "Encoding: ", encoding, sizeof(encoding)); /* break apart attachements */ for (i = 0; i < MAX_ATTACHMENTS; i++) attachment[i][0] = 0; for (i = 0; i < MAX_ATTACHMENTS; i++) { if (i == 0) p2 = strtok(attachment_all, ","); else p2 = strtok(NULL, ","); if (p2 != NULL) strcpy(attachment[i], p2); else break; } el_decode(p, "Locked by: ", locked_by, sizeof(locked_by)); if (locked_by[0]) { xfree(message); sprintf(error_str, loc("Entry #%d is locked on remote server"), message_id); return -1; } p = strstr(message, "========================================\n"); /* check for \n -> \r conversion (e.g. zipping/unzipping) */ if (p == NULL) p = strstr(message, "========================================\r"); if (p != NULL) { p += 41; /* remove last CR */ if (p[strlen(p) - 1] == '\n') p[strlen(p) - 1] = 0; status = el_submit(lbs, message_id, !bnew, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to, encoding, attachment, FALSE, "", NULL); xfree(message); if (status != message_id) { strlcpy(error_str, loc("Cannot save remote entry locally"), 256); return -1; } for (i = 0; i < MAX_ATTACHMENTS; i++) { if (attachment[i][0]) { combine_url(lbs, url, "", str, sizeof(str), &ssl); strlcpy(str2, attachment[i], sizeof(str2)); str2[13] = '/'; strlcat(str, str2, sizeof(str)); size = retrieve_url(lbs, str, ssl, &message); p = strstr(message, "\r\n\r\n"); if (p == NULL) { xfree(message); sprintf(error_str, loc("Cannot receive \"%s\""), str); return -1; } p += 4; header_size = p - message; el_submit_attachment(lbs, attachment[i], p, size - header_size, NULL); xfree(message); } } } else { xfree(message); return -1; } return 1; } /*------------------------------------------------------------------*/ void submit_config(LOGBOOK * lbs, char *server, char *buffer, char *error_str) { int i, n, port, sock, content_length, header_length, ssl; char str[256], upwd[80]; char subdir[256], param[256], remote_host_name[256]; char *content, boundary[80], request[10000], response[10000]; #ifdef HAVE_SSL SSL *ssl_con = NULL; #else void *ssl_con = NULL; #endif error_str[0] = 0; combine_url(lbs, server, "", str, sizeof(str), &ssl); split_url(str, remote_host_name, &port, subdir, param); sock = elog_connect(remote_host_name, port); if (sock == -1) { sprintf(error_str, loc("Cannot connect to host %s, port %d"), remote_host_name, port); return; } #ifdef HAVE_SSL if (ssl) if (ssl_connect(sock, &ssl_con) < 0) { strlcpy(error_str, "Error initiating SSL connection\n", 256); return; } #endif content_length = 100000; content = (char *) xmalloc(content_length); /* compose content */ sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand()); strcpy(content, boundary); strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSave\r\n"); if (isparam("unm")) { sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n", boundary, getparam("unm")); if (isparam("upwd")) strlcpy(upwd, getparam("upwd"), sizeof(upwd)); else get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL); sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n", boundary, upwd); } sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"Text\"\r\n\r\n%s\r\n%s\r\n", boundary, buffer, boundary); content_length = strlen(content); /* compose request */ strcpy(request, "POST "); if (subdir[0]) { if (subdir[0] != '/') strcat(request, "/"); strcat(request, subdir); if (request[strlen(request) - 1] != '/') strcat(request, "/"); } strcat(request, " HTTP/1.0\r\n"); sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary); sprintf(request + strlen(request), "Host: %s\r\n", host_name); sprintf(request + strlen(request), "User-Agent: ELOGD\r\n"); sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length); if (isparam("wpwd")) sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", getparam("wpwd")); strcat(request, "\r\n"); header_length = strlen(request); send_with_timeout(ssl_con, sock, request, header_length); send_with_timeout(ssl_con, sock, content, content_length); #ifdef HAVE_SSL if (ssl) /* receive response */ i = SSL_read(ssl_con, response, 10000); else #endif /* receive response */ i = recv(sock, response, 10000, 0); if (i < 0) { closesocket(sock); strlcpy(error_str, "Cannot receive response", 256); return; } /* discard remainder of response */ n = i; while (i > 0) { i = recv(sock, response + n, 10000, 0); if (i > 0) n += i; } response[n] = 0; #ifdef HAVE_SSL if (ssl) { SSL_shutdown(ssl_con); SSL_free(ssl_con); } #endif closesocket(sock); /* check response status */ if (strstr(response, "302 Found")) { if (strstr(response, "Location:")) { if (strstr(response, "fail")) strlcpy(error_str, "Invalid usr name or password\n", 256); } } else if (strstr(response, "Logbook Selection")) strlcpy(error_str, "No logbook specified\n", 256); else if (strstr(response, "enter password")) strlcpy(error_str, "Missing or invalid password\n", 256); else if (strstr(response, "form name=form1")) strlcpy(error_str, "Missing or invalid user name/password\n", 256); else if (strstr(response, "Error: Attribute")) { strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str)); if (strchr(str, '<')) *strchr(str, '<') = 0; sprintf(error_str, "Missing required attribute \"%s\"\n", str); } else strlcpy(error_str, "Error transmitting message\n", 256); } /*------------------------------------------------------------------*/ void receive_config(LOGBOOK * lbs, char *server, char *error_str) { char str[256], pwd[256], *buffer, *p; int status, version, ssl; error_str[0] = 0; do { combine_url(lbs, server, "", str, sizeof(str), &ssl); if (lbs == NULL) strcat(str, "?cmd=GetConfig"); // request complete config file else strcat(str, "?cmd=Download"); // request config section of logbook if (retrieve_url(lbs, str, ssl, &buffer) < 0) { *strchr(str, '?') = 0; sprintf(error_str, "Cannot contact elogd server at http://%s", str); return; } /* check version */ p = strstr(buffer, "ELOG HTTP "); if (!p) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); sprintf(error_str, "Remote server is not an ELOG server"); xfree(buffer); return; } version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14); if (version < 254) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); strlcpy(str, p + 10, 10); if (strchr(str, '\r')) *strchr(str, '\r') = 0; sprintf(error_str, "Incorrect remote ELOG server version %s, must be 2.5.4 or later", str); xfree(buffer); return; } /* evaluate status */ p = strchr(buffer, ' '); if (p == NULL) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); xfree(buffer); *strchr(str, '?') = 0; sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "", str); xfree(buffer); return; } p++; status = atoi(p); if (status == 401) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); xfree(buffer); eprintf("Please enter password to access remote elogd server: "); fgets(pwd, sizeof(pwd), stdin); while (pwd[strlen(pwd) - 1] == '\n' || pwd[strlen(pwd) - 1] == '\r') pwd[strlen(pwd) - 1] = 0; } else if (status != 200) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); xfree(buffer); *strchr(str, '?') = 0; sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "", str); return; } } while (status != 200); p = strstr(buffer, "\r\n\r\n"); if (p == NULL) { if (get_verbose() >= VERBOSE_INFO) puts(buffer); xfree(buffer); sprintf(error_str, loc("Cannot receive \"%s\""), str); return; } p += 4; if (lbs == NULL) { if (!save_config(p, str)) rsprintf("%s", str); } else { if (!save_admin_config(lbs->name, p, str)) rsprintf("%s", str); } xfree(buffer); } /*------------------------------------------------------------------*/ int adjust_config(char *url) { int fh, i, length; char *buf, *buf2, *p1, *p2; char str[256]; fh = open(config_file, O_RDWR | O_BINARY, 0644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); eputs(str); return 0; } /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = (char *) xmalloc(2 * length + 1000); read(fh, buf, length); buf[length] = 0; /* add mirror server */ p1 = stristr(buf, "Mirror server ="); if (p1 != NULL) { p2 = strchr(p1, '\n'); if (p2 && *(p2 - 1) == '\r') p2--; } else { p1 = strstr(buf, "[global]"); if (p1 == NULL) { eputs("Cannot find [global] section in config file."); return 0; } p1 = strchr(p1, '\n'); while (*p1 == '\n' || *p1 == '\r') p1++; p2 = p1; } /* save tail */ buf2 = NULL; if (p2) buf2 = xstrdup(p2); sprintf(p1, "Mirror server = %s\r\n", url); strlcat(p1, buf2, length + 1000); xfree(buf2); eprintf("Option \"Mirror server = %s\" added to config file.\n", url); /* outcomment "URL = xxx" */ p1 = strstr(buf, "URL ="); if (p1 != NULL) { /* save tail */ buf2 = xstrdup(p1); /* add comment */ sprintf(p1, "; Following line has been outcommented after cloning\r\n"); strlcat(p1, "; ", length + 1000); strlcat(p1, buf2, length + 1000); xfree(buf2); eputs("Option \"URL = xxx\" has been outcommented from config file."); } adjust_crlf(buf, 2 * length + 1000); lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); eputs(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); return 1; } /*------------------------------------------------------------------*/ void receive_pwdfile(LOGBOOK * lbs, char *server, char *error_str) { char str[256], url[256], pwd[256], *buffer, *buf, *p; int i, status, version, fh, ssl; error_str[0] = 0; do { combine_url(lbs, server, "", url, sizeof(url), &ssl); strlcpy(str, url, sizeof(str)); strcat(str, "?cmd=GetPwdFile"); // request password file if (retrieve_url(lbs, str, ssl, &buffer) < 0) { *strchr(str, '?') = 0; sprintf(error_str, "Cannot contact elogd server at http://%s", str); return; } /* check version */ p = strstr(buffer, "ELOG HTTP "); if (!p) { sprintf(error_str, "Remote server is not an ELOG server"); xfree(buffer); return; } version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14); if (version < 254) { strlcpy(str, p + 10, 10); if (strchr(str, '\r')) *strchr(str, '\r') = 0; sprintf(error_str, "Incorrect remote ELOG server version %s, must be 2.5.4 or later", str); xfree(buffer); return; } /* evaluate status */ p = strchr(buffer, ' '); if (p == NULL) { xfree(buffer); *strchr(str, '?') = 0; sprintf(error_str, "Received invalid response from elogd server at http://%s", str); xfree(buffer); return; } p++; status = atoi(p); if (status != 200 && status != 302 && status != 404) { xfree(buffer); *strchr(str, '?') = 0; sprintf(error_str, "Received invalid response from elogd server at http://%s", str); return; } p = strstr(buffer, "\r\n\r\n"); if (p == NULL) { xfree(buffer); sprintf(error_str, loc("Cannot receive \"%s\""), str); return; } p += 4; /* check for logbook access */ if (strstr(p, loc("Please login")) || strstr(p, "GetPwdFile") || status == 302) { if (strstr(buffer, "?fail=")) eprintf("\nInvalid username or password."); if (strstr(p, loc("Please login")) == NULL && strstr(p, "GetPwdFile") && isparam("unm")) eprintf("\nUser \"%s\" has no admin rights on remote server.", getparam("unm")); /* ask for username and password */ eprintf("\nPlease enter admin username to access\n%s: ", url); fgets(str, sizeof(str), stdin); while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n') str[strlen(str) - 1] = 0; setparam("unm", str); eprintf("\nPlease enter admin password to access\n%s: ", url); read_password(str, sizeof(str)); eprintf("\n"); while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n') str[strlen(str) - 1] = 0; do_crypt(str, pwd, sizeof(pwd)); setparam("upwd", pwd); status = 0; } } while (status != 200); get_password_file(lbs, str, sizeof(str)); fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644); if (fh < 0) { sprintf(error_str, loc("Cannot open file %s"), str); strcat(error_str, ": "); strcat(error_str, strerror(errno)); return; } buf = (char *) xmalloc(2 * strlen(p)); strlcpy(buf, p, 2 * strlen(p)); adjust_crlf(buf, 2 * strlen(p)); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(error_str, loc("Cannot write to %s"), str); strcat(error_str, ": "); strcat(error_str, strerror(errno)); close(fh); xfree(buf); xfree(buffer); return; } TRUNCATE(fh); close(fh); xfree(buf); xfree(buffer); } /*------------------------------------------------------------------*/ int save_md5(LOGBOOK * lbs, char *server, MD5_INDEX * md5_index, int n) { char str[256], url[256], file_name[256]; int i, j; FILE *f; combine_url(lbs, server, "", url, sizeof(url), NULL); url_decode(url); if (strstr(url, "http://")) strlcpy(str, url + 7, sizeof(str)); else if (strstr(url, "https://")) strlcpy(str, url + 8, sizeof(str)); else strlcpy(str, url, sizeof(str)); for (i = 0; i < (int) strlen(str); i++) if (strchr(":/\\ ", str[i])) str[i] = '_'; while (str[strlen(str) - 1] == '_') str[strlen(str) - 1] = 0; strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, ".md5", sizeof(file_name)); f = fopen(file_name, "wt"); if (f == NULL) return -1; for (i = 0; i < n; i++) { fprintf(f, "ID%d: ", md5_index[i].message_id); for (j = 0; j < 16; j++) fprintf(f, "%02X", md5_index[i].md5_digest[j]); fprintf(f, "\n"); } fclose(f); return 1; } /*------------------------------------------------------------------*/ int load_md5(LOGBOOK * lbs, char *server, MD5_INDEX ** md5_index) { char str[256], url[256], file_name[256], *p; int i, j, x; FILE *f; *md5_index = NULL; combine_url(lbs, server, "", url, sizeof(url), NULL); url_decode(url); if (strstr(url, "http://")) strlcpy(str, url + 7, sizeof(str)); else if (strstr(url, "https://")) strlcpy(str, url + 8, sizeof(str)); else strlcpy(str, url, sizeof(str)); for (i = 0; i < (int) strlen(str); i++) if (strchr(":/\\ ", str[i])) str[i] = '_'; while (str[strlen(str) - 1] == '_') str[strlen(str) - 1] = 0; strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); strlcat(file_name, ".md5", sizeof(file_name)); f = fopen(file_name, "rt"); if (f == NULL) return 0; for (i = 0; !feof(f); i++) { str[0] = 0; fgets(str, sizeof(str), f); if (!str[0]) break; if (i == 0) *md5_index = (MD5_INDEX *) xcalloc(sizeof(MD5_INDEX), 1); else *md5_index = (MD5_INDEX *) xrealloc(*md5_index, sizeof(MD5_INDEX) * (i + 1)); p = str + 2; (*md5_index)[i].message_id = atoi(p); while (*p && *p != ' ') p++; while (*p && *p == ' ') p++; for (j = 0; j < 16; j++) { sscanf(p + j * 2, "%02X", &x); (*md5_index)[i].md5_digest[j] = (unsigned char) x; } } fclose(f); return i; } /*------------------------------------------------------------------*/ BOOL equal_md5(unsigned char m1[16], unsigned char m2[16]) { int i; for (i = 0; i < 16; i++) if (m1[i] != m2[i]) break; return i == 16; } /*------------------------------------------------------------------*/ #define SYNC_HTML 1 #define SYNC_CRON 2 #define SYNC_CLONE 3 void mprint(LOGBOOK * lbs, int mode, char *str) { char line[1000]; if (mode == SYNC_HTML) rsprintf("%s\n", str); else if (mode == SYNC_CRON) { if (_logging_level > 1) { sprintf(line, "MIRROR: %s", str); write_logfile(lbs, line); } } else eputs(str); } void synchronize_logbook(LOGBOOK * lbs, int mode, BOOL sync_all) { int index, i, j, i_msg, i_remote, i_cache, n_remote, n_cache, nserver, remote_id, exist_remote, exist_cache, message_id, max_id, ssl; int all_identical, n_delete; char str[2000], url[256], loc_ref[256], rem_ref[256], pwd[256], locked_by[256], draft[256]; MD5_INDEX *md5_remote, *md5_cache; char list[MAX_N_LIST][NAME_LENGTH], error_str[256], *buffer; unsigned char digest[16]; if (!getcfg(lbs->name, "Mirror server", str, sizeof(str))) { show_error(loc("No mirror server defined in configuration file")); return; } nserver = strbreak(str, list, MAX_N_LIST, ",", FALSE); for (index = 0; index < nserver; index++) { if (mode == SYNC_HTML) { rsprintf("%s", lbs->name_enc, lbs->name); else if (sync_all) sprintf(loc_ref, "%s", lbs->name_enc, lbs->name); else sprintf(loc_ref, "%s", lbs->name); sprintf(str, loc("Synchronizing logbook %s with server \"%s\""), loc_ref, list[index]); rsprintf("\n", str); rsprintf("
%s

\n"); rsprintf("

");
      } else if (mode == SYNC_CLONE) {
         if (list[index][strlen(list[index]) - 1] != '/')
            eprintf("\nRetrieving entries from \"%s/%s\"...\n", list[index], lbs->name);
         else
            eprintf("\nRetrieving entries from \"%s%s\"...\n", list[index], lbs->name);
      }

      /* send partial return buffer */
      flush_return_buffer();

      do {

         n_remote = retrieve_remote_md5(lbs, list[index], &md5_remote, error_str);
         if (n_remote <= 0) {

            if ((n_remote == -2 || n_remote == -3) && mode == SYNC_CLONE) {

               if (n_remote == -3)
                  eprintf("\nInvalid username or password.");

               combine_url(lbs, list[index], "", url, sizeof(url), NULL);
               /* ask for username and password */
               eprintf("\nPlease enter username to access\n%s: ", url);
               fgets(str, sizeof(str), stdin);
               while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
                  str[strlen(str) - 1] = 0;
               setparam("unm", str);

               eprintf("\nPlease enter password to access\n%s: ", url);
               read_password(str, sizeof(str));
               eprintf("\n");
               while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
                  str[strlen(str) - 1] = 0;
               do_crypt(str, pwd, sizeof(pwd));
               setparam("upwd", pwd);

            } else {

               mprint(lbs, mode, error_str);

               if (md5_remote)
                  xfree(md5_remote);

               if (mode == SYNC_HTML)
                  rsprintf("
\n"); break; } } } while (n_remote <= 0); if (n_remote <= 0) continue; /* load local copy of remote MD5s from file */ n_cache = load_md5(lbs, list[index], &md5_cache); all_identical = TRUE; /*---- check for configuration file ----*/ if (getcfg(lbs->name, "Mirror config", str, sizeof(str)) && atoi(str) == 1 && md5_cache && mode != SYNC_CLONE) { load_config_section(lbs->name, &buffer, error_str); if (error_str[0]) mprint(lbs, mode, error_str); else { remove_crlf(buffer); MD5_checksum(buffer, strlen(buffer), digest); } /* compare MD5s */ if (get_verbose() >= VERBOSE_INFO) { eprintf("CONFIG : "); for (j = 0; j < 16; j++) eprintf("%02X", digest[j]); eprintf("\nCache : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_cache[0].md5_digest[j]); eprintf("\nRemote : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_remote[0].md5_digest[j]); eprintf("\n\n"); } if (n_remote > 0) { /* if config has been changed on this server, but not remotely, send it */ if (!equal_md5(md5_cache[0].md5_digest, digest) && equal_md5(md5_cache[0].md5_digest, md5_remote[0].md5_digest)) { all_identical = FALSE; if (_logging_level > 1) write_logfile(lbs, "MIRROR send config"); /* submit configuration section */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { submit_config(lbs, list[index], buffer, error_str); if (error_str[0]) mprint(lbs, mode, error_str); else mprint(lbs, mode, "Local config submitted"); md5_cache[0].message_id = -1; } else mprint(lbs, mode, "Local config should be submitted"); } else /* if config has been changed remotely, but not on this server, receive it */ if (!equal_md5(md5_cache[0].md5_digest, md5_remote[0].md5_digest) && equal_md5(md5_cache[0].md5_digest, digest)) { all_identical = FALSE; if (_logging_level > 1) write_logfile(lbs, "MIRROR receive config"); if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { receive_config(lbs, list[index], error_str); if (error_str[0]) mprint(lbs, mode, error_str); else mprint(lbs, mode, "Remote config received"); md5_cache[0].message_id = -1; } else mprint(lbs, mode, loc("Remote config should be received")); } else /* if config has been changed remotely and on this server, show conflict */ if (!equal_md5(md5_cache[0].md5_digest, md5_remote[0].md5_digest) && !equal_md5(md5_cache[0].md5_digest, digest) && !equal_md5(md5_remote[0].md5_digest, digest)) { if (_logging_level > 1) write_logfile(lbs, "MIRROR config conflict"); sprintf(str, "%s. ", loc("Configuration has been changed locally and remotely")); strcat(str, loc("Please merge manually to resolve conflict")); strcat(str, "."); mprint(lbs, mode, str); } else { /* configs are identical */ md5_cache[0].message_id = -1; } } else { /* n_remote == 0 */ sprintf(str, loc("Logbook \"%s\" does not exist on remote server"), lbs->name); mprint(lbs, mode, str); continue; } flush_return_buffer(); if (buffer) xfree(buffer); } /*---- loop through logbook entries ----*/ n_delete = 0; for (i_msg = 0; i_msg < *lbs->n_el_index; i_msg++) { message_id = lbs->el_index[i_msg].message_id; /* check if message is locked */ el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, locked_by, draft); if (locked_by[0]) { sprintf(str, "ID%d:\t%s", message_id, loc("Entry is locked on local server and therefore skipped")); mprint(lbs, mode, str); all_identical = FALSE; continue; } /* look for message id in MD5s */ for (i_remote = 0; i_remote < n_remote; i_remote++) if (md5_remote[i_remote].message_id == message_id) break; exist_remote = i_remote < n_remote; for (i_cache = 0; i_cache < n_cache; i_cache++) if (md5_cache[i_cache].message_id == message_id) break; exist_cache = i_cache < n_cache; /* if message exists in both lists, compare MD5s */ if (exist_remote && exist_cache) { /* compare MD5s */ if (get_verbose() >= VERBOSE_INFO) { eprintf("ID%-5d: ", message_id); for (j = 0; j < 16; j++) eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]); eprintf("\nCache : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_cache[i_cache].md5_digest[j]); eprintf("\nRemote : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_remote[i_remote].md5_digest[j]); eprintf("\n\n"); } /* if message has been changed on this server, but not remotely, send it */ if (!equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest) && equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)) { all_identical = FALSE; if (_logging_level > 1) { sprintf(str, "MIRROR send entry #%d", message_id); write_logfile(lbs, str); } /* submit local message */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { submit_message(lbs, list[index], message_id, error_str); /* not that submit_message() may have changed attr_list !!! */ if (error_str[0]) sprintf(str, "%s: %s", loc("Error sending local entry"), error_str); else sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted")); mprint(lbs, mode, str); md5_cache[i_cache].message_id = -1; } else { sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted")); mprint(lbs, mode, str); } } else /* if message has been changed remotely, but not on this server, receive it */ if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest) && equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)) { all_identical = FALSE; if (mode == SYNC_CLONE) { eprintf("ID%d:\t", message_id); } else if (mode == SYNC_HTML) { if (getcfg_topgroup()) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else if (sync_all) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else rsprintf("ID%d:\t", message_id, message_id); flush_return_buffer(); } if (_logging_level > 1) { sprintf(str, "MIRROR receive entry #%d", message_id); write_logfile(lbs, str); } if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { receive_message(lbs, list[index], message_id, error_str, FALSE); if (error_str[0]) { sprintf(str, "%s: %s", loc("Error receiving message"), error_str); mprint(lbs, mode, str); } else if (mode == SYNC_HTML) { rsprintf("%s\n", loc("Remote entry received")); } else if (mode == SYNC_CLONE) { eprintf("%s\n", loc("Remote entry received")); } else { sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry received")); mprint(lbs, mode, str); } if (!error_str[0]) md5_cache[i_cache].message_id = -1; } else { sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received")); mprint(lbs, mode, str); } } else /* if message has been changed remotely and on this server, show conflict */ if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest) && !equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest) && !equal_md5(md5_remote[i_remote].md5_digest, lbs->el_index[i_msg].md5_digest)) { all_identical = FALSE; if (mode == SYNC_CLONE) { eprintf("Warning: Entry #%d has been changed locally and remotely, will not be retrieved\n", message_id); } else { if (_logging_level > 1) { sprintf(str, "MIRROR conflict entry #%d", message_id); write_logfile(lbs, str); } combine_url(lbs, list[index], "", str, sizeof(str), NULL); if (getcfg_topgroup()) sprintf(loc_ref, "%s", lbs->name_enc, message_id, loc("local")); else if (sync_all) sprintf(loc_ref, "%s", lbs->name_enc, message_id, loc("local")); else sprintf(loc_ref, "%s", message_id, loc("local")); sprintf(rem_ref, "%s", str, message_id, loc("remote")); sprintf(str, "ID%d:\t%s. ", message_id, loc("Entry has been changed locally and remotely")); sprintf(str + strlen(str), loc("Please delete %s or %s entry to resolve conflict"), loc_ref, rem_ref); strcat(str, "."); mprint(lbs, mode, str); } } else { /* messages are identical */ md5_cache[i_cache].message_id = -1; } } if (exist_cache && !exist_remote) { /* if message has been changed locally, send it */ if (!equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)) { /* compare MD5s */ if (get_verbose() >= VERBOSE_INFO) { eprintf("ID%-5d: ", message_id); for (j = 0; j < 16; j++) eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]); eprintf("\nCache : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_cache[i_cache].md5_digest[j]); eprintf("\nRemote : none"); eprintf("\n\n"); } all_identical = FALSE; if (_logging_level > 1) { sprintf(str, "MIRROR send entry #%d", message_id); write_logfile(lbs, str); } /* submit local message */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { submit_message(lbs, list[index], message_id, error_str); /* not that submit_message() may have changed attr_list !!! */ if (error_str[0]) sprintf(str, "%s: %s", loc("Error sending local message"), error_str); else sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted")); mprint(lbs, mode, str); md5_cache[i_cache].message_id = -1; } else { sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted")); mprint(lbs, mode, str); } } else { /* if message exists only in cache, but not remotely, it must have been deleted remotely, so remove it locally */ if (!isparam("confirm") && mode == SYNC_HTML) { combine_url(lbs, list[index], "", str, sizeof(str), NULL); if (getcfg_topgroup()) sprintf(loc_ref, "%s", lbs->name_enc, message_id, loc("local")); else if (sync_all) sprintf(loc_ref, "%s", lbs->name_enc, message_id, loc("Local entry")); else sprintf(loc_ref, "%s", message_id, loc("Local entry")); sprintf(str, loc("%s should be deleted"), loc_ref); rsprintf("ID%d:\t%s\n", message_id, str); n_delete++; } if (!isparam("confirm") && mode == SYNC_CLONE) { sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted locally")); mprint(lbs, mode, str); } else { all_identical = FALSE; if (mode == SYNC_CLONE) { el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE); sprintf(str, "ID%d:\t%s", message_id, loc("Entry deleted locally")); mprint(lbs, mode, str); /* message got deleted from local message list, so redo current index */ i_msg--; } else { if (_logging_level > 1) { sprintf(str, "MIRROR delete local entry #%d", message_id); write_logfile(lbs, str); } if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE); sprintf(str, "ID%d:\t%s", message_id, loc("Entry deleted locally")); mprint(lbs, mode, str); /* message got deleted from local message list, so redo current index */ i_msg--; } else { sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted locally")); mprint(lbs, mode, str); } } /* mark message non-conflicting */ md5_cache[i_cache].message_id = -1; } } } /* if message does not exist in cache and remotely, it must be new, so send it */ if (!exist_cache && !exist_remote) { all_identical = FALSE; if (_logging_level > 1) { sprintf(str, "MIRROR send entry #%d", message_id); write_logfile(lbs, str); } remote_id = 0; if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { remote_id = submit_message(lbs, list[index], message_id, error_str); if (error_str[0]) sprintf(str, "%s: %s", loc("Error sending local entry"), error_str); else if (remote_id != message_id) sprintf(str, "Error: Submitting entry #%d resulted in remote entry #%d\n", message_id, remote_id); else sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted")); mprint(lbs, mode, str); } else { sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted")); mprint(lbs, mode, str); } } /* if message does not exist in cache but remotely, messages were added on both sides, so resubmit local one and retrieve remote one if messages are different */ if (!exist_cache && exist_remote && !equal_md5(md5_remote[i_remote].md5_digest, lbs->el_index[i_msg].md5_digest)) { /* compare MD5s */ if (get_verbose() >= VERBOSE_INFO) { eprintf("ID%-5d: ", message_id); for (j = 0; j < 16; j++) eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]); eprintf("\nCache : none"); eprintf("\nRemote : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_remote[i_remote].md5_digest[j]); eprintf("\n\n"); } all_identical = FALSE; /* find max id both locally and remotely */ max_id = 1; for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id > max_id) max_id = lbs->el_index[i].message_id; for (i = 0; i < n_remote; i++) if (md5_remote[i].message_id > max_id) max_id = md5_remote[i].message_id; if (_logging_level > 1) { sprintf(str, "MIRROR change entry #%d to #%d", message_id, max_id + 1); write_logfile(lbs, str); } /* rearrange local message not to conflict with remote message */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { el_move_message(lbs, message_id, max_id + 1); sprintf(str, "ID%d:\t", message_id); sprintf(str + strlen(str), loc("Changed local entry ID to %d"), max_id + 1); mprint(lbs, mode, str); /* current message has been changed, so start over */ i_msg--; } else { sprintf(str, "ID%d:\t", message_id); sprintf(str + strlen(str), loc("Local entry ID should be changed to %d"), max_id + 1); mprint(lbs, mode, str); } } flush_return_buffer(); } /* go through remote message which do not exist locally */ for (i_remote = 0; i_remote < n_remote; i_remote++) if (md5_remote[i_remote].message_id) { message_id = md5_remote[i_remote].message_id; for (i_msg = 0; i_msg < *lbs->n_el_index; i_msg++) if (message_id == lbs->el_index[i_msg].message_id) break; if (i_msg == *lbs->n_el_index) { for (i_cache = 0; i_cache < n_cache; i_cache++) if (md5_cache[i_cache].message_id == message_id) break; exist_cache = i_cache < n_cache; if (!exist_cache) { all_identical = FALSE; if (mode == SYNC_HTML) { if (getcfg_topgroup()) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else if (sync_all) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else rsprintf("ID%d:\t", message_id, message_id); flush_return_buffer(); } else if (mode == SYNC_CLONE) { eprintf("ID%d:\t", message_id); } /* if message does not exist locally and in cache, it is new, so retrieve it */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { receive_message(lbs, list[index], message_id, error_str, TRUE); if (error_str[0]) { sprintf(str, "Error receiving message: %s", error_str); mprint(lbs, mode, str); } else if (mode == SYNC_HTML) { rsprintf("%s\n", loc("Remote entry received")); } else if (mode == SYNC_CLONE) { eprintf("%s\n", loc("Remote entry received")); } else { sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry received")); mprint(lbs, mode, str); } } else { sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received")); mprint(lbs, mode, str); } } else { if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)) { /* compare MD5s */ if (get_verbose() >= VERBOSE_INFO) { eprintf("ID-%5: none", message_id); eprintf("\nCache : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_cache[i_cache].md5_digest[j]); eprintf("\nRemote : "); for (j = 0; j < 16; j++) eprintf("%02X", md5_remote[i_remote].md5_digest[j]); eprintf("\n\n"); } all_identical = FALSE; /* if message has changed remotely, receive it */ if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { receive_message(lbs, list[index], message_id, error_str, TRUE); if (error_str[0]) { sprintf(str, "Error receiving message: %s", error_str); mprint(lbs, mode, str); } else if (mode == SYNC_HTML) { if (getcfg_topgroup()) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else if (sync_all) rsprintf("ID%d:\t", lbs->name_enc, message_id, message_id); else rsprintf("ID%d:\t", message_id, message_id); rsprintf("%s\n", loc("Remote entry received")); } } else { sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received")); mprint(lbs, mode, str); } } else { /* if message does not exist locally but in cache, delete remote message */ all_identical = FALSE; if (!isparam("confirm") && mode == SYNC_HTML) { combine_url(lbs, list[index], "", str, sizeof(str), NULL); sprintf(rem_ref, "%s", str, message_id, loc("Remote entry")); sprintf(str, loc("%s should be deleted"), rem_ref); rsprintf("ID%d:\t%s\n", message_id, str); n_delete++; } else if (!isparam("confirm") && mode == SYNC_CLONE) { sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted remotely")); mprint(lbs, mode, str); } else { if (_logging_level > 1) { sprintf(str, "MIRROR delete remote entry #%d", message_id); write_logfile(lbs, str); } sprintf(str, "%d?cmd=%s&confirm=%s", message_id, loc("Delete"), loc("Yes")); combine_url(lbs, list[index], str, url, sizeof(url), &ssl); if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) { retrieve_url(lbs, url, ssl, &buffer); if (strstr(buffer, "Location: ")) { if (mode == SYNC_HTML) rsprintf("ID%d:\t%s\n", message_id, loc("Entry deleted remotely")); } else { if (mode == SYNC_HTML && isparam("debug")) rsputs(buffer); mprint(lbs, mode, loc("Error deleting remote entry")); } md5_cache[i_cache].message_id = -1; xfree(buffer); } else { sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted remotely")); mprint(lbs, mode, str); } } } } } flush_return_buffer(); } xfree(md5_remote); /* save remote MD5s in file */ if (!all_identical) { n_remote = retrieve_remote_md5(lbs, list[index], &md5_remote, error_str); if (n_remote < 0) rsprintf("%s\n", error_str); /* keep conflicting messages in cache */ for (i = 0; i < n_cache; i++) if (md5_cache[i].message_id != -1) { if (i == 0) memcpy(md5_remote[0].md5_digest, md5_cache[0].md5_digest, 16); else for (j = 0; j < n_remote; j++) if (md5_remote[j].message_id == md5_cache[i].message_id) { memcpy(md5_remote[j].md5_digest, md5_cache[i].md5_digest, 16); break; } } if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) save_md5(lbs, list[index], md5_remote, n_remote); if (md5_remote) xfree(md5_remote); } if (md5_cache) xfree(md5_cache); if (mode == SYNC_HTML && n_delete) { if (getcfg_topgroup()) rsprintf("
", lbs->name_enc); else if (sync_all) rsprintf("
", lbs->name_enc); else rsprintf("
", lbs->name_enc); if (n_delete > 1) rsprintf(loc("Click here to delete %d entries"), n_delete); else rsprintf(loc("Click here to delete this entry")); rsprintf("\n"); } if (mode == SYNC_HTML && all_identical) rsprintf(loc("All entries identical")); if (mode == SYNC_CLONE && all_identical) mprint(lbs, mode, loc("All entries identical")); if (mode == SYNC_HTML) rsprintf("\n"); } flush_return_buffer(); keep_alive = FALSE; } /*------------------------------------------------------------------*/ void synchronize(LOGBOOK * lbs, int mode) { int i; char str[256], pwd[256]; if (mode == SYNC_HTML) { show_html_header(NULL, FALSE, loc("Synchronization"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("\n"); } if (lbs == NULL) { for (i = 0; lb_list[i].name[0]; i++) if (getcfg(lb_list[i].name, "mirror server", str, sizeof(str))) { if (exist_top_group() && getcfg_topgroup()) if (lb_list[i].top_group[0] && !strieq(lb_list[i].top_group, getcfg_topgroup())) continue; /* skip if excluded */ if (getcfg(lb_list[i].name, "Mirror exclude", str, sizeof(str)) && atoi(str) == 1) continue; /* if called by cron, set user name and password */ if (mode == SYNC_CRON && getcfg(lb_list[i].name, "mirror user", str, sizeof(str))) { if (get_user_line(&lb_list[i], str, pwd, NULL, NULL, NULL, NULL, NULL) == EL_SUCCESS) { setparam("unm", str); setparam("upwd", pwd); } } synchronize_logbook(&lb_list[i], mode, TRUE); } } else synchronize_logbook(lbs, mode, FALSE); if (mode == SYNC_HTML) { rsprintf("\n", loc("Back")); rsprintf("
%s

\n"); rsprintf("\n"); flush_return_buffer(); keep_alive = FALSE; } } /*------------------------------------------------------------------*/ void display_line(LOGBOOK * lbs, int message_id, int number, char *mode, int expand, int level, BOOL printable, int n_line, int show_attachments, int show_att_column, char *date, char *in_reply_to, char *reply_to, int n_attr_disp, char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL disp_attr_link[MAX_N_ATTR + 4], char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, BOOL show_text, char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], char *encoding, BOOL select, int *n_display, char *locked_by, int highlight, regex_t * re_buf, int highlight_mid, int absolute_link, char *draft) { char str[NAME_LENGTH], ref[256], *nowrap, rowstyle[80], tdstyle[80], format[256], file_name[MAX_PATH_LENGTH], *slist, *svalue, comment[256], param[80], subdir[256]; char display[NAME_LENGTH], attr_icon[80]; int i, j, n, i_line, index, colspan, n_attachments, line_len, thumb_status, max_line_len, n_lines, max_n_lines; BOOL skip_comma; FILE *f; struct tm *pts; time_t ltime; slist = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH); svalue = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH); _current_message_id = message_id; ref[0] = 0; if (absolute_link) compose_base_url(lbs, ref, sizeof(ref), FALSE); sprintf(ref + strlen(ref), "../%s/%d", lbs->name_enc, message_id); if (strieq(mode, "Summary")) { if (draft && draft[0]) strcpy(rowstyle, "listdraft"); else if (highlight_mid == message_id) { if (number % 2 == 1) strcpy(rowstyle, "list1h"); else strcpy(rowstyle, "list2h"); } else { if (number % 2 == 1) strcpy(rowstyle, "list1"); else strcpy(rowstyle, "list2"); } } else if (strieq(mode, "Full")) { if (highlight_mid == message_id) strcpy(rowstyle, "list1h"); else strcpy(rowstyle, "list1"); } else if (strieq(mode, "Threaded")) { if (draft && draft[0]) strcpy(rowstyle, "threaddraft"); else if (highlight) { if (highlight == message_id) strcpy(rowstyle, "thread"); else strcpy(rowstyle, "threadreply"); } else { if (highlight_mid == message_id) { if (level == 0) strcpy(rowstyle, "threadh"); else strcpy(rowstyle, "threadreply"); } else { if (level == 0) strcpy(rowstyle, "thread"); else strcpy(rowstyle, "threadreply"); } } } rsprintf(""); /* check attributes for row style */ for (i = 0; i < n_attr; i++) { if (attrib[i][0] == 0) sprintf(str, "Style %s \"\"", attr_list[i]); else sprintf(str, "Style %s %s", attr_list[i], attrib[i]); if (getcfg(lbs->name, str, display, sizeof(display))) { sprintf(str, "%s\" style=\"%s", rowstyle, display); strlcpy(rowstyle, str, sizeof(rowstyle)); break; } } /* only single cell for threaded display */ if (strieq(mode, "Threaded")) { rsprintf("", rowstyle); if (locked_by && locked_by[0]) { sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by); rsprintf("\"%s\" ", str, str); } /* show select box */ if (select && level == 0) rsprintf("\n", (*n_display)++, message_id); for (i = 0; i < level; i++) rsprintf("   "); /* display "+" if expandable */ if (expand == 0 && (reply_to[0] || in_reply_to[0])) rsprintf("+ "); } nowrap = printable ? "" : "nowrap"; skip_comma = FALSE; if (getcfg(lbs->name, "List conditions", str, sizeof(str)) && atoi(str) == 1) evaluate_conditions(lbs, attrib); if (strieq(mode, "Threaded") && getcfg(lbs->name, "Thread display", display, sizeof(display))) { /* check if to use icon from attributes */ attr_icon[0] = 0; if (getcfg(lbs->name, "Thread icon", attr_icon, sizeof(attr_icon))) { for (i = 0; i < n_attr; i++) if (strieq(attr_list[i], attr_icon)) break; if (i < n_attr && attrib[i][0]) strcpy(attr_icon, attrib[i]); else attr_icon[0] = 0; } if (highlight != message_id) rsprintf("\n", ref); if (attr_icon[0]) rsprintf("\n\"%s\"\n ", attr_icon, attr_icon, attr_icon); else { /* display standard icons */ if (level == 0) rsprintf("\n\"%s\"\n ", loc("Entry"), loc("Entry")); else rsprintf("\n\"%s\"\n ", loc("Reply"), loc("Reply")); } if (highlight != message_id) rsprintf("\n"); j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); if (highlight != message_id) rsprintf("\n\n", ref); else rsprintf("\n"); if (is_html(display) && !is_script(display) && html_allowed(lbs)) rsputs(display); else rsputs2(lbs, absolute_link, display); rsputs(" "); for (i = n = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) n++; if (n > 5) { if (highlight != message_id) rsprintf("", ref); rsprintf("%dx", n); rsprintf(""); } else { for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) { strlcpy(str, attachment[i], sizeof(str)); str[13] = 0; sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14); url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */ rsprintf("", ref); rsprintf ("\"%s\"", attachment[i] + 14, attachment[i] + 14); } } if (highlight != message_id) rsprintf("\n", ref); else rsprintf("\n"); } else { /* show select box */ if (select && !strieq(mode, "Threaded")) { rsprintf("", rowstyle); rsprintf("\n", (*n_display)++, message_id); rsprintf("\n"); } if (strieq(mode, "Threaded")) { if (highlight != message_id) rsprintf("\n\n", ref); else rsprintf("\n"); } skip_comma = TRUE; for (index = 0; index < n_attr_disp; index++) { if (strieq(disp_attr[index], loc("ID"))) { if (strieq(mode, "Threaded")) { if (level == 0) rsprintf("\n\"%s\" ", loc("Entry"), loc("Entry")); else rsprintf("\n\"%s\" ", loc("Reply"), loc("Reply")); } else { rsprintf("", rowstyle); if (locked_by && locked_by[0]) { sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by); rsprintf("\n\"%s\" ", str, str); } if (draft && draft[0]) { strlcpy(display, loc("Draft"), sizeof(display)); } else { if (getcfg(lbs->name, "ID display", display, sizeof(display))) { j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); } else sprintf(display, "%d", message_id); } rsprintf("\n  %s  \n", ref, display); rsprintf("\n"); } } if (strieq(disp_attr[index], loc("Logbook"))) { if (strieq(mode, "Threaded")) { if (skip_comma) { rsprintf(" %s", lbs->name); skip_comma = FALSE; } else rsprintf(", %s", lbs->name); } else { if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf("\n%s\n", rowstyle, nowrap, ref, lbs->name); else rsprintf("\n%s\n", rowstyle, nowrap, lbs->name); } } if (strieq(disp_attr[index], loc("Edit"))) { if (!strieq(mode, "Threaded")) { rsprintf("\n", rowstyle, nowrap, ref, loc("Edit")); rsprintf("\n\"%s\"\n", loc("Edit entry"), loc("Edit entry")); } } if (strieq(disp_attr[index], loc("Delete"))) { if (!strieq(mode, "Threaded")) { rsprintf("\n", rowstyle, nowrap, ref, loc("Delete")); rsprintf("\n\"%s\"\n", loc("Delete entry"), loc("Delete entry")); } } if (strieq(disp_attr[index], loc("Date"))) { if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = date_to_ltime(date); pts = localtime(<ime); assert(pts); my_strftime(str, sizeof(str), format, pts); if (strieq(mode, "Threaded")) { if (skip_comma) { rsprintf(" %s", str); skip_comma = FALSE; } else rsprintf(", %s", str); } else { if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf("\n%s\n", rowstyle, nowrap, ref, str); else rsprintf("\n%s\n", rowstyle, nowrap, str); } } for (i = 0; i < n_attr; i++) if (strieq(disp_attr[index], attr_list[i])) { /* check attributes for cell style */ strlcpy(tdstyle, rowstyle, sizeof(tdstyle)); sprintf(str, "Cell Style %s %s", attr_list[i], attrib[i]); if (getcfg(lbs->name, str, display, sizeof(display))) { sprintf(str, "%s\" style=\"%s", rowstyle, display); strlcpy(tdstyle, str, sizeof(rowstyle)); } if (strieq(mode, "Threaded")) { if (strieq(attr_options[i][0], "boolean")) { if (atoi(attrib[i]) == 1) { if (skip_comma) { rsprintf(" "); skip_comma = FALSE; } else rsprintf(", "); if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs)) rsputs(attrib[i]); else rsputs2(lbs, absolute_link, attrib[i]); rsprintf(" "); } } else if (attr_flags[i] & AF_DATE) { if (skip_comma) { rsprintf(" "); skip_comma = FALSE; } else rsprintf(", "); sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); rsputs(str); } else if (attr_flags[i] & AF_DATETIME) { if (skip_comma) { rsprintf(" "); skip_comma = FALSE; } else rsprintf(", "); sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); rsputs(str); } else if (attr_flags[i] & AF_ICON) { sprintf(str, "Icon comment %s", attrib[i]); getcfg(lbs->name, str, comment, sizeof(comment)); if (!comment[0]) strcpy(comment, attrib[i]); if (attrib[i][0]) rsprintf(" \n\"%s\" ", attrib[i], comment, comment); } else { if (skip_comma) { rsprintf(" "); skip_comma = FALSE; } else rsprintf(", "); if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs)) rsputs(attrib[i]); else rsputs2(lbs, absolute_link, attrib[i]); } } else { if (strieq(attr_options[i][0], "boolean")) { if (atoi(attrib[i]) == 1) rsprintf("\n\n", tdstyle); else rsprintf("\n\n", tdstyle); } else if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf("\n%s\n", tdstyle, nowrap, ref, str); else rsprintf("\n%s\n", tdstyle, nowrap, str); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf("\n%s\n", tdstyle, nowrap, ref, str); else rsprintf("\n%s\n", tdstyle, nowrap, str); } else if (attr_flags[i] & AF_ICON) { rsprintf("", rowstyle); sprintf(str, "Icon comment %s", attrib[i]); getcfg(lbs->name, str, comment, sizeof(comment)); if (!comment[0]) strcpy(comment, attrib[i]); if (attrib[i][0]) rsprintf("\n\"%s\"", attrib[i], comment, comment); rsprintf(" "); } else { rsprintf("", tdstyle); if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf("", ref); sprintf(str, "List Change %s", attr_list[i]); if (getcfg(lbs->name, str, display, sizeof(display))) { j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); } else strcpy(display, attrib[i]); if (is_html(display) && !is_script(display) && html_allowed(lbs)) rsputs(display); else { if (isparam(attr_list[i])) { highlight_searchtext(re_buf + 1 + i, display, str, TRUE); strlcpy(display, str, sizeof(display)); } else if (isparam("subtext") && isparam("sall") && atoi(getparam("sall"))) { highlight_searchtext(re_buf, display, str, TRUE); strlcpy(display, str, sizeof(display)); } rsputs2(lbs, absolute_link, display); } if (disp_attr_link == NULL || disp_attr_link[index]) rsprintf(""); /* at least one space to produce non-empty table cell */ if (!display[0]) rsprintf(" "); rsprintf(""); } } } } if (strieq(mode, "Threaded")) { rsputs(" "); for (i = n = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) n++; if (n > 5) { if (highlight != message_id) rsprintf("", ref); rsprintf("%dx", n); rsprintf(""); } else { for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) { strlcpy(str, attachment[i], sizeof(str)); str[13] = 0; sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14); url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */ rsprintf("", ref); rsprintf ("\"%s\"", attachment[i] + 14, attachment[i] + 14); } } if (highlight != message_id) rsprintf("\n"); else rsprintf("\n"); } } if (strieq(mode, "Threaded") && expand > 1 && show_text) { rsprintf("\n"); rsprintf(""); if (expand == 2) { for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; i++, line_len++) { str[i] = text[i]; if (line_break(text + i, encoding)) { i_line++; line_len = 0; } else /* limit line length to 150 characters */ if (line_len > 150 && text[i] == ' ') { str[i] = '\n'; i_line++; line_len = 0; } if (i_line == n_line) break; } str[i] = 0; /* only show text, not to rip apart HTML documents, e.g. only the start of a table */ if (strieq(encoding, "HTML")) strip_html(str); if (str[0]) strencode_nouml(str); else rsputs(" "); } else { if (strieq(encoding, "plain")) { rsputs("

");
            if (text[0])
               rsputs2(lbs, absolute_link, text);
            else
               rsputs(" ");
            rsputs("
"); } else if (strieq(encoding, "ELCode")) rsputs_elcode(lbs, FALSE, text); else if (text[0]) rsputs(text); else rsputs(" "); } rsprintf("\n"); } if (strieq(mode, "Summary") && n_line > 0 && show_text) { rsprintf(""); if (getcfg(lbs->name, "Summary line length", param, sizeof(param))) max_line_len = atoi(param); else max_line_len = n_line >= 10 ? 140 : 40; for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; line_len++, i++) { str[i] = text[i]; if (line_break(text + i, encoding)) { i_line++; line_len = 0; } else /* limit line length to max_line_len characters */ if (line_len > max_line_len && text[i] == ' ') { str[i] = '\n'; i_line++; line_len = 0; } if (i_line == n_line) break; } str[i] = 0; /* only show text, not to rip apart HTML documents, e.g. only the start of a table */ if (strieq(encoding, "HTML")) strip_html(str); if (str[0]) strencode_nouml(str); else rsputs(" "); rsputs("\n"); } if (show_att_column) { /* show attachment icons */ rsputs(" "); for (i = n = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) n++; if (n > 5) { if (highlight != message_id) rsprintf("", ref); rsprintf("%dx", n); rsprintf(""); } else { for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment && attachment[i][0]) { strlcpy(str, attachment[i], sizeof(str)); str[13] = 0; sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14); url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */ rsprintf("", ref); rsprintf ("\"%s\"", attachment[i] + 14, attachment[i] + 14); } } rsputs(" "); } colspan = n_attr_disp; if (select) colspan++; if (strieq(mode, "Full") && show_text) { if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) { rsprintf("", colspan); if (strieq(encoding, "plain")) { rsputs("
");
            rsputs2(lbs, absolute_link, text);
            rsputs("
"); } else if (strieq(encoding, "ELCode")) rsputs_elcode(lbs, FALSE, text); else rsputs(text); rsprintf("\n"); } /* count number of attachments */ n_attachments = 0; if (show_attachments) { for (index = 0; index < MAX_ATTACHMENTS; index++) { if (attachment[index][0]) { /* check if attachment is inlined */ sprintf(str, "[img]elog:/%d[/img]", index + 1); if (strieq(encoding, "ELCode") && stristr(text, str)) continue; n_attachments++; } } } for (index = 0; index < MAX_ATTACHMENTS; index++) { if (show_attachments && attachment[index][0]) { /* check if attachment is inlined */ if (is_inline_attachment(encoding, message_id, text, index, attachment[index])) continue; strlcpy(str, attachment[index], sizeof(str)); str[13] = 0; sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[index] + 14); url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */ strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[index], sizeof(file_name)); thumb_status = create_thumbnail(lbs, file_name); if (!show_attachments) { rsprintf("%s     ", ref, attachment[index] + 14); } else { if (thumb_status) { rsprintf ("%s %d: %s\n", colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14); if (show_attachments) { rsprintf("\n", colspan); if (thumb_status == 2) { for (i = 0;; i++) { strlcpy(str, file_name, sizeof(str)); if (chkext(str, ".pdf") || chkext(str, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; sprintf(str + strlen(str), "-%d.png", i); if (file_exist(str)) { strlcpy(str, ref, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; sprintf(str + strlen(str), "-%d.png", i); rsprintf("\n", index + 1, ref); rsprintf("\"%s\"\n", str, attachment[index] + 14, attachment[index] + 14); } else break; } } else { rsprintf("\n", index + 1, ref); strlcpy(str, ref, sizeof(str)); if (chkext(str, ".pdf") || chkext(str, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; strlcat(str, ".png", sizeof(str)); rsprintf("\"%s\"\n", str, attachment[index] + 14, attachment[index] + 14); } rsprintf("\n\n"); } } else { if (is_image(attachment[index])) { rsprintf ("%s %d: %s\n", colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14); if (show_attachments) { rsprintf(""); rsprintf("", colspan); rsprintf("\"%s\"", ref, attachment[index] + 14, attachment[index] + 14); rsprintf("\n"); } } else { rsprintf ("%s %d: %s\n", colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14); strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[index], sizeof(file_name)); if (is_ascii(file_name) && !chkext(attachment[index], ".PS") && !chkext(attachment[index], ".PDF") && !chkext(attachment[index], ".EPS") && !chkext(attachment[index], ".SVG") && !chkext(attachment[index], ".HTM") && !chkext(attachment[index], ".HTML") && show_attachments) { rsprintf("\n"); /* display attachment */ rsprintf("\n", colspan); /* anchor for references */ rsprintf("\n", index + 1); /* display attachment */ if (!chkext(attachment[index], ".HTML")) rsprintf("
");

                        strlcpy(file_name, lbs->data_dir, sizeof(file_name));
                        generate_subdir_name(attachment[index], subdir, sizeof(subdir));
                        strlcat(file_name, subdir, sizeof(file_name));
                        strlcat(file_name, attachment[index], sizeof(file_name));

                        f = fopen(file_name, "rt");

                        n_lines = 0;
                        if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
                           max_n_lines = atoi(str);
                        else
                           max_n_lines = 300;

                        if (f != NULL) {
                           while (!feof(f)) {
                              str[0] = 0;
                              fgets(str, sizeof(str), f);

                              if (n_lines < max_n_lines) {
                                 if (!chkext(attachment[index], ".HTML"))
                                    rsputs2(lbs, absolute_link, str);
                                 else
                                    rsputs(str);
                              }
                              n_lines++;
                           }
                           fclose(f);
                        }

                        if (!chkext(attachment[index], ".HTML"))
                           rsprintf("
"); rsprintf("\n"); if (max_n_lines == 0) rsprintf("%d lines\n", n_lines); else if (n_lines > max_n_lines) rsprintf("... %d more lines ...\n", n_lines - max_n_lines); } rsprintf("\n"); } } } } } } xfree(slist); xfree(svalue); } /*------------------------------------------------------------------*/ void display_reply(LOGBOOK * lbs, int message_id, int printable, int expand, int n_line, int n_attr_disp, char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL show_text, int level, int highlight, regex_t * re_buf, int highlight_mid, int absolute_link) { char *date, *text, *in_reply_to, *reply_to, *encoding, *attachment, *locked_by, *draft, *attrib, *p; int status, size; text = (char *) xmalloc(TEXT_SIZE); attachment = (char *) xmalloc(MAX_PATH_LENGTH * MAX_ATTACHMENTS); attrib = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH); date = (char *) xmalloc(80); in_reply_to = (char *) xmalloc(80); reply_to = (char *) xmalloc(256); encoding = (char *) xmalloc(80); locked_by = (char *) xmalloc(256); draft = (char *) xmalloc(256); if (draft == NULL) return; reply_to[0] = 0; size = TEXT_SIZE; status = el_retrieve(lbs, message_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, (char (*)[256]) attachment, encoding, locked_by, draft); if (status != EL_SUCCESS || draft[0]) { xfree(text); xfree(attachment); xfree(attrib); xfree(date); xfree(in_reply_to); xfree(reply_to); xfree(encoding); xfree(locked_by); xfree(draft); return; } display_line(lbs, message_id, 0, "threaded", expand, level, printable, n_line, FALSE, FALSE, date, in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, (char (*)[1500]) attrib, lbs->n_attr, text, show_text, (char (*)[256]) attachment, encoding, 0, NULL, locked_by, highlight, &re_buf[0], highlight_mid, absolute_link, draft); if (reply_to[0]) { p = reply_to; do { display_reply(lbs, atoi(p), printable, expand, n_line, n_attr_disp, disp_attr, show_text, level + 1, highlight, &re_buf[0], highlight_mid, absolute_link); while (*p && isdigit(*p)) p++; while (*p && (*p == ',' || *p == ' ')) p++; } while (*p); } xfree(text); xfree(attachment); xfree(attrib); xfree(date); xfree(in_reply_to); xfree(reply_to); xfree(encoding); xfree(locked_by); xfree(draft); } /*------------------------------------------------------------------*/ int msg_compare(const void *m1, const void *m2) { return strcoll(((MSG_LIST *) m1)->string, ((MSG_LIST *) m2)->string); } int msg_compare_reverse(const void *m1, const void *m2) { return strcoll(((MSG_LIST *) m2)->string, ((MSG_LIST *) m1)->string); } int msg_compare_numeric(const void *m1, const void *m2) { return ((MSG_LIST *) m1)->number - ((MSG_LIST *) m2)->number; } int msg_compare_reverse_numeric(const void *m1, const void *m2) { return ((MSG_LIST *) m2)->number - ((MSG_LIST *) m1)->number; } /*------------------------------------------------------------------*/ char *param_in_str(char *str, char *param) { char *p; p = str; do { if (stristr(p, param) == NULL) return NULL; p = stristr(p, param); /* if parameter not followed by '=', skip it */ if (*(p + strlen(param)) != '=') { p += strlen(param); continue; } /* if parameter is value of another parameter, skip it */ if (p > str + 1 && *(p - 1) == '=') { p += strlen(param); continue; } if (*p == 0) return NULL; return p; } while (1); } /*------------------------------------------------------------------*/ BOOL subst_param(char *str, int size, char *param, char *value) { int len; char *p1, *p2, *s, param_enc[256], str2[256]; strlcpy(param_enc, param, sizeof(param_enc)); url_slash_encode(param_enc, sizeof(param_enc)); if (!value[0]) { /* remove parameter */ s = param_in_str(str, param_enc); if (s == NULL) return FALSE; /* remove parameter */ p1 = s - 1; for (p2 = p1 + strlen(param_enc) + 1; *p2 && *p2 != '&'; p2++); strlcpy(p1, p2, size - (p1 - str)); if (!strchr(str, '?') && strchr(str, '&')) *strchr(str, '&') = '?'; return TRUE; } if ((p1 = param_in_str(str, param_enc)) == NULL) { if (strchr(str, '?')) strlcat(str, "&", size); else strlcat(str, "?", size); strlcat(str, param_enc, size); strlcat(str, "=", size); strlcat(str, value, size); return FALSE; } p1 += strlen(param_enc) + 1; for (p2 = p1; *p2 && *p2 != '&'; p2++); len = p2 - p1; if (len > (int) strlen(value)) { /* new value is shorter than old one */ strlcpy(str2, value, size - (p1 - str)); strlcpy(str2 + strlen(value), p2, size - (p1 + strlen(value) - str)); strlcpy(p1, str2, size - (p1 - str)); } else { /* new value is longer than old one */ s = (char *) xmalloc(size); strlcpy(s, p2, size); strlcpy(str2, value, size - (p1 - str)); strlcat(str2, s, size - (p1 + strlen(value) - str)); strlcpy(p1, str2, size - (p1 - str)); xfree(s); } return TRUE; } /*------------------------------------------------------------------*/ BOOL logged_in(LOGBOOK *lbs) { if (isparam("unm")) { if (check_login_user(lbs, getparam("unm")) && check_login(lbs, getparam("sid"))) return TRUE; } return FALSE; } /*------------------------------------------------------------------*/ BOOL is_user_allowed(LOGBOOK * lbs, char *command) { char str[1000], users[2000]; char list[MAX_N_LIST][NAME_LENGTH]; int i, n; /* check for user level access */ if (!getcfg(lbs->name, "Password file", str, sizeof(str))) return TRUE; /* check for deny */ sprintf(str, "Deny %s", command); if (getcfg(lbs->name, str, users, sizeof(users))) { if (!isparam("unm")) return FALSE; /* check if current user in list */ n = strbreak(users, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strieq(list[i], getparam("unm"))) break; if (i < n) return FALSE; } /* check admin command */ if (strieq(command, loc("Admin"))) { if (getcfg(lbs->name, "Admin user", str, sizeof(str))) { return is_admin_user(lbs, getparam("unm")); } } /* check for allow */ sprintf(str, "Allow %s", command); if (!getcfg(lbs->name, str, users, sizeof(users))) return TRUE; /* check if current user in list */ if (!isparam("unm")) return FALSE; n = strbreak(users, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strieq(list[i], getparam("unm"))) return TRUE; return FALSE; } /*------------------------------------------------------------------*/ int is_draft(LOGBOOK * lbs, int message_id) { char draft[256]; el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, draft); return draft[0]; } /*------------------------------------------------------------------*/ BOOL is_command_allowed(LOGBOOK * lbs, char *command, int message_id) { char str[1000], menu_str[1000], other_str[1000]; char menu_item[MAX_N_LIST][NAME_LENGTH]; int i, n; if (command[0] == 0) return TRUE; /* check for guest access */ if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs)) getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str)); /* default menu commands */ if (menu_str[0] == 0) { strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Synchronize, Find, "); if (getcfg(lbs->name, "Password file", str, sizeof(str))) { if (is_admin_user(lbs, getparam("unm"))) { strcat(menu_str, "Admin, "); strcat(menu_str, "Change config file, "); strcat(menu_str, "Delete this logbook, "); strcat(menu_str, "Rename this logbook, "); strcat(menu_str, "Create new logbook, "); strcat(menu_str, "GetPwdFile, "); if (is_admin_user(NULL, getparam("unm"))) { if (lbs->top_group[0]) { sprintf(str, "Change [global %s]", lbs->top_group); strcat(menu_str, str); strcat(menu_str, ", "); } if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) { strcat(menu_str, "Change [global]"); strcat(menu_str, ", "); } } } strcat(menu_str, "Config, Logout, "); } else { strcat(menu_str, "Config, "); strcat(menu_str, "Change [global], "); strcat(menu_str, "Delete this logbook, "); strcat(menu_str, "Rename this logbook, "); strcat(menu_str, "Create new logbook, "); } strcat(menu_str, "Help, HelpELCode, "); } else { /* check for admin command */ n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE); menu_str[0] = 0; for (i = 0; i < n; i++) { if (strcmp(menu_item[i], "Admin") == 0) { if (!is_admin_user(lbs, getparam("unm"))) continue; } strcat(menu_str, menu_item[i]); strcat(menu_str, ", "); } strcat(menu_str, "HelpELCode, Synchronize, "); if (is_admin_user(lbs, getparam("unm"))) { strcat(menu_str, "Change config file, "); strcat(menu_str, "Delete this logbook, "); strcat(menu_str, "Rename this logbook, "); strcat(menu_str, "Create new logbook, "); strcat(menu_str, "GetPwdFile, "); if (is_admin_user(NULL, getparam("unm"))) { if (lbs->top_group[0]) { sprintf(str, "Change [global %s]", lbs->top_group); strcat(menu_str, str); strcat(menu_str, ", "); } if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) { strcat(menu_str, "Change [global]"); strcat(menu_str, ", "); } } } } /* check list menu commands */ str[0] = 0; if (!getcfg(lbs->name, "Guest List Menu commands", str, sizeof(str)) || logged_in(lbs)) getcfg(lbs->name, "list menu commands", str, sizeof(str)); if (!str[0]) { if (!getcfg(lbs->name, "Guest Find Menu commands", str, sizeof(str)) || logged_in(lbs)) getcfg(lbs->name, "Find Menu commands", str, sizeof(str)); } if (str[0]) strlcat(menu_str, str, sizeof(menu_str)); else { strlcat(menu_str, "New, Find, Select, Last x, Help, HelpELCode, ", sizeof(menu_str)); if (getcfg(lbs->name, "Password file", str, sizeof(str))) strlcat(menu_str, "Admin, Config, Logout, ", sizeof(menu_str)); else strlcat(menu_str, "Config, ", sizeof(menu_str)); } strlcpy(other_str, "Preview, Back, Search, Download, Import, CSV Import, XML Import, ", sizeof(other_str)); strlcat(other_str, "Cancel, First, Last, Previous, Next, Requested, Forgot, ", sizeof(other_str)); /* only allow Submit & Co if "New" is allowed */ if (stristr(menu_str, "New,") || stristr(menu_str, "Edit")) strlcat(other_str, "Update, Upload, Submit, Save, ", sizeof(other_str)); /* add save for new user registration */ if (isparam("new_user_name")) strlcat(other_str, "Save, ", sizeof(other_str)); /* admin commands */ if (is_admin_user(lbs, getparam("unm"))) { strlcat(other_str, "Remove user, New user, Activate, ", sizeof(other_str)); } else if (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0) { strlcat(other_str, "Remove user, New user, ", sizeof(other_str)); } /* allow always edit of draft messages */ if (is_draft(lbs, message_id)) strlcat(other_str, "Edit, ", sizeof(other_str)); /* allow change password if "config" possible */ if (strieq(command, loc("Change password")) && stristr(menu_str, "Config")) { return TRUE; } /* exclude non-localized submit for elog */ else if (command[0] && strieq(command, "Submit") && (stristr(menu_str, "New,") || stristr(menu_str, "Edit"))) { return TRUE; } /* exclude other non-localized commands */ else if (command[0] && strieq(command, "GetMD5")) { return TRUE; } else if (command[0] && strieq(command, "IM")) { return TRUE; } /* check if command is present in the menu list */ else if (command[0]) { n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i]))) break; if (i == n) { n = strbreak(other_str, menu_item, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i]))) break; if (i == n) return FALSE; } } return TRUE; } /*------------------------------------------------------------------*/ void build_ref(char *ref, int size, char *mode, char *expand, char *attach, char *new_entries) { char str[1000], *p; if (strchr(getparam("cmdline"), '?')) strlcat(ref, strchr(getparam("cmdline"), '?'), size); /* eliminate old search */ if (strstr(ref, "cmd=Search&")) strlcpy(strstr(ref, "cmd=Search&"), strstr(ref, "cmd=Search&") + 11, sizeof(str)); /* eliminate id=xxx */ if (strstr(ref, "id=")) { p = strstr(ref, "id=") + 3; while (*p && isdigit(*p)) p++; strlcpy(strstr(ref, "id="), p, sizeof(str)); if (strlen(ref) > 0 && ref[strlen(ref) - 1] == '?') ref[strlen(ref) - 1] = 0; } /* eliminate old mode if new one is present */ if (mode[0]) subst_param(ref, size, "mode", mode); /* eliminate old expand if new one is present */ if (expand[0]) subst_param(ref, size, "expand", expand); /* eliminate old attach if new one is present */ if (attach[0]) subst_param(ref, size, "attach", attach); /* eliminate old new_entries if new one is present */ if (new_entries[0]) subst_param(ref, size, "new_entries", new_entries); /* eliminate old last= */ if (isparam("last")) subst_param(ref, size, "last", getparam("last")); /* replace any '&' by '&' */ strlcpy(str, ref, sizeof(str)); strencode2(ref, str, size); } /*------------------------------------------------------------------*/ void show_page_filters(LOGBOOK * lbs, int n_msg, int page_n, BOOL mode_commands, char *mode) { int cur_exp, n, i, j, i1, i2, index, attr_index, size; char ref[256], str[NAME_LENGTH], comment[NAME_LENGTH], list[MAX_N_LIST][NAME_LENGTH], option[NAME_LENGTH], option_whole[NAME_LENGTH]; rsprintf("\n"); rsprintf("\n"); rsprintf("\n"); if (mode_commands) { rsprintf("\n"); } rsprintf("
\n"); if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) { if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; build_ref(ref, sizeof(ref), "full", "", "", ""); if (strieq(mode, "full")) rsprintf(" %s |", loc("Full")); else rsprintf(" %s |", ref, loc("Full")); } if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; build_ref(ref, sizeof(ref), "summary", "", "", ""); if (strieq(mode, "summary")) rsprintf(" %s |", loc("Summary")); else rsprintf(" %s |", ref, loc("Summary")); if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; build_ref(ref, sizeof(ref), "threaded", "", "", ""); if (strieq(mode, "threaded")) rsprintf(" %s ", loc("Threaded")); else rsprintf(" %s ", ref, loc("Threaded")); if (strieq(mode, "full")) { if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; cur_exp = 0; if (strieq(mode, "full")) cur_exp = 1; if (isparam("elattach")) cur_exp = atoi(getparam("elattach")); if (isparam("attach")) cur_exp = atoi(getparam("attach")); if (cur_exp) { build_ref(ref, sizeof(ref), "", "", "0", ""); rsprintf("| %s ", ref, loc("Hide attachments")); } else { build_ref(ref, sizeof(ref), "", "", "1", ""); rsprintf("| %s ", ref, loc("Show attachments")); } } if (strieq(mode, "threaded")) { if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; cur_exp = 1; if (getcfg(lbs->name, "Expand default", str, sizeof(str))) cur_exp = atoi(str); if (isparam("expand")) cur_exp = atoi(getparam("expand")); if (cur_exp > 0) { sprintf(str, "%d", cur_exp > 0 ? cur_exp - 1 : 0); build_ref(ref, sizeof(ref), "", str, "", ""); rsprintf("| %s ", ref, loc("Collapse")); } else rsprintf("| %s ", loc("Collapse")); if (cur_exp < 3) { if (page_n != 1) sprintf(ref, "page%d", page_n); else ref[0] = 0; sprintf(str, "%d", cur_exp < 3 ? cur_exp + 1 : 3); build_ref(ref, sizeof(ref), "", str, "", ""); rsprintf("| %s ", ref, loc("Expand")); } else rsprintf("| %s ", loc("Expand")); } rsprintf("\n"); /*---- filter menu text ----*/ if (getcfg(lbs->name, "filter menu text", str, sizeof(str))) { FILE *f; char file_name[256], *buf; /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strcpy(file_name, str); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } f = fopen(file_name, "rb"); if (f != NULL) { fseek(f, 0, SEEK_END); size = TELL(fileno(f)); fseek(f, 0, SEEK_SET); buf = (char *) xmalloc(size + 1); fread(buf, 1, size, f); buf[size] = 0; fclose(f); rsputs(buf); } else rsprintf("
Error: file \"%s\" not found
", file_name); } ref[0] = 0; if (!isparam("new_entries") || atoi(getparam("new_entries")) == 0) { build_ref(ref, sizeof(ref), "", "", "", "1"); rsprintf ("\"%s\"  ", ref, loc("Show only new entries"), loc("Show only new entries")); } else { build_ref(ref, sizeof(ref), "", "", "", "0"); rsprintf ("\"%s\"  ", ref, loc("Show all entries"), loc("Show all entries")); } if (getcfg(lbs->name, "Quick filter", str, sizeof(str))) { n = strbreak(str, list, MAX_N_LIST, ",", FALSE); if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str)) rsprintf("\n"); for (index = 0; index < n; index++) { /* find according attribute index */ for (attr_index = 0; attr_index < lbs->n_attr; attr_index++) if (strieq(list[index], attr_list[attr_index])) break; if (attr_index == lbs->n_attr && !strieq(list[index], "Date") && !strieq(list[index], "Subtext") && !strieq(list[index], "ID")) { rsprintf("Error: Attribute \"%s\" for quick filter not found", list[index]); attr_index = 0; } if (strieq(list[index], "Date")) { i = isparam("last") ? atoi(getparam("last")) : 0; rsprintf("\n"); } else if (strieq(attr_options[attr_index][0], "boolean")) { sprintf(str, loc("Select %s"), list[index]); rsprintf("\n"); } else { /* check if attribute has options */ if (attr_list[attr_index][0] == 0 || attr_options[attr_index][0][0] == 0) { if (attr_flags[attr_index] & (AF_DATE | AF_DATETIME)) { rsprintf("\n"); } else { if (strieq(list[index], "Subtext")) { rsprintf ("\n", str); } else { sprintf(str, loc("Enter %s"), list[index]); rsprintf ("\n", list[index], str); } } } else { sprintf(str, loc("Select %s"), list[index]); rsprintf(" \n"); } } } /* show button if JavaScript is switched off */ rsprintf("\n"); /* show submit button for IE (otherwise will not work) */ if (strstr(browser, "MSIE")) rsprintf("\n", loc("Search")); } rsprintf(" %d %s ", n_msg, loc("Entries")); rsprintf("
\n\n"); } /*------------------------------------------------------------------*/ void show_page_navigation(LOGBOOK * lbs, int n_msg, int page_n, int n_page) { int i, num_pages, skip, max_n_msg; char ref[256], str[256]; if (!page_n || n_msg <= n_page) return; rsprintf("\n"); rsprintf("%s \n", loc("Goto page")); if (page_n > 1) { sprintf(ref, "page%d", page_n - 1); build_ref(ref, sizeof(ref), "", "", "", ""); rsprintf("%s  ", ref, loc("Previous")); } if (page_n && n_msg > n_page) { /* number of pages */ num_pages = (n_msg - 1) / n_page + 1; skip = FALSE; for (i = 1; i <= num_pages; i++) { sprintf(ref, "page%d", i); build_ref(ref, sizeof(ref), "", "", "", ""); if (i <= 3 || (i >= page_n - 1 && i <= page_n + 1) || i >= num_pages - 2) { if (i > 1 && !skip) rsprintf(", \n"); skip = FALSE; } else { if (!skip) rsprintf(" ... "); skip = TRUE; } if (skip) continue; if (page_n == i) rsprintf("%d", i); else rsprintf("%d", ref, i); /* if (i == num_pages ) rsprintf("  \n"); else rsprintf(", "); */ } rsprintf("  \n"); } if (page_n != -1 && n_page < n_msg && page_n * n_page < n_msg) { sprintf(ref, "page%d", page_n + 1); build_ref(ref, sizeof(ref), "", "", "", ""); rsprintf("%s  ", ref, loc("Next")); } if (getcfg(lbs->name, "All display limit", str, sizeof(str))) max_n_msg = atoi(str); else max_n_msg = 500; if (page_n != -1 && n_page < n_msg && n_msg < max_n_msg) { sprintf(ref, "page"); build_ref(ref, sizeof(ref), "", "", "", ""); rsprintf("%s\n", ref, loc("All")); } rsprintf("\n"); } /*------------------------------------------------------------------*/ void show_select_navigation(LOGBOOK * lbs) { int i, n_log; char str[NAME_LENGTH]; char lbk_list[MAX_N_LIST][NAME_LENGTH]; rsprintf("\n"); rsprintf("\n"); rsprintf("%s: \n", loc("Selected entries")); rsprintf("\n", loc("Toggle all")); if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Delete")) { rsprintf("\n", loc("Delete")); } if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Edit")) { rsprintf("\n", loc("Edit")); } if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Copy to")) { rsprintf("\n", loc("Copy to")); rsprintf("\n"); } if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Move to")) { rsprintf("\n", loc("Move to")); rsprintf("\n"); } rsprintf("\n\n"); } /*------------------------------------------------------------------*/ time_t retrieve_date(char *index, BOOL bstart) { int year, month, day, hour, min, sec, current_year, current_month; char pm[10], py[10], pd[10], ph[10], pn[10], ps[10], str[NAME_LENGTH], str2[NAME_LENGTH]; struct tm tms; time_t ltime; sprintf(pm, "m%s", index); sprintf(py, "y%s", index); sprintf(pd, "d%s", index); sprintf(ph, "h%s", index); sprintf(pn, "n%s", index); sprintf(ps, "c%s", index); time(<ime); memcpy(&tms, localtime(<ime), sizeof(tms)); current_year = tms.tm_year + 1900; current_month = tms.tm_mon + 1; if (!isparam(pm) && !isparam(py) && !isparam(pd)) return 0; /* if year not given, use current year */ if (!isparam(py)) year = current_year; else year = atoi(getparam(py)); if (year < 1970) { sprintf(str, "Error: Year %s out of range", getparam(py)); strencode2(str2, str, sizeof(str2)); show_error(str2); return -1; } /* if month not given, use current month */ if (isparam(pm)) { month = atoi(getparam(pm)); } else month = current_month; if (isparam(pd)) day = atoi(getparam(pd)); else { /* if day not given, use 1 if start date */ if (bstart) day = 1; else { /* use last day of month */ memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1 + 1; tms.tm_mday = 1; tms.tm_hour = 12; if (tms.tm_year < 90) tms.tm_year += 100; ltime = mktime(&tms); ltime -= 3600 * 24; memcpy(&tms, localtime(<ime), sizeof(struct tm)); day = tms.tm_mday; } } /* if hour not given, use 0 */ if (isparam(ph)) { hour = atoi(getparam(ph)); } else hour = 0; /* if minute not given, use 0 */ if (isparam(pn)) { min = atoi(getparam(pn)); } else min = 0; /* if second not given, use 0 */ if (isparam(ps)) { sec = atoi(getparam(ps)); } else sec = 0; memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1; tms.tm_mday = day; tms.tm_hour = hour; tms.tm_min = min; tms.tm_sec = sec; tms.tm_isdst = -1; if (tms.tm_year < 90) tms.tm_year += 100; ltime = mktime(&tms); if (!bstart && isparam(ph) == 0) /* end time is first second of next day */ ltime += 3600 * 24; return ltime; } /*------------------------------------------------------------------*/ time_t convert_date(char *date_string) { /* convert date string in MM/DD/YY or DD.MM.YY format into Unix time */ int year, month, day; char *p, str[256]; struct tm tms; time_t ltime; strlcpy(str, date_string, sizeof(str)); month = day = year = 0; if (strchr(str, '/')) { /* MM/DD/YY format */ p = strtok(str, "/"); if (p) { month = atoi(p); p = strtok(NULL, "/"); if (p) { day = atoi(p); p = strtok(NULL, "/"); if (p) year = atoi(p); } } } else if (strchr(str, '.')) { /* DD.MM.YY format */ p = strtok(str, "."); if (p) { day = atoi(p); p = strtok(NULL, "."); if (p) { month = atoi(p); p = strtok(NULL, "."); if (p) year = atoi(p); } } } else return 0; /* calculate years */ if (year > 1900) /* 1900-2100 */ year += 0; else if (year < 70) /* 00-69 */ year += 2000; else if (year < 100) /* 70-99 */ year += 1900; /* use last day of month */ memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1; tms.tm_mday = day; tms.tm_hour = 12; ltime = mktime(&tms); return ltime; } /*------------------------------------------------------------------*/ time_t convert_datetime(char *date_string) { /* convert date string in MM/DD/YY h:m:s AM/PM or DD.MM.YY hh:m:s format into Unix time */ int year, month, day, hour, min = 0, sec = 0; char *p, str[256]; struct tm tms; time_t ltime; strlcpy(str, date_string, sizeof(str)); month = day = year = 0; if (strchr(str, '/')) { /* MM/DD/YY format */ p = strtok(str, "/"); if (p) { month = atoi(p); p = strtok(NULL, "/"); if (p) { day = atoi(p); p = strtok(NULL, "/"); if (p) year = atoi(p); } } } else if (strchr(str, '.')) { /* DD.MM.YY format */ p = strtok(str, "."); if (p) { day = atoi(p); p = strtok(NULL, "."); if (p) { month = atoi(p); p = strtok(NULL, "."); if (p) year = atoi(p); } } } else return 0; if (!strchr(p, ' ')) return 0; p = strchr(p, ' ') + 1; strlcpy(str, p, sizeof(str)); p = strtok(str, ":"); if (p) { hour = atoi(p); p = strtok(NULL, ":"); if (p) { min = atoi(p); p = strtok(NULL, ":"); if (p) sec = atoi(p); } } else return 0; if (stristr(p, "PM") && hour < 12) hour += 12; /* calculate years */ if (year > 1900) /* 1900-2100 */ year += 0; else if (year < 70) /* 00-69 */ year += 2000; else if (year < 100) /* 70-99 */ year += 1900; /* use last day of month */ memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1; tms.tm_mday = day; tms.tm_hour = hour; tms.tm_min = min; tms.tm_sec = sec; ltime = mktime(&tms); return ltime; } /*------------------------------------------------------------------*/ void show_rss_feed(LOGBOOK * lbs) { int i, n, size, index, message_id, offset; char str[256], charset[256], url[256], attrib[MAX_N_ATTR][NAME_LENGTH], date[80], *text, title[2000], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], draft[1000]; time_t ltime; struct tm *ts; rsprintf("HTTP/1.1 200 Document follows\r\n"); rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision()); if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); rsprintf("Content-Type: text/xml;charset=%s\r\n", charset); rsprintf("\r\n"); rsprintf("\n", charset); rsprintf("\n"); rsprintf("\n"); rsprintf("ELOG %s\n", lbs->name); /* retrive URL */ getcfg(lbs->name, "URL", url, sizeof(url)); /* if HTTP request comes from localhost, use localhost as absolute link (needed if running on DSL at home) */ if (!url[0] && strstr(http_host, "localhost")) { if (_ssl_flag) strcpy(url, "https://localhost"); else strcpy(url, "http://localhost"); if (elog_tcp_port != 80) sprintf(url + strlen(url), ":%d", elog_tcp_port); strcat(url, "/"); } if (!url[0]) { /* assemble absolute path from host name and port */ if (_ssl_flag) sprintf(url, "https://%s", host_name); else sprintf(url, "http://%s", host_name); if (elog_tcp_port != 80) sprintf(url + strlen(url), ":%d", elog_tcp_port); strcat(url, "/"); } /* add trailing '/' if not present */ if (url[strlen(url) - 1] != '/') strcat(url, "/"); strlcat(url, lbs->name_enc, sizeof(url)); rsprintf("%s\n", url); if (getcfg(lbs->name, "Comment", str, sizeof(str))) { rsprintf(""); xmlencode(str); rsprintf("\n"); } rsprintf("ELOG V%s\n", VERSION); rsprintf("\n"); rsprintf("%s/elog.png\n", url); rsprintf("ELOG %s\n", lbs->name_enc); rsprintf("%s\n", url); rsprintf("\n"); /*---- show last items ----*/ n = 15; if (getcfg(lbs->name, "RSS Entries", str, sizeof(str))) n = atoi(str); text = (char *) xmalloc(TEXT_SIZE); message_id = el_search_message(lbs, EL_LAST, 0, FALSE); for (index = 0; index < n && message_id; index++) { size = TEXT_SIZE; el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, NULL, NULL, NULL, NULL, NULL, draft); /* skip drafts */ if (draft[0]) { message_id = el_search_message(lbs, EL_PREV, message_id, FALSE); continue; } /* limit text size to 2k */ text[2048] = 0; if (getcfg(lbs->name, "RSS Title", title, sizeof(title))) { i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &i); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &i, 0); strsubst_list(title, sizeof(title), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, i); } else { title[0] = 0; for (i = 0; i < lbs->n_attr; i++) { if (attrib[i][0]) { strlcat(title, attrib[i], sizeof(title)); if (i < lbs->n_attr - 1) strlcat(title, ", ", sizeof(title)); } } } rsprintf("\n"); /* convert date to RFC-822 date */ setlocale(LC_ALL, "C"); ltime = date_to_ltime(date); ts = localtime(<ime); assert(ts); strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S", ts); offset = (-(int) my_timezone()); if (ts->tm_isdst) offset += 3600; snprintf(date, sizeof(date) - 1, "%s %+03d%02d", str, (int) (offset / 3600), (int) ((abs((int) offset) / 60) % 60)); getcfg("global", "Language", str, sizeof(str)); if (str[0]) setlocale(LC_ALL, str); rsprintf(""); xmlencode(title); rsprintf("\n"); rsprintf(""); strlcpy(str, url, sizeof(str)); sprintf(str + strlen(str), "/%d", message_id); xmlencode(str); rsprintf("\n"); rsprintf("\n"); xmlencode(text); rsprintf("\n"); rsprintf("\n"); rsprintf("%s", date); rsprintf("\n"); rsprintf("\n"); message_id = el_search_message(lbs, EL_PREV, message_id, FALSE); } xfree(text); rsprintf("\n"); rsprintf("\n"); } /*------------------------------------------------------------------*/ void highlight_searchtext(regex_t * re_buf, char *src, char *dst, int hidden) { char *pt, *pt1; int size, status; regmatch_t pmatch[10]; dst[0] = 0; pt = src; /* original text */ pt1 = dst; /* text with inserted coloring */ do { status = regexec(re_buf, pt, 10, pmatch, 0); if (status != REG_NOMATCH) { size = pmatch[0].rm_so; /* abort if zero length match, for example from "m*" */ if (pmatch[0].rm_eo - pmatch[0].rm_so == 0) { status = REG_NOMATCH; strcpy(pt1, pt); break; } /* copy first part original text */ memcpy(pt1, pt, size); pt1 += size; pt += size; /* add coloring 1st part */ /* here: \001='<', \002='>', /003='"', and \004=' ' */ /* see also rsputs2(char* ) */ if (hidden) strcpy(pt1, "\001B\004style=\003color:black;background-color:#ffff66\003\002"); else strcpy(pt1, ""); pt1 += strlen(pt1); /* copy origial search text */ size = pmatch[0].rm_eo - pmatch[0].rm_so; memcpy(pt1, pt, size); pt1 += size; pt += size; /* add coloring 2nd part */ if (hidden) strcpy(pt1, "\001/B\002"); else strcpy(pt1, ""); pt1 += strlen(pt1); } } while (status != REG_NOMATCH); strcpy(pt1, pt); } /*------------------------------------------------------------------*/ time_t search_last_reply(LOGBOOK * lbs, int *message_id) { char reply_to[MAX_REPLY_TO * 10], date[80]; int n_reply, i, id; char *list; time_t lt, last; list = (char *) xmalloc(MAX_REPLY_TO * NAME_LENGTH); el_retrieve(lbs, *message_id, date, NULL, NULL, 0, NULL, 0, NULL, reply_to, NULL, NULL, NULL, NULL); lt = date_to_ltime(date); /* if no reply, this is the last message in thread */ if (reply_to[0] == 0) { xfree(list); return lt; } n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_REPLY_TO, ",", FALSE); last = lt; for (i = 0; i < n_reply; i++) { id = atoi(list + i * NAME_LENGTH); lt = search_last_reply(lbs, &id); if (lt > last) { last = lt; *message_id = id; } } xfree(list); return last; } /*------------------------------------------------------------------*/ void show_elog_list(LOGBOOK * lbs, int past_n, int last_n, int page_n, BOOL default_page, char *info) { int i, j, n, index, size, status, d1, m1, y1, h1, n1, c1, d2, m2, y2, h2, n2, c2, n_line, flags, printable, n_logbook, n_display, reverse, numeric, n_attr_disp, n_msg, search_all, message_id, n_page, i_start, i_stop, in_reply_to_id, page_mid, page_mid_head, level, refresh, disp_attr_flags[MAX_N_ATTR + 4]; char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], *list, *text, *text1, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], str[NAME_LENGTH], ref[256], img[80], comment[NAME_LENGTH], mode[80], mid[80], menu_str[1000], menu_item[MAX_N_LIST][NAME_LENGTH], param[NAME_LENGTH], format[80], sort_attr[MAX_N_ATTR + 4][NAME_LENGTH], mode_cookie[80], charset[25], sort_item[NAME_LENGTH], refr[80], str2[80], draft[256]; char *p, *pt1, *pt2, *slist, *svalue, *gattr, line[1024], iattr[256]; BOOL show_attachments, threaded, csv, xml, raw, mode_commands, expand, filtering, date_filtering, disp_filter, show_text, text_in_attr, searched, found, disp_attr_link[MAX_N_ATTR + 4], sort_attributes, show_att_column = 0; time_t ltime, ltime_start, ltime_end, now, ltime1, ltime2, entry_ltime; struct tm tms, *ptms; MSG_LIST *msg_list; LOGBOOK *lbs_cur; regex_t re_buf[MAX_N_ATTR + 1]; regmatch_t pmatch[10]; /* redirect if empty parameters */ if (strstr(_cmdline, "=&")) { while ((pt1 = strstr(_cmdline, "=&")) != NULL) { pt2 = pt1; while (*pt1 != '&' && *pt1 != '?') pt1--; pt1++; strcpy(param, pt1); param[pt2 - pt1] = 0; memmove(pt1, pt2 + 2, strlen(pt2 + 2) + 1); /* remove param from lastcmd if present */ if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) { sprintf(str, "%s%%3D", param); if ((pt1 = strstr(_cmdline, str)) != NULL) { pt2 = pt1 + strlen(str); while (*pt2 && *pt2 != '%') pt2++; if (*pt2 == '%') pt2 += 3; memmove(pt1, pt2, strlen(pt2) + 1); } } } if (_cmdline[strlen(_cmdline) - 1] == '=') { pt1 = _cmdline + strlen(_cmdline) - 1; while (*pt1 != '&' && *pt1 != '?') pt1--; pt1++; strcpy(param, pt1); if (param[strlen(param) - 1] == '=') param[strlen(param) - 1] = 0; *pt1 = 0; /* remove param from lastcmd if present */ if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) { sprintf(str, "%s%%3D", param); if ((pt1 = strstr(_cmdline, str)) != NULL) { pt2 = pt1 + strlen(str); while (*pt2 && *pt2 != '%' && *pt2 != '&') pt2++; if (*pt2 == '%') pt2 += 3; memmove(pt1, pt2, strlen(pt2) + 1); } } } if (_cmdline[strlen(_cmdline) - 1] == '&') _cmdline[strlen(_cmdline) - 1] = 0; redirect(lbs, _cmdline); return; } /* redirect "go" command */ if (isparam("lastcmd")) { strlcpy(str, getparam("lastcmd"), sizeof(str)); url_decode(str); /* subsitute "last" in command line from new parameter */ if (isparam("last")) { if (strieq(getparam("last"), "_all_")) subst_param(str, sizeof(str), "last", ""); else subst_param(str, sizeof(str), "last", getparam("last")); } /* subsitute attributes in command line from new parameter */ for (i = 0; i < MAX_N_ATTR; i++) if (isparam(attr_list[i])) { if (strieq(getparam(attr_list[i]), "_all_")) subst_param(str, sizeof(str), attr_list[i], ""); else subst_param(str, sizeof(str), attr_list[i], getparam(attr_list[i])); } /* do the same for subtext */ if (isparam("subtext")) subst_param(str, sizeof(str), "subtext", getparam("subtext")); redirect(lbs, str); return; } /* remove remaining "_all_" in parameters */ if (isparam("last") && strieq(getparam("last"), "_all_")) { strlcpy(str, _cmdline, sizeof(str)); subst_param(str, sizeof(str), "last", ""); redirect(lbs, str); return; } /* remove remaining "_all_" or empty or "--++--" parameters */ strlcpy(str, _cmdline, sizeof(str)); found = 0; for (i = 0; i < MAX_N_ATTR; i++) { if (isparam(attr_list[i])) { if (strieq(getparam(attr_list[i]), "_all_")) { subst_param(str, sizeof(str), attr_list[i], ""); found = 1; } if (*getparam(attr_list[i]) == 0) { subst_param(str, sizeof(str), attr_list[i], ""); found = 1; } sprintf(ref, "-- %s --", attr_list[i]); if (strieq(getparam(attr_list[i]), ref)) { subst_param(str, sizeof(str), attr_list[i], ""); found = 1; } } } if (isparam("subtext")) { if (*getparam("subtext") == 0) { subst_param(str, sizeof(str), "subtext", ""); found = 1; } sprintf(ref, "-- %s --", loc("Text")); if (strieq(getparam("subtext"), ref)) { subst_param(str, sizeof(str), "subtext", ""); found = 1; } } if (found) { redirect(lbs, str); return; } slist = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH); svalue = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH); gattr = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH); list = (char *) xmalloc(10000); printable = isparam("Printable") ? atoi(getparam("Printable")) : 0; /* in printable mode, display all pages */ if (printable) page_n = -1; if (isparam("Reverse")) reverse = atoi(getparam("Reverse")); else { reverse = 0; if (getcfg(lbs->name, "Reverse sort", str, sizeof(str))) reverse = atoi(str); } /* get message ID from "list" command */ if (isparam("id")) page_mid = atoi(getparam("id")); else page_mid = 0; page_mid_head = 0; /* default mode */ strlcpy(mode, "Summary", sizeof(mode)); show_attachments = FALSE; /* check for valid page_n */ if (page_n < -1) page_n = 0; if (past_n || last_n || page_n || page_mid || default_page) { /* for page display, get mode from config file */ if (getcfg(lbs->name, "Display Mode", str, sizeof(str))) strlcpy(mode, str, sizeof(mode)); /* supersede mode from cookie */ if (isparam("elmode")) strlcpy(mode, getparam("elmode"), sizeof(mode)); /* supersede mode from direct parameter */ if (isparam("mode")) strlcpy(mode, getparam("mode"), sizeof(mode)); } else { /* for find result, get mode from find form */ if (isparam("mode")) strlcpy(mode, getparam("mode"), sizeof(mode)); else strlcpy(mode, "Full", sizeof(mode)); } /* set cookie if mode changed */ mode_cookie[0] = 0; if (strieq(mode, "Summary") || strieq(mode, "Full") || strieq(mode, "Threaded")) { if (!isparam("elmode") || !strieq(getparam("elmode"), mode)) sprintf(mode_cookie, "elmode=%s", mode); } threaded = strieq(mode, "threaded"); csv = strieq(mode, "CSV1") || strieq(mode, "CSV2") || strieq(mode, "CSV3"); xml = strieq(mode, "XML"); raw = strieq(mode, "Raw"); if (csv || xml || raw) { page_n = -1; /* display all pages */ show_attachments = FALSE; /* hide attachments */ } /* show attachments in full mode by default */ if (strieq(mode, "Full")) show_attachments = TRUE; /* supersede attachment mode if in cookie */ if (isparam("elattach")) show_attachments = atoi(getparam("elattach")); /* supersede attachment mode if in parameter */ if (isparam("attach")) show_attachments = atoi(getparam("attach")); /* set cookie if attachment mode changed in full view */ if (mode_cookie[0] == 0 && strieq(mode, "Full")) { if (!isparam("elattach") || atoi(getparam("elattach")) != show_attachments) sprintf(mode_cookie, "elattach=%d", show_attachments); } /*---- convert dates to ltime ----*/ time(&now); ptms = localtime(&now); assert(ptms); ltime_end = ltime_start = 0; d1 = m1 = y1 = h1 = n1 = c1 = d2 = m2 = y2 = h2 = n2 = c2 = 0; if (!past_n && !last_n) { ltime_start = retrieve_date("a", TRUE); if (ltime_start < 0) { xfree(slist); xfree(svalue); xfree(gattr); xfree(list); return; } if (ltime_start) { memcpy(&tms, localtime(<ime_start), sizeof(struct tm)); y1 = tms.tm_year + 1900; m1 = tms.tm_mon + 1; d1 = tms.tm_mday; h1 = tms.tm_hour; n1 = tms.tm_min; c1 = tms.tm_sec; } ltime_end = retrieve_date("b", FALSE); if (ltime_end < 0) { xfree(slist); xfree(svalue); xfree(gattr); xfree(list); return; } if (ltime_end) { if (ltime_end <= ltime_start) { sprintf(str, "Error: Start date after end date"); show_error(str); xfree(slist); xfree(svalue); xfree(gattr); xfree(list); return; } memcpy(&tms, localtime(<ime_end), sizeof(struct tm)); y2 = tms.tm_year + 1900; m2 = tms.tm_mon + 1; d2 = tms.tm_mday; h2 = tms.tm_hour; n2 = tms.tm_min; c2 = tms.tm_sec; } } if (ltime_start && ltime_end && ltime_start > ltime_end) { show_error(loc("Error: start date after end date")); xfree(slist); xfree(svalue); xfree(gattr); xfree(list); return; } /*---- if user present but not allowed, log it out (required when several logbooks are used with different access rights and global passwords ----*/ if (isparam("unm") && !check_login_user(lbs, getparam("unm"))) { unsetparam("unm"); sid_remove(getparam("sid")); set_sid_cookie(lbs, "", ""); } /*---- apply last login cut ----*/ if (isparam("new_entries") && atoi(getparam("new_entries")) == 1 && isparam("unm")) get_user_line(lbs, getparam("unm"), NULL, NULL, NULL, NULL, <ime_start, NULL); /*---- assemble message list ----*/ /* check for search all */ search_all = isparam("all") ? atoi(getparam("all")) : 0; if (getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) && atoi(str) == 0) search_all = 0; n_msg = 0; n_display = 0; if (search_all) { /* count logbooks */ for (n_logbook = 0;; n_logbook++) { if (!lb_list[n_logbook].name[0]) break; if (lbs->top_group[0] && !strieq(lbs->top_group, lb_list[n_logbook].top_group)) continue; if (isparam("unm") && !check_login_user(&lb_list[n_logbook], getparam("unm"))) continue; n_msg += *lb_list[n_logbook].n_el_index; } } else { n_logbook = 1; n_msg = *lbs->n_el_index; } msg_list = (MSG_LIST *) xmalloc(sizeof(MSG_LIST) * n_msg); lbs_cur = lbs; numeric = TRUE; for (i = n = 0; i < n_logbook; i++) { if (search_all) lbs_cur = &lb_list[i]; if (lbs->top_group[0] && !strieq(lbs->top_group, lbs_cur->top_group)) continue; if (isparam("unm") && !check_login_user(lbs_cur, getparam("unm"))) continue; for (j = 0; j < *lbs_cur->n_el_index; j++) { msg_list[n].lbs = lbs_cur; msg_list[n].index = j; msg_list[n].number = (int) lbs_cur->el_index[j].file_time; msg_list[n].in_reply_to = lbs_cur->el_index[j].in_reply_to; n++; } } /*---- apply start/end date cut ----*/ date_filtering = FALSE; if (past_n > 0) ltime_start = now - 3600 * 24 * past_n; // past n days else if (past_n < 0) ltime_start = now + 3600 * past_n; // past n hours if (last_n && last_n < n_msg) { date_filtering = TRUE; for (i = n_msg - last_n - 1; i >= 0; i--) msg_list[i].lbs = NULL; } if (ltime_start) { date_filtering = TRUE; for (i = 0; i < n_msg; i++) if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < ltime_start) msg_list[i].lbs = NULL; } if (ltime_end) { date_filtering = TRUE; for (i = 0; i < n_msg; i++) if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time > ltime_end) msg_list[i].lbs = NULL; } if (isparam("last")) { date_filtering = TRUE; n = atoi(getparam("last")); if (n > 0) { for (i = 0; i < n_msg; i++) if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < now - 3600 * 24 * n) msg_list[i].lbs = NULL; } } /*---- filter message list ----*/ filtering = FALSE; show_text = TRUE; searched = found = FALSE; for (i = 0; i < lbs->n_attr; i++) { /* check if attribute filter */ if (isparam(attr_list[i])) break; if (attr_flags[i] & (AF_DATE | AF_DATETIME)) { sprintf(str, "%da", i); if (retrieve_date(str, TRUE)) break; sprintf(str, "%db", i); if (retrieve_date(str, TRUE)) break; } if (attr_flags[i] & AF_MULTI) { for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) { filtering = TRUE; break; } } } if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) { for (j = 0; j < MAX_N_LIST; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) { filtering = TRUE; break; } } } /* check if sort by attribute */ if ((isparam("sort") && strieq(getparam("sort"), attr_list[i])) || (isparam("rsort") && strieq(getparam("rsort"), attr_list[i]))) break; } /* turn on filtering if found */ if (i < lbs->n_attr) filtering = TRUE; if (isparam("subtext")) filtering = TRUE; if (getcfg(lbs->name, "Sort Attributes", list, 10000)) filtering = TRUE; text = (char *) xmalloc(TEXT_SIZE); text1 = (char *) xmalloc(TEXT_SIZE); /* prepare for regex search */ memset(re_buf, 0, sizeof(re_buf)); /* compile regex for subtext */ if (isparam("subtext")) { strlcpy(str, getparam("subtext"), sizeof(str)); flags = REG_EXTENDED; if (!isparam("casesensitive")) flags |= REG_ICASE; status = regcomp(re_buf, str, flags); if (status) { sprintf(line, loc("Error in regular expression \"%s\""), str); strlcat(line, ": ", sizeof(line)); regerror(status, re_buf, str, sizeof(str)); strlcat(line, str, sizeof(line)); strencode2(str, line, sizeof(str)); show_error(str); return; } } /* compile regex for attributes */ for (i = 0; i < lbs->n_attr; i++) { if (isparam(attr_list[i])) { strlcpy(str, getparam(attr_list[i]), sizeof(str)); /* if value starts with '$', substitute it */ if (str[0] == '$') { j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); setparam(attr_list[i], str); } flags = REG_EXTENDED; if (!isparam("casesensitive")) flags |= REG_ICASE; status = regcomp(re_buf + i + 1, str, flags); if (status) { sprintf(line, loc("Error in regular expression \"%s\""), str); strlcat(line, ": ", sizeof(line)); regerror(status, re_buf + i + 1, str, sizeof(str)); strlcat(line, str, sizeof(line)); strencode2(str, line, sizeof(str)); show_error(str); return; } } } sort_item[0] = 0; if (isparam("sort")) strlcpy(sort_item, getparam("sort"), sizeof(sort_item)); if (isparam("rsort")) strlcpy(sort_item, getparam("rsort"), sizeof(sort_item)); sort_attributes = getcfg(lbs->name, "Sort Attributes", str, sizeof(str)); /* do filtering */ for (index = 0; index < n_msg; index++) { if (!msg_list[index].lbs) continue; /* retrieve message */ size = TEXT_SIZE; message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id; if (filtering) { status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) break; /* apply filter for attributes */ for (i = 0; i < lbs->n_attr; i++) { /* replace icon name with their comments if present */ if (attr_flags[i] & AF_ICON) { sprintf(str, "Icon comment %s", attrib[i]); if (getcfg(lbs->name, str, comment, sizeof(comment))) strlcpy(attrib[i], comment, NAME_LENGTH); } /* check for multi attributes */ if (attr_flags[i] & AF_MULTI) { /* OR of any of the values */ searched = found = FALSE; for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) { searched = TRUE; if (strstr(attrib[i], getparam(str))) { found = TRUE; break; } } } /* search for parameter without '_' coming from quick filter */ if (isparam(attr_list[i])) { searched = TRUE; strlcpy(str, getparam(attr_list[i]), sizeof(str)); if (str[0] == '^' && str[strlen(str) - 1] == '$') { str[strlen(str) - 1] = 0; strlcpy(comment, str + 1, NAME_LENGTH); } else strlcpy(comment, str, NAME_LENGTH); strlcpy(str, comment, sizeof(str)); if (strstr(attrib[i], str)) found = TRUE; } if (searched && !found) break; } /* check for multi user list or multi user email */ else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) { /* OR of any of the values */ searched = found = FALSE; for (j = 0; j < MAX_N_LIST; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) { searched = TRUE; if (strstr(attrib[i], getparam(str))) { found = TRUE; break; } } } /* search for parameter without '_' coming from quick filter */ if (isparam(attr_list[i])) { searched = TRUE; if (strstr(attrib[i], getparam(attr_list[i]))) found = TRUE; } if (searched && !found) break; } else if (attr_flags[i] & (AF_DATE | AF_DATETIME)) { /* check for last[i]/next[i] */ ltime = isparam(attr_list[i]) ? atoi(getparam(attr_list[i])) : 0; /* today 12h noon */ time(&now); memcpy(&tms, localtime(&now), sizeof(struct tm)); tms.tm_hour = 12; tms.tm_min = 0; tms.tm_sec = 0; now = mktime(&tms); /* negative i: last [i] days */ if (ltime < 0) if (atoi(attrib[i]) < now + ltime * 3600 * 24 - 3600 * 12 || atoi(attrib[i]) > now) break; /* positive i: next [i] days */ if (ltime > 0) if (atoi(attrib[i]) > now + ltime * 3600 * 24 + 3600 * 12 || atoi(attrib[i]) < now) break; /* check for start date / end date */ sprintf(str, "%da", i); ltime = retrieve_date(str, TRUE); if (ltime > 0 && atoi(attrib[i]) < ltime) break; sprintf(str, "%db", i); ltime = retrieve_date(str, FALSE); if (ltime > 0 && (atoi(attrib[i]) > ltime || atoi(attrib[i]) == 0)) break; } else { strlcpy(str, isparam(attr_list[i]) ? getparam(attr_list[i]) : "", sizeof(str)); /* if value starts with '$', substitute it */ if (str[0] == '$') { j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(mid, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", mid, &j); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); setparam(attr_list[i], str); } if (isparam(attr_list[i])) { status = regexec(re_buf + 1 + i, attrib[i], 10, pmatch, 0); if (status == REG_NOMATCH) break; } } } if (i < lbs->n_attr) { msg_list[index].lbs = NULL; continue; } if (isparam("subtext")) { status = regexec(re_buf, text, 10, pmatch, 0); if (isparam("sall") && atoi(getparam("sall")) && status == REG_NOMATCH) { // search text in attributes for (i = 0; i < lbs->n_attr; i++) { status = regexec(re_buf, attrib[i], 10, pmatch, 0); if (status != REG_NOMATCH) break; } if (i == lbs->n_attr) { msg_list[index].lbs = NULL; continue; } } else if (status == REG_NOMATCH) { msg_list[index].lbs = NULL; continue; } } } // if (filtering) /* evaluate "sort attributes" */ if (sort_attributes) { getcfg(lbs->name, "Sort Attributes", list, 10000); msg_list[index].string[0] = 0; n = strbreak(list, sort_attr, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n; i++) { for (j = 0; j < lbs->n_attr; j++) { if (strieq(sort_attr[i], attr_list[j])) { strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string)); strlcat(msg_list[index].string, attrib[j], sizeof(msg_list[index].string)); if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) { msg_list[index].number = atoi(attrib[j]); numeric = TRUE; } else numeric = FALSE; break; } } if (strieq(sort_attr[i], loc("ID"))) { strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string)); sprintf(str, "%08d", message_id); strlcat(msg_list[index].string, str, sizeof(msg_list[index].string)); } else if (strieq(sort_attr[i], loc("Logbook"))) { strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string)); strlcat(msg_list[index].string, msg_list[index].lbs->name, sizeof(msg_list[index].string)); } else if (strieq(sort_attr[i], loc("Date"))) { strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string)); entry_ltime = date_to_ltime(date); sprintf(str, "%08d", (int) entry_ltime); strlcat(msg_list[index].string, str, sizeof(msg_list[index].string)); } } } /* add attribute for sorting */ if (sort_item[0]) { for (i = 0; i < lbs->n_attr; i++) { if (strieq(sort_item, attr_list[i])) { if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) { numeric = TRUE; msg_list[index].number = atoi(attrib[i]); } else { numeric = FALSE; strlcpy(msg_list[index].string, attrib[i], 256); } } if (strieq(sort_item, loc("ID"))) { numeric = TRUE; msg_list[index].number = message_id; } if (strieq(sort_item, loc("Logbook"))) strlcpy(msg_list[index].string, msg_list[index].lbs->name, 256); } if (isparam("rsort")) reverse = 1; if (isparam("sort")) reverse = 0; } } /*---- in threaded mode, set date of latest entry of thread ----*/ if (threaded && !filtering && !date_filtering) { for (index = 0; index < n_msg; index++) { if (!msg_list[index].lbs) continue; message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id; in_reply_to_id = msg_list[index].lbs->el_index[msg_list[index].index].in_reply_to; if (!in_reply_to_id) continue; do { message_id = in_reply_to_id; /* search index of message */ for (i = 0; i < *msg_list[index].lbs->n_el_index; i++) if (msg_list[index].lbs->el_index[i].message_id == message_id) break; /* stop if not found */ if (i == *msg_list[index].lbs->n_el_index) break; in_reply_to_id = msg_list[index].lbs->el_index[i].in_reply_to; } while (in_reply_to_id); /* if head not found, skip message */ if (i == *msg_list[index].lbs->n_el_index) { msg_list[index].lbs = NULL; continue; } /* set new page message ID with head message */ if (page_mid && msg_list[index].lbs->el_index[msg_list[index].index].message_id == page_mid) page_mid_head = message_id; /* search message head in list */ for (j = 0; j < n_msg; j++) if (msg_list[j].lbs == msg_list[index].lbs && msg_list[j].index == i) break; if (j < index) { /* set date from current message, if later */ if (msg_list[j].number < msg_list[index].number) msg_list[j].number = msg_list[index].number; } /* now delete current message, to leave only heads in list */ msg_list[index].lbs = NULL; } } /*---- compact messasges ----*/ for (i = j = 0; i < n_msg; i++) if (msg_list[i].lbs) memcpy(&msg_list[j++], &msg_list[i], sizeof(MSG_LIST)); n_msg = j; /*---- sort messasges ----*/ if (numeric) qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse_numeric : msg_compare_numeric); else qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse : msg_compare); /*---- search page for specific message ----*/ if (getcfg(lbs->name, "Entries per page", str, sizeof(str))) n_page = atoi(str); else n_page = 20; if (isparam("npp")) n_page = atoi(getparam("npp")); if (page_mid) { default_page = 0; for (i = 0; i < n_msg; i++) if (msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid || msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid_head) break; if (i < n_msg) page_n = i / n_page + 1; } /*---- number of messages per page ----*/ n_attr_disp = n_line = 0; i_start = 0; i_stop = n_msg - 1; if (!csv && !xml && !raw) { if (page_n || default_page) { if (default_page && page_n != -1) page_n = reverse ? 1 : (n_msg - 1) / n_page + 1; if (page_n != -1) { i_start = (page_n - 1) * n_page; i_stop = i_start + n_page - 1; if (i_start >= n_msg && n_msg > 0) { page_n = 1; i_start = 0; } if (i_stop >= n_msg) i_stop = n_msg - 1; } } } /*---- header ----*/ if (getcfg(lbs->name, "List Page Title", str, sizeof(str))) { i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, NULL, TRUE); strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, i); strip_html(str); } else sprintf(str, "ELOG %s", lbs->name); if (csv) { /* no menus and tables */ show_plain_header(0, "export.csv"); rsprintf("\"%s\"", loc("Message ID")); if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); rsprintf("\"%s\"", loc("Date")); if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); for (i = 0; i < lbs->n_attr; i++) { strlcpy(str, attr_list[i], sizeof(str)); if (str[0]) { rsputs("\""); pt1 = str; while ((pt2 = strchr(pt1, '"')) != NULL) { *pt2 = 0; rsputs(pt1); rsputs("\"\""); pt1 = pt2 + 1; } rsputs(pt1); rsputs("\""); } if (i < lbs->n_attr - 1) { if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); } else { if (strieq(mode, "CSV3")) rsprintf(";\"Text\""); rsprintf("\r\n"); } } } else if (xml) { /* no menus and tables */ show_plain_header(0, "export.xml"); if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); rsprintf("\n", charset); rsprintf("\n", VERSION); rsprintf("\n"); } else if (raw) { /* no menus and tables */ show_plain_header(0, "export.txt"); } else { if (getcfg(lbs->name, "Refresh", refr, sizeof(refr))) refresh = atoi(refr); else refresh = 0; show_standard_header(lbs, TRUE, str, NULL, TRUE, mode_cookie, NULL, refresh); /*---- title ----*/ strlcpy(str, ", ", sizeof(str)); if (past_n == 1) strcat(str, loc("Last day")); else if (past_n > 1) sprintf(str + strlen(str), loc("Last %d days"), past_n); else if (past_n < 0) sprintf(str + strlen(str), loc("Last %d hours"), -past_n); else if (last_n) sprintf(str + strlen(str), loc("Last %d entries"), last_n); else if (page_n == -1) strlcpy(str + strlen(str), loc("all entries"), sizeof(str) - strlen(str)); else if (page_n) sprintf(str + strlen(str), loc("Page %d of %d"), page_n, (n_msg - 1) / n_page + 1); if (strlen(str) == 2) str[0] = 0; if (printable) show_standard_title(lbs, str, 1); else show_standard_title(lbs, str, 0); /*---- menu buttons ----*/ if (!printable) { rsprintf("\n"); /* current command line for select command */ strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str)); /* remove select switch */ if (strstr(str, "select=1")) { *strstr(str, "select=1") = 0; if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?')) str[strlen(str) - 1] = 0; } /* store current command line as hidden parameter for page navigation */ if (str[0] && !strieq(str, "?")) { rsprintf("\n", str); } if (!getcfg(lbs->name, "Guest Find menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs)) getcfg(lbs->name, "Find menu commands", menu_str, sizeof(menu_str)); if (!menu_str[0]) { if (!getcfg(lbs->name, "Guest list menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs)) getcfg(lbs->name, "list menu commands", menu_str, sizeof(menu_str)); } /* default menu commands */ if (menu_str[0] == 0) { strlcpy(menu_str, "New, Find, Select, Import, ", sizeof(menu_str)); if (getcfg(lbs->name, "Password file", str, sizeof(str))) strlcat(menu_str, "Config, Logout, ", sizeof(menu_str)); else strlcat(menu_str, "Config, ", sizeof(menu_str)); if (getcfg(lbs->name, "Mirror server", str, sizeof(str))) strlcat(menu_str, "Synchronize, ", sizeof(menu_str)); strlcpy(str, loc("Last x"), sizeof(str)); strlcat(menu_str, "Last x, Help, ", sizeof(menu_str)); } n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) { if (is_user_allowed(lbs, menu_item[i])) { if (strieq(menu_item[i], "Last x")) { if (past_n > 0) { sprintf(str, loc("Last %d days"), past_n * 2); rsprintf(" %s |\n", past_n * 2, mode, str); } else { strlcpy(str, loc("Last day"), sizeof(str)); rsprintf(" %s |\n", mode, str); } if (last_n) { sprintf(str, loc("Last %d entries"), last_n * 2); rsprintf(" %s |\n", last_n * 2, mode, str); } } else if (strieq(menu_item[i], "Select")) { strlcpy(str, getparam("cmdline"), sizeof(str)); if (isparam("select") && atoi(getparam("select")) == 1) { /* remove select switch */ if (strstr(str, "select=1")) { *strstr(str, "select=1") = 0; if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?')) str[strlen(str) - 1] = 0; } } else { /* add select switch */ if (strchr(str, '?')) strcat(str, "&select=1"); else strcat(str, "?select=1"); } rsprintf(" %s |\n", loc("Select")); } else { strlcpy(str, loc(menu_item[i]), sizeof(str)); url_encode(str, sizeof(str)); if (i < n - 1) rsprintf(" %s |\n", str, loc(menu_item[i])); else rsprintf(" %s \n", str, loc(menu_item[i])); } } } rsprintf("\n\n"); } /*---- list menu text ----*/ if ((getcfg(lbs->name, "find menu text", str, sizeof(str)) || getcfg(lbs->name, "list menu text", str, sizeof(str))) && !printable) { FILE *f; char file_name[256], *buf; rsprintf("\n"); /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strlcpy(file_name, str, sizeof(file_name)); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } f = fopen(file_name, "rb"); if (f != NULL) { fseek(f, 0, SEEK_END); size = TELL(fileno(f)); fseek(f, 0, SEEK_SET); buf = (char *) xmalloc(size + 1); fread(buf, 1, size, f); buf[size] = 0; fclose(f); rsputs(buf); } else rsprintf("
Error: file \"%s\" not found
", file_name); rsprintf("
"); } /*---- display filters ----*/ disp_filter = isparam("ma") || isparam("ya") || isparam("da") || isparam("mb") || isparam("yb") || isparam("db") || isparam("subtext") || isparam("last"); for (i = 0; i < lbs->n_attr; i++) if (isparam(attr_list[i]) && (attr_flags[i] & (AF_DATE | AF_DATETIME)) == 0) disp_filter = TRUE; for (i = 0; i < lbs->n_attr; i++) { if (attr_flags[i] & (AF_DATE | AF_DATETIME)) { sprintf(str, "%da", i); ltime = retrieve_date(str, TRUE); if (ltime > 0) disp_filter = TRUE; sprintf(str, "%db", i); ltime = retrieve_date(str, FALSE); if (ltime > 0) disp_filter = TRUE; } if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) { for (j = 0; j < MAX_N_LIST; j++) { sprintf(str, "%s_%d", attr_list[i], j); if (isparam(str)) disp_filter = TRUE; if (isparam(attr_list[i])) disp_filter = TRUE; } } } if (isparam("new_entries") && atoi(getparam("new_entries")) == 1) { rsprintf("\n"); rsprintf("\n"); rsprintf("", loc("New entries since")); memcpy(&tms, localtime(<ime_start), sizeof(struct tm)); my_strftime(str, sizeof(str), "%c", &tms); rsprintf("", str); rsprintf("
%s:%s
\n\n"); } if (disp_filter) { rsprintf("\n"); rsprintf("\n"); if (isparam("last")) { rsprintf("", loc("Restrict search to last")); strencode2(str, getparam("last"), sizeof(str)); rsprintf("", str, loc("days")); } if (isparam("ma") || isparam("ya") || isparam("da") || isparam("ha") || isparam("na") || isparam("ca")) { memset(&tms, 0, sizeof(struct tm)); tms.tm_year = y1 - 1900; tms.tm_mon = m1 - 1; tms.tm_mday = d1; tms.tm_hour = h1; tms.tm_min = n1; tms.tm_sec = c1; if (tms.tm_year < 90) tms.tm_year += 100; mktime(&tms); if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); strftime(str, sizeof(str), format, &tms); rsprintf("", loc("Start date")); rsprintf("", str); } if (isparam("mb") || isparam("yb") || isparam("db") || isparam("hb") || isparam("nb") || isparam("cb")) { memset(&tms, 0, sizeof(struct tm)); tms.tm_year = y2 - 1900; tms.tm_mon = m2 - 1; tms.tm_mday = d2; tms.tm_hour = h2; tms.tm_min = n2; tms.tm_sec = c2; if (tms.tm_year < 90) tms.tm_year += 100; ltime = mktime(&tms); memcpy(&tms, localtime(<ime), sizeof(struct tm)); if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); strftime(str, sizeof(str), format, &tms); rsprintf("", loc("End date")); rsprintf("", str); } for (i = 0; i < lbs->n_attr; i++) { if (attr_flags[i] & (AF_DATE | AF_DATETIME)) { sprintf(str, "%da", i); ltime1 = retrieve_date(str, TRUE); sprintf(str, "%db", i); ltime2 = retrieve_date(str, TRUE); if (ltime1 > 0 || ltime2 > 0) { rsprintf("", attr_list[i]); rsprintf("", comment); } } else if (attr_flags[i] & AF_MULTI) { line[0] = 0; for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) { sprintf(iattr, "%s_%d", attr_list[i], j); if (isparam(iattr)) { comment[0] = 0; if (attr_flags[i] & AF_ICON) { sprintf(str, "Icon comment %s", getparam(iattr)); getcfg(lbs->name, str, comment, sizeof(comment)); } if (line[0]) strlcat(line, " | ", sizeof(line)); if (comment[0] == 0) { strlcpy(str, getparam(iattr), sizeof(str)); if (str[0] == '^' && str[strlen(str) - 1] == '$') { str[strlen(str) - 1] = 0; strlcpy(comment, str + 1, NAME_LENGTH); } else strlcpy(comment, str, NAME_LENGTH); strlcpy(str, comment, sizeof(str)); strencode2(line + strlen(line), str, sizeof(line) - strlen(line)); } else strlcat(line, comment, sizeof(line)); } } if (isparam(attr_list[i])) { comment[0] = 0; if (attr_flags[i] & AF_ICON) { sprintf(str, "Icon comment %s", getparam(attr_list[i])); getcfg(lbs->name, str, comment, sizeof(comment)); } if (line[0]) strlcat(line, " | ", sizeof(line)); if (comment[0] == 0) { strlcpy(str, getparam(attr_list[i]), sizeof(str)); if (str[0] == '^' && str[strlen(str) - 1] == '$') { str[strlen(str) - 1] = 0; strlcpy(comment, str + 1, NAME_LENGTH); } else strlcpy(comment, str, NAME_LENGTH); strlcpy(str, comment, sizeof(str)); strencode2(line + strlen(line), str, sizeof(line) - strlen(line)); } else strlcat(line, comment, sizeof(line)); } if (line[0]) { rsprintf("", attr_list[i]); rsprintf("", line); } } else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) { line[0] = 0; for (j = 0; j < MAX_N_LIST; j++) { sprintf(iattr, "%s_%d", attr_list[i], j); if (isparam(iattr)) { if (line[0]) strlcat(line, " | ", sizeof(line)); strlcat(line, getparam(iattr), sizeof(line)); } } if (isparam(attr_list[i])) { if (line[0]) strlcat(line, " | ", sizeof(line)); strencode2(line + strlen(line), getparam(attr_list[i]), sizeof(line) - strlen(line)); } if (line[0]) { rsprintf("", attr_list[i]); rsprintf("", line); } } else if (isparam(attr_list[i])) { strlcpy(str, getparam(attr_list[i]), sizeof(str)); if (str[0] && !strieq(str, "_all_") && strncmp(str, "--", 2) != 0) { comment[0] = 0; if (attr_flags[i] & AF_ICON) { sprintf(str, "Icon comment %s", getparam(attr_list[i])); getcfg(lbs->name, str, comment, sizeof(comment)); } if (comment[0] == 0) { strlcpy(str, getparam(attr_list[i]), sizeof(str)); if (str[0] == '^' && str[strlen(str) - 1] == '$') { str[strlen(str) - 1] = 0; strlcpy(comment, str + 1, NAME_LENGTH); } else strlcpy(comment, str, NAME_LENGTH); strlcpy(str, comment, sizeof(str)); strencode2(comment, str, sizeof(comment)); } rsprintf("", attr_list[i]); rsprintf("", comment); } } } if (isparam("subtext")) { rsprintf("", loc("Text")); rsprintf("", str); } rsprintf("
%s:%s %s
%s:%s
%s:%s
%s:"); if (ltime1) { memcpy(&tms, localtime(<ime1), sizeof(struct tm)); if (attr_flags[i] & AF_DATE) strcpy(format, DEFAULT_DATE_FORMAT); else strcpy(format, DEFAULT_TIME_FORMAT); my_strftime(str, sizeof(str), format, &tms); if (ltime2 > 0) rsprintf("%s %s", loc("From"), str); else rsprintf("%s %s", loc("After"), str); } if (ltime2) { memcpy(&tms, localtime(<ime2), sizeof(struct tm)); if (attr_flags[i] & AF_DATE) strcpy(format, DEFAULT_DATE_FORMAT); else strcpy(format, DEFAULT_TIME_FORMAT); my_strftime(str, sizeof(str), format, &tms); if (ltime1 > 0) rsprintf(" %s %s", loc("to"), str); else rsprintf("%s %s", loc("Before"), str); } rsprintf("
%s:"); rsprintf("%s
%s:"); rsprintf("%s
%s:"); rsprintf("%s
%s:"); strencode2(str, getparam("subtext"), sizeof(str)); rsprintf("%s
\n\n"); } /* get number of summary lines */ n_line = 3; if (getcfg(lbs->name, "Summary lines", str, sizeof(str))) n_line = atoi(str); /* suppress summary completely if text body is disabled */ if (getcfg(lbs->name, "Show text", str, sizeof(str)) && atoi(str) == 0) n_line = 0; /* suppress attachment colum if switched off */ show_att_column = strieq(mode, "Summary"); if (getcfg(lbs->name, "Enable attachments", str, sizeof(str)) && atoi(str) == 0) show_att_column = FALSE; /* get mode commands flag */ mode_commands = TRUE; if (getcfg(lbs->name, "Mode commands", str, sizeof(str)) && atoi(str) == 0) mode_commands = FALSE; /*---- evaluate conditions for quick filters */ for (i = 0; i < lbs->n_attr; i++) { attrib[i][0] = 0; if (isparam(attr_list[i])) { strlcpy(str, getparam(attr_list[i]), sizeof(str)); if (str[0] == '^' && str[strlen(str) - 1] == '$') { str[strlen(str) - 1] = 0; strlcpy(attrib[i], str + 1, NAME_LENGTH); } else strlcpy(attrib[i], str, NAME_LENGTH); } } evaluate_conditions(lbs, attrib); /*---- notification message ----*/ if (info && info[0]) { rsprintf("%s\n", info); } /*---- page navigation ----*/ if (!printable) { show_page_filters(lbs, n_msg, page_n, mode_commands, mode); show_page_navigation(lbs, n_msg, page_n, n_page); } /*---- select navigation ----*/ if (isparam("select") && atoi(getparam("select")) == 1) show_select_navigation(lbs); /*---- table titles ----*/ /* overall listing table */ rsprintf("\n"); size = printable ? 2 : 3; show_text = TRUE; text_in_attr = FALSE; list[0] = 0; getcfg(lbs->name, "List display", list, 10000); /* evaluate Guest display list */ if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest list display", str, sizeof(str)) && !isparam("unm")) { strcpy(list, str); n = strbreak(list, (char (*)[NAME_LENGTH]) gattr, MAX_N_ATTR, ",", FALSE); for (j = 0; j < n; j++) if (strieq(gattr + j * NAME_LENGTH, "text")) break; if (n > 0 && j == n) show_text = FALSE; else text_in_attr = TRUE; } memset(disp_attr_flags, 0, sizeof(disp_attr_flags)); if (list[0]) { n_attr_disp = strbreak(list, disp_attr, MAX_N_ATTR, ",", FALSE); /* if text is in guest display list, adjust number of *real* attributes */ if (text_in_attr) n_attr_disp--; if (search_all) { for (i = n_attr_disp - 1; i >= 0; i--) strcpy(disp_attr[i + 1], disp_attr[i]); strcpy(disp_attr[0], loc("Logbook")); n_attr_disp++; } } else { if (search_all) { n_attr_disp = lbs->n_attr + 3; strcpy(disp_attr[0], loc("Logbook")); strcpy(disp_attr[1], loc("ID")); strcpy(disp_attr[2], loc("Date")); memcpy(disp_attr + 3, attr_list, sizeof(attr_list)); memcpy(disp_attr_flags + 3, attr_flags, sizeof(attr_flags)); } else { n_attr_disp = lbs->n_attr + 2; strcpy(disp_attr[0], loc("ID")); strcpy(disp_attr[1], loc("Date")); memcpy(disp_attr + 2, attr_list, sizeof(attr_list)); memcpy(disp_attr_flags + 2, attr_flags, sizeof(attr_flags)); } } list[0] = 0; getcfg(lbs->name, "Link display", list, 10000); if (list[0]) { n = strbreak(list, (char (*)[NAME_LENGTH]) gattr, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_attr_disp; i++) { for (j = 0; j < n; j++) if (strieq(gattr + j * NAME_LENGTH, disp_attr[i])) break; if (j < n) disp_attr_link[i] = TRUE; else disp_attr_link[i] = FALSE; } } else for (i = 0; i < n_attr_disp; i++) disp_attr_link[i] = TRUE; if (threaded) { } else { rsprintf("\n"); /* empty title for selection box */ if (isparam("select") && atoi(getparam("select")) == 1) rsprintf("\n"); for (i = 0; i < n_attr_disp; i++) { /* assemble current command line, replace sort statements */ strlcpy(ref, getparam("cmdline"), sizeof(ref)); strlcpy(str, disp_attr[i], sizeof(str)); url_encode(str, sizeof(str)); if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0) { subst_param(ref, sizeof(ref), "sort", ""); subst_param(ref, sizeof(ref), "rsort", str); } else { if (ref[0] == 0) { if (getcfg(lbs->name, "Reverse sort", str2, sizeof(str2)) && atoi(str2) == 1) sprintf(ref, "?rsort=%s", str); else sprintf(ref, "?sort=%s", str); } else { subst_param(ref, sizeof(ref), "rsort", ""); subst_param(ref, sizeof(ref), "sort", str); } } img[0] = 0; if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0) sprintf(img, "\"%s\"", loc("up"), loc("up")); else if (isparam("rsort") && strcmp(getparam("rsort"), disp_attr[i]) == 0) sprintf(img, "\"%s\"", loc("down"), loc("down")); sprintf(str, "Tooltip %s", disp_attr[i]); if (getcfg(lbs->name, str, comment, sizeof(comment))) sprintf(str, "title=\"%s\"", comment); else str[0] = 0; if (strieq(disp_attr[i], "Edit") || strieq(disp_attr[i], "Delete")) rsprintf("\n", str, disp_attr[i]); else { rsprintf("\n", disp_attr[i], img); } } if (!strieq(mode, "Full") && n_line > 0 && show_text) rsprintf("\n", loc("Text")); if (show_att_column) rsprintf("\n\n"); } } /* if (!csv && !xml) */ /*---- display message list ----*/ for (index = i_start; index <= i_stop; index++) { size = TEXT_SIZE; message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id; status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) break; /* skip drafts */ if (getcfg(lbs->name, "List drafts", str, sizeof(str)) && atoi(str) == 0) if (draft[0]) continue; if (csv) { rsprintf("%d", message_id); if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); strlcpy(str, date, sizeof(str)); while (strchr(str, ',')) *strchr(str, ',') = ' '; rsprintf(str); if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); for (i = 0; i < lbs->n_attr; i++) { strlcpy(str, attrib[i], sizeof(str)); if (str[0]) { if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); ptms = localtime(<ime); assert(ptms); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, ptms); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); ptms = localtime(<ime); assert(ptms); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, ptms); } rsputs("\""); pt1 = str; while ((pt2 = strchr(pt1, '"')) != NULL) { *pt2 = 0; rsputs(pt1); rsputs("\"\""); pt1 = pt2 + 1; } rsputs(pt1); rsputs("\""); } if (i < lbs->n_attr - 1) { if (strieq(mode, "CSV1")) rsprintf(","); else rsprintf(";"); } else { if (strlen(text)>0 && strieq(mode, "CSV3")) { rsprintf(";"); strlcpy(str, text, sizeof(str)); rsputs("\""); pt1 = str; while ((pt2 = strchr(pt1, '"')) != NULL) { *pt2 = 0; rsputs(pt1); rsputs("\"\""); pt1 = pt2 + 1; } rsputs(pt1); rsputs("\""); } rsprintf("\r\n"); } } } else if (xml) { rsputs("\t\n"); rsprintf("\t\t%d\n", message_id); rsprintf("\t\t%s\n", date); if (in_reply_to[0]) rsprintf("\t\t%s\n", in_reply_to); if (reply_to[0]) rsprintf("\t\t%s\n", reply_to); if (attachment[0][0]) { rsprintf("\t\t"); rsprintf(attachment[0]); for (i = 1; i < MAX_ATTACHMENTS; i++) if (attachment[i][0]) rsprintf(",%s", attachment[i]); rsprintf("\n", attachment); } rsprintf("\t\t%s\n", encoding); for (i = 0; i < lbs->n_attr; i++) { strcpy(iattr, attr_list[i]); for (j = 0; j < (int) strlen(iattr); j++) /* replace special characters with "_", exclude any UTF-8 */ if (!isalnum(iattr[j]) && ((unsigned char) iattr[j] < 128)) iattr[j] = '_'; rsprintf("\t\t<%s>", iattr); strlcpy(str, attrib[i], sizeof(str)); if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); ptms = localtime(<ime); assert(ptms); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, ptms); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); ptms = localtime(<ime); assert(ptms); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, ptms); } xmlencode(str); rsprintf("\n", iattr); } rsputs("\t\t"); xmlencode(text); rsputs("\n"); rsputs("\t\n"); } else if (raw) { rsprintf("$@MID@$: %d\r\n", message_id); rsprintf("Date: %s\r\n", date); if (reply_to[0]) rsprintf("Reply to: %s\r\n", reply_to); if (in_reply_to[0]) rsprintf("In reply to: %s\r\n", in_reply_to); for (i = 0; i < lbs->n_attr; i++) rsprintf("%s: %s\r\n", attr_list[i], attrib[i]); rsprintf("Attachment: "); if (attachment[0][0]) { rsprintf("%s", attachment[0]); for (i = 1; i < MAX_ATTACHMENTS; i++) if (attachment[i][0]) rsprintf(",%s", attachment[i]); } rsprintf("\r\n"); rsprintf("Encoding: %s\r\n", encoding); if (locked_by[0]) rsprintf("Locked by: %s\r\n", locked_by); rsprintf("========================================\r\n"); rsputs(text); rsputs("\r\n"); } else { /*---- add highlighting for searched subtext ----*/ if (isparam("subtext")) { highlight_searchtext(re_buf, text, text1, strieq(encoding, "plain") || strieq(encoding, "ELCode") || !strieq(mode, "Full")); strlcpy(text, text1, TEXT_SIZE); } /*---- display line ----*/ expand = 1; if (threaded) { if (getcfg(lbs->name, "Expand default", str, sizeof(str))) expand = atoi(str); if (isparam("expand")) expand = atoi(getparam("expand")); } level = 0; if (!filtering && !date_filtering) { if (expand == 0 && (!getcfg(lbs->name, "Collapse to last", str, sizeof(str)) || atoi(str) == 1)) { /* search last entry in this thread */ if (reply_to[0]) { search_last_reply(msg_list[index].lbs, &message_id); size = TEXT_SIZE; status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); if (status == SUCCESS) level = 1; } } } else if (in_reply_to[0]) level = 1; display_line(msg_list[index].lbs, message_id, index, mode, expand, level, printable, n_line, show_attachments, show_att_column, date, in_reply_to, reply_to, n_attr_disp, disp_attr, disp_attr_link, attrib, lbs->n_attr, text, show_text, attachment, encoding, isparam("select") ? atoi(getparam("select")) : 0, &n_display, locked_by, 0, re_buf, page_mid, FALSE, draft); if (threaded && !filtering && !date_filtering) { if (reply_to[0] && expand > 0) { p = reply_to; do { display_reply(msg_list[index].lbs, atoi(p), printable, expand, n_line, n_attr_disp, disp_attr, show_text, 1, 0, re_buf, page_mid, FALSE); while (*p && isdigit(*p)) p++; while (*p && (*p == ',' || *p == ' ')) p++; } while (*p); } } } /* if (!csv && !xml) */ } /* for() */ if (!csv && !xml && !raw) { rsprintf("
 %s%s%s%s\"%s\"", loc("Attachments"), loc("Attachments")); rsprintf("
\n"); if (n_display) rsprintf("\n", n_display); rsprintf("\n"); if (n_msg == 0) rsprintf("%s", loc("No entries found")); /*---- page navigation ----*/ if (!printable) show_page_navigation(lbs, n_msg, page_n, n_page); rsprintf("\n"); show_bottom_text(lbs); rsprintf("\r\n"); } if (xml) { rsputs("
\n"); } regfree(re_buf); for (i = 0; i < lbs->n_attr; i++) regfree(re_buf + 1 + i); xfree(slist); xfree(svalue); xfree(gattr); xfree(list); xfree(msg_list); xfree(text); xfree(text1); } /*------------------------------------------------------------------*/ int find_thread_head(LOGBOOK * lbs, int message_id) { int i; /* search index of message */ for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id == message_id) break; if (lbs->el_index[i].in_reply_to) return find_thread_head(lbs, lbs->el_index[i].in_reply_to); return message_id; } /*------------------------------------------------------------------*/ void show_elog_thread(LOGBOOK * lbs, int message_id, int absolute_links, int highlight_mid) { int size, head_id, n_display, n_attr_disp; char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256], disp_attr[MAX_N_ATTR + 4][NAME_LENGTH]; char *p; text = (char *) xmalloc(TEXT_SIZE); /* retrieve message */ size = TEXT_SIZE; el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); /* find message head */ if (atoi(in_reply_to)) head_id = find_thread_head(lbs, atoi(in_reply_to)); else head_id = message_id; n_attr_disp = lbs->n_attr + 2; strcpy(disp_attr[0], loc("ID")); strcpy(disp_attr[1], loc("Date")); memcpy(disp_attr + 2, attr_list, sizeof(attr_list)); size = TEXT_SIZE; el_retrieve(lbs, head_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft); rsprintf("\n"); display_line(lbs, head_id, 0, "Threaded", 1, 0, FALSE, 0, FALSE, FALSE, date, in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, attrib, lbs->n_attr, text, FALSE, attachment, encoding, 0, &n_display, locked_by, message_id, NULL, highlight_mid, absolute_links, draft); if (reply_to[0]) { p = reply_to; do { display_reply(lbs, atoi(p), FALSE, 1, 0, n_attr_disp, disp_attr, FALSE, 1, message_id, NULL, highlight_mid, absolute_links); while (*p && isdigit(*p)) p++; while (*p && (*p == ',' || *p == ' ')) p++; } while (*p); } rsprintf("
\n"); rsprintf("\n"); xfree(text); } /*------------------------------------------------------------------*/ int has_attachments(LOGBOOK * lbs, int message_id) { char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH]; el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, attachment, NULL, NULL, NULL); return attachment[0][0] > 0; } /*------------------------------------------------------------------*/ void format_email_attachments(LOGBOOK * lbs, int message_id, int attachment_type, char att_file[MAX_ATTACHMENTS][256], char *mail_text, int size, char *multipart_boundary, int content_id) { int i, index, n_att, fh, n, is_inline, length; char str[256], file_name[256], buffer[256], domain[256], subdir[256]; /* count attachments */ for (n_att = 0; att_file[n_att][0] && n_att < MAX_ATTACHMENTS; n_att++); for (index = 0; index < MAX_ATTACHMENTS; index++) { if (att_file[index][0] == 0) continue; is_inline = is_inline_attachment(getparam("encoding"), message_id, getparam("text"), index, att_file[index]); if (attachment_type == 1 && is_inline) continue; if (attachment_type == 2 && !is_inline) continue; snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "\r\n--%s\r\n", multipart_boundary); /* return proper Content-Type for file type */ for (i = 0; i < (int) strlen(att_file[index]) && i < (int) sizeof(str) - 1; i++) str[i] = toupper(att_file[index][i]); str[i] = 0; for (i = 0; filetype[i].ext[0]; i++) if (strstr(str, filetype[i].ext)) break; if (filetype[i].ext[0]) snprintf(str, sizeof(str), "Content-Type: %s; name=\"%s\"\r\n", filetype[i].type, att_file[index] + 14); else if (strchr(str, '.') == NULL) snprintf(str, sizeof(str), "Content-Type: text/plain; name=\"%s\"\r\n", att_file[index] + 14); else snprintf(str, sizeof(str), "Content-Type: application/octet-stream; name=\"%s\"\r\n", att_file[index] + 14); strlcat(mail_text, str, size); strlcat(mail_text, "Content-Transfer-Encoding: BASE64\r\n", size); if (content_id) { retrieve_domain(domain, sizeof(domain)); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-ID: \r\n", index, domain); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Disposition: inline; filename=\"%s\"\r\n\r\n", att_file[index] + 14); } else snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", att_file[index] + 14); /* encode file */ strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(att_file[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, att_file[index], sizeof(file_name)); if (is_image(file_name)) { get_thumb_name(file_name, str, sizeof(str), 0); if (str[0]) strlcpy(file_name, str, sizeof(file_name)); } fh = open(file_name, O_RDONLY | O_BINARY); length = strlen(mail_text); if (fh > 0) { do { n = my_read(fh, buffer, 45); if (n <= 0) break; base64_bufenc((unsigned char *) buffer, n, str); if (length + (int) strlen(str) + 2 < size) { strcpy(mail_text + length, str); length += strlen(str); strcpy(mail_text + length, "\r\n"); length += 2; } } while (1); close(fh); } } } /*------------------------------------------------------------------*/ void format_email_text(LOGBOOK * lbs, char attrib[MAX_N_ATTR][NAME_LENGTH], char att_file[MAX_ATTACHMENTS][256], int old_mail, char *url, char *multipart_boundary, char *mail_text, int size) { int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR]; char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256], list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], heading[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH]; time_t ltime; struct tm *pts; if (multipart_boundary[0]) { if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary, size); strlcat(mail_text, "\r\n", size); sprintf(mail_text + strlen(mail_text), "Content-Type: text/plain; charset=%s; format=flowed\r\n", charset); sprintf(mail_text + strlen(mail_text), "Content-Transfer-Encoding: 7bit\r\n\r\n"); } else strlcat(mail_text, "\r\n", size); flags = 63; if (getcfg(lbs->name, "Email format", str, sizeof(str))) flags = atoi(str); retrieve_email_from(lbs, mail_from, mail_from_name, attrib); if (flags & 1) { if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) { if (old_mail) { if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading))) getcfg(lbs->name, "Use Email heading", heading, sizeof(heading)); } i = build_subst_list(lbs, slist, svalue, attrib, TRUE); strsubst_list(heading, sizeof(heading), slist, svalue, i); strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text)); } else { if (old_mail) strlcpy(mail_text + strlen(mail_text), loc("An old ELOG entry has been updated"), size - strlen(mail_text)); else strlcpy(mail_text + strlen(mail_text), loc("A new ELOG entry has been submitted"), size - strlen(mail_text)); strcat(mail_text, ":"); } strlcpy(mail_text + strlen(mail_text), "\r\n\r\n", size - strlen(mail_text)); } if (flags & 32) sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"), lbs->name); if (flags & 2) { if (getcfg(lbs->name, "Email attributes", str, sizeof(str))) { n_email_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_email_attr; i++) { for (j = 0; j < lbs->n_attr; j++) if (strieq(attr_list[j], list[i])) break; if (!strieq(attr_list[j], list[i])) /* attribute not found */ j = 0; attr_index[i] = j; } } else { for (i = 0; i < lbs->n_attr; i++) attr_index[i] = i; n_email_attr = lbs->n_attr; } for (j = 0; j < n_email_attr; j++) { i = attr_index[j]; strcpy(str, " "); memcpy(str, attr_list[i], strlen(attr_list[i])); comment[0] = 0; if (attr_flags[i] & AF_ICON) { sprintf(str2, "Icon comment %s", attrib[i]); getcfg(lbs->name, str2, comment, sizeof(comment)); } else if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(comment, "-"); else my_strftime(comment, sizeof(str), format, pts); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(comment, "-"); else my_strftime(comment, sizeof(str), format, pts); } if (!comment[0]) strcpy(comment, attrib[i]); if (strieq(attr_options[i][0], "boolean")) strcpy(comment, atoi(attrib[i]) ? "1" : "0"); for (k = strlen(str) - 1; k > 0; k--) if (str[k] != ' ') break; if (k < 20) sprintf(str + 20, ": %s\r\n", comment); else sprintf(str + k + 1, ": %s\r\n", comment); strcpy(mail_text + strlen(mail_text), str); } } if (flags & 4) sprintf(mail_text + strlen(mail_text), "\r\n%s URL : %s\r\n", loc("Logbook"), url); if (flags & 64) { for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++) sprintf(mail_text + strlen(mail_text), "\r\n%s %d : %s (%s/%d)\r\n", loc("Attachment"), i + 1, att_file[i] + 14, url, i + 1); } if (flags & 8) { if (isparam("text")) { sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s", getparam("text")); } } strlcat(mail_text, "\r\n\r\n", size); } /*------------------------------------------------------------------*/ void format_email_html(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char att_file[MAX_ATTACHMENTS][256], int old_mail, char *encoding, char *url, char *multipart_boundary, char *mail_text, int size) { int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR], attachments_present; char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256], list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], multipart_boundary_related[256], heading[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH]; time_t ltime; struct tm *pts; if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); if (multipart_boundary[0]) { strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary, size); strlcat(mail_text, "\r\n", size); } attachments_present = has_attachments(lbs, message_id); if (attachments_present) { sprintf(multipart_boundary_related, "------------%04X%04X%04X", rand(), rand(), rand()); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "MIME-Version: 1.0\r\nContent-Type: multipart/related;\r\n boundary=\"%s\"\r\n\r\n", multipart_boundary_related); strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary_related, size); strlcat(mail_text, "\r\n", size); } snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Type: text/html; charset=\"%s\"\r\n", charset); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Transfer-Encoding: 7bit\r\n\r\n"); retrieve_email_from(lbs, mail_from, mail_from_name, attrib); flags = 63; if (getcfg(lbs->name, "Email format", str, sizeof(str))) flags = atoi(str); strcpy(mail_text + strlen(mail_text), "\r\n"); strcpy(mail_text + strlen(mail_text), "\r\n\r\n \r\n\r\n\r\n"); if (flags & 1) { strcpy(mail_text + strlen(mail_text), "

\r\n"); if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) { if (old_mail) { if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading))) getcfg(lbs->name, "Use Email heading", heading, sizeof(heading)); } i = build_subst_list(lbs, slist, svalue, attrib, TRUE); strsubst_list(heading, sizeof(heading), slist, svalue, i); strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text)); } else { if (old_mail) sprintf(mail_text + strlen(mail_text), loc("A old entry has been updated on %s"), host_name); else sprintf(mail_text + strlen(mail_text), loc("A new entry has been submitted on %s"), host_name); strcat(mail_text, ":"); } strlcpy(mail_text + strlen(mail_text), "

\r\n", size - strlen(mail_text)); } strlcpy(mail_text + strlen(mail_text), "\r\n", size - strlen(mail_text)); if (flags & 32) { sprintf(mail_text + strlen(mail_text), "", loc("Logbook")); sprintf(mail_text + strlen(mail_text), "\r\n", lbs->name); } if (flags & 2) { if (getcfg(lbs->name, "Email attributes", str, sizeof(str))) { n_email_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_email_attr; i++) { for (j = 0; j < lbs->n_attr; j++) if (strieq(attr_list[j], list[i])) break; if (!strieq(attr_list[j], list[i])) /* attribute not found */ j = 0; attr_index[i] = j; } } else { for (i = 0; i < lbs->n_attr; i++) attr_index[i] = i; n_email_attr = lbs->n_attr; } for (j = 0; j < n_email_attr; j++) { i = attr_index[j]; strcpy(str, " "); memcpy(str, attr_list[i], strlen(attr_list[i])); comment[0] = 0; if (attr_flags[i] & AF_ICON) { sprintf(str2, "Icon comment %s", attrib[i]); getcfg(lbs->name, str2, comment, sizeof(comment)); } else if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(comment, "-"); else my_strftime(comment, sizeof(str), format, pts); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(comment, "-"); else my_strftime(comment, sizeof(str), format, pts); } if (!comment[0]) strcpy(comment, attrib[i]); if (strieq(attr_options[i][0], "boolean")) strcpy(comment, atoi(attrib[i]) ? "1" : "0"); for (k = strlen(str) - 1; k > 0; k--) if (str[k] != ' ') break; sprintf(mail_text + strlen(mail_text), "", attr_list[i]); sprintf(mail_text + strlen(mail_text), "\r\n", comment); } } if (flags & 4) { sprintf(mail_text + strlen(mail_text), "\r\n", url, url); } if (flags & 64) { for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++) { sprintf(mail_text + strlen(mail_text), "\r\n", url, i + 1, att_file[i] + 14); } } sprintf(mail_text + strlen(mail_text), "
%s%s
%s%s
%s URL", loc("Logbook")); sprintf(mail_text + strlen(mail_text), "%s
%s %d", loc("Attachment"), i + 1); sprintf(mail_text + strlen(mail_text), "%s
\r\n"); if (flags & 8) { if (isparam("text")) { if (encoding[0] == 'H') sprintf(mail_text + strlen(mail_text), "\r\n
\r\n%s", getparam("text")); else if (encoding[0] == 'E') { sprintf(mail_text + strlen(mail_text), "\r\n
\r\n"); strlen_retbuf = 0; rsputs_elcode(lbs, TRUE, getparam("text")); strlcpy(mail_text + strlen(mail_text), return_buffer, TEXT_SIZE + 1000 - strlen(mail_text)); strlen_retbuf = 0; } else sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s", getparam("text")); } } strcpy(mail_text + strlen(mail_text), "\r\n\r\n\r\n"); if (attachments_present) { format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related, TRUE); strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary_related, size); strlcat(mail_text, "--\r\n\r\n", size); } } /*------------------------------------------------------------------*/ void format_email_html2(LOGBOOK * lbs, int message_id, char att_file[MAX_ATTACHMENTS][256], int old_mail, char *multipart_boundary, char *mail_text, int size) { char str[256], charset[256], multipart_boundary_related[256], *p; int attachments_present; sprintf(str, "%d", message_id); if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); if (multipart_boundary[0]) { strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary, size); strlcat(mail_text, "\r\n", size); } attachments_present = has_attachments(lbs, message_id); if (attachments_present) { sprintf(multipart_boundary_related, "------------%04X%04X%04X", rand(), rand(), rand()); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "MIME-Version: 1.0\r\nContent-Type: multipart/related;\r\n boundary=\"%s\"\r\n\r\n", multipart_boundary_related); strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary_related, size); strlcat(mail_text, "\r\n", size); } snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Type: text/html; charset=\"%s\"\r\n", charset); snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-Transfer-Encoding: 7bit\r\n\r\n"); strlen_retbuf = 0; if (old_mail) show_elog_entry(lbs, str, "oldemail"); else show_elog_entry(lbs, str, "email"); p = strstr(return_buffer, "\r\n\r\n"); if (p) strlcpy(mail_text + strlen(mail_text), p + 4, size - strlen(mail_text)); strlen_retbuf = 0; strlcat(mail_text, "\r\n", size); if (attachments_present) { format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related, TRUE); strlcat(mail_text, "--", size); strlcat(mail_text, multipart_boundary_related, size); strlcat(mail_text, "--\r\n\r\n", size); } } /*------------------------------------------------------------------*/ int compose_email(LOGBOOK * lbs, char *rcpt_to, char *mail_to, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char *mail_param, int old_mail, char att_file[MAX_ATTACHMENTS][256], char *encoding, int reply_id) { int i, n, flags, status, mail_encoding, mail_text_size, n_attachments; char str[NAME_LENGTH + 100], mail_from[256], mail_from_name[256], *mail_text, smtp_host[256], subject[256], error[256]; char list[MAX_PARAM][NAME_LENGTH], url[256]; char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH]; char multipart_boundary[80]; if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) { show_error(loc("No SMTP host defined in [global] section of configuration file")); return 0; } evaluate_conditions(lbs, attrib); flags = 63; if (getcfg(lbs->name, "Email format", str, sizeof(str))) flags = atoi(str); /* get initial HTML flag from message encoding */ mail_encoding = 1; // 1:text, 2:short HTML, 4:full HTML if (encoding[0] == 'E' || encoding[0] == 'H') mail_encoding = 4; /* overwrite with config setting */ if (getcfg(lbs->name, "Email encoding", str, sizeof(str))) mail_encoding = atoi(str); retrieve_email_from(lbs, mail_from, mail_from_name, attrib); /* compose subject from attributes */ if (old_mail && getcfg(lbs->name, "Use Email Subject Edit", subject, sizeof(subject))) { i = build_subst_list(lbs, slist, svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list(slist, svalue, "message id", str, &i); strsubst_list(subject, sizeof(subject), slist, svalue, i); } else if (getcfg(lbs->name, "Use Email Subject", subject, sizeof(subject))) { i = build_subst_list(lbs, slist, svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list(slist, svalue, "message id", str, &i); strsubst_list(subject, sizeof(subject), slist, svalue, i); } else { if (old_mail) strcpy(subject, "Updated ELOG entry"); else strcpy(subject, "New ELOG entry"); } /* count attachments */ n_attachments = 0; if (att_file) for (i = 0; att_file[i][0] && i < MAX_ATTACHMENTS; i++) { if ((mail_encoding & 6) == 0 || !is_inline_attachment(encoding, message_id, getparam("text"), i, att_file[i])) n_attachments++; } compose_base_url(lbs, str, sizeof(str), TRUE); sprintf(url, "%s%d", str, message_id); mail_text_size = MAX_CONTENT_LENGTH + 1000; mail_text = xmalloc(mail_text_size); mail_text[0] = 0; compose_email_header(lbs, subject, mail_from_name, mail_to, url, mail_text, mail_text_size, mail_encoding, n_attachments, multipart_boundary, message_id, reply_id); if (mail_encoding & 1) format_email_text(lbs, attrib, att_file, old_mail, url, multipart_boundary, mail_text, mail_text_size); if (mail_encoding & 2) format_email_html(lbs, message_id, attrib, att_file, old_mail, encoding, url, multipart_boundary, mail_text, mail_text_size); if (mail_encoding & 4) format_email_html2(lbs, message_id, att_file, old_mail, multipart_boundary, mail_text, mail_text_size); if (n_attachments && (flags & 16)) { if ((mail_encoding & 6) > 0) /* only non-inline attachements */ format_email_attachments(lbs, message_id, 1, att_file, mail_text, mail_text_size, multipart_boundary, FALSE); else /* all attachments */ format_email_attachments(lbs, message_id, 0, att_file, mail_text, mail_text_size, multipart_boundary, FALSE); } if (multipart_boundary[0]) { strlcat(mail_text, "--", mail_text_size); strlcat(mail_text, multipart_boundary, mail_text_size); strlcat(mail_text, "--\r\n\r\n", mail_text_size); } status = sendmail(lbs, smtp_host, mail_from, rcpt_to, mail_text, error, sizeof(error)); /* { int fh; fh = open("mail.html", O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644); write(fh, mail_text, strlen(mail_text)); close(fh); } */ if (status < 0) { sprintf(str, loc("Error sending Email via \"%s\""), smtp_host); if (error[0]) { strlcat(str, ": ", sizeof(str)); strlcat(str, error, sizeof(str)); } url_encode(str, sizeof(str)); sprintf(mail_param, "?error=%s", str); } else if (error[0]) { sprintf(str, loc("Error sending Email via \"%s\""), smtp_host); strlcat(str, ": ", sizeof(str)); strlcat(str, error, sizeof(str)); url_encode(str, sizeof(str)); sprintf(mail_param, "?error=%s", str); } else { if (!getcfg(lbs->name, "Display email recipients", str, sizeof(str)) || atoi(str) == 1) { if (mail_param[0] == 0) strcpy(mail_param, "?"); else strcat(mail_param, "&"); /* convert '"',CR,LF,TAB to ' ' */ while (strchr(mail_to, '"')) *strchr(mail_to, '"') = ' '; while (strchr(mail_to, '\r')) *strchr(mail_to, '\r') = ' '; while (strchr(mail_to, '\n')) *strchr(mail_to, '\n') = ' '; while (strchr(mail_to, '\t')) *strchr(mail_to, '\t') = ' '; n = strbreak(mail_to, list, MAX_PARAM, ",", FALSE); if (n < 10) { for (i = 0; i < n && i < MAX_PARAM; i++) { strlcpy(str, list[i], sizeof(str)); url_encode(str, sizeof(str)); sprintf(mail_param + strlen(mail_param), "mail%d=%s", i, str); if (i < n - 1) strcat(mail_param, "&"); } } else { sprintf(str, "%d%%20%s", n, loc("recipients")); sprintf(mail_param + strlen(mail_param), "mail0=%s", str); } } } xfree(mail_text); return status; } /*------------------------------------------------------------------*/ int execute_shell(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char att_file[MAX_ATTACHMENTS][256], char *sh_cmd) { int i; char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH]; char shell_cmd[10000], tail[1000], str[NAME_LENGTH], *p, subdir[256]; if (!enable_execute) { eprintf("Shell execution not enabled via -x flag.\n"); return SUCCESS; } strlcpy(shell_cmd, sh_cmd, sizeof(shell_cmd)); i = build_subst_list(lbs, slist, svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list(slist, svalue, "message id", str, &i); add_subst_list(slist, svalue, "text", getparam("text"), &i); strsubst_list(shell_cmd, sizeof(shell_cmd), slist, svalue, i); if (att_file && stristr(shell_cmd, "$attachments")) { /* substitute attachments */ p = stristr(shell_cmd, "$attachments"); strlcpy(tail, p + strlen("$attachments"), sizeof(tail)); *p = 0; for (i = 0; i < MAX_ATTACHMENTS; i++) { generate_subdir_name(att_file[i], subdir, sizeof(subdir)); if (att_file[i][0] && strlen(shell_cmd) + strlen(lbs->data_dir) + strlen(subdir) + strlen(att_file[i]) < sizeof(shell_cmd) + 1) { strcpy(p, "\""); strcat(p, lbs->data_dir); strlcat(str, subdir, sizeof(str)); strlcpy(str, att_file[i], sizeof(str)); str_escape(str, sizeof(str)); strcat(p, str); strcat(p, "\" "); p += strlen(p); } } strlcat(shell_cmd, tail, sizeof(shell_cmd)); } sprintf(str, "SHELL \"%s\"", shell_cmd); write_logfile(lbs, str); my_shell(shell_cmd, str, sizeof(str)); return SUCCESS; } /*------------------------------------------------------------------*/ int add_attribute_option(LOGBOOK * lbs, char *attrname, char *attrvalue, char *condition) { int fh, i, length; char str[NAME_LENGTH], av_encoded[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3; fh = open(config_file, O_RDWR | O_BINARY, 0644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return 0; } /* do not allow HTML code in value */ strencode2(av_encoded, attrvalue, sizeof(av_encoded)); /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = xmalloc(length + strlen(av_encoded) + 3); read(fh, buf, length); buf[length] = 0; /* find location of options */ if (condition && condition[0]) set_condition(condition); else set_condition(""); sprintf(str, "Options %s", attrname); p1 = (char *) find_param(buf, lbs->name, str); if (p1 == NULL) { sprintf(str, "MOptions %s", attrname); p1 = (char *) find_param(buf, lbs->name, str); } if (p1 == NULL) { sprintf(str, "ROptions %s", attrname); p1 = (char *) find_param(buf, lbs->name, str); } if (p1 == NULL) return 0; p2 = strchr(p1, '\n'); if (p2 && *(p2 - 1) == '\r') p2--; /* save tail */ buf2 = NULL; if (p2) buf2 = xstrdup(p2); /* add option */ p3 = strchr(p1, '\n'); if (p3 == NULL) p3 = p1 + strlen(p1); while (*(p3 - 1) == '\n' || *(p3 - 1) == '\r' || *(p3 - 1) == ' ' || *(p3 - 1) == '\t') p3--; sprintf(p3, ", %s", av_encoded); if (p2) { strlcat(buf, buf2, length + strlen(av_encoded) + 3); xfree(buf2); } lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); return 1; } /*------------------------------------------------------------------*/ int set_attributes(LOGBOOK * lbs, char attributes[][NAME_LENGTH], int n) { int fh, i, length, size; char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3; fh = open(config_file, O_RDWR | O_BINARY, 0644); if (fh < 0) { sprintf(str, loc("Cannot open file %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); return 0; } /* determine length of attributes */ for (i = size = 0; i < n; i++) size += strlen(attributes[i]) + 2; /* read previous contents */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = xmalloc(length + size + 3); read(fh, buf, length); buf[length] = 0; /* find location of attributes */ p1 = (char *) find_param(buf, lbs->name, "Attributes"); if (p1 == NULL) { sprintf(str, loc("No 'Attributes' option present in %s"), config_file); show_error(str); return 0; } p2 = strchr(p1, '\n'); if (p2 && *(p2 - 1) == '\r') p2--; /* save tail */ buf2 = NULL; if (p2) buf2 = xstrdup(p2); /* add list */ p3 = strchr(p1, '='); if (p3 == NULL) return 0; p3++; while (*p3 == ' ') p3++; for (i = 0; i < n - 1; i++) { sprintf(p3, "%s, ", attributes[i]); p3 += strlen(p3); } sprintf(p3, "%s", attributes[i]); if (p2) { strlcat(buf, buf2, length + size + 3); xfree(buf2); } lseek(fh, 0, SEEK_SET); i = write(fh, buf, strlen(buf)); if (i < (int) strlen(buf)) { sprintf(str, loc("Cannot write to %s"), config_file); strcat(str, ": "); strcat(str, strerror(errno)); show_error(str); close(fh); xfree(buf); return 0; } TRUNCATE(fh); close(fh); xfree(buf); /* force re-read of config file */ check_config_file(TRUE); return 1; } /*------------------------------------------------------------------*/ int propagate_attrib(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH]) { int n, i, j, status; char str[NAME_LENGTH], att_file[MAX_ATTACHMENTS][256], *attr, *list, reply_to[MAX_REPLY_TO * 10]; list = (char *)xmalloc(MAX_N_ATTR*NAME_LENGTH); attr = (char *)xmalloc(MAX_N_ATTR*NAME_LENGTH); status = el_retrieve(lbs, message_id, NULL, attr_list, (char (*)[NAME_LENGTH]) attr, lbs->n_attr, NULL, NULL, NULL, reply_to, att_file, NULL, NULL, NULL); if (status != EL_SUCCESS) { xfree(list); xfree(attr); return status; } getcfg(lbs->name, "Propagate attributes", str, sizeof(str)); n = strbreak(str, (char (*)[1500])list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n; i++) { for (j = 0; j < lbs->n_attr; j++) if (stricmp(attr_list[j], list+i*NAME_LENGTH) == 0) { strlcpy(attr+j*NAME_LENGTH, attrib[j], NAME_LENGTH); break; } } message_id = el_submit(lbs, message_id, TRUE, "", attr_list, (char (*)[1500])attr, lbs->n_attr, "", "", "", "", att_file, TRUE, NULL, NULL); if (message_id < 0) { xfree(list); xfree(attr); return 0; } // go through all replies of this entry n = strbreak(reply_to, (char (*)[1500])list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n; i++) propagate_attrib(lbs, atoi(list+i*NAME_LENGTH), attrib); xfree(list); xfree(attr); return EL_SUCCESS; } /*------------------------------------------------------------------*/ int submit_elog_reply(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char *text) { int n_reply, i, status; char str1[80], str2[80], att_file[MAX_ATTACHMENTS][256], reply_to[MAX_REPLY_TO * 10], list[MAX_N_ATTR][NAME_LENGTH]; status = el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0, NULL, NULL, NULL, reply_to, att_file, NULL, NULL, NULL); if (status != EL_SUCCESS) return status; sprintf(str1, "- %s -", loc("keep original text")); sprintf(str2, "

- %s -

", loc("keep original text")); if (strcmp(text, str1) == 0 || strcmp(text, str2) == 0) message_id = el_submit(lbs, message_id, TRUE, "", attr_list, attrib, lbs->n_attr, "", "", "", "", att_file, TRUE, NULL, NULL); else message_id = el_submit(lbs, message_id, TRUE, "", attr_list, attrib, lbs->n_attr, text, "", "", "", att_file, TRUE, NULL, NULL); if (message_id < 0) return 0; if (isparam("elmode") && strieq(getparam("elmode"), "threaded")) { // go through all replies in threaded mode n_reply = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_reply; i++) { submit_elog_reply(lbs, atoi(list[i]), attrib, text); } } return EL_SUCCESS; } /*------------------------------------------------------------------*/ void submit_elog(LOGBOOK * lbs) { char str[NAME_LENGTH], str2[NAME_LENGTH], file_name[256], error[1000], date[80], *mail_list, *rcpt_list, list[10000], *p, locked_by[256], encoding[80], attrib[MAX_N_ATTR][NAME_LENGTH], subst_str[MAX_PATH_LENGTH], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], user[256], user_email[256], mail_param[1000], *mail_to, *rcpt_to, full_name[256], att_file[MAX_ATTACHMENTS][256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], ua[NAME_LENGTH], draft[256]; int i, j, k, n, missing, first, index, mindex, suppress, message_id, resubmit_orig, mail_to_size, rcpt_to_size, ltime, year, month, day, hour, min, sec, n_attr, email_notify[1000], allowed_encoding, status, bdraft, old_mail; BOOL bedit, bmultiedit; struct tm tms; bmultiedit = isparam("nsel"); bedit = isparam("edit_id") && atoi(getparam("edit_id")); bdraft = isparam("draft"); /* check for condition */ if (isparam("condition")) { set_condition(getparam("condition")); /* rescan attributes */ n_attr = scan_attributes(lbs->name); } else n_attr = lbs->n_attr; /* check for required attributs */ missing = 0; for (i = 0; i < lbs->n_attr; i++) { strcpy(ua, attr_list[i]); stou(ua); if (attr_flags[i] & AF_REQUIRED) { if (attr_flags[i] & AF_DATE) { sprintf(str, "d%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "m%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "y%d", i); if (isparam(str) == 0) missing = 1; if (missing) break; } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "d%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "m%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "y%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "h%d", i); if (isparam(str) == 0) missing = 1; sprintf(str, "n%d", i); if (isparam(str) == 0) missing = 1; if (missing) break; } else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))) { for (j = 0; j < MAX_N_LIST; j++) { sprintf(str, "%s_%d", ua, j); if (isparam(str)) break; /* check for attributes without the _ from elog */ if (isparam(ua)) break; } if (j == MAX_N_LIST) { missing = 1; break; } } else if (isparam(ua) == 0 || *getparam(ua) == 0) { missing = 1; break; } } } if (missing && !bdraft) { sprintf(error, ""); sprintf(error + strlen(error), loc("Error: Attribute %s not supplied"), attr_list[i]); sprintf(error + strlen(error), ".

\n"); sprintf(error + strlen(error), loc("Please go back and enter the %s field"), attr_list[i]); strcat(error, ".\n"); show_error(error); return; } /* check for numeric attributes */ if (!bdraft) { for (index = 0; index < lbs->n_attr; index++) if (attr_flags[index] & AF_NUMERIC) { strcpy(ua, attr_list[index]); stou(ua); strlcpy(str, isparam(ua) ? getparam(ua) : "", sizeof(str)); for (j = 0; i < (int) strlen(str); i++) if (!isdigit(str[i])) break; sprintf(str2, "- %s -", loc("keep original values")); if (i < (int) strlen(str) && strcmp(str, "") != 0 && strcmp(str, str2) != 0) { sprintf(error, loc("Error: Attribute %s must be numeric"), attr_list[index]); show_error(error); return; } } } for (i = 0; i < n_attr; i++) { strcpy(ua, attr_list[i]); stou(ua); if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) strcat(ua, "_0"); if (isparam(ua) && *getparam(ua) && attr_options[i][0][0]) { if (strieq(attr_options[i][0], "boolean") && !bdraft) { if (atoi(getparam(ua)) != 0 && atoi(getparam(ua)) != 1 && strcmp(getparam(ua), "") != 0) { strencode2(str, getparam(ua), sizeof(str)); sprintf(error, loc("Error: Value %s not allowed for boolean attributes"), str); show_error(error); return; } } else { /* check if option exists */ for (j = 0; attr_options[i][j][0]; j++) if (strieq(attr_options[i][j], getparam(ua))) break; /* check if option without {n} exists */ if (!attr_options[i][j][0]) { for (j = 0; attr_options[i][j][0]; j++) { strlcpy(str, attr_options[i][j], sizeof(str)); if (strchr(str, '{')) *strchr(str, '{') = 0; if (strieq(str, getparam(ua))) break; } } if (!attr_options[i][j][0] && isparam(ua) && strcmp(getparam(ua), "") != 0) { if (attr_flags[i] & AF_EXTENDABLE) { /* check if maximal number of options exceeded */ if (attr_options[i][MAX_N_LIST - 1][0]) { strcpy(error, loc("Maximum number of attribute options exceeded")); strcat(error, "
"); strcat(error, loc("Please increase MAX_N_LIST in elogd.c and recompile")); show_error(error); return; } if (!add_attribute_option(lbs, attr_list[i], getparam(ua), getparam("condition"))) return; } else { char encoded[100]; strencode2(encoded, getparam(ua), sizeof(encoded)); sprintf(error, loc("Error: Attribute option %s not existing"), encoded); show_error(error); return; } } } } } /* check if allowed encoding */ if (getcfg(lbs->name, "Allowed encoding", str, sizeof(str))) allowed_encoding = atoi(str); else allowed_encoding = 7; strlcpy(encoding, isparam("encoding") ? getparam("encoding") : "plain", sizeof(encoding)); /* check for valid encoding */ if (!strieq(encoding, "plain") && !strieq(encoding, "ELCode") && !strieq(encoding, "HTML")) strcpy(encoding, "plain"); if (strieq(encoding, "plain") && (allowed_encoding & 1) == 0) { show_error("Plain encoding not allowed"); return; } if (strieq(encoding, "ELCode") && (allowed_encoding & 2) == 0) { show_error("ELCode encoding not allowed"); return; } if (strieq(encoding, "HTML") && (allowed_encoding & 4) == 0) { show_error("HTML encoding not allowed"); return; } /* get attachments */ for (i = 0; i < MAX_ATTACHMENTS; i++) { sprintf(str, "attachment%d", i); strcpy(att_file[i], isparam(str) ? getparam(str) : ""); } /* retrieve attributes */ for (i = 0; i < n_attr; i++) { strcpy(ua, attr_list[i]); stou(ua); if (strieq(attr_options[i][0], "boolean") && !isparam(ua)) { strcpy(attrib[i], "0"); } else if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) { if (isparam(ua)) { strlcpy(attrib[i], getparam(ua), NAME_LENGTH); } else { attrib[i][0] = 0; first = 1; for (j = 0; j < MAX_N_LIST; j++) { sprintf(str, "%s_%d", ua, j); if (isparam(str)) { if (first) first = 0; else strlcat(attrib[i], " | ", NAME_LENGTH); if (strlen(attrib[i]) + strlen(getparam(str)) < NAME_LENGTH - 2) strlcat(attrib[i], getparam(str), NAME_LENGTH); else break; } } } } else if (attr_flags[i] & AF_DATE) { if (isparam(ua)) // from edit/reply of fixed attributes strlcpy(attrib[i], getparam(ua), NAME_LENGTH); else { sprintf(str, "m%d", i); if (isparam(str) && strieq(getparam(str), "")) strcpy(attrib[i], ""); else { sprintf(str, "y%d", i); year = isparam(str) ? atoi(getparam(str)) : 0; if (year < 100) year += 2000; sprintf(str, "m%d", i); month = isparam(str) ? atoi(getparam(str)) : 0; sprintf(str, "d%d", i); day = isparam(str) ? atoi(getparam(str)) : 0; if (month == 0 || day == 0) strcpy(attrib[i], ""); else { memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1; tms.tm_mday = day; tms.tm_hour = 12; ltime = (int) mktime(&tms); if (ltime == -1) { show_error(loc("Date must be between 1970 and 2037")); return; } sprintf(attrib[i], "%d", ltime); } } } } else if (attr_flags[i] & AF_DATETIME) { if (isparam(ua)) // from edit/reply of fixed attributes strlcpy(attrib[i], getparam(ua), NAME_LENGTH); else { sprintf(str, "m%d", i); if (isparam(str) && strieq(getparam(str), "")) strcpy(attrib[i], ""); else { sprintf(str, "y%d", i); year = isparam(str) ? atoi(getparam(str)) : 0; if (year < 100) year += 2000; sprintf(str, "m%d", i); month = isparam(str) ? atoi(getparam(str)) : 0; sprintf(str, "d%d", i); day = isparam(str) ? atoi(getparam(str)) : 0; sprintf(str, "h%d", i); hour = isparam(str) ? atoi(getparam(str)) : 0; sprintf(str, "n%d", i); min = isparam(str) ? atoi(getparam(str)) : 0; sprintf(str, "c%d", i); sec = isparam(str) ? atoi(getparam(str)) : 0; if (month == 0 || day == 0) strcpy(attrib[i], ""); else { memset(&tms, 0, sizeof(struct tm)); tms.tm_year = year - 1900; tms.tm_mon = month - 1; tms.tm_mday = day; tms.tm_hour = hour; tms.tm_min = min; tms.tm_sec = sec; tms.tm_isdst = -1; ltime = (int) mktime(&tms); if (ltime == -1) { show_error(loc("Date must be between 1970 and 2037")); return; } sprintf(attrib[i], "%d", ltime); } } } } else { strlcpy(attrib[i], isparam(ua) ? getparam(ua) : "", NAME_LENGTH); /* remove any CR/LF */ if (strchr(attrib[i], '\r')) *strchr(attrib[i], '\r') = 0; if (strchr(attrib[i], '\n')) *strchr(attrib[i], '\n') = 0; /* strip trailing "{...}" */ if (is_cond_attr(i) && strchr(attrib[i], '{') && strchr(attrib[i], '}')) *strchr(attrib[i], '{') = 0; } } /* compile substitution list */ n = build_subst_list(lbs, slist, svalue, attrib, TRUE); if (bedit) add_subst_list(slist, svalue, "message id", getparam("edit_id"), &n); /* substitute attributes */ if (!bedit && !isparam("reply_to")) { for (index = 0; index < n_attr; index++) { sprintf(str, "Subst %s", attr_list[index]); if (getcfg(lbs->name, str, subst_str, sizeof(subst_str))) { /* do not format date for date attributes */ i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME)) == 0); strsubst_list(subst_str, sizeof(subst_str), slist, svalue, i); /* check for index substitution if not in edit mode */ if (!bedit && strchr(subst_str, '#')) { /* get index */ get_auto_index(lbs, index, subst_str, str, sizeof(str)); strcpy(subst_str, str); } strcpy(attrib[index], subst_str); } } } /* subst attributes for edits */ if (bedit) { for (index = 0; index < n_attr; index++) { sprintf(str, "Subst on edit %s", attr_list[index]); if (getcfg(lbs->name, str, str2, sizeof(str2))) { /* do not format date for date attributes */ i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME)) == 0); add_subst_list(slist, svalue, "message id", getparam("edit_id"), &i); strsubst_list(str2, sizeof(str2), slist, svalue, i); if (strlen(str2) > NAME_LENGTH - 100) { if (strstr(str2 + 100, "
")) { strlcpy(str, strstr(str2 + 100, "
"), sizeof(str)); } else strlcpy(str, str2 + 100, sizeof(str)); strcpy(str2, "..."); strlcat(str2, str, sizeof(str2)); } if (strncmp(str2, "
", 4) == 0) strcpy(attrib[index], str2 + 4); else strcpy(attrib[index], str2); } } } /* subst attributes for replies */ if (isparam("reply_to")) { for (index = 0; index < n_attr; index++) { sprintf(str, "Subst on reply %s", attr_list[index]); if (getcfg(lbs->name, str, str2, sizeof(str2))) { /* do not format date for date attributes */ i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME)) == 0); if (isparam("reply_to")) add_subst_list(slist, svalue, "Reply to", getparam("reply_to"), &i); strsubst_list(str2, sizeof(str2), slist, svalue, i); strcpy(attrib[index], str2); } } } /* check for attributes to keep */ if (bmultiedit) { sprintf(str, "- %s -", loc("keep original values")); for (i = 0; i < n_attr; i++) { if (strieq(str, attrib[i])) strlcpy(attrib[i], "", NAME_LENGTH); } } message_id = 0; reply_to[0] = 0; in_reply_to[0] = 0; date[0] = 0; resubmit_orig = 0; locked_by[0] = 0; draft[0] = 0; if (isparam("draft")) strlcpy(draft, getparam("draft"), sizeof(draft)); if (bedit && isparam("resubmit") && atoi(getparam("resubmit")) == 1) { resubmit_orig = atoi(getparam("edit_id")); /* get old links */ el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0, NULL, 0, in_reply_to, reply_to, NULL, NULL, NULL, NULL); /* if not message head, move all preceeding messages */ /* outcommented, users want only resubmitted message occur at end (see what's new) if (in_reply_to[0]) { do { resubmit_orig = atoi(in_reply_to); el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0, NULL, 0, in_reply_to, reply_to, NULL, NULL); } while (in_reply_to[0]); } */ message_id = atoi(getparam("edit_id")); strcpy(in_reply_to, ""); strcpy(reply_to, ""); date[0] = 0; } else { if (bedit) { message_id = atoi(getparam("edit_id")); strcpy(in_reply_to, ""); strcpy(reply_to, ""); strcpy(date, ""); if (bdraft) strcpy(locked_by, ""); } else strcpy(in_reply_to, isparam("reply_to") ? getparam("reply_to") : ""); } if (_logging_level > 1) { if (bmultiedit) sprintf(str, "EDIT multiple entries"); else if (bdraft) sprintf(str, "DRAFT entry #%d", message_id); else if (bedit && !resubmit_orig) sprintf(str, "EDIT entry #%d", message_id); else sprintf(str, "NEW entry #%d", message_id); write_logfile(lbs, str); } if (bmultiedit) { for (i = n = 0; i < atoi(getparam("nsel")); i++) { sprintf(str, "s%d", i); if (isparam(str)) { message_id = atoi(getparam(str)); status = submit_elog_reply(lbs, message_id, attrib, getparam("text")); if (status != EL_SUCCESS) { sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir); strcat(str, "\n

"); strcat(str, loc("Please check that it exists and elogd has write access and disk is not full")); show_error(str); return; } } } redirect(lbs, isparam("redir") ? getparam("redir") : ""); return; /* no email notifications etc */ } else { message_id = el_submit(lbs, message_id, bedit, date, attr_list, attrib, n_attr, getparam("text"), in_reply_to, reply_to, encoding, att_file, TRUE, locked_by, draft); if (message_id <= 0) { sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir); strcat(str, "\n

"); strcat(str, loc("Please check that it exists and elogd has write access and disk is not full")); show_error(str); return; } if (bdraft) { show_http_header(lbs, FALSE, NULL); rsprintf("OK %d\n", message_id); return; } } /* evaluate propagation of attributes */ if (getcfg(lbs->name, "Propagate attributes", str, sizeof(str))) propagate_attrib(lbs, find_thread_head(lbs, message_id), attrib); /* resubmit thread if requested */ if (resubmit_orig) message_id = el_move_message_thread(lbs, resubmit_orig); /* retrieve submission date */ if (date[0] == 0) el_retrieve(lbs, message_id, date, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); /*---- replace relative elog:/x link by elog:n/x link */ if (stristr(getparam("text"), "elog:/")) { p = getparam("text"); if (stricmp(encoding, "HTML") == 0) { sprintf(str, "%d/", message_id); } else sprintf(str, "elog:%d/", message_id); strsubst(p, TEXT_SIZE, "elog:/", str); el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to, encoding, att_file, TRUE, NULL, NULL); } /*---- replace elog: by HTML link ----*/ if (strieq(encoding, "HTML") && stristr(getparam("text"), "elog:")) { p = stristr(getparam("text"), "elog:"); while (p) { for (i = 0; i < 5 || (p[i] == '/' || isalnum(p[i])); i++) str[i] = p[i]; str[i] = 0; convert_elog_link(lbs, str + 5, str + 5, str2, 0, message_id); strsubst(p, TEXT_SIZE, str, str2); p += strlen(str2); p = stristr(p, "elog:"); } el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, getparam("text"), in_reply_to, reply_to, encoding, att_file, TRUE, NULL, NULL); } /*---- email notifications ----*/ suppress = isparam("suppress") ? atoi(getparam("suppress")) : 0; /* check for mail submissions */ mail_param[0] = 0; mail_to = (char *) xmalloc(256); mail_to[0] = 0; mail_to_size = 256; rcpt_to = (char *) xmalloc(256); rcpt_to[0] = 0; rcpt_to_size = 256; mail_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH); rcpt_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH); if (suppress == 1 || suppress == 3) { if (suppress == 1) strcpy(mail_param, "?suppress=1"); } else { /* go throuch "Email xxx" in configuration file */ for (index = mindex = 0; index <= n_attr; index++) { strcpy(ua, attr_list[index]); stou(ua); if (index < n_attr) { strcpy(str, "Email "); if (strchr(attr_list[index], ' ')) sprintf(str + strlen(str), "\"%s\"", attr_list[index]); else strlcat(str, attr_list[index], sizeof(str)); strcat(str, " "); if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) { sprintf(str2, "%s_%d", ua, mindex); mindex++; if (mindex == MAX_N_LIST) mindex = 0; else index--; /* repeat this loop */ } else strlcpy(str2, ua, sizeof(str2)); if (isparam(str2)) { if (strchr(getparam(str2), ' ')) { strlcat(str, "\"", sizeof(str)); strlcat(str, getparam(str2), sizeof(str)); strlcat(str, "\"", sizeof(str)); } else strlcat(str, getparam(str2), sizeof(str)); } } else sprintf(str, "Email ALL"); if (getcfg(lbs->name, str, list, sizeof(list))) { i = build_subst_list(lbs, slist, svalue, attrib, TRUE); strsubst_list(list, sizeof(list), slist, svalue, i); n = strbreak(list, (char (*)[1500]) mail_list, 200, ",", FALSE); if (get_verbose() >= VERBOSE_INFO) eprintf("\n%s to %s\n\n", str, list); for (i = 0; i < n; i++) { /* remove possible 'mailto:' */ if ((p = strstr(&mail_list[i * NAME_LENGTH], "mailto:")) != NULL) memmove(p, p + 7, strlen(p + 7) + 1); if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= mail_to_size) { mail_to_size += 256; mail_to = xrealloc(mail_to, mail_to_size); } strcat(mail_to, &mail_list[i * NAME_LENGTH]); strcat(mail_to, ","); if ((int) strlen(rcpt_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= rcpt_to_size) { rcpt_to_size += 256; rcpt_to = xrealloc(rcpt_to, rcpt_to_size); } strcat(rcpt_to, &mail_list[i * NAME_LENGTH]); strcat(rcpt_to, ","); } } } if (!getcfg(lbs->name, "Suppress Email to users", str, sizeof(str)) || atoi(str) == 0) { /* go through password file */ for (index = 0;; index++) { if (!enum_user_line(lbs, index, user, sizeof(user))) break; get_user_line(lbs, user, NULL, full_name, user_email, email_notify, NULL, NULL); for (i = 0; lb_list[i].name[0] && i < 1000; i++) if (strieq(lb_list[i].name, lbs->name)) break; if (email_notify[i]) { /* check if user has access to this logbook */ if (!check_login_user(lbs, user)) continue; sprintf(str, "\"%s\" <%s>,", full_name, user_email); if ((int) strlen(mail_to) + (int) strlen(str) + 1 >= mail_to_size) { mail_to_size += 256; mail_to = xrealloc(mail_to, mail_to_size); } strcat(mail_to, str); sprintf(str, "%s,", user_email); if ((int) strlen(rcpt_to) + (int) strlen(str) + 1 >= rcpt_to_size) { rcpt_to_size += 256; rcpt_to = xrealloc(rcpt_to, rcpt_to_size); } strcat(rcpt_to, str); } } } } if (strlen(mail_to) > 0) { /* convert any '|' to ',', remove duplicate email to's */ n = strbreak(rcpt_to, (void *) rcpt_list, MAX_N_EMAIL, ",|", TRUE); strbreak(mail_to, (void *) mail_list, MAX_N_EMAIL, ",|", TRUE); for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (rcpt_list[i * NAME_LENGTH] && rcpt_list[j * NAME_LENGTH] && strstr(&rcpt_list[i * NAME_LENGTH], &rcpt_list[j * NAME_LENGTH])) { for (k = j; k < n - 1; k++) { memcpy(&rcpt_list[k * NAME_LENGTH], &rcpt_list[(k + 1) * NAME_LENGTH], NAME_LENGTH); memcpy(&mail_list[k * NAME_LENGTH], &mail_list[(k + 1) * NAME_LENGTH], NAME_LENGTH); } memset(&rcpt_list[k * NAME_LENGTH], 0, NAME_LENGTH); memset(&mail_list[k * NAME_LENGTH], 0, NAME_LENGTH); j = j - 1; n = n - 1; break; } } } rcpt_to[0] = 0; mail_to[0] = 0; for (i = 0; i < n; i++) { if ((int) strlen(rcpt_to) + (int) strlen(&rcpt_list[i * NAME_LENGTH]) + 5 >= rcpt_to_size) { rcpt_to_size += 256; rcpt_to = xrealloc(rcpt_to, rcpt_to_size); } strcat(rcpt_to, &rcpt_list[i * NAME_LENGTH]); if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 5 >= mail_to_size) { mail_to_size += 256; mail_to = xrealloc(mail_to, mail_to_size); } strcat(mail_to, &mail_list[i * NAME_LENGTH]); if (i < MAX_N_EMAIL - 1 && rcpt_list[(i + 1) * NAME_LENGTH]) { strcat(rcpt_to, ","); strcat(mail_to, ",\r\n\t"); } } /* fix for edited draft messages. new_entry is a hidden field persisting draft cycles */ old_mail = bedit; if (isparam("new_entry")) old_mail = 0; if (resubmit_orig) old_mail = 0; if (compose_email(lbs, rcpt_to, mail_to, message_id, attrib, mail_param, old_mail, att_file, isparam("encoding") ? getparam("encoding") : "plain", atoi(in_reply_to)) == 0) { xfree(mail_to); xfree(rcpt_to); xfree(mail_list); xfree(rcpt_list); return; } } xfree(mail_to); xfree(rcpt_to); xfree(mail_list); xfree(rcpt_list); /*---- shell execution ----*/ if (!(isparam("shell_suppress") && atoi(getparam("shell_suppress")))) { if (!bedit) { if (getcfg(lbs->name, "Execute new", str, sizeof(str))) execute_shell(lbs, message_id, attrib, att_file, str); } else { if (getcfg(lbs->name, "Execute edit", str, sizeof(str))) execute_shell(lbs, message_id, attrib, att_file, str); } } /*---- custom submit page ----*/ if (getcfg(lbs->name, "Submit page", str, sizeof(str))) { /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strcpy(file_name, str); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } send_file_direct(file_name); return; } if (getcfg(lbs->name, "List after submit", str, sizeof(str)) && atoi(str) == 1) str[0] = 0; else sprintf(str, "%d%s", message_id, mail_param); redirect(lbs, str); } /*------------------------------------------------------------------*/ void submit_elog_mirror(LOGBOOK * lbs) { char str[1000], date[80], attrib_value[MAX_N_ATTR][NAME_LENGTH], attrib_name[MAX_N_ATTR][NAME_LENGTH], in_reply_to[80], encoding[80], reply_to[MAX_REPLY_TO * 10], att_file[MAX_ATTACHMENTS][256], name[NAME_LENGTH], value[NAME_LENGTH]; int i, message_id, n_attr; BOOL bedit; /* get attachments */ for (i = 0; i < MAX_ATTACHMENTS; i++) { sprintf(str, "attachment%d", i); strcpy(att_file[i], isparam(str) ? getparam(str) : ""); } reply_to[0] = 0; in_reply_to[0] = 0; date[0] = 0; encoding[0] = 0; bedit = FALSE; n_attr = 0; message_id = 0; /* retrieve attributes */ for (i = 0; i < MAX_PARAM; i++) { if (enumparam(i, name, value)) { if (strieq(name, "mirror_id")) message_id = atoi(value); else if (strieq(name, "entry_date")) strlcpy(date, value, sizeof(date)); else if (strieq(name, "reply_to")) strlcpy(reply_to, value, sizeof(reply_to)); else if (strieq(name, "in_reply_to")) strlcpy(in_reply_to, value, sizeof(in_reply_to)); else if (strieq(name, "encoding")) strlcpy(encoding, value, sizeof(encoding)); else if (!strieq(name, "cmd") && !strieq(name, "full_name") && !strieq(name, "user_email") && !strieq(name, "unm") && !strieq(name, "upwd") && !strieq(name, "wpwd") && strncmp(name, "attachment", 10) != 0) { strlcpy(attrib_name[n_attr], name, NAME_LENGTH); strlcpy(attrib_value[n_attr++], value, NAME_LENGTH); } } else break; } if (message_id == 0 || date[0] == 0) { show_error(loc("Invalid mirror_id or entry_date")); return; } /* check if message already exists */ for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id == message_id) { bedit = TRUE; break; } message_id = el_submit(lbs, message_id, bedit, date, attrib_name, attrib_value, n_attr, getparam("text"), in_reply_to, reply_to, encoding, att_file, FALSE, NULL, NULL); if (message_id <= 0) { sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir); strcat(str, "\n

"); strcat(str, loc("Please check that it exists and elogd has write access")); show_error(str); return; } sprintf(str, "%d", message_id); redirect(lbs, str); } /*------------------------------------------------------------------*/ void copy_to(LOGBOOK * lbs, int src_id, char *dest_logbook, int move, int orig_id) { int size, i, j, n, n_done, n_done_reply, n_reply, index, status, fh, source_id, message_id, thumb_status, next_id = 0; char str[256], str2[256], file_name[MAX_PATH_LENGTH], thumb_name[MAX_PATH_LENGTH], *attrib, date[80], *text, msg_str[32], in_reply_to[80], subdir[256], reply_to[MAX_REPLY_TO * 10], *attachment, encoding[80], locked_by[256], draft[256], *buffer, *list; LOGBOOK *lbs_dest; BOOL bedit; attachment = xmalloc(MAX_ATTACHMENTS * MAX_PATH_LENGTH); attrib = xmalloc(MAX_N_ATTR * NAME_LENGTH); list = xmalloc(MAX_N_ATTR * NAME_LENGTH); text = xmalloc(TEXT_SIZE); for (i = 0; lb_list[i].name[0]; i++) if (strieq(lb_list[i].name, dest_logbook)) break; if (!lb_list[i].name[0]) return; lbs_dest = &lb_list[i]; if (src_id) n = 1; else n = isparam("nsel") ? atoi(getparam("nsel")) : 0; n_done = n_done_reply = source_id = status = next_id = 0; for (index = 0; index < n; index++) { if (src_id) source_id = src_id; else { sprintf(str, "s%d", index); if (!isparam(str)) continue; source_id = isparam(str) ? atoi(getparam(str)) : 0; } /* get message */ size = TEXT_SIZE; status = el_retrieve(lbs, source_id, date, attr_list, (void *) attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, (void *) attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) { sprintf(msg_str, "%d", source_id); sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name); show_error(str); xfree(attachment); xfree(attrib); xfree(list); xfree(text); return; } if (orig_id == 0) { /* search message head */ while (atoi(in_reply_to) > 0) { source_id = atoi(in_reply_to); size = TEXT_SIZE; status = el_retrieve(lbs, source_id, date, attr_list, (void *) attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, (void *) attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) { sprintf(msg_str, "%d", source_id); sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name); show_error(str); xfree(attachment); xfree(attrib); xfree(list); xfree(text); return; } } } /* read attachments */ for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment[i * MAX_PATH_LENGTH]) { strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment + i * MAX_PATH_LENGTH, subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment + i * MAX_PATH_LENGTH, sizeof(file_name)); fh = open(file_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); size = TELL(fh); lseek(fh, 0, SEEK_SET); buffer = xmalloc(size); read(fh, buffer, size); close(fh); /* keep original file name for inline references */ strlcpy(file_name, attachment + i * MAX_PATH_LENGTH, MAX_PATH_LENGTH); el_submit_attachment(lbs_dest, file_name, buffer, size, NULL); if (buffer) xfree(buffer); } else /* attachment is invalid */ attachment[i * MAX_PATH_LENGTH] = 0; /* check for thumbnail */ strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment + i * MAX_PATH_LENGTH, subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment + i * MAX_PATH_LENGTH, sizeof(file_name)); thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0); if (thumb_status == 1) { fh = open(thumb_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); size = TELL(fh); lseek(fh, 0, SEEK_SET); buffer = xmalloc(size); read(fh, buffer, size); close(fh); /* keep original file name for inline references */ if (strrchr(thumb_name, '\\')) strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str)); else strlcpy(str, thumb_name, sizeof(str)); el_submit_attachment(lbs_dest, str, buffer, size, NULL); if (buffer) xfree(buffer); } } if (thumb_status == 2) { for (j = 0;; j++) { get_thumb_name(file_name, thumb_name, sizeof(thumb_name), j); if (thumb_name[0]) { fh = open(thumb_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); size = TELL(fh); lseek(fh, 0, SEEK_SET); buffer = xmalloc(size); read(fh, buffer, size); close(fh); /* keep original file name for inline references */ if (strrchr(thumb_name, '\\')) strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str)); else strlcpy(str, thumb_name, sizeof(str)); el_submit_attachment(lbs_dest, str, buffer, size, NULL); if (buffer) xfree(buffer); } } else break; } } } /* correct possible references to attachments */ if (strieq(encoding, "ELCode")) { sprintf(str, "[IMG]elog:%d/", src_id); while (stristr(text, str)) strsubst(text, TEXT_SIZE, str, "[IMG]elog:/"); } else if (strieq(encoding, "HTML")) { sprintf(str, "?lb=%s\"", lbs->name_enc); sprintf(str2, "?lb=%s\"", dest_logbook); while (stristr(text, str)) strsubst(text, TEXT_SIZE, str, str2); sprintf(str, "?lb=%s&", lbs->name_enc); sprintf(str2, "?lb=%s&", dest_logbook); while (stristr(text, str)) strsubst(text, TEXT_SIZE, str, str2); } /* keep original message ID if requested */ message_id = 0; bedit = FALSE; if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1) { message_id = source_id; /* test if entry exists already */ status = el_retrieve(lbs_dest, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); bedit = (status == EL_SUCCESS); } /* submit in destination logbook without links, submit all attributes from the source logbook even if the destination has a differnt number of attributes */ if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1) message_id = el_submit(lbs_dest, message_id, bedit, date, attr_list, (void *) attrib, lbs->n_attr, text, in_reply_to, reply_to, encoding, (void *) attachment, FALSE, NULL, NULL); else { /* if called recursively (for threads), put in correct in_reply_to */ str[0] = 0; if (orig_id) sprintf(str, "%d", orig_id); message_id = el_submit(lbs_dest, message_id, bedit, date, attr_list, (void *) attrib, lbs->n_attr, text, str, "", encoding, (void *) attachment, TRUE, NULL, NULL); } if (message_id <= 0) { sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs_dest->data_dir); strcat(str, "\n

"); strcat(str, loc("Please check that it exists and elogd has write access")); show_error(str); xfree(attachment); xfree(attrib); xfree(list); xfree(text); return; } n_done++; /* submit all replies */ n_reply = strbreak(reply_to, (void *) list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_reply; i++) { copy_to(lbs, atoi(list + i * NAME_LENGTH), dest_logbook, move, message_id); } n_done_reply += n_reply; /* delete original message for move */ next_id = source_id; if (move && orig_id == 0) { /* find next message head */ next_id = el_search_message(lbs, EL_NEXT, source_id, TRUE); if (next_id <= 0) next_id = el_search_message(lbs, EL_LAST, 0, FALSE); el_delete_message(lbs, source_id, TRUE, NULL, TRUE, TRUE); } } xfree(attachment); xfree(attrib); xfree(list); xfree(text); if (orig_id) return; /* redirect to next entry of source logbook */ if (next_id) sprintf(str, "%d", next_id); else str[0] = 0; redirect(lbs, str); return; } /*------------------------------------------------------------------*/ int is_inline_attachment(char *encoding, int message_id, char *text, int i, char *att) { char str[256], att_enc[256], domain[256], *pt, *p; if (text == NULL) return 0; if (strieq(encoding, "ELCode")) { sprintf(str, "[img]elog:/%d[/img]", i + 1); if (stristr(text, str)) return 1; sprintf(str, "[img]elog:%d/%d[/img]", message_id, i + 1); if (stristr(text, str)) return 1; } else if (strieq(encoding, "HTML")) { strlcpy(att_enc, att, sizeof(att_enc)); att_enc[13] = '/'; pt = text; while (stristr(pt, att_enc)) { /* make sure that it's really an inline image */ for (p = stristr(pt, att_enc); p > pt; p--) if (*p == '<') break; if (p > pt) { strncpy(str, p, 5); if (stristr(str, "name, "Thumbnail size", str, sizeof(str))) { if (strcmp(str, "0") == 0) return 0; sprintf(thumb_size, " -thumbnail '%s'", str); } else thumb_size[0] = 0; getcfg(lbs->name, "Thumbnail options", thumb_options, sizeof(thumb_options)); if (!chkext(file_name, ".ps") && !chkext(file_name, ".pdf") && !chkext(file_name, ".eps") && !chkext(file_name, ".gif") && !chkext(file_name, ".jpg") && !chkext(file_name, ".jpeg") && !chkext(file_name, ".png") && !chkext(file_name, ".ico") && !chkext(file_name, ".tif") && !chkext(file_name, ".svg")) return 0; i = get_thumb_name(file_name, str, sizeof(str), 0); if (i) return i; strlcpy(str, file_name, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) { if (strrchr(str, '.')) *strrchr(str, '.') = 0; } if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) strlcat(str, "-%d.png", sizeof(str)); else strlcat(str, ".png", sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) snprintf(cmd, sizeof(cmd), "%s %s '%s[0-7]'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size, str); else snprintf(cmd, sizeof(cmd), "%s %s '%s'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size, str); #ifdef OS_WINNT for (i = 0; i < (int) strlen(cmd); i++) if (cmd[i] == '\'') cmd[i] = '\"'; #endif snprintf(str, sizeof(str), "SHELL \"%s\"", cmd); write_logfile(lbs, str); if (get_verbose() >= VERBOSE_INFO) { eprintf(str); eprintf("\n"); } my_shell(cmd, str, sizeof(str)); i = get_thumb_name(file_name, str, sizeof(str), 0); if (i) return i; return 3; } /*------------------------------------------------------------------*/ int get_thumb_name(const char *file_name, char *thumb_name, int size, int index) { char str[MAX_PATH_LENGTH]; thumb_name[0] = 0; /* append .png for all files as thumbnail name, except for PDF files (convert bug!) */ memset(str, 0, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) { strlcpy(str, file_name, sizeof(str)); if (strrchr(str, '.')) *strrchr(str, '.') = 0; snprintf(str + strlen(str), sizeof(str) - strlen(str) - 1, "-%d.png", index); if (file_exist(str)) { strlcpy(thumb_name, str, size); return 2; } if (index > 0) return 0; strlcpy(str, file_name, sizeof(str)); if (strrchr(str, '.')) *strrchr(str, '.') = 0; strlcat(str, ".png", sizeof(str)); if (file_exist(str)) { strlcpy(thumb_name, str, size); return 1; } } else { strlcpy(str, file_name, sizeof(str)); sprintf(str + strlen(str), "-%d.png", index); if (file_exist(str)) { strlcpy(thumb_name, str, size); return 2; } if (index > 0) return 0; strlcpy(str, file_name, sizeof(str)); strlcat(str, ".png", sizeof(str)); if (file_exist(str)) { strlcpy(thumb_name, str, size); return 1; } } return 0; } /*------------------------------------------------------------------*/ void call_image_magick(LOGBOOK * lbs) { char str[1024], cmd[1024], file_name[256], thumb_name[256], subdir[256]; int cur_width, cur_height, new_size, cur_rot, new_rot, thumb_status; if (!isparam("req") || !isparam("img")) { show_error("Unknown IM request received"); return; } strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(getparam("img"), subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, getparam("img"), sizeof(file_name)); thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0); sprintf(cmd, "%s -format '%%wx%%h %%c' '%s'", _identify_cmd, thumb_name); #ifdef OS_WINNT { int i; for (i = 0; i < (int) strlen(cmd); i++) if (cmd[i] == '\'') cmd[i] = '\"'; } #endif my_shell(cmd, str, sizeof(str)); if (atoi(str) > 0) { cur_width = atoi(str); if (strchr(str, 'x')) { cur_height = atoi(strchr(str, 'x') + 1); } else cur_height = cur_width; if (strchr(str, ' ')) { cur_rot = atoi(strchr(str, ' ') + 1); } else cur_rot = 0; } else { show_http_header(NULL, FALSE, NULL); rsputs(str); return; } if (thumb_status == 2) strsubst(thumb_name, sizeof(thumb_name), "-0", "-%d"); cmd[0] = 0; if (strieq(getparam("req"), "rotleft")) { new_rot = (cur_rot + 360 - 90) % 360; sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name, new_rot, cur_height, new_rot, thumb_name); } if (strieq(getparam("req"), "rotright")) { new_rot = (cur_rot + 90) % 360; sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name, new_rot, cur_height, new_rot, thumb_name); } if (strieq(getparam("req"), "original")) { new_size = (int) (cur_width / 1.5); sprintf(cmd, "%s '%s' '%s'", _convert_cmd, file_name, thumb_name); } if (strieq(getparam("req"), "smaller")) { new_size = (int) (cur_width / 1.5); sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name, cur_rot, new_size, cur_rot, thumb_name); } if (strieq(getparam("req"), "larger")) { new_size = (int) (cur_width * 1.5); sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name, cur_rot, new_size, cur_rot, thumb_name); } if (cmd[0]) { #ifdef OS_WINNT int i; for (i = 0; i < (int) strlen(cmd); i++) if (cmd[i] == '\'') cmd[i] = '\"'; #endif my_shell(cmd, str, sizeof(str)); show_http_header(NULL, TRUE, NULL); rsputs(str); } return; } /*------------------------------------------------------------------*/ void show_elog_entry(LOGBOOK * lbs, char *dec_path, char *command) { int size, i, j, k, n, n_log, status, fh, length, message_error, index, n_hidden, message_id, orig_message_id, format_flags[MAX_N_ATTR], att_hide[MAX_ATTACHMENTS], att_inline[MAX_ATTACHMENTS], n_attachments, n_lines, n_disp_attr, attr_index[MAX_N_ATTR], thumb_status, max_n_lines; char str[2 * NAME_LENGTH], str2[NAME_LENGTH], ref[256], file_enc[256], attrib[MAX_N_ATTR][NAME_LENGTH]; char date[80], text[TEXT_SIZE], menu_str[1000], cmd[256], script[256], orig_tag[80], reply_tag[MAX_REPLY_TO * 10], display[NAME_LENGTH], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], att[256], lattr[256], mid[80], menu_item[MAX_N_LIST][NAME_LENGTH], format[80], slist[MAX_N_ATTR + 10][NAME_LENGTH], file_name[MAX_PATH_LENGTH], gattr[MAX_N_ATTR][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], *p, lbk_list[MAX_N_LIST][NAME_LENGTH], comment[256], class_name[80], class_value[80], fl[8][NAME_LENGTH], list[MAX_N_ATTR][NAME_LENGTH], domain[256], subdir[256], draft[256]; FILE *f; BOOL first, show_text, display_inline, subtable, email; struct tm *pts; struct tm ts; struct stat st; time_t ltime, entry_ltime; message_id = atoi(dec_path); message_error = EL_SUCCESS; _current_message_id = message_id; email = strieq(command, "email") || strieq(command, "oldemail"); /* check for custom form to display entry */ if (getcfg(lbs->name, "Custom display form", str, sizeof(str))) { /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strcpy(file_name, str); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } send_file_direct(str); return; } /* check for guest access */ if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs)) getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str)); /* default menu commands */ if (menu_str[0] == 0) { strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Find, "); if (getcfg(lbs->name, "Password file", str, sizeof(str))) { strcat(menu_str, "Config, Logout, "); } else { strcat(menu_str, "Config, "); } strcat(menu_str, "Help"); } else { /* check for admin command */ n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE); menu_str[0] = 0; for (i = 0; i < n; i++) { if (strcmp(menu_item[i], "Admin") == 0) { if (!is_admin_user(lbs, getparam("unm"))) continue; } strcat(menu_str, menu_item[i]); if (i < n - 1) strcat(menu_str, ", "); } } /*---- check next/previous message -------------------------------*/ if (strieq(command, loc("Next")) || strieq(command, loc("Previous")) || strieq(command, loc("Last")) || strieq(command, loc("First"))) { orig_message_id = message_id; if (strieq(command, loc("Last"))) message_id = el_search_message(lbs, EL_LAST, 0, FALSE); if (strieq(command, loc("First"))) message_id = el_search_message(lbs, EL_FIRST, 0, FALSE); /* avoid display of "invalid id '0'", if "start page = 0?cmd=Last" */ if (!message_id) dec_path[0] = 0; first = TRUE; do { if (strieq(command, loc("Next"))) message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE); if (strieq(command, loc("Previous"))) message_id = el_search_message(lbs, EL_PREV, message_id, FALSE); if (!first) { if (strieq(command, loc("First"))) message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE); if (strieq(command, loc("Last"))) message_id = el_search_message(lbs, EL_PREV, message_id, FALSE); } else first = FALSE; if (message_id == 0) { if (strieq(command, loc("Next"))) message_error = EL_LAST_MSG; else message_error = EL_FIRST_MSG; message_id = orig_message_id; break; } size = sizeof(text); el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag, reply_tag, attachment, encoding, locked_by, draft); /* check for locked attributes */ for (i = 0; i < lbs->n_attr; i++) { sprintf(lattr, "l%s", attr_list[i]); if (isparam(lattr) == '1' && !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]), attrib[i]))) break; } if (i < lbs->n_attr) continue; /* check for attribute filter if not browsing */ if (!isparam("browsing")) { for (i = 0; i < lbs->n_attr; i++) { if (isparam(attr_list[i]) && !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]), attrib[i]))) break; } if (i < lbs->n_attr) continue; } sprintf(str, "%d", message_id); for (i = 0; i < lbs->n_attr; i++) { sprintf(lattr, "l%s", attr_list[i]); if (isparam(lattr) == '1') { if (strchr(str, '?') == NULL) sprintf(str + strlen(str), "?%s=1", lattr); else sprintf(str + strlen(str), "&%s=1", lattr); } } redirect(lbs, str); return; } while (TRUE); } /*---- check for valid URL ---------------------------------------*/ if (dec_path[0] && atoi(dec_path) == 0) { strencode2(str2, dec_path, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); return; } /*---- get current message ---------------------------------------*/ if (message_id == 0) message_id = el_search_message(lbs, EL_LAST, 0, FALSE); status = 0; reply_tag[0] = orig_tag[0] = 0; if (message_id) { size = sizeof(text); status = el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag, reply_tag, attachment, encoding, locked_by, draft); if (status != EL_SUCCESS) message_error = status; else { if (_logging_level > 2) { sprintf(str, "READ entry #%d", message_id); write_logfile(lbs, str); } } } else message_error = EL_EMPTY; /*---- check for conditional attribute ----*/ evaluate_conditions(lbs, attrib); /*---- header ----*/ /* header */ if (status == EL_SUCCESS && message_error != EL_EMPTY) { str[0] = 0; if (getcfg(lbs->name, "Page Title", str, sizeof(str))) { i = build_subst_list(lbs, slist, svalue, attrib, TRUE); sprintf(mid, "%d", message_id); add_subst_list(slist, svalue, "message id", mid, &i); add_subst_time(lbs, slist, svalue, "entry time", date, &i, 0); strsubst_list(str, sizeof(str), slist, svalue, i); strip_html(str); } else strcpy(str, "ELOG"); if (email) { /* show absolute link for CSS */ show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, TRUE, 0); rsprintf("\n"); } else { sprintf(ref, "%d", message_id); strlcpy(script, "OnLoad=\"document.onkeypress=browse;\"", sizeof(script)); if (str[0]) show_standard_header(lbs, TRUE, str, ref, FALSE, NULL, script, 0); else show_standard_header(lbs, TRUE, lbs->name, ref, FALSE, NULL, script, 0); } } else show_standard_header(lbs, TRUE, "", "", FALSE, NULL, NULL, 0); /*---- title ----*/ if (email) rsprintf("\n"); else show_standard_title(lbs, "", 0); /*---- menu buttons ----*/ if (!email) { rsprintf("\n\n"); /*---- menu text ----*/ if (getcfg(lbs->name, "menu text", str, sizeof(str))) { FILE *f; char file_name[256], *buf; rsprintf(""); } } // if (!email) /*---- message ----*/ if (reply_tag[0] || orig_tag[0]) show_elog_thread(lbs, message_id, email, 0); if (message_error == EL_EMPTY) rsprintf("\n", loc("Logbook is empty")); else if (message_error == EL_NO_MSG) rsprintf("\n", loc("This entry has been deleted")); else { /* overall message table */ rsprintf("\n"); // 2 column table rsputs("
\n"); rsprintf("\n"); rsprintf("\n"); /*---- next/previous buttons ----*/ if (!getcfg(lbs->name, "Enable browsing", str, sizeof(str)) || atoi(str) == 1) { rsprintf("\n"); } n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE); rsprintf("\n\n"); rsprintf("
\n"); /* check if first.png exists, just put link there if not */ strlcpy(file_name, resource_dir, sizeof(file_name)); if (file_name[0] && file_name[strlen(file_name) - 1] != DIR_SEPARATOR) strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, "themes", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); if (theme_name[0]) { strlcat(file_name, theme_name, sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); } strlcat(file_name, "first.png", sizeof(file_name)); if (stat(file_name, &st) >= 0) { rsprintf("\n", loc("First entry, Ctrl-Home"), loc("First entry, Ctrl-Home")); rsprintf("\n", loc("Previous entry, Ctrl-PgUp"), loc("Previous entry, Ctrl-PgUp")); rsprintf("\n", loc("Next entry, Ctrl-PgDn"), loc("Next entry, Ctrl-PgDn")); rsprintf("\n", loc("Last entry, Ctrl-End"), loc("Last entry, Ctrl-End")); } else { rsprintf("|< \n", message_id, loc("First")); rsprintf("< \n", message_id, loc("Previous")); rsprintf("> \n", message_id, loc("Next")); rsprintf(">| \n", message_id, loc("Last")); } rsprintf("\n"); for (i = 0; i < n; i++) { /* display menu item */ strcpy(cmd, menu_item[i]); /* only display allowed commands */ if (!is_user_allowed(lbs, cmd)) continue; if (strieq(cmd, "Copy to") || strieq(cmd, "Move to")) { rsprintf(" \n", loc(cmd)); if (strieq(cmd, "Copy to")) rsprintf("\n"); if (getcfg(lbs->name, cmd, str, sizeof(str))) { n_log = strbreak(str, lbk_list, MAX_N_LIST, ",", FALSE); for (j = 0; j < n_log; j++) rsprintf("
\n"); /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strcpy(file_name, str); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } f = fopen(file_name, "rb"); if (f != NULL) { fseek(f, 0, SEEK_END); size = TELL(fileno(f)); fseek(f, 0, SEEK_SET); buf = xmalloc(size + 1); fread(buf, 1, size, f); buf[size] = 0; fclose(f); rsputs(buf); } else rsprintf("
Error: file \"%s\" not found
", file_name); rsprintf("
%s
%s
\n"); /* check for locked attributes */ for (i = 0; i < lbs->n_attr; i++) { sprintf(lattr, "l%s", attr_list[i]); if (isparam(lattr) == '1') break; } if (i < lbs->n_attr) { if (isparam(attr_list[i])) sprintf(str, " %s \"%s = %s\"", loc("with"), attr_list[i], getparam(attr_list[i])); } else str[0] = 0; if (message_error == EL_LAST_MSG) rsprintf("\n", loc("This is the last entry"), str); if (message_error == EL_FIRST_MSG) rsprintf("\n", loc("This is the first entry"), str); /* check for mail submissions */ if (isparam("suppress")) { rsprintf("\n", loc("Email notification suppressed")); } else if (isparam("error")) { strencode2(str, getparam("error"), sizeof(str)); rsprintf("\n", str); } else { for (i = 0;; i++) { sprintf(str, "mail%d", i); if (isparam(str)) { if (i == 0) rsprintf("\n"); } /*---- display message ID ----*/ _current_message_id = message_id; if (email) { rsprintf("\n"); } if (locked_by[0]) { sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by); rsprintf ("\n", str, loc("You can \"steal\" the lock by editing this entry"), loc("You might however then overwrite each other's modifications")); } else { if (draft[0]) { rsprintf("\n"); } } rsprintf("\n"); /*---- display attributes ----*/ /* retrieve attribute flags */ for (i = 0; i < lbs->n_attr; i++) { format_flags[i] = 0; sprintf(str, "Format %s", attr_list[i]); if (getcfg(lbs->name, str, format, sizeof(format))) { n = strbreak(format, fl, 8, ",", FALSE); if (n > 0) format_flags[i] = atoi(fl[0]); } } /* 2 column table for all attributes */ rsprintf("\n"); subtable = 0; } else rsprintf(""); } } rsputs("
%s %s
%s %s
%s
%s
"); strencode2(str, getparam(str), sizeof(str)); rsprintf("%s %s
\n", loc("Email sent to"), str); } else break; } if (i > 0) rsprintf("
\n"); if (strieq(command, "oldemail")) rsprintf("%s:", loc("An old ELOG entry has been updated")); else rsprintf("%s:", loc("A new ELOG entry has been submitted")); rsprintf("
\"%s\"\n", loc("stop"), loc("stop")); rsprintf("%s.
%s.
%s.
%s\n", loc("This is a draft message, edit and submit it to make it permanent")); rsprintf(" ", loc("Edit"), message_id, loc("Edit")); rsprintf("
\n"); for (i = 0; i < lbs->n_attr; i++) { strencode2(str, attrib[i], sizeof(str)); rsprintf("\n", attr_list[i], str); } /* browsing flag to distinguish "/../=" from browsing */ rsprintf("\n"); if (getcfg(lbs->name, "ID display", display, sizeof(display))) { j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &j, 0); strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j); } else sprintf(display, "%d", message_id); if (email) { compose_base_url(lbs, str, sizeof(str), TRUE); sprintf(str + strlen(str), "%d", message_id); rsprintf("%s: %s  ", loc("Logbook"), lbs->name); rsprintf("%s: %d", loc("Message ID"), str, message_id); } else rsprintf("%s: %s\n", loc("Message ID"), display); /*---- display date ----*/ if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); entry_ltime = date_to_ltime(date); pts = localtime(&entry_ltime); assert(pts); my_strftime(str, sizeof(str), format, pts); rsprintf("    %s: %s\n", loc("Entry time"), str); /*---- link to original message or reply ----*/ if (message_error != EL_FILE_ERROR && (reply_tag[0] || orig_tag[0])) { if (orig_tag[0]) { if (email) compose_base_url(lbs, ref, sizeof(ref), TRUE); else ref[0] = 0; sprintf(ref + strlen(ref), "%s", orig_tag); rsprintf("    %s: ", loc("In reply to")); rsprintf("%s\n", ref, orig_tag); } if (reply_tag[0]) { rsprintf("    %s: ", loc("Reply to this")); p = strtok(reply_tag, ","); do { if (email) compose_base_url(lbs, ref, sizeof(ref), TRUE); else ref[0] = 0; sprintf(ref + strlen(ref), "%s", p); rsprintf("%s\n", ref, p); p = strtok(NULL, ","); if (p) rsprintf("  \n"); } while (p); rsprintf("\n"); } } rsprintf("
"); subtable = 0; /* generate list of attributes to show */ if (getcfg(lbs->name, "Show attributes", str, sizeof(str))) { n_disp_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE); for (i = 0; i < n_disp_attr; i++) { for (j = 0; j < lbs->n_attr; j++) if (strieq(attr_list[j], list[i])) break; if (!strieq(attr_list[j], list[i])) /* attribute not found */ j = 0; attr_index[i] = j; } } else { for (i = 0; i < lbs->n_attr; i++) attr_index[i] = i; n_disp_attr = lbs->n_attr; } for (j = 0; j < n_disp_attr; j++) { i = attr_index[j]; if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str, sizeof(str)) && !isparam("unm")) { n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE); for (k = 0; k < n; k++) if (strieq(gattr[k], attr_list[i])) break; if (k == n) continue; } strcpy(class_name, "attribname"); strcpy(class_value, "attribvalue"); sprintf(str, "Format %s", attr_list[i]); if (getcfg(lbs->name, str, format, sizeof(format))) { n = strbreak(format, fl, 8, ",", FALSE); if (n > 1) strlcpy(class_name, fl[1], sizeof(class_name)); if (n > 2) strlcpy(class_value, fl[2], sizeof(class_value)); } if (format_flags[i] & AFF_SAME_LINE) /* if attribute on same line, do nothing */ rsprintf(""); else if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE)) { /* if next attribute on same line, start a new subtable */ rsprintf("\n"); subtable = 0; } else rsprintf(""); } /* if last attribute, close row or subtable */ if (i == lbs->n_attr - 1) { if (subtable) { rsprintf("
"); subtable = 1; } else /* for normal attribute, start new row */ rsprintf(""); sprintf(lattr, "l%s", attr_list[i]); /* display cell with optional tooltip */ sprintf(str, "Tooltip %s", attr_list[i]); if (getcfg(lbs->name, str, comment, sizeof(comment))) rsprintf("\n", attr_list[i], class_value); else rsprintf("%s:\n", attr_list[i], class_value); } /* display image for icon */ else if (attr_flags[i] & AF_ICON) { rsprintf("%s:\n"); } else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) && (format_flags[i] & AFF_MULTI_LINE)) { rsprintf("%s:\n"); } else if (attr_flags[i] & AF_DATE) { sprintf(str, "Date format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); rsprintf("%s:\n", attr_list[i], class_value, str); } else if (attr_flags[i] & AF_DATETIME) { sprintf(str, "Time format %s", attr_list[i]); if (!getcfg(lbs->name, str, format, sizeof(format))) if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); ltime = atoi(attrib[i]); pts = localtime(<ime); assert(pts); if (ltime == 0) strcpy(str, "-"); else my_strftime(str, sizeof(str), format, pts); rsprintf("%s:\n", attr_list[i], class_value, str); } else { rsprintf("%s:\n"); } if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE) == 0) { /* if next attribute not on same line, close row or subtable */ if (subtable) { rsprintf("
", comment, class_name); else rsprintf("", class_name); if (getcfg(lbs->name, "Filtered browsing", str, sizeof(str)) && atoi(str) == 1) { if (isparam(lattr) == '1') rsprintf(" ", lattr); else rsprintf(" ", lattr); } /* display checkbox for boolean attributes */ if (strieq(attr_options[i][0], "boolean")) { if (atoi(attrib[i]) == 1) rsprintf("%s:\n", attr_list[i], class_value); if (attrib[i][0]) { sprintf(str, "Icon comment %s", attrib[i]); getcfg(lbs->name, str, comment, sizeof(comment)); if (comment[0]) rsprintf("\"%s\"", attrib[i], comment, comment); else rsprintf("\"%s\"", attrib[i], attrib[i], attrib[i]); } rsprintf(" \n", attr_list[i], class_value); /* separate options into individual lines */ strlcpy(str, attrib[i], sizeof(str)); p = strtok(str, "|"); while (p) { while (*p == ' ') p++; rsputs2(lbs, email, p); p = strtok(NULL, "|"); if (p) rsprintf("
"); } rsprintf("
%s %s \n", attr_list[i], class_value); sprintf(str, "Change %s", attr_list[i]); if (getcfg(lbs->name, str, display, sizeof(display))) { k = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE); sprintf(str, "%d", message_id); add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &k); add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date, &k, attr_flags[i]); strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, k); } else strcpy(display, attrib[i]); if (is_html(display) && !is_script(display) && html_allowed(lbs)) rsputs(display); else rsputs2(lbs, email, display); rsprintf(" 
\n"); rsputs("\n"); /*---- message text ----*/ show_text = !getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1; if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str, sizeof(str)) && !isparam("unm")) { n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE); for (j = 0; j < n; j++) if (strieq(gattr[j], "text")) break; if (j == n) show_text = FALSE; } if (show_text) { rsprintf(""); if (strieq(encoding, "html")) { if (email) replace_inline_img(lbs, text); rsputs(text); } else if (strieq(encoding, "ELCode")) { rsputs_elcode(lbs, email, text); } else { rsputs("

");
            rsputs2(lbs, email, text);
            rsputs("
"); } rsputs("\n"); n_hidden = 0; for (i = 0, n_attachments = 0; i < MAX_ATTACHMENTS; i++) { att_inline[i] = 0; att_hide[i] = getcfg(lbs->name, "Show attachments", str, sizeof(str)) && atoi(str) == 0; if (is_inline_attachment(encoding, message_id, text, i, attachment[i])) att_inline[i] = 1; if (attachment[i][0]) n_attachments++; } if (isparam("hide")) { strlcpy(str, getparam("hide"), sizeof(str)); p = strtok(str, ","); while (p != NULL) { if (atoi(p) < MAX_ATTACHMENTS) { att_hide[atoi(p)] = 1; n_hidden++; } p = strtok(NULL, ","); } } if (isparam("show")) { strlcpy(str, getparam("show"), sizeof(str)); p = strtok(str, ","); while (p != NULL) { if (atoi(p) < MAX_ATTACHMENTS) { att_hide[atoi(p)] = 0; } p = strtok(NULL, ","); } } for (index = 0; index < MAX_ATTACHMENTS; index++) { if (attachment[index][0] && strlen(attachment[index]) > 14 && !att_inline[index]) { for (i = 0; i < (int) strlen(attachment[index]); i++) att[i] = toupper(attachment[index][i]); att[i] = 0; /* determine size of attachment */ strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[index], sizeof(file_name)); thumb_status = create_thumbnail(lbs, file_name); length = 0; fh = open(file_name, O_RDONLY | O_BINARY); if (fh > 0) { lseek(fh, 0, SEEK_END); length = TELL(fh); close(fh); } strlcpy(str, attachment[index], sizeof(str)); str[13] = 0; strcpy(file_enc, attachment[index] + 14); url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */ if (email) { retrieve_domain(domain, sizeof(domain)); sprintf(ref, "cid:att%d@%s", index, domain); } else sprintf(ref, "%s/%s", str, file_enc); /* overall table */ rsprintf("\n"); rsprintf("\n", loc("Attachment"), index + 1); if (email) rsprintf("
%s %d:%s\n", attachment[index] + 14); else rsprintf("%s\n", ref, attachment[index] + 14); rsprintf(" "); if (length < 1024) rsprintf("%d Bytes", length); else if (length < 1024 * 1024) rsprintf("%d kB", length / 1024); else rsprintf("%1.3lf MB", length / 1024.0 / 1024.0); rsprintf("\n"); /* retrieve submission date */ memset(&ts, 0, sizeof(ts)); ts.tm_mon = (attachment[index][2] - '0') * 10 + attachment[index][3] - '0' - 1; ts.tm_mday = (attachment[index][4] - '0') * 10 + attachment[index][5] - '0'; ts.tm_year = (attachment[index][0] - '0') * 10 + attachment[index][1] - '0'; ts.tm_hour = (attachment[index][7] - '0') * 10 + attachment[index][8] - '0'; ts.tm_min = (attachment[index][9] - '0') * 10 + attachment[index][10] - '0'; ts.tm_sec = (attachment[index][11] - '0') * 10 + attachment[index][12] - '0'; if (ts.tm_year < 90) ts.tm_year += 100; ltime = mktime(&ts); /* show upload date/time only if different from entry date/time */ if (abs((int) (ltime - entry_ltime)) > 3600) { if (!getcfg(lbs->name, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); my_strftime(str, sizeof(str), format, &ts); rsprintf(" "); rsprintf("Uploaded %s", str); rsprintf("\n"); } /* determine if displayed inline */ display_inline = is_image(file_name) || is_ascii(file_name); if (chkext(att, ".PS") || chkext(att, ".PDF")) display_inline = 0; if ((chkext(att, ".HTM") || chkext(att, ".HTML")) && is_full_html(file_name)) display_inline = 0; if (thumb_status) display_inline = 1; if (display_inline) { /* hide this / show this */ if (!email) { rsprintf(""); rsprintf(" | "); if (att_hide[index]) { rsprintf("%s", loc("Show")); } else { rsprintf("%s", loc("Hide")); } /* hide all */ if (n_hidden < n_attachments) { rsprintf(" | %s", loc("Hide all")); } /* show all */ if (n_hidden > 0) { for (i = 0; i < MAX_ATTACHMENTS; i++) if (att_hide[i]) break; if (i < MAX_ATTACHMENTS) { rsprintf(" | %s", loc("Show all")); } } rsprintf("\n"); } } rsprintf("
\n"); strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(attachment[index], subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, attachment[index], sizeof(file_name)); if (!att_hide[index] && display_inline) { if (thumb_status) { rsprintf("\n"); if (thumb_status == 3) { rsprintf("%s\n", loc("Cannot create thumbnail, please check ImageMagick installation")); } else { if (thumb_status == 2 && !email) { for (i = 0;; i++) { strlcpy(str, file_name, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; sprintf(str + strlen(str), "-%d.png", i); if (file_exist(str)) { strlcpy(str, ref, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; sprintf(str + strlen(str), "-%d.png", i); rsprintf("\n", index + 1, ref); rsprintf("\"%s\"\n", str, attachment[index] + 14, attachment[index] + 14); } else break; } } else { if (!email) { rsprintf("\n", index + 1, ref); strlcpy(str, ref, sizeof(str)); if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) if (strrchr(str, '.')) *strrchr(str, '.') = 0; strlcat(str, ".png", sizeof(str)); rsprintf("\"%s\"\n", str, attachment[index] + 14, attachment[index] + 14); } } } rsprintf("\n\n"); } else if (is_image(att)) { if (!email) { rsprintf("\n"); rsprintf("\n", index + 1); rsprintf("\"%s\"\n", ref, attachment[index] + 14, attachment[index] + 14); rsprintf("\n\n"); } } else { if (is_ascii(file_name)) { /* display attachment */ rsprintf("\n"); /* anchor for references */ rsprintf("\n", index + 1); if (!chkext(att, ".HTML")) rsprintf("
");

                        f = fopen(file_name, "rt");
                        n_lines = 0;
                        if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
                           max_n_lines = atoi(str);
                        else
                           max_n_lines = 300;

                        if (f != NULL) {
                           while (!feof(f)) {
                              str[0] = 0;
                              fgets(str, sizeof(str), f);

                              if (n_lines < max_n_lines) {
                                 if (!chkext(att, ".HTML"))
                                    rsputs2(lbs, email, str);
                                 else
                                    rsputs(str);
                              }
                              n_lines++;
                           }
                           fclose(f);
                        }

                        if (!chkext(att, ".HTML"))
                           rsprintf("
"); rsprintf("\n"); if (max_n_lines == 0) rsprintf("%d lines\n", n_lines); else if (n_lines > max_n_lines) rsprintf("... %d more lines ...\n", n_lines - max_n_lines); rsprintf("\n"); } } } } } } } /* overall table (class "frame" from show_standard_header) */ rsprintf("\r\n\r\n"); show_bottom_text(lbs); if (!email) rsprintf("\n"); rsprintf("\r\n"); } /*------------------------------------------------------------------*/ BOOL convert_password_file(char *file_name) { char name[256], password[256], full_name[256], email[256], email_notify[256]; int i, len, fh, status; char *buf, *p; PMXML_NODE root, list, node, npwd; printf("Converting password file \"%s\" to new XML format ... ", file_name); fh = open(file_name, O_RDONLY | O_BINARY); if (fh < 0) return FALSE; len = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = xmalloc(len + 1); assert(buf); i = my_read(fh, buf, len); buf[i] = 0; close(fh); /* create backup */ strlcpy(name, file_name, sizeof(name)); strlcat(name, "_bak", sizeof(name)); fh = open(name, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644); if (fh > 0) { write(fh, buf, len); close(fh); } p = buf; /* skip leading spaces or new lines */ while (*p && isspace(*p)) p++; root = mxml_create_root_node(); list = mxml_add_node(root, "list", NULL); while (*p) { /* skip comment lines */ if (*p != ';' && *p != '#') { for (i = 0; i < (int) sizeof(name) - 1 && *p && *p != ':'; i++) name[i] = *p++; name[i] = 0; if (*p++ != ':') { xfree(buf); return FALSE; } for (i = 0; i < (int) sizeof(password) - 1 && *p && *p != ':'; i++) password[i] = *p++; password[i] = 0; if (*p++ != ':') { xfree(buf); return FALSE; } for (i = 0; i < (int) sizeof(full_name) - 1 && *p && *p != ':'; i++) full_name[i] = *p++; full_name[i] = 0; if (*p++ != ':') { xfree(buf); return FALSE; } for (i = 0; i < (int) sizeof(email) - 1 && *p && *p != ':'; i++) email[i] = *p++; email[i] = 0; if (*p++ != ':') { xfree(buf); return FALSE; } for (i = 0; i < (int) sizeof(email_notify) - 1 && *p && *p != '\r' && *p != '\n'; i++) email_notify[i] = *p++; email_notify[i] = 0; if (*p && *p != '\n' && *p != '\r') { xfree(buf); return FALSE; } while (*p && (*p == '\r' || *p == '\n')) p++; node = mxml_add_node(list, "user", NULL); mxml_add_node(node, "name", name); npwd = mxml_add_node(node, "password", password); mxml_add_attribute(npwd, "encoding", "SHA256"); mxml_add_node(node, "full_name", full_name); mxml_add_node(node, "last_logout", "0"); mxml_add_node(node, "last_activity", "0"); mxml_add_node(node, "email", email); mxml_add_node(node, "email_notify", email_notify); mxml_add_node(node, "inactive", "0"); } while (*p && isspace(*p)) p++; } status = mxml_write_tree(file_name, root); mxml_free_tree(root); xfree(buf); if (status) printf("Ok\n"); else { printf("Error writing to password file\n"); return FALSE; } return TRUE; } /*------------------------------------------------------------------*/ BOOL convert_password_encoding(LOGBOOK * lbs) { PMXML_NODE node, pwd; int i; char str[256], oldpwd[256], file_name[256]; if (lbs->pwd_xml_tree == NULL) return FALSE; if ((node = mxml_find_node(lbs->pwd_xml_tree, "/list/user[1]/password")) == NULL) return FALSE; str[0] = 0; if (mxml_get_attribute(node, "encoding") != NULL) strlcpy(str, mxml_get_attribute(node, "encoding"), sizeof(str)); if (!strieq(str, "SHA256")) { if ((node = mxml_find_node(lbs->pwd_xml_tree, "/list")) == NULL) return FALSE; printf("Converting password file for logbook \"%s\" to new encoding ... ", lbs->name); for (i = 0; i < mxml_get_number_of_children(node); i++) { sprintf(str, "/list/user[%d]/password", i + 1); pwd = mxml_find_node(lbs->pwd_xml_tree, str); if (pwd && mxml_get_value(pwd)) { strlcpy(str, mxml_get_value(pwd), sizeof(str)); /* assume base64 encoding, might be wrong if HAVE_CRYPT was used */ base64_decode(str, oldpwd); do_crypt(oldpwd, str, sizeof(str)); mxml_replace_node_value(pwd, str); mxml_add_attribute(pwd, "encoding", "SHA256"); } } if (get_password_file(lbs, file_name, sizeof(file_name))) mxml_write_tree(file_name, lbs->pwd_xml_tree); printf("ok\n"); } return TRUE; } /*------------------------------------------------------------------*/ PMXML_NODE load_password_file(LOGBOOK * lbs, char *error, int error_size) { PMXML_NODE root, xml_tree; char str[256], line[256], file_name[256]; int fh; struct stat st; if (error) error[0] = 0; if (!get_password_file(lbs, file_name, sizeof(file_name))) return NULL; fh = open(file_name, O_RDONLY); /* if password file doen't exist, try to create it */ if (fh < 0) { fh = open(file_name, O_CREAT | O_RDWR, 0600); if (fh < 0) { sprintf(str, "Cannot open file \"%s\"", file_name); strcat(str, ": "); strlcat(str, strerror(errno), sizeof(str)); show_error(str); eprintf(str); strlcpy(error, str, error_size); return NULL; } close(fh); /* put empty XML tree into password file */ printf("\nCreate empty password file \"%s\"\n", file_name); root = mxml_create_root_node(); mxml_add_node(root, "list", NULL); mxml_write_tree(file_name, root); mxml_free_tree(root); } else { /* check for write access to password file */ if (stat(file_name, &st) < 0) { sprintf(str, "Cannot access password file \"%s\"", file_name); strlcpy(error, str, error_size); return NULL; } #ifdef OS_WINNT if ((st.st_mode & _S_IWRITE) == 0) { #else if ((st.st_mode & S_IWUSR) == 0) { #endif sprintf(str, "Cannot access write protected password file \"%s\"", file_name); strlcpy(error, str, error_size); return NULL; } /* check if in XML format, otherwise convert it */ line[0] = 0; read(fh, line, sizeof(line)); close(fh); if (strstr(line, "name, "Password file", str, sizeof(str)); if (!str[0]) return 0; if (lbs->pwd_xml_tree) { if (user[0]) { sprintf(str, "/list/user[name=%s]", user); if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return 2; } else if (email && email[0]) { sprintf(str, "/list/user[email=%s]", email); if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return 2; } else return 0; /* if user found, retrieve other info */ if ((node = mxml_find_node(user_node, "name")) != NULL && user && mxml_get_value(node)) strlcpy(user, mxml_get_value(node), 256); if ((node = mxml_find_node(user_node, "password")) != NULL && password && mxml_get_value(node)) strlcpy(password, mxml_get_value(node), 256); if ((node = mxml_find_node(user_node, "full_name")) != NULL && full_name && mxml_get_value(node)) strlcpy(full_name, mxml_get_value(node), 256); if ((node = mxml_find_node(user_node, "email")) != NULL && email && mxml_get_value(node)) strlcpy(email, mxml_get_value(node), 256); if ((node = mxml_find_node(user_node, "last_logout")) != NULL && last_logout && mxml_get_value(node)) { *last_logout = date_to_ltime(mxml_get_value(node)); if (*last_logout == -1) *last_logout = 0; } if ((node = mxml_find_node(user_node, "inactive")) != NULL && inactive && mxml_get_value(node)) *inactive = atoi(mxml_get_value(node)); if ((node = mxml_find_node(user_node, "email_notify")) != NULL && email_notify) { if (mxml_get_number_of_children(node)) { for (i = 0; i < 1000; i++) email_notify[i] = FALSE; for (i = 0; i < mxml_get_number_of_children(node); i++) { subnode = mxml_subnode(node, i); for (j = 0; lb_list[j].name[0]; j++) if (strieq(lb_list[j].name, mxml_get_value(subnode))) { email_notify[j] = TRUE; break; } } } else { for (i = 0; i < 1000; i++) if (strieq(mxml_get_value(node), "all")) email_notify[i] = TRUE; else email_notify[i] = FALSE; } } return 1; } else { if (!user[0]) return 1; /* open password file */ load_password_files(); return get_user_line(lbs, user, password, full_name, email, email_notify, last_logout, inactive); } } /*------------------------------------------------------------------*/ int get_full_name(LOGBOOK * lbs, char *uname, char *full_name) { return get_user_line(lbs, uname, NULL, full_name, NULL, NULL, NULL, NULL); } /*------------------------------------------------------------------*/ int is_file_system_full(char *file_name) { char str[256]; char buf[1024]; int n, fh; strlcpy(str, file_name, sizeof(str)); strlcat(str, ".tmp", sizeof(str)); fh = open(str, O_CREAT | O_RDWR, 0644); if (fh < 0) return 0; n = write(fh, buf, sizeof(buf)); close(fh); remove(str); return n < (int) sizeof(buf); } /*------------------------------------------------------------------*/ int set_user_login_time(LOGBOOK * lbs, char *user) { int i; char str[256], global[256], orig_topgroup[256], file_name[256]; PMXML_NODE user_node, node; time_t last, now; /* if global password file is requested, search for first logbook with same password file than global section */ orig_topgroup[0] = 0; if (lbs == NULL) { getcfg("global", "Password file", global, sizeof(global)); if (getcfg_topgroup() && *getcfg_topgroup()) strcpy(orig_topgroup, getcfg_topgroup()); for (i = 0; lb_list[i].name[0]; i++) { if (lb_list[i].top_group[0]) setcfg_topgroup(lb_list[i].top_group); getcfg(lb_list[i].name, "Password file", str, sizeof(str)); if (strieq(str, global)) { lbs = lb_list + i; break; } } if (!lb_list[i].name[0]) return 1; if (orig_topgroup[0]) setcfg_topgroup(orig_topgroup); } getcfg(lbs->name, "Password file", str, sizeof(str)); if (!str[0] || !user[0]) return 1; if (lbs->pwd_xml_tree) { sprintf(str, "/list/user[name=%s]", user); if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return 1; if ((node = mxml_find_node(user_node, "last_activity")) != NULL) { strlcpy(str, mxml_get_value(node), sizeof(str)); last = date_to_ltime(str); } else last = 0; time(&now); /* check if activity time changed significantly */ if (now > last + 60) { /* if last activity is more than one hour ago, set new logout time from last activity */ if (now > last + 3600) { strcpy(str, "0"); if ((node = mxml_find_node(user_node, "last_activity")) != NULL) strlcpy(str, mxml_get_value(node), sizeof(str)); if ((node = mxml_find_node(user_node, "last_logout")) != NULL) mxml_replace_node_value(node, str); else mxml_add_node(user_node, "last_logout", str); } /* set new last activity */ strcpy(str, ctime(&now)); str[24] = 0; if ((node = mxml_find_node(user_node, "last_activity")) != NULL) mxml_replace_node_value(node, str); else mxml_add_node(user_node, "last_activity", str); /* flush to password file */ if (get_password_file(lbs, file_name, sizeof(file_name))) { /* check if file system if full */ if (is_file_system_full(file_name)) return 0; mxml_write_tree(file_name, lbs->pwd_xml_tree); } } } return 1; } /*------------------------------------------------------------------*/ int set_user_inactive(LOGBOOK * lbs, char *user, int inactive) { int i; char str[256], global[256], orig_topgroup[256], file_name[256]; PMXML_NODE user_node, node; /* if global password file is requested, search for first logbook with same password file than global section */ orig_topgroup[0] = 0; if (lbs == NULL) { getcfg("global", "Password file", global, sizeof(global)); if (getcfg_topgroup() && *getcfg_topgroup()) strcpy(orig_topgroup, getcfg_topgroup()); for (i = 0; lb_list[i].name[0]; i++) { if (lb_list[i].top_group[0]) setcfg_topgroup(lb_list[i].top_group); getcfg(lb_list[i].name, "Password file", str, sizeof(str)); if (strieq(str, global)) { lbs = lb_list + i; break; } } if (!lb_list[i].name[0]) return 1; if (orig_topgroup[0]) setcfg_topgroup(orig_topgroup); } getcfg(lbs->name, "Password file", str, sizeof(str)); if (!str[0] || !user[0]) return 1; if (lbs->pwd_xml_tree) { sprintf(str, "/list/user[name=%s]", user); if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return 0; sprintf(str, "%d", inactive); if ((node = mxml_find_node(user_node, "inactive")) != NULL) mxml_replace_node_value(node, str); else mxml_add_node(user_node, "inactive", str); /* flush to password file */ if (get_password_file(lbs, file_name, sizeof(file_name))) { /* check if file system if full */ if (is_file_system_full(file_name)) return 0; mxml_write_tree(file_name, lbs->pwd_xml_tree); } } return 1; } /*------------------------------------------------------------------*/ int set_user_password(LOGBOOK * lbs, char *user, char *password) { int i; char str[256], pwd_enc[256], file_name[256], orig_topgroup[256], global[256]; PMXML_NODE user_node, node, npwd; orig_topgroup[0] = 0; if (lbs == NULL) { getcfg("global", "Password file", global, sizeof(global)); if (getcfg_topgroup() && *getcfg_topgroup()) strcpy(orig_topgroup, getcfg_topgroup()); for (i = 0; lb_list[i].name[0]; i++) { if (lb_list[i].top_group[0]) setcfg_topgroup(lb_list[i].top_group); getcfg(lb_list[i].name, "Password file", str, sizeof(str)); if (str[0] && strieq(str, global)) { lbs = lb_list + i; break; } } if (!lb_list[i].name[0]) return 0; if (orig_topgroup[0]) setcfg_topgroup(orig_topgroup); } if (lbs->pwd_xml_tree) { sprintf(str, "/list/user[name=%s]", user); if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return 0; do_crypt(password, pwd_enc, sizeof(pwd_enc)); if ((node = mxml_find_node(user_node, "password")) != NULL) mxml_replace_node_value(node, pwd_enc); else { npwd = mxml_add_node(user_node, "password", pwd_enc); mxml_add_attribute(npwd, "encoding", "SHA256"); } /* flush to password file */ if (get_password_file(lbs, file_name, sizeof(file_name))) { /* check if file system if full */ if (is_file_system_full(file_name)) return 0; mxml_write_tree(file_name, lbs->pwd_xml_tree); } } return 1; } /*------------------------------------------------------------------*/ BOOL enum_user_line(LOGBOOK * lbs, int n, char *user, int size) { char str[256], file_name[256]; int i; PMXML_NODE node; if (lbs == NULL) { getcfg(NULL, "password file", file_name, sizeof(file_name)); for (i = 0; lb_list[i].name[0]; i++) { getcfg(lb_list[i].name, "password file", str, sizeof(str)); if (strieq(file_name, str)) break; } if (lb_list[i].name[0] == 0) lbs = &lb_list[0]; else lbs = &lb_list[i]; } if (!lbs) return FALSE; if (lbs->pwd_xml_tree == NULL) return FALSE; sprintf(str, "/list/user[%d]/name", n + 1); if ((node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL) return FALSE; strlcpy(user, mxml_get_value(node), size); return TRUE; } /*------------------------------------------------------------------*/ BOOL check_login_user(LOGBOOK * lbs, char *user) { int i, n, status; char str[1000]; char list[MAX_N_LIST][NAME_LENGTH]; if (user == NULL) return FALSE; /* check if usr is in password file */ status = get_user_line(lbs, user, NULL, NULL, NULL, NULL, NULL, NULL); if (status == 2) return FALSE; /* treat admin user as login user */ if (getcfg(lbs->name, "Admin user", str, sizeof(str)) && user[0]) { n = strbreak(str, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strcmp(user, list[i]) == 0) return TRUE; } if (getcfg(lbs->name, "Login user", str, sizeof(str)) && user[0]) { n = strbreak(str, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strcmp(user, list[i]) == 0) break; if (i == n) return FALSE; } return TRUE; } /*------------------------------------------------------------------*/ BOOL is_admin_user(LOGBOOK *lbs, char *user) { int i, n; char str[1000]; char list[MAX_N_LIST][NAME_LENGTH]; char logbook[1000]; if (lbs == NULL) strlcpy(logbook, "global", sizeof(logbook)); else strlcpy(logbook, lbs->name, sizeof(logbook)); /* Removed user[0] for cloning, have to check implications, same below. if (getcfg(logbook, "Admin user", str, sizeof(str)) && user[0]) { */ if (user == NULL) return FALSE; if (getcfg(logbook, "Admin user", str, sizeof(str))) { n = strbreak(str, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strcmp(user, list[i]) == 0) break; if (i == n) return FALSE; } /* make sure user is logged in */ if (lbs && !logged_in(lbs)) return FALSE; return TRUE; } /*------------------------------------------------------------------*/ BOOL is_admin_user_global(char *user) { int i, n; char str[1000]; char list[MAX_N_LIST][NAME_LENGTH]; if (user == NULL) return FALSE; if (getcfg_simple("global", "Admin user", str, sizeof(str), FALSE)) { n = strbreak(str, list, MAX_N_LIST, ",", FALSE); for (i = 0; i < n; i++) if (strcmp(user, list[i]) == 0) break; if (i == n) return FALSE; } return TRUE; } /*------------------------------------------------------------------*/ void show_login_page(LOGBOOK * lbs, char *redir, int fail) { char str[256], str2[256]; int show_forgot_link, show_self_register; /* if URL is specified in configuration file, check if login happens for the specified host, in order to get cookies right... */ if (getcfg(lbs->name, "URL", str, sizeof(str))) { extract_host(str); strlcpy(str2, http_host, sizeof(str)); if (strchr(str2, ':')) *strchr(str2, ':') = 0; if (!strieq(str, str2)) { redirect(lbs, _cmdline); return; } } sprintf(str, "ELOG %s", loc("Login")); show_html_header(lbs, TRUE, str, TRUE, FALSE, NULL, FALSE, 0); /* set focus on name field */ rsprintf("\n"); rsprintf("
\n\n"); /* define hidden fields for current destination */ strlcpy(str, redir, sizeof(str)); if (strchr(str, '<')) url_encode(str, sizeof(str)); if (strchr(str, ' ')) return; rsprintf("\n", str); rsprintf(""); rsprintf("\n", loc("Please login")); if (fail == 1) { strlcpy(str, loc("Invalid user name or password"), sizeof(str)); rsprintf("\n", str); } if (fail == 2) { sprintf(str, loc("User \"%s\" has no access to this logbook"), getparam("unm")); rsprintf("\n", str); } if (isparam("unm")) strencode2(str, getparam("unm"), sizeof(str)); rsprintf("\n"); rsprintf("\n"); if (!getcfg(lbs->name, "Login expiration", str, sizeof(str)) || atof(str) > 0) { rsprintf("\n"); } show_forgot_link = (!getcfg(lbs->name, "allow password change", str, sizeof(str)) || atoi(str) == 1); show_self_register = (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0); if (show_forgot_link || show_self_register) rsprintf("", str, loc("Register as new user")); } if (show_forgot_link || show_self_register) rsprintf("\n"); rsprintf("", loc("Submit")); rsprintf("
%s
%s!
%s!
\n"); rsprintf("\n"); rsprintf("\n", loc("Username")); rsprintf("\n", isparam("unm") ? str : "", loc("Username")); rsprintf("
\n"); rsprintf("\n"); rsprintf("\n", loc("Password")); rsprintf("\n"); rsprintf("
"); if (isparam("urem") && atoi(getparam("urem")) == 0) rsprintf("\n"); else rsprintf("\n"); rsprintf("%s\n", loc("Keep me logged in on this computer")); if (str[0] == 0) rsprintf(loc("for the next %d days"), 31); else if (atof(str) < 1) rsprintf(loc("for the next %d minutes"), (int) (atof(str) * 60)); else if (atof(str) == 1) rsprintf(loc("for the next hour")); else if (atof(str) <= 48) rsprintf(loc("for the next %d hours"), (int) atof(str)); else rsprintf(loc("for the next %d days"), (int) (atof(str) / 24)); rsprintf(" %s", loc("or until I log out")); rsprintf("
\n"); if (show_forgot_link) rsprintf("%s", loc("Forgot"), loc("Forgot password?")); if (show_self_register) { strlcpy(str, loc("New user"), sizeof(str)); url_encode(str, sizeof(str)); if (show_forgot_link) rsprintf("
"); rsprintf("%s
\n"); show_bottom_text_login(lbs); rsprintf("
\r\n"); } /*------------------------------------------------------------------*/ BOOL check_login(LOGBOOK * lbs, char *sid) { char str[1000], pwd_file[256], user_name[256], upwd[256]; int status, inactive, skip_sid_check; /* show new user screen if password file is empty */ if (!enum_user_line(lbs, 0, str, sizeof(str))) { if (isparam("new_user_name")) return TRUE; getcfg(lbs->name, "Authentication", str, sizeof(str)); if (stristr(str, "Webserver")) { show_new_user_page(lbs, http_user); } else { show_new_user_page(lbs, NULL); } return FALSE; } /* check for "forgot password" */ if (isparam("cmd") && strcmp(getparam("cmd"), loc("Forgot")) == 0) { if (getcfg(lbs->name, "forgot password link", str, sizeof(str)) && atoi(str) == 0) return FALSE; show_forgot_pwd_page(lbs); return FALSE; } /* check for password login (elog & mirroring) */ skip_sid_check = FALSE; if (isparam("unm") && isparam("upwd")) { get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL); if (strcmp(upwd, getparam("upwd")) != 0) { show_login_page(lbs, "", 0); return FALSE; } else { strlcpy(user_name, getparam("unm"), sizeof(user_name)); skip_sid_check = TRUE; } } /* if invalid or no session ID, show login page, unless we outsourced the authentication to webserver */ if (!skip_sid_check && !sid_check(sid, user_name)) { if (isparam("redir")) strlcpy(str, getparam("redir"), sizeof(str)); else strlcpy(str, isparam("cmdline") ? getparam("cmdline") : _cmdline, sizeof(str)); /* avoid recursive loops with ?cmd=Login */ if (stristr(str, loc("Login"))) str[0] = 0; show_login_page(lbs, str, 0); return FALSE; } /* if user not in password file (external authentication!) obtain user info */ status = get_user_line(lbs, user_name, NULL, NULL, NULL, NULL, NULL, &inactive); /* fail if password file cannot be accessed */ if (status == 0) { getcfg(lbs->name, "Password file", pwd_file, sizeof(pwd_file)); sprintf(str, loc("Cannot open file %s"), pwd_file); strcat(str, ": "); strlcat(str, strerror(errno), sizeof(str)); show_error(str); return FALSE; } /* if user cannot be found in password file, ask for new user info */ if (status == 2 && !isparam("new_user_name")) { show_new_user_page(lbs, user_name); return FALSE; } /* show error for inactive account */ if (inactive) { show_error("This account is currently deactivated"); return FALSE; } /* remember user name for other places */ setparam("unm", user_name); /* check if user has access to logbook */ if (!check_login_user(lbs, user_name) && !isparam("new_user_name")) { show_login_page(lbs, "", 2); return FALSE; } /* set access time for user */ if (user_name[0] && !isparam("new_user_name")) { if (!set_user_login_time(lbs, user_name)) { show_error(loc("File system full, ELOG cannot continue to work")); return FALSE; } } return TRUE; } /*------------------------------------------------------------------*/ int node_contains(LBLIST pn, char *logbook) { int i; for (i = 0; i < pn->n_members; i++) { /* check if logbook in this group */ if (strieq(pn->member[i]->name, logbook)) return 1; /* check if loogbook is in subgroups */ if (pn->member[i]->n_members > 0 && node_contains(pn->member[i], logbook)) return 1; } return 0; } /*------------------------------------------------------------------*/ void show_logbook_node(LBLIST plb, LBLIST pparent, int level, int btop) { int i, index, j, expand, expand_all, message_id; char str[10000], date[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], mid[80]; if (plb->n_members > 0) { expand = 0; if (isparam("gexp")) { if (strieq(plb->name, getparam("gexp")) || node_contains(plb, getparam("gexp")) || strieq(getparam("gexp"), "all")) expand = 1; } if (!getcfg(plb->name, "Expand selection page", str, sizeof(str)) || atoi(str) == 1) expand_all = 1; else expand_all = 0; /* do not display top groups */ if (!plb->is_top) { rsprintf(""); for (i = 0; i < level; i++) rsprintf(" \n"); rsprintf("", 13 - level); for (i = 0; i < level; i++) rsprintf("  "); if (expand) { if (expand_all) rsprintf("%s", plb->name); else { if (pparent != NULL) { if (getcfg_topgroup()) rsprintf("- %s ", getcfg_topgroup(), pparent->name, plb->name); else rsprintf("- %s ", pparent->name, plb->name); } else { if (getcfg_topgroup()) rsprintf("- %s ", getcfg_topgroup(), plb->name); else rsprintf("- %s ", plb->name); } } } else { if (expand_all) rsprintf("%s", plb->name); else { if (getcfg_topgroup()) rsprintf("+ %s ", getcfg_topgroup(), plb->name, plb->name); else rsprintf("+ %s ", plb->name, plb->name); } } rsprintf("\n"); } if (plb->is_top || expand || expand_all) for (i = 0; i < plb->n_members; i++) show_logbook_node(plb->member[i], plb->is_top ? NULL : plb, level + 1, btop); } else { if (!getcfg(plb->name, "Hidden", str, sizeof(str)) || atoi(str) == 0) { /* search logbook in list */ for (index = 0; lb_list[index].name[0]; index++) if (strieq(plb->name, lb_list[index].name)) break; if (!lb_list[index].name[0]) return; rsprintf(""); for (j = 0; j < level; j++) rsprintf(" \n"); rsprintf("", 10 - level); if (btop) rsprintf("%s", lb_list[index].name_enc, lb_list[index].name); else rsprintf("%s", lb_list[index].name_enc, lb_list[index].name); if ((getcfg(lb_list[index].name, "Password file", str, sizeof(str)) && !getcfg(lb_list[index].name, "Guest menu commands", str, sizeof(str)))) rsprintf("  \"%s\"", loc("This logbook requires authentication"), loc("This logbook requires authentication")); rsprintf("
\n"); if (!getcfg(plb->name, "Hide Comments", str, sizeof(str)) || atoi(str) == 0) { str[0] = 0; getcfg(lb_list[index].name, "Comment", str, sizeof(str)); rsprintf(""); if (is_html(str)) rsputs(str); else rsputs3(str); rsprintf("\n"); } rsprintf(""); rsprintf("%d", *lb_list[index].n_el_index); rsprintf("\n"); rsprintf(""); if (*lb_list[index].n_el_index == 0) rsprintf("-"); else { char attrib[MAX_N_ATTR][NAME_LENGTH]; lb_list[index].n_attr = scan_attributes(lb_list[index].name); message_id = el_search_message(&lb_list[index], EL_LAST, 0, FALSE); el_retrieve(&lb_list[index], message_id, date, attr_list, attrib, lb_list[index].n_attr, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); if (!getcfg(lb_list[index].name, "Last submission", str, sizeof(str))) { sprintf(str, "$entry time"); for (i = 0; i < lb_list[index].n_attr; i++) if (strieq(attr_list[i], "Author")) break; if (i < lb_list[index].n_attr) sprintf(str + strlen(str), " %s $author", loc("by")); } j = build_subst_list(&lb_list[index], slist, svalue, attrib, TRUE); sprintf(mid, "%d", message_id); add_subst_list(slist, svalue, "message id", mid, &j); add_subst_time(&lb_list[index], slist, svalue, "entry time", date, &j, 0); strsubst_list(str, sizeof(str), slist, svalue, j); rsputs(str); } rsprintf("\n"); } } } /*------------------------------------------------------------------*/ void show_top_selection_page() { int i; char str[10000], name[NAME_LENGTH], name_enc[NAME_LENGTH]; LBLIST phier; /* if selection page protected, check password */ if (getcfg("global", "password file", str, sizeof(str)) && getcfg("global", "protect selection page", str, sizeof(str)) && atoi(str) == 1) if (!check_login(NULL, getparam("sid"))) return; if (getcfg("global", "Page Title", str, sizeof(str))) { strip_html(str); show_html_header(NULL, TRUE, str, TRUE, FALSE, NULL, FALSE, 0); } else show_html_header(NULL, TRUE, "ELOG Logbook Selection", TRUE, FALSE, NULL, FALSE, 0); rsprintf("\n\n"); rsprintf("\n"); rsprintf("\n"); phier = get_logbook_hierarchy(); for (i = 0; i < phier->n_members; i++) if (phier->member[i]->is_top) { rsprintf("\n"); } free_logbook_hierarchy(phier); rsprintf("
\n"); if (getcfg("global", "Welcome title", str, sizeof(str))) { rsputs(str); } else { rsprintf("%s.
\n", loc("Several logbooks groups are defined on this host")); rsprintf("%s:\n", loc("Please select one to list the logbooks in that group")); } rsprintf("
"); strlcpy(name, phier->member[i]->name, sizeof(name)); strlcpy(name_enc, name, sizeof(name_enc)); url_encode(name_enc, sizeof(name_enc)); rsprintf("%s", name_enc, name); rsprintf("
\n"); rsprintf("\r\n\r\n"); } /*------------------------------------------------------------------*/ void show_selection_page(void) { int i, j, expand_all, show_title; char str[10000], file_name[256]; LBLIST phier; /* check if at least one logbook defined */ if (!lb_list[0].name[0]) { show_standard_header(NULL, FALSE, "ELOG", "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("\n"); rsprintf("
\n"); rsprintf(loc("No logbook defined on this server")); rsprintf("
\n"); rsprintf("%s", loc("Create new logbook"), loc("Create new logbook")), rsprintf("
\n"); rsprintf("\n"); return; } /* check for Guest Selection Page */ if (getcfg("global", "Guest Selection Page", str, sizeof(str)) && !(isparam("unm"))) { /* check for URL */ if (strstr(str, "http://") || strstr(str, "https://")) { redirect(NULL, str); return; } /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strlcpy(file_name, str, sizeof(file_name)); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } send_file_direct(file_name); return; } /* top group present and no top group in URL */ if (exist_top_group() && getcfg_topgroup() == NULL) { if (getcfg("global", "show top groups", str, sizeof(str)) && atoi(str) == 1) { show_top_selection_page(); return; } else return; /* abort connection */ } /* if selection page protected, check password */ if (getcfg("global", "password file", str, sizeof(str)) && getcfg("global", "protect selection page", str, sizeof(str)) && atoi(str) == 1) if (!check_login(NULL, getparam("sid"))) return; if (getcfg("global", "Page Title", str, sizeof(str))) { strip_html(str); show_html_header(NULL, TRUE, str, TRUE, FALSE, NULL, FALSE, 0); } else show_html_header(NULL, TRUE, "ELOG Logbook Selection", TRUE, FALSE, NULL, FALSE, 0); rsprintf("\n\n"); rsprintf("\n"); rsprintf("\n"); if (getcfg("global", "mirror server", str, sizeof(str))) { /* only admin user sees synchronization link */ if (is_admin_user(NULL, getparam("unm"))) { rsprintf("\n"); rsprintf("\n", loc("Synchronize all logbooks")); rsprintf("\n"); } } phier = get_logbook_hierarchy(); show_title = 0; if (getcfg_topgroup()) { for (i = 0; i < phier->n_members; i++) if (strieq(getcfg_topgroup(), phier->member[i]->name)) { if (phier->member[i]->n_members == 0) show_title = 1; else for (j = 0; j < phier->member[i]->n_members; j++) if (phier->member[i]->member[j]->n_members == 0) show_title = 1; break; } } else for (i = 0; i < phier->n_members; i++) if (phier->member[i]->n_members == 0) show_title = 1; if (!getcfg("global", "Expand selection page", str, sizeof(str)) || atoi(str) == 1) expand_all = 1; else expand_all = 0; if (isparam("gexp") || expand_all) show_title = 1; if (show_title) { rsprintf("\n"); rsprintf("\n", loc("Logbook")); rsprintf("\n", loc("Entries")); rsprintf("\n", loc("Last submission")); rsprintf("\n"); } else { rsprintf("\n"); rsprintf("\n", loc("Expand all")); rsprintf("\n"); } if (getcfg_topgroup()) { for (i = 0; i < phier->n_members; i++) if (strieq(getcfg_topgroup(), phier->member[i]->name)) { show_logbook_node(phier->member[i], NULL, -1, 1); break; } } else for (i = 0; i < phier->n_members; i++) show_logbook_node(phier->member[i], NULL, 0, 0); free_logbook_hierarchy(phier); rsprintf("
\n"); if (getcfg("global", "Welcome title", str, sizeof(str))) { rsputs(str); } else { rsprintf("%s.
\n", loc("Several logbooks are defined on this host")); rsprintf("%s:\n", loc("Please select the one to connect to")); } rsprintf("
\n"); rsprintf("%s
%s%s%s
\n"); rsprintf("%s
\n"); rsprintf("\r\n\r\n"); } /*------------------------------------------------------------------*/ void get_password(char *password) { static char last_password[32]; if (strncmp(password, "set=", 4) == 0) strlcpy(last_password, password + 4, sizeof(last_password)); else strcpy(password, last_password); } /*------------------------------------------------------------------*/ int do_self_register(LOGBOOK * lbs, char *command) /* evaluate self-registration commands */ { char str[256]; if (command == NULL) return 1; /* display new user page if "self register" is clicked */ if (strieq(command, loc("New user"))) { show_new_user_page(lbs, NULL); return 0; } /* save user info if "save" is pressed */ if (strieq(command, loc("Save")) && isparam("new_user_name") && !isparam("config")) { if (!save_user_config(lbs, getparam("new_user_name"), TRUE)) return 0; if (lbs) sprintf(str, "../%s/", lbs->name_enc); else sprintf(str, "."); redirect(lbs, str); return 0; } /* display account request notification */ if (strieq(command, loc("Requested"))) { show_standard_header(lbs, FALSE, loc("ELOG registration"), "", FALSE, NULL, NULL, 0); rsprintf(""); rsprintf("
\n"); rsprintf("%s.", loc("Your request has been forwarded to the administrator")); rsprintf("%s.", loc("You will be notified by email upon activation of your new account")); rsprintf("
\n"); show_bottom_text(lbs); rsprintf("\n"); return 0; } /* indicate continue */ return 1; } /*------------------------------------------------------------------*/ void show_day(char *css_class, char *day) { if (day[0]) { rsprintf("%s\n", day); } else { /* empty cell */ rsprintf(" \n", css_class); } } void show_calendar(LOGBOOK * lbs) { int i, j, cur_mon, cur_year, today_day, today_mon, today_year; time_t now, stime; struct tm *ts; char str[256], index[10]; time(&now); ts = localtime(&now); assert(ts); today_mon = ts->tm_mon + 1; today_day = ts->tm_mday; today_year = ts->tm_year + 1900; if (isparam("m") && isparam("y")) { cur_mon = atoi(getparam("m")); cur_year = atoi(getparam("y")); ts->tm_mday = 1; ts->tm_mon = cur_mon - 1; ts->tm_year = cur_year - 1900; mktime(ts); } else { cur_mon = ts->tm_mon + 1; cur_year = ts->tm_year + 1900; } if (isparam("i")) strencode2(index, getparam("i"), sizeof(index)); else strcpy(index, "1"); show_html_header(lbs, FALSE, loc("Calendar"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("
\n"); rsprintf("\n", index); rsprintf("\n", cur_year); rsprintf("\n\n"); rsprintf(""); rsprintf(""); rsprintf("
\n"); rsprintf("\n"); /* link to previous year */ rsprintf("  "); rsprintf("", index, cur_mon, cur_year - 1); rsprintf("\"%s\"", loc("Previous Year"), loc("Previous Year")); /* current year */ rsprintf(" %d ", cur_year); /* link to next year */ rsprintf("", index, cur_mon, cur_year + 1); rsprintf("\"%s\"", loc("Next Year"), loc("Next Year")); /* go to first day of month */ ts->tm_mday = 1; stime = mktime(ts); if (stime < 0) { rsprintf("
Invalid date
\n
\n"); return; } /* go to last sunday */ stime = stime - 3600 * 24 * ts->tm_wday; rsprintf("\n"); for (i = 0; i < 7; i++) { ts = localtime(&stime); assert(ts); strftime(str, sizeof(str), "%a", ts); rsprintf("%s\n", str); stime += 3600 * 24; } rsprintf("\n"); stime -= 3600 * 24 * 7; ts = localtime(&stime); assert(ts); for (i = 0; i < 6; i++) { rsprintf("\n"); for (j = 0; j < 7; j++) { if (ts->tm_mon + 1 == cur_mon) sprintf(str, "%d", ts->tm_mday); else strcpy(str, ""); if (ts->tm_mday == today_day && ts->tm_mon + 1 == today_mon && ts->tm_year + 1900 == today_year) show_day("calcurday", str); else { if (j == 0) show_day("calsun", str); else if (j == 6) show_day("calsat", str); else show_day("calday", str); } stime += 3600 * 24; ts = localtime(&stime); assert(ts); } rsprintf("\n"); if (ts->tm_mon + 1 != cur_mon) break; } rsprintf("\n\n"); } /*------------------------------------------------------------------*/ void show_uploader(LOGBOOK * lbs) { char str[256]; show_html_header(lbs, FALSE, loc("Upload image"), TRUE, FALSE, NULL, FALSE, 0); rsprintf("
\n"); rsprintf("\n"); rsprintf("\n\n"); rsprintf(""); strcpy(str, loc("Maximum allowed file size is")); if (MAX_CONTENT_LENGTH >= 1024 * 1024) sprintf(str + strlen(str), " %d MB", MAX_CONTENT_LENGTH / 1024 / 1024); else sprintf(str + strlen(str), " %d kB", MAX_CONTENT_LENGTH / 1024); rsprintf("\n", loc("Enter filename or URL"), str); rsprintf("\n"); rsprintf("\n"); rsprintf("
%s: (%s)

\n"); rsprintf("\n", loc("Upload")); rsprintf("\n", loc("Cancel")); rsprintf("

\n"); rsprintf("
\n"); } /*------------------------------------------------------------------*/ void show_uploader_finished(LOGBOOK * lbs) { int i; char str[256], att[256], base_url[256], file_enc[256], ref[256], ref_thumb[256]; show_html_header(lbs, FALSE, loc("Image uploaded successfully"), FALSE, FALSE, NULL, FALSE, 0); rsprintf("\n\n"); rsprintf("\n"); rsprintf("\n"); /* find last attachment */ att[0] = 0; for (i = MAX_ATTACHMENTS - 1; i >= 0; i--) { sprintf(str, "attachment%d", i); if (isparam(str)) { strlcpy(att, getparam(str), sizeof(att)); break; } } if (att[0]) { strlcpy(str, att, sizeof(str)); str[13] = 0; strcpy(file_enc, att + 14); url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */ sprintf(ref, "%s/%s?lb=%s", str, file_enc, lbs->name_enc); sprintf(ref_thumb, "%s/%s?lb=%s&thumb=1", str, file_enc, lbs->name_enc); compose_base_url(lbs, base_url, sizeof(base_url), TRUE); rsprintf("\n\n"); rsprintf("
\n"); rsprintf(loc("Image \"%s\" uploaded successfully"), att + 14); rsprintf("
\n"); } rsprintf("\n"); } /*------------------------------------------------------------------*/ void show_uploader_json(LOGBOOK *lbs) { char charset[256]; char filename[256], thumbname[256], attchname[256], subdir[256]; int i, j, attch_count; // maximum number of files that can be uploaded this way (drag and drop into the editor) const long MAX_FILE_COUNT = 100; rsprintf("HTTP/1.1 200 Document follows\r\n"); rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision()); rsprintf("Accept-Ranges: bytes\r\n"); rsprintf("Pragma: no-cache\r\n"); rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n"); if (keep_alive) { rsprintf("Connection: Keep-Alive\r\n"); rsprintf("Keep-Alive: timeout=60, max=10\r\n"); } if (!getcfg("global", "charset", charset, sizeof(charset))) strcpy(charset, DEFAULT_HTTP_CHARSET); rsprintf("Content-Type: application/json;charset=%s\r\n\r\n", charset); attch_count = strtol(getparam("drop-count"), NULL, 10); // limit the number of files that can be uploaded if(attch_count > MAX_FILE_COUNT) { attch_count = MAX_FILE_COUNT; } rsprintf("{\r\n"); rsprintf(" \"attachments\" : [\r\n"); for(i = 0; i < attch_count; i++) { sprintf(attchname, "attachment%d", i); rsprintf(" {\r\n"); rsprintf(" \"fullName\": \"%s\",\r\n", getparam(attchname)); strlcpy(filename, lbs->data_dir, sizeof(filename)); generate_subdir_name(getparam(attchname), subdir, sizeof(subdir)); strlcat(filename, subdir, sizeof(filename)); strlcat(filename, getparam(attchname), sizeof(filename)); if (create_thumbnail(lbs, filename)) { get_thumb_name(filename, thumbname, sizeof(thumbname), 0); if (strrchr(thumbname, '/')) rsprintf(" \"thumbName\": \"%s\",\r\n", strrchr(thumbname, '/')+1); else rsprintf(" \"thumbName\": \"%s\",\r\n", thumbname); } rsprintf(" \"contentType\": "); for (j = 0; filetype[j].ext[0]; j++) if (chkext(filename, filetype[j].ext)) break; if (filetype[j].ext[0]) rsprintf("\"%s\"\r\n", filetype[j].type); else if (is_ascii(filename)) rsprintf("\"%s\"\r\n", "text/plain"); else rsprintf("\"%s\"\r\n", "application/octet-stream\r\n"); if(i == attch_count - 1) rsprintf(" }\r\n"); else rsprintf(" },\r\n"); } rsprintf(" ]\r\n"); rsprintf("}\r\n"); return; } /*------------------------------------------------------------------*/ void interprete(char *lbook, char *path) /******************************************************************** Routine: interprete Purpose: Interprete parametersand generate HTML output. Input: char *path Message path _param/_value array accessible via getparam() \********************************************************************/ { int status, i, j, n, message_id, inactive; char list[1000], section[256], str[NAME_LENGTH], str1[NAME_LENGTH], str2[NAME_LENGTH], edit_id[80], file_name[256], command[256], enc_path[256], dec_path[256], uname[80], full_name[256], user_email[256], logbook[256], logbook_enc[256], *experiment, group[256], css[256], *pfile, attachment[MAX_PATH_LENGTH], str3[NAME_LENGTH], thumb_name[256], sid[32], error_str[256], subdir[256], *s; LOGBOOK *lbs; FILE *f; /* encode path for further usage */ strcpy(dec_path, path); url_decode(dec_path); strcpy(enc_path, dec_path); url_encode(enc_path, sizeof(enc_path)); strencode2(command, isparam("cmd") ? getparam("cmd") : "", sizeof(command)); strencode2(group, isparam("group") ? getparam("group") : "", sizeof(group)); experiment = getparam("exp"); if (getcfg(lbook, "Logging Level", str, sizeof(str))) _logging_level = atoi(str); else _logging_level = 2; set_condition(""); message_id = atoi(dec_path); /* evaluate "jcmd" */ if (isparam("jcmd") && *getparam("jcmd")) strlcpy(command, getparam("jcmd"), sizeof(command)); /* check for localization command */ if (stricmp(command, "loc") == 0) { show_http_header(NULL, FALSE, NULL); if (isparam("value") && *getparam("value")) rsputs(loc(getparam("value"))); /* dummy strings for JS-only translations */ s = loc("Drop attachments here..."); s = loc("Insert Timestamp"); if (s) s = NULL; // avoid compiler warning return; } /* if experiment given, use it as logbook (for elog!) */ if (experiment && experiment[0]) { strcpy(logbook_enc, experiment); strcpy(logbook, experiment); url_decode(logbook); /* check if logbook exists */ for (i = 0;; i++) { if (!enumgrp(i, str)) break; if (strieq(logbook, str)) break; } if (!strieq(logbook, str)) { sprintf(str, "Error: logbook \"%s\" not defined in %s", logbook_enc, CFGFILE); show_error(str); return; } } else { strcpy(logbook_enc, lbook); strcpy(logbook, lbook); url_decode(logbook); } /* check for top group */ setcfg_topgroup(""); sprintf(str, "Top group %s", logbook); if (getcfg("global", str, list, sizeof(list))) { setcfg_topgroup(logbook); logbook[0] = 0; } /* check if new logbook */ for (i = j = 0;; i++) { if (!enumgrp(i, str)) break; if (is_logbook(str)) { /* redo index if logbooks in cfg file do not match lb_list */ if (!strieq(str, lb_list[j++].name)) { el_index_logbooks(); break; } } } /* check for deleted logbook */ if (lb_list[j].name[0] != 0) el_index_logbooks(); /*---- direct commands (registration etc) ----*/ if (!logbook[0]) { /* check for self register */ if (getcfg(group, "Self register", str, sizeof(str)) && atoi(str) > 0) { if (!do_self_register(NULL, getparam("cmd"))) return; } /* check for activate */ if (strieq(command, loc("Activate")) && isparam("new_user_name") && isparam("code")) { if (!activate_user(NULL, getparam("new_user_name"), atoi(getparam("code")))) return; setparam("cfg_user", getparam("new_user_name")); get_user_line(NULL, getparam("new_user_name"), NULL, full_name, user_email, NULL, NULL, NULL); sprintf(str, "%s <%s>", full_name, user_email); sprintf(str2, loc("Activation notice has been sent to %s"), str); setparam("notice", str2); show_config_page(NULL); return; } /* check for save after activate */ if (strieq(command, loc("Save"))) { if (isparam("config")) { /* change existing user */ if (!isparam("config") || !save_user_config(NULL, getparam("config"), FALSE)) return; } redirect(NULL, "."); return; } /* check for password recovery */ if (isparam("cmd") || isparam("newpwd")) { if (isparam("newpwd") || strieq(command, loc("Change password"))) { /* if logged in via SID, set user name */ if (sid_check(getparam("sid"), uname)) setparam("unm", uname); show_change_pwd_page(NULL); return; } } /* check for new login */ if (isparam("uname") && isparam("upassword")) { /* log logins */ strlcpy(uname, getparam("uname"), sizeof(uname)); sprintf(str, "LOGIN user \"%s\" (attempt) for logbook selection page", uname); write_logfile(NULL, str); if (isparam("redir")) strlcpy(str, getparam("redir"), sizeof(str)); else strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str)); /* authorize user via password file or site authentication */ if (!auth_verify_password(NULL, uname, getparam("upassword"), error_str, sizeof(error_str))) { if (error_str[0]) show_error(error_str); else show_login_page(NULL, str, 1); return; } /* check if user in password file */ if (get_user_line(NULL, uname, NULL, full_name, NULL, NULL, NULL, NULL) == 2) { /* if self registering not allowed, go back to login screen */ if (!getcfg(group, "Self register", str, sizeof(str)) || atoi(str) == 0) { show_login_page(NULL, str, 1); return; } } /* put encoded password into password file */ set_user_password(NULL, uname, getparam("upassword")); sprintf(str, "LOGIN user \"%s\" (success)", uname); write_logfile(NULL, str); /* get a new session ID */ sid_new(NULL, uname, (char *) inet_ntoa(rem_addr), sid); /* set SID cookie */ set_sid_cookie(NULL, sid, full_name); return; } /* check for global selection page if no logbook given */ if (!logbook[0] && getcfg("global", "Selection page", str, sizeof(str))) { /* check for URL */ if (strstr(str, "http://") || strstr(str, "https://")) { redirect(NULL, str); return; } /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strlcpy(file_name, str, sizeof(file_name)); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } send_file_direct(file_name); return; } /* check for global synchronization */ if (strieq(command, "Synchronize")) { synchronize(NULL, SYNC_HTML); return; } } /* count logbooks */ for (n = 0; lb_list[n].name[0]; n++); /* if no logbook given, display logbook selection page */ if (!logbook[0] && !path[0]) { if (n > 1) { /* check for forgot password page */ if (strieq(command, loc("Forgot"))) { show_forgot_pwd_page(NULL); return; } show_selection_page(); return; } strcpy(logbook, lb_list[0].name); strcpy(logbook_enc, logbook); url_encode(logbook_enc, sizeof(logbook_enc)); } /* get logbook from list */ for (i = 0; lb_list[i].name[0]; i++) if (strieq(logbook, lb_list[i].name)) break; lbs = &lb_list[i]; /* set top level group for logbook */ if (lbs->top_group[0]) setcfg_topgroup(lbs->top_group); /* get theme for logbook */ if (getcfg(logbook, "Theme", str, sizeof(str))) strlcpy(theme_name, str, sizeof(theme_name)); else strlcpy(theme_name, "default", sizeof(theme_name)); lbs = lb_list + i; lbs->n_attr = scan_attributes(lbs->name); /* check for error during attribute scan */ if (lbs->n_attr < 0) return; /* evaluate AJAX xommand */ if (isparam("acmd") && *getparam("acmd")) { if (strieq(getparam("acmd"), "Upload")) show_uploader_json(lbs); return; } /* if we outsource the authentication to Webserver and have no sid, just set a new sid */ getcfg(lbs->name, "Authentication", str, sizeof(str)); if (stristr(str, "Webserver")) { if (http_user[0]) { if (!sid_check(getparam("sid"), http_user)) { /* if we don't have a sid yet, set it */ /* get a new session ID */ sid_new(lbs, http_user, (char *) inet_ntoa(rem_addr), sid); /* set SID cookie */ set_sid_cookie(lbs, sid, http_user); } } else { sprintf(str, "Error: Misconfigured webserver, did not get X-Forwarded-User from it."); show_error(str); return; } } /* check for new login */ if (isparam("uname") && isparam("upassword")) { /* log logins */ strlcpy(uname, getparam("uname"), sizeof(uname)); sprintf(str, "LOGIN user \"%s\" (attempt)", uname); write_logfile(lbs, str); if (isparam("redir")) strlcpy(str, getparam("redir"), sizeof(str)); else strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str)); /* authorize user via password file or site authentication */ if (!auth_verify_password(lbs, uname, getparam("upassword"), error_str, sizeof(error_str))) { if (error_str[0]) show_error(error_str); else show_login_page(lbs, str, 1); return; } /* check if user in password file */ if (get_user_line(lbs, uname, NULL, full_name, NULL, NULL, NULL, &inactive) == 2) { /* if self registering not allowed, go back to login screen */ if (!getcfg(lbs->name, "Self register", str, sizeof(str)) || atoi(str) == 0) { show_login_page(lbs, str, 1); return; } } /* show error for inactive account */ if (inactive) { show_error("This account is currently deactivated"); return; } /* check if user has access to logbook */ if (!check_login_user(lbs, getparam("uname"))) { show_error("User has no access to this logbook"); return; } /* put encoded password into password file */ set_user_password(lbs, uname, getparam("upassword")); sprintf(str, "LOGIN user \"%s\" (success)", uname); write_logfile(lbs, str); /* get a new session ID */ sid_new(lbs, uname, (char *) inet_ntoa(rem_addr), sid); /* set SID cookie */ set_sid_cookie(lbs, sid, full_name); return; } /* deliver icons without password */ if (chkext(path, ".gif") || chkext(path, ".jpg") || chkext(path, ".png") || chkext(path, ".ico") || chkext(path, ".htm") || chkext(path, ".css")) { /* check if file in resource directory */ strlcpy(str, resource_dir, sizeof(str)); strlcat(str, path, sizeof(str)); if (exist_file(str)) { send_file_direct(str); return; } else { /* else search file in themes directory */ strlcpy(str, resource_dir, sizeof(str)); strlcat(str, "themes", sizeof(str)); strlcat(str, DIR_SEPARATOR_STR, sizeof(str)); strlcat(str, theme_name, sizeof(str)); strlcat(str, DIR_SEPARATOR_STR, sizeof(str)); strlcat(str, path, sizeof(str)); if (exist_file(str)) { send_file_direct(str); return; } } } /* check for valid logbook */ if (!logbook[0]) { strencode2(str2, path, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); return; } /* check for self activation */ if (strieq(command, loc("Activate")) && isparam("unm") && isparam("code") && !isparam("new_user_name")) { strlcpy(uname, getparam("unm"), sizeof(uname)); if (!activate_user(lbs, uname, atoi(getparam("code")))) return; sprintf(str, "ACTIVATE user \"%s\" (success)", uname); write_logfile(lbs, str); /* get a new session ID */ sid_new(lbs, uname, (char *) inet_ntoa(rem_addr), sid); /* redirect to config page */ sprintf(str, "?cmd=%s¬ice=%s. %s", loc("Config"), loc("Your account has been activated"), loc("Please subscribe to logbooks if you want to receive automatic email notifications")); setparam("redir", str); /* set SID cookie */ set_sid_cookie(lbs, sid, uname); return; } /* if password file given, check session ID */ if (getcfg(lbs->name, "Password file", str, sizeof(str))) { /* get current CSS */ strlcpy(css, "elog.css", sizeof(css)); if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str))) strlcpy(css, str, sizeof(css)); else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str))) strlcpy(css, str, sizeof(css)); /* check if guest access */ if (getcfg(lbs->name, "Guest menu commands", str, sizeof(str))) { /* if logged in via SID, set user name */ if (sid_check(getparam("sid"), uname)) setparam("unm", uname); } /* set access time for user */ if (uname[0] && !isparam("new_user_name")) { if (!set_user_login_time(lbs, uname)) { show_error(loc("File system full, ELOG cannot continue to work")); return; } } if (!(getcfg(lbs->name, "Guest menu commands", str, sizeof(str)) && !isparam("fail"))) { if (strcmp(path, css) != 0) { /* if no guest menu commands but self register, evaluate new user commands */ if (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0) { if (!do_self_register(lbs, command)) return; } /* check for correct session ID */ if (!check_login(lbs, getparam("sid"))) return; } } } if (strieq(command, loc("Login"))) { check_login(lbs, ""); return; } if (strieq(command, loc("New")) || strieq(command, loc("Edit")) || strieq(command, loc("Reply")) || strieq(command, loc("Duplicate")) || strieq(command, loc("Delete")) || strieq(command, loc("Upload")) || strieq(command, loc("Submit")) || strieq(command, loc("Preview"))) { sprintf(str, "%s?cmd=%s", path, command); } if (strieq(command, loc("Delete")) || strieq(command, loc("Config")) || strieq(command, loc("Copy to")) || strieq(command, loc("Move to"))) { sprintf(str, "%s?cmd=%s", path, command); } /* check for "Back" button */ if (strieq(command, loc("Back"))) { if (isparam("edit_id")) { /* unlock message */ el_lock_message(lbs, atoi(getparam("edit_id")), NULL, FALSE); /* remove draft */ el_draft_message(lbs, atoi(getparam("edit_id")), NULL, FALSE); /* redirect to message */ strlcpy(edit_id, getparam("edit_id"), sizeof(edit_id)); sprintf(str, "../%s/%s", logbook_enc, edit_id); } else sprintf(str, "../%s/", logbook_enc); if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1) strcpy(str, "../"); redirect(lbs, str); return; } /* check for "Detelte" button */ if (strieq(command, "XDelete")) { if (getparam("edit_id")) { status = el_delete_message(lbs, atoi(getparam("edit_id")), TRUE, NULL, TRUE, TRUE); if (status != EL_SUCCESS) { sprintf(str, "%s = %d", loc("Error deleting message: status"), status); show_error(str); return; } } redirect(lbs, ""); return; } /* check for "Unlock" command */ if (strieq(command, "Unlock")) { if (isparam("edit_id")) { /* unlock message and remove draft */ el_lock_message(lbs, atoi(getparam("edit_id")), NULL, FALSE); /* redirect to message */ strlcpy(edit_id, getparam("edit_id"), sizeof(edit_id)); sprintf(str, "../%s/%s", logbook_enc, edit_id); } else sprintf(str, "../%s/", logbook_enc); if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1) strcpy(str, "../"); redirect(lbs, str); return; } /* check for "List" button */ if (strieq(command, loc("List"))) { if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1) { redirect(lbs, "../"); return; } show_elog_list(lbs, 0, 0, 0, TRUE, NULL); return; } /* check for "Cancel" button */ if (strieq(command, loc("Cancel"))) { sprintf(str, "../%s/%s", logbook_enc, path); redirect(lbs, str); return; } /* check for "Last n*2 Entries" */ strlcpy(str, isparam("last") ? getparam("last") : "", sizeof(str)); if (strchr(str, ' ')) { i = atoi(strchr(str, ' ')); sprintf(str, "last%d", i); if (isparam("mode")) { sprintf(str + strlen(str), "?mode="); strlcat(str, getparam("mode"), sizeof(str)); } redirect(lbs, str); return; } strlcpy(str, isparam("past") ? getparam("past") : "", sizeof(str)); if (strchr(str, ' ')) { i = atoi(strchr(str, ' ')); sprintf(str, "past%d", i); redirect(lbs, str); return; } /* check for pastxx */ if (strncmp(path, "past", 4) == 0 && (isdigit(path[4]) || isdigit(path[5])) && isparam("cmd") == 0) { show_elog_list(lbs, atoi(path + 4), 0, 0, FALSE, NULL); return; } if (strncmp(path, "last", 4) == 0 && !chkext(path, ".png") && (!isparam("cmd") || strieq(getparam("cmd"), loc("Select"))) && !isparam("newpwd") && atoi(path + 4) > 0) { show_elog_list(lbs, 0, atoi(path + 4), 0, FALSE, NULL); return; } if (strncmp(path, "page", 4) == 0 && isparam("cmd") == 0) { if (!path[4]) show_elog_list(lbs, 0, 0, -1, FALSE, NULL); else show_elog_list(lbs, 0, 0, atoi(path + 4), FALSE, NULL); return; } /* check for calender */ if (strieq(dec_path, "cal.html")) { show_calendar(lbs); return; } /* check for rss-feed */ if (strieq(dec_path, "elog.rdf")) { show_rss_feed(lbs); return; } /* check for upload window */ if (strieq(dec_path, "upload.html")) { show_uploader(lbs); return; } /* check for finished JavaScript upload */ if (isparam("jcmd") && isparam("jcmd") && strieq(getparam("jcmd"), "JUpload")) { show_uploader_finished(lbs); return; } /*---- check if file requested -----------------------------------*/ /* skip elog message id in front of possible attachment */ pfile = dec_path; if (strchr(pfile, '/') && pfile[13] != '/' && isdigit(pfile[0])) pfile = strchr(pfile, '/') + 1; if ((strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '_') || (strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '/') || chkext(pfile, ".gif") || chkext(pfile, ".ico") || chkext(pfile, ".jpg") || chkext(pfile, ".jpeg") || chkext(pfile, ".png") || chkext(pfile, ".css") || chkext(pfile, ".js") || chkext(pfile, ".html")) { if ((strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '_') || (strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '/')) { if (pfile[13] == '/') pfile[13] = '_'; /* file from data directory requested */ strlcpy(file_name, lbs->data_dir, sizeof(file_name)); generate_subdir_name(pfile, subdir, sizeof(subdir)); strlcat(file_name, subdir, sizeof(file_name)); strlcat(file_name, pfile, sizeof(file_name)); } else { /* file from theme directory requested */ strlcpy(file_name, resource_dir, sizeof(file_name)); if (file_name[0] && file_name[strlen(file_name) - 1] != DIR_SEPARATOR) strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, "themes", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); if (theme_name[0]) { strlcat(file_name, theme_name, sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); } strlcat(file_name, pfile, sizeof(file_name)); } if (isparam("thumb")) { get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0); if (thumb_name[0]) send_file_direct(thumb_name); else send_file_direct(file_name); } else send_file_direct(file_name); return; } /* from here on, logbook must be valid */ if (!logbook[0]) { show_selection_page(); return; } /*---- check if attachment requested -----------------------------*/ if (strchr(dec_path, '/')) { message_id = atoi(dec_path); n = atoi(strchr(dec_path, '/') + 1) - 1; status = el_retrieve_attachment(lbs, message_id, n, attachment); if (status != EL_SUCCESS || n >= MAX_ATTACHMENTS) { sprintf(str, "Attachment #%d of entry #%d not found", n + 1, message_id); show_error(str); } else { if (isparam("thumb")) strlcat(attachment, "?thumb=1", sizeof(attachment)); redirect(lbs, attachment); } return; } /* check for new syntax in config file */ if (getcfg(lbs->name, "Types", str, sizeof(str))) { show_upgrade_page(lbs); return; } /* correct for image buttons */ if (isparam("cmd_first.x")) strcpy(command, loc("First")); if (isparam("cmd_previous.x")) strcpy(command, loc("Previous")); if (isparam("cmd_next.x")) strcpy(command, loc("Next")); if (isparam("cmd_last.x")) strcpy(command, loc("Last")); /* check if command allowed for current user */ if (command[0] && !is_user_allowed(lbs, command)) { if (isparam("uname")) get_full_name(lbs, getparam("uname"), full_name); else full_name[0] = 0; strencode2(str2, command, sizeof(str2)); strencode2(str3, full_name, sizeof(str3)); sprintf(str, loc("Error: Command \"%s\" is not allowed for user \"%s\""), str2, str3); show_error(str); return; } /* check if command in menu list */ if (!is_command_allowed(lbs, command, message_id)) { /* redirect to login page for new command */ if (strieq(command, loc("New")) && !isparam("unm")) { show_login_page(lbs, _cmdline, 0); return; } strencode2(str2, command, sizeof(str3)); sprintf(str, loc("Error: Command \"%s\" not allowed"), str2); show_error(str); return; } /*---- check for various commands --------------------------------*/ if (strieq(command, loc("Help"))) { if (getcfg(lbs->name, "Help URL", str, sizeof(str))) { /* if URL is given, redirect */ if (strstr(str, "http://") || strstr(str, "https://")) { redirect(lbs, str); return; } /* send file from resource directory */ strlcpy(file_name, resource_dir, sizeof(file_name)); strlcat(file_name, "resources", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); f = fopen(file_name, "r"); if (f == NULL) { sprintf(str, "Cannot find file \"%s\"", file_name); show_error(str); } else { fclose(f); send_file_direct(file_name); } return; } /* send local help file */ strlcpy(file_name, resource_dir, sizeof(file_name)); strlcat(file_name, "resources", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, "eloghelp_", sizeof(file_name)); if (getcfg("global", "Language", str, sizeof(str))) { for (i = 0; i < (int) strlen(str); i++) str[i] = my_tolower(str[i]); strlcat(file_name, str, sizeof(file_name)); } else strlcat(file_name, "english", sizeof(file_name)); strlcat(file_name, ".html", sizeof(file_name)); f = fopen(file_name, "r"); if (f == NULL) redirect(lbs, "https://midas.psi.ch/elog/eloghelp_english.html"); else { fclose(f); send_file_direct(file_name); } return; } if (strieq(command, loc("HelpELCode"))) { /* send local help file */ strlcpy(file_name, resource_dir, sizeof(file_name)); strlcat(file_name, "resources", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, "elcode_", sizeof(file_name)); if (getcfg("global", "Language", str, sizeof(str))) { for (i = 0; i < (int) strlen(str); i++) str[i] = my_tolower(str[i]); strlcat(file_name, str, sizeof(file_name)); } else strlcat(file_name, "english", sizeof(file_name)); strlcat(file_name, ".html", sizeof(file_name)); f = fopen(file_name, "r"); if (f == NULL) redirect(lbs, "https://midas.psi.ch/elog/elcode_english.html"); else { fclose(f); send_file_direct(file_name); } return; } if (strieq(command, loc("New"))) { if (check_drafts(lbs)) return; show_edit_form(lbs, 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE); return; } /* check for deletion of attachments */ for (i = 0; i < MAX_ATTACHMENTS; i++) { sprintf(str, "delatt%d", i); if (isparam(str) || (isparam("smcmd") && stricmp(getparam("smcmd"), str) == 0)) { sprintf(str, "attachment%d", i); strlcpy(file_name, getparam(str), sizeof(file_name)); el_delete_attachment(lbs, file_name); /* re-order attachments */ for (j = i; j < MAX_ATTACHMENTS; j++) { sprintf(str, "attachment%d", j + 1); if (isparam(str)) strlcpy(file_name, getparam(str), sizeof(file_name)); else file_name[0] = 0; sprintf(str, "attachment%d", j); if (file_name[0]) setparam(str, file_name); else unsetparam(str); } show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE); return; } } if (strieq(command, loc("Upload")) || strieq(command, "Upload")) { show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE); return; } if (strieq(command, loc("Edit"))) { if (message_id) { show_edit_form(lbs, message_id, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE); return; } else if (isparam("nsel")) { show_edit_form(lbs, 0, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE); return; } } if (strieq(command, loc("Reply"))) { show_edit_form(lbs, message_id, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE); return; } if (strieq(command, loc("Update"))) { show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE); return; } if ((strieq(command, loc("Save")) || strieq(command, "Save")) && !isparam("cfgpage") && !isparam("new_user_name")) { /* save draft message */ if (isparam("unm")) strlcpy(str, getparam("unm"), sizeof(str)); else strlcpy(str, loc("user"), sizeof(str)); setparam("draft", str); submit_elog(lbs); return; } if (strieq(command, loc("Duplicate"))) { if (message_id) { show_edit_form(lbs, message_id, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE); return; } } if (strieq(command, loc("Preview"))) { show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE); return; } if (strieq(command, loc("Submit")) || strieq(command, "Submit")) { if (isparam("mirror_id")) submit_elog_mirror(lbs); else submit_elog(lbs); /* elog command line utility wants to remove sid after submission */ if (isparam("sidclose")) sid_remove(getparam("sid")); return; } if (strieq(command, loc("Find"))) { /* stip message id */ if (dec_path[0]) { sprintf(str, "../%s/?cmd=%s", lbs->name_enc, loc("Find")); redirect(lbs, str); return; } show_find_form(lbs); return; } if (strieq(command, loc("Search"))) { if (dec_path[0] && atoi(dec_path) == 0 && strchr(dec_path, '/') != NULL) { strencode2(str2, dec_path, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); return; } show_elog_list(lbs, 0, 0, 0, TRUE, NULL); return; } if (strieq(command, loc("Last day"))) { redirect(lbs, "past1"); return; } if (strieq(command, loc("Last 10"))) { redirect(lbs, "last10"); return; } if (strieq(command, loc("Copy to"))) { copy_to(lbs, message_id, isparam("destc") ? getparam("destc") : "", 0, 0); return; } if (strieq(command, loc("Move to"))) { copy_to(lbs, message_id, isparam("destm") ? getparam("destm") : "", 1, 0); return; } if (strieq(command, loc("Admin")) || strieq(command, loc("Change config file"))) { show_admin_page(lbs, NULL); return; } sprintf(str, loc("Change %s"), "[global]"); if (strieq(command, str)) { show_admin_page(lbs, "global"); return; } sprintf(str2, "[global %s]", lbs->top_group); sprintf(str, loc("Change %s"), str2); if (strieq(command, str)) { show_admin_page(lbs, lbs->top_group); return; } if (strieq(command, loc("Delete this logbook"))) { show_logbook_delete(lbs); return; } if (strieq(command, loc("Rename this logbook"))) { show_logbook_rename(lbs); return; } if (strieq(command, loc("Create new logbook"))) { if (isparam("tmp") && strieq(getparam("tmp"), "Cancel")) { if (getcfg(lbs->name, "Password file", str, sizeof(str))) sprintf(str, "?cmd=%s", loc("Change config file")); else sprintf(str, "?cmd=%s", loc("Config")); redirect(lbs, str); return; } show_logbook_new(lbs); return; } if (strieq(command, "GetPwdFile")) { if (get_password_file(lbs, file_name, sizeof(file_name))) send_file_direct(file_name); return; } if (strieq(command, loc("Change password")) || (isparam("newpwd") && !strieq(command, loc("Cancel")) && !strieq(command, loc("Save")))) { show_change_pwd_page(lbs); return; } if (strieq(command, loc("Save")) && (isparam("cfgpage") || isparam("new_user_name"))) { if (isparam("config") && isparam("new_user_name")) { if (!strieq(getparam("config"), getparam("new_user_name"))) { if (get_user_line(lbs, getparam("new_user_name"), NULL, NULL, NULL, NULL, NULL, NULL) == 1) { sprintf(str, "%s \"%s\" %s", loc("Login name"), getparam("new_user_name"), loc("exists already")); show_error(str); return; } } /* change existing user */ if (!save_user_config(lbs, isparam("config") ? getparam("config") : "", FALSE)) return; /* go back to logbook */ redirect(lbs, ""); return; } else if (isparam("new_user_name")) { /* new user */ if (!save_user_config(lbs, getparam("new_user_name"), TRUE)) return; } else { if (isparam("global")) { if (strieq(getparam("global"), "global")) strcpy(section, "global"); else { sprintf(section, "global "); strlcat(section, getparam("global"), sizeof(section)); } } else strlcpy(section, lbs->name, sizeof(section)); if (!save_admin_config(section, _mtext, str)) { /* save cfg file */ show_error(str); return; } } if (lbs) sprintf(str, "../%s/", lbs->name_enc); else sprintf(str, "."); if (isparam("new_user_name")) { sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config")); strlcat(str, getparam("new_user_name"), sizeof(str)); } else if (isparam("cfg_user")) { sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config")); strlcat(str, getparam("cfg_user"), sizeof(str)); } else if (getcfg(lbs->name, "password file", str2, sizeof(str2))) sprintf(str + strlen(str), "?cmd=%s", loc("Config")); redirect(lbs, str); return; } if (strieq(command, loc("Activate")) && isparam("new_user_name") && isparam("code")) { if (!activate_user(lbs, getparam("new_user_name"), atoi(getparam("code")))) return; setparam("cfg_user", getparam("new_user_name")); get_user_line(lbs, getparam("new_user_name"), NULL, full_name, user_email, NULL, NULL, NULL); sprintf(str, "%s <%s>", full_name, user_email); sprintf(str2, loc("Activation notice has been sent to %s"), str); setparam("notice", str2); show_config_page(lbs); return; } if (strieq(command, loc("Remove user")) && isparam("config")) { if (!remove_user(lbs, getparam("config"))) return; /* if removed user is current user, do logout */ if (isparam("unm") && strieq(getparam("config"), getparam("unm"))) { /* log activity */ write_logfile(lbs, "LOGOUT"); /* set cookies */ sid_remove(getparam("sid")); set_sid_cookie(lbs, "", ""); } /* continue configuration as administrator */ unsetparam("cfg_user"); show_config_page(lbs); return; } if (strieq(command, loc("New user"))) { show_new_user_page(lbs, NULL); return; } /* check for forgot password page */ if (strieq(command, loc("Forgot"))) { show_forgot_pwd_page(lbs); return; } if (strieq(command, loc("Config"))) { if (!getcfg(lbs->name, "Password file", str, sizeof(str))) show_admin_page(lbs, NULL); else show_config_page(lbs); return; } if (strieq(command, loc("Download")) || strieq(command, "Download")) { show_download_page(lbs, dec_path); return; } if (strieq(command, loc("Import"))) { strcpy(str, loc("CSV Import")); url_encode(str, sizeof(str)); sprintf(str1, "?cmd=%s", str); strcpy(str, loc("XML Import")); url_encode(str, sizeof(str)); sprintf(str2, "?cmd=%s", str); show_query(lbs, loc("ELOG import"), loc("Please choose format to import:"), "CSV", str1, "XML", str2); return; } if (strieq(command, loc("CSV Import"))) { show_import_page_csv(lbs); return; } if (strieq(command, loc("XML Import"))) { show_import_page_xml(lbs); return; } if (strieq(command, "getmd5")) { show_md5_page(lbs); return; } if (strieq(command, loc("Synchronize"))) { synchronize(lbs, SYNC_HTML); return; } if (strieq(command, loc("Logout"))) { /* log activity */ write_logfile(lbs, "LOGOUT"); if (getcfg(lbs->name, "Logout to main", str, sizeof(str)) && atoi(str) == 1) { sprintf(str, "../"); setparam("redir", str); } else { if (getcfg(lbs->name, "Logout to URL", str, sizeof(str))) setparam("redir", str); } set_sid_cookie(lbs, "", ""); sid_remove(getparam("sid")); return; } if (strieq(command, loc("Delete"))) { show_elog_delete(lbs, message_id); return; } if (strieq(command, "IM")) { call_image_magick(lbs); return; } /* check for welcome page */ if (!_cmdline[0] && getcfg(lbs->name, "Welcome page", str, sizeof(str)) && str[0]) { /* check if file starts with an absolute directory */ if (str[0] == DIR_SEPARATOR || str[1] == ':') strcpy(file_name, str); else { strlcpy(file_name, resource_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } send_file_direct(file_name); return; } /* check for start page */ if (!_cmdline[0] && getcfg(lbs->name, "Start page", str, sizeof(str)) && str[0]) { redirect(lbs, str); return; } /* show page listing or display single entry */ if (dec_path[0] == 0) show_elog_list(lbs, 0, 0, 0, TRUE, NULL); else show_elog_entry(lbs, dec_path, command); return; } /*------------------------------------------------------------------*/ void decode_get(char *logbook, char *string) { char path[256]; char *p, *pitem; setparam("cmdline", string); strlcpy(path, string, sizeof(path)); path[255] = 0; if (strchr(path, '?')) *strchr(path, '?') = 0; setparam("path", path); if (strchr(string, '?')) { p = strchr(string, '?') + 1; /* cut trailing "/" from netscape */ if (p[strlen(p) - 1] == '/') p[strlen(p) - 1] = 0; p = strtok(p, "&"); while (p != NULL) { pitem = p; p = strchr(p, '='); if (p != NULL) { *p++ = 0; url_decode(pitem); url_decode(p); if (!setparam(pitem, p)) return; p = strtok(NULL, "&"); } } } interprete(logbook, path); } /*------------------------------------------------------------------*/ void decode_post(char *logbook, LOGBOOK * lbs, const char *string, const char *boundary, int length) { int n_att, size, status, header_size; const char *pinit, *p, *pctmp, *pbody; char *buffer, *ptmp; char file_name[MAX_PATH_LENGTH], full_name[MAX_PATH_LENGTH], str[NAME_LENGTH], str2[NAME_LENGTH], line[NAME_LENGTH], item[NAME_LENGTH]; n_att = 0; pinit = string; /* return if no boundary defined */ if (!boundary[0]) return; /* skip first boundary */ if (strstr(string, boundary)) string = strstr(string, boundary) + strlen(boundary); do { if (strstr(string, "name=")) { strlcpy(line, strstr(string, "name=") + 5, sizeof(line)); if (strchr(line, '\r')) *strchr(line, '\r') = 0; if (strchr(line, '\n')) *strchr(line, '\n') = 0; strlcpy(item, line, sizeof(item)); if (item[0] == '\"') { strlcpy(item, line + 1, sizeof(item)); if (strchr(item, '\"')) *strchr(item, '\"') = 0; } else if (strchr(item, ' ')) *strchr(item, ' ') = 0; if (strncmp(item, "attachment", 10) == 0) { /* attachment names from previous uploads */ n_att = atoi(item + 10) + 1; } if (strncmp(item, "csvfile", 7) == 0 || strncmp(item, "xmlfile", 7) == 0) { /* evaluate CSV/XML import file */ if (strstr(string, "filename=")) { p = strstr(string, "filename=") + 9; if (*p == '\"') p++; if (strstr(p, "\r\n\r\n")) string = strstr(p, "\r\n\r\n") + 4; else if (strstr(p, "\r\r\n\r\r\n")) string = strstr(p, "\r\r\n\r\r\n") + 6; if (strchr(p, '\"')) *strchr(p, '\"') = 0; /* set attachment filename */ strlcpy(file_name, p, sizeof(file_name)); if (file_name[0]) { if (get_verbose() >= VERBOSE_INFO) eprintf("decode_post: Found CSV/XML import file\n"); } /* find next boundary */ pctmp = string; do { while (*pctmp != '-' && pctmp < string + length) pctmp++; if (pctmp == string + length) return; if ((p = strstr(pctmp, boundary)) != NULL) { if (*(p - 1) == '-') p--; while (*p == '-') p--; if (*p == 10) p--; if (*p == 13) p--; p++; break; } else pctmp += strlen(pctmp); } while (TRUE); /* import CSV/XML file */ if (file_name[0] && !(isparam("cmd") && strieq(getparam("cmd"), loc("Cancel")))) { if (strncmp(item, "csvfile", 7) == 0) { setparam("csvfile", file_name); csv_import(lbs, string, file_name); return; } else if (strncmp(item, "xmlfile", 7) == 0) { setparam("xmlfile", file_name); xml_import(lbs, string, file_name); return; } } string = strstr(p, boundary) + strlen(boundary); } else string = strstr(string, boundary) + strlen(boundary); } else if (strncmp(item, "attfile", 7) == 0) { /* evaluate file attachment */ if (strstr(string, "filename=")) { p = strstr(string, "filename=") + 9; if (*p == '\"') p++; if (strstr(p, "\r\n\r\n")) string = strstr(p, "\r\n\r\n") + 4; else if (strstr(p, "\r\r\n\r\r\n")) string = strstr(p, "\r\r\n\r\r\n") + 6; if (strchr(p, '\"')) *strchr(p, '\"') = 0; /* set attachment filename */ strlcpy(file_name, p, sizeof(file_name)); /* remove spaces */ btou(file_name); if (file_name[0]) { if (get_verbose() >= VERBOSE_INFO) eprintf("decode_post: Found attachment %s\n", file_name); /* check filename for invalid characters */ if (strpbrk(file_name, ",;+=")) { strencode2(str2, file_name, sizeof(str2)); sprintf(str, "Error: Filename \"%s\" contains invalid character", str2); show_error(str); return; } } /* find next boundary */ pctmp = string; do { while (*pctmp != '-' && pctmp < string + length) pctmp++; if (pctmp == string + length) return; if ((p = strstr(pctmp, boundary)) != NULL) { if (*(p - 1) == '-') p--; while (*p == '-') p--; if (*p == 10) p--; if (*p == 13) p--; p++; break; } else pctmp += strlen(pctmp); } while (TRUE); /* check attachment size */ if (file_name[0] && (p - string) == 0) { /* check for URL */ if (stristr(file_name, "http://") || stristr(file_name, "https://")) { size = retrieve_url(lbs, file_name, stristr(file_name, "https://") != NULL, &buffer); if (size <= 0) { strencode2(str2, file_name, sizeof(str2)); sprintf(str, loc("Cannot retrieve file from URL \"%s\""), str2); show_error(str); return; } /* check for HTTP header */ pbody = strstr(buffer, "\r\n\r\n"); if (!pbody) { show_error(loc("Invalid HTTP header")); xfree(buffer); return; } pbody += 4; header_size = pbody - buffer; /* check for file found */ if (strchr(buffer, ' ')) { status = atoi(strchr(buffer, ' ') + 1); if (status != 200) { strencode2(str2, file_name, sizeof(str2)); sprintf(str, loc("File not found at URL \"%s\""), str2); show_error(str); return; } } el_submit_attachment(lbs, file_name, pbody, size - header_size, full_name); xfree(buffer); sprintf(str, "attachment%d", n_att++); setparam(str, full_name); } else { strencode2(str2, file_name, sizeof(str2)); sprintf(str, loc("Attachment file \"%s\" empty or not found"), str2); show_error(str); return; } } else if (file_name[0]) { /* save attachment */ if (el_submit_attachment(lbs, file_name, string, (int) (p - string), full_name) < 0) return; sprintf(str, "attachment%d", n_att++); setparam(str, full_name); } string = strstr(p, boundary) + strlen(boundary); } else string = strstr(string, boundary) + strlen(boundary); } else { p = string; if (strstr(p, "\r\n\r\n")) p = strstr(p, "\r\n\r\n") + 4; else if (strstr(p, "\r\r\n\r\r\n")) p = strstr(p, "\r\r\n\r\r\n") + 6; if (strstr(p, boundary)) { string = strstr(p, boundary) + strlen(boundary); if (stricmp(item, "text") == 0) { if (string - p > TEXT_SIZE) { sprintf(str, "Error: Entry text too big. Please increase TEXT_SIZE and recompile elogd\n"); show_error(str); return; } strlcpy(_mtext, p, sizeof(_mtext)); if (strstr(_mtext, boundary)) *strstr(_mtext, boundary) = 0; ptmp = _mtext + (strlen(_mtext) - 1); while (*ptmp == '-') *ptmp-- = 0; while (*ptmp == '\n' || *ptmp == '\r') *ptmp-- = 0; } else { strlcpy(str, p, sizeof(str)); if (strstr(str, boundary)) *strstr(str, boundary) = 0; ptmp = str + (strlen(str) - 1); while (*ptmp == '-') *ptmp-- = 0; while (*ptmp == '\n' || *ptmp == '\r') *ptmp-- = 0; if (setparam(item, str) == 0) return; } } else { strlcpy(str, p, sizeof(str)); if (setparam(item, str) == 0) return; string = p + strlen(p); } } while (*string == '-' || *string == '\n' || *string == '\r') string++; } else return; /* invalid request */ } while ((int) (string - pinit) < length); if (lbs) interprete(lbs->name, ""); else interprete(logbook, ""); } /*------------------------------------------------------------------*/ #define N_MAX_CONNECTION 100 #define KEEP_ALIVE_TIME 60 int ka_sock[N_MAX_CONNECTION]; int ka_time[N_MAX_CONNECTION]; #ifdef HAVE_SSL SSL *ka_ssl_con[N_MAX_CONNECTION]; #endif struct in_addr remote_addr[N_MAX_CONNECTION]; char remote_host[N_MAX_CONNECTION][256]; int process_http_request(const char *request, int i_conn) { int i, n, authorized, header_length, content_length, strsize; char str2[1000], url[2000], format[256], cookie[256], boundary[256], list[1000], theme[256], host_list[MAX_N_LIST][NAME_LENGTH], logbook[256], logbook_enc[256], global_cmd[256]; char *p, *str; struct hostent *phe; time_t now; struct tm *ts; const char *cookie_list[] = { "upwd", "unm", "ufnm", "elmode", "urem", "wpwd", "apwd", "uname", "upassword", "elattach", "hsm", "sid", NULL }; if (!strchr(request, '\r')) return 0; strsize = strlen(request)+1001; str = xmalloc(strsize); if (get_verbose() < VERBOSE_DEBUG) { if (get_verbose() > 0) { strlcpy(str, request, strsize); if (strchr(str, '\r')) *strchr(str, '\r') = 0; if (strchr(str, '\n')) *strchr(str, '\n') = 0; eputs(str); } } else if (get_verbose() >= VERBOSE_DEBUG) { eputs("\n"); eputs(request); } /* initialize parametr array */ initparam(); content_length = 0; /* extract cookies */ if ((p = strstr(request, "Cookie:")) != NULL) { p += 6; do { p++; while (*p && *p == ' ') p++; if (*p == '\r' || *p == '\n') break; strlcpy(str, p, strsize); for (i = 0; i < (int) strlen(str); i++) if (str[i] == '=' || str[i] == ';') break; if (str[i] == '=') { str[i] = 0; p += i + 1; for (i = 0; *p && *p != ';' && *p != '\r' && *p != '\n'; p++) if (i < (int) sizeof(cookie) - 1) cookie[i++] = *p; cookie[i] = 0; } else { /* empty cookie */ str[i] = 0; cookie[0] = 0; p += i; } /* store cookie as parameter */ for (i = 0; cookie_list[i]; i++) { if (strcmp(cookie_list[i], str) == 0) { setparam(str, cookie); break; } } if (cookie_list[i] == NULL && get_verbose() >= VERBOSE_INFO) eprintf("Received unknown cookie \"%s\"\n", str); } while (*p && *p == ';'); } /* extract referer */ referer[0] = 0; if ((p = strstr(request, "Referer:")) != NULL) { p += 9; while (*p && *p == ' ') p++; strlcpy(referer, p, sizeof(referer)); if (strchr(referer, '\r')) *strchr(referer, '\r') = 0; if (strchr(referer, '?')) *strchr(referer, '?') = 0; for (p = referer + strlen(referer) - 1; p > referer && *p != '/'; p--) *p = 0; if (strchr(referer, ' ')) url_encode(referer, sizeof(referer)); } /* extract browser */ browser[0] = 0; if ((p = strstr(request, "User-Agent:")) != NULL) { p += 11; while (*p && *p == ' ') p++; strlcpy(browser, p, sizeof(browser)); if (strchr(browser, '\r')) *strchr(browser, '\r') = 0; } /* extract host */ http_host[0] = 0; if ((p = strstr(request, "Host:")) != NULL) { p += 5; while (*p && *p == ' ') p++; strlcpy(http_host, p, sizeof(http_host)); if (strchr(http_host, '\r')) *strchr(http_host, '\r') = 0; } /* extract X-Forwarded-Host, overwrite "Host:" if found */ if ((p = strstr(request, "X-Forwarded-Host:")) != NULL) { p += 17; while (*p && *p == ' ') p++; strlcpy(http_host, p, sizeof(http_host)); if (strchr(http_host, '\r')) *strchr(http_host, '\r') = 0; } /* extract X-Forwarded-User into http_user if Authentication==Webserver */ http_user[0] = 0; if ((p = strstr(request, "X-Forwarded-User:")) != NULL) { p += 17; while (*p && *p == ' ') p++; strlcpy(http_user, p, sizeof(http_user)); if (strchr(http_user, '\r')) *strchr(http_user, '\r') = 0; } /* extract "X-Forwarded-For:" */ if ((p = strstr(request, "X-Forwarded-For:")) != NULL) { p += 16; while (*p && *p == ' ') p++; strlcpy(str, p, strsize); if (strchr(str, '\r')) *strchr(str, '\r') = 0; #ifdef OS_WINNT rem_addr.S_un.S_addr = inet_addr(str); #else rem_addr.s_addr = inet_addr(str); #endif if (getcfg("global", "Resolve host names", str, strsize) && atoi(str) == 1) { phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET); if (phe != NULL) strcpy(remote_host[i_conn], phe->h_name); else strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr)); } else strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr)); strcpy(rem_host, remote_host[i_conn]); } if (_logging_level > 3) { strlcpy(str, request, strsize); if (strchr(str, '\r')) *strchr(str, '\r') = 0; write_logfile(NULL, str); } memset(return_buffer, 0, return_buffer_size); strlen_retbuf = 0; if (strncmp(request, "GET", 3) != 0 && strncmp(request, "POST", 4) != 0) { xfree(str); return 0; } return_length = 0; /* check for Keep-alive */ if (strstr(request, "Keep-Alive") != NULL && use_keepalive) keep_alive = TRUE; if (strstr(request, "keep-alive") != NULL && use_keepalive) keep_alive = TRUE; /* extract logbook */ if (strchr(request, '/') == NULL || strchr(request, '\r') == NULL || strstr(request, "HTTP") == NULL) { /* invalid request, make valid */ strcpy(str, "GET / HTTP/1.0\r\n\r\n"); xfree(str); return process_http_request(str, i_conn); } /* initialize topgroups */ setcfg_topgroup(""); p = strchr(request, '/') + 1; /* check for ../.. to avoid serving of files on top of the elog directory */ for (i = 0; p[i] && p[i] != ' ' && p[i] != '?' && i < (int) sizeof(url); i++) url[i] = p[i]; url[i] = 0; if (strstr(url, "../..")) { strencode2(str2, url, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); xfree(str); return 1; } /* check if file is in scripts directory or in its subdirs */ for (i = 0; p[i] && p[i] != ' ' && p[i] != '?' && i < (int) sizeof(url); i++) url[i] = (p[i] == '/') ? DIR_SEPARATOR : p[i]; url[i] = 0; if (strchr(url, '.')) { /* do not allow '..' in file name */ if (strstr(url, "..")) { strencode2(str2, url, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); xfree(str); return 1; } strlcpy(str, resource_dir, strsize); strlcat(str, "scripts", strsize); strlcat(str, DIR_SEPARATOR_STR, strsize); strlcat(str, url, strsize); if (exist_file(str)) { send_file_direct(str); xfree(str); return 1; } } logbook[0] = 0; for (i = 0; *p && *p != '/' && *p != '?' && *p != ' ' && i < (int) sizeof(logbook); i++) logbook[i] = *p++; logbook[i] = 0; strcpy(logbook_enc, logbook); url_decode(logbook); /* check for trailing '/' after logbook */ if (strncmp(request, "POST", 4) != 0) { // fix for konqueror if (logbook[0] && *p == ' ') { if (!chkext(logbook, ".css") && !chkext(logbook, ".htm") && !chkext(logbook, ".gif") && !chkext(logbook, ".jpg") && !chkext(logbook, ".png") && !chkext(logbook, ".ico")) { sprintf(str, "%s/", logbook_enc); redirect(NULL, str); xfree(str); return 1; } } } /* check for trailing '/' after logbook/ID */ if (logbook[0] && *p == '/' && *(p + 1) != ' ') { sprintf(url, "%s", logbook_enc); for (i = strlen(url); *p && *p != ' ' && i < (int) sizeof(url); i++) url[i] = *p++; url[i] = 0; if (*(p - 1) == '/') { strencode2(str2, url, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); xfree(str); return 1; } } /* check for invalid URL in the form "http:/server//path" */ if (!logbook[0] && *p == '/') { for (i = 0; *p && *p != ' ' && i < (int) sizeof(url); i++) url[i] = *p++; url[i] = 0; strencode2(str2, url, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); xfree(str); return 1; } /* check for global command */ global_cmd[0] = 0; if ((p = strstr(request, "?cmd=")) != NULL) { p += 5; strlcpy(global_cmd, p, sizeof(global_cmd)); if (strchr(global_cmd, ' ')) *strchr(global_cmd, ' ') = 0; if (strchr(global_cmd, '\r')) *strchr(global_cmd, '\r') = 0; } /* redirect image request from inside CKeditor */ if (strieq(logbook, "ckeditor")) { if (strstr(url, "?lb=")) { strlcpy(logbook, strstr(url, "?lb=") + 4, sizeof(logbook)); if (strchr(logbook, '&')) *strchr(logbook, '&') = 0; url_decode(logbook); } } /* check if logbook exists */ for (i = 0;; i++) { if (!enumgrp(i, str)) break; if (strieq(logbook, str) && is_logbook(logbook)) break; } if (chkext(logbook, ".gif") || chkext(logbook, ".jpg") || chkext(logbook, ".jpg") || chkext(logbook, ".png") || chkext(logbook, ".ico") || chkext(logbook, ".htm") || chkext(logbook, ".css") || chkext(logbook, ".js")) { /* do not allow '..' in file name */ if (strstr(logbook, "..")) { strencode2(str2, logbook, sizeof(str2)); sprintf(str, "%s: %s", loc("Invalid URL"), str2); show_error(str); xfree(str); return 1; } /* check if file in resource directory */ strlcpy(str, resource_dir, strsize); strlcat(str, logbook, strsize); if (exist_file(str)) send_file_direct(str); else { /* else search file in themes directory */ strlcpy(str, resource_dir, strsize); strlcat(str, "themes", strsize); strlcat(str, DIR_SEPARATOR_STR, strsize); if (getcfg("global", "theme", theme, sizeof(theme))) strlcat(str, theme, strsize); else strlcat(str, "default", strsize); strlcat(str, DIR_SEPARATOR_STR, strsize); strlcat(str, logbook, strsize); send_file_direct(str); } xfree(str); return 1; } else { if (logbook[0] && (!strieq(logbook, str) || !is_logbook(logbook))) { /* check for top group */ sprintf(str, "Top group %s", logbook); if (!getcfg("global", str, list, sizeof(list))) { sprintf(str, "Error: logbook \"%s\" not defined in %s", logbook_enc, CFGFILE); show_error(str); xfree(str); return 1; } } } /* if no logbook is given and only one logbook defined, use this one */ if (!logbook[0] && !global_cmd[0]) { for (i = n = 0;; i++) { if (!enumgrp(i, str)) break; if (is_logbook(str)) n++; } if (n == 1) { strlcpy(logbook, str, sizeof(logbook)); strlcpy(logbook_enc, logbook, sizeof(logbook_enc)); url_encode(logbook_enc, sizeof(logbook_enc)); strlcat(logbook_enc, "/", sizeof(logbook_enc)); /* redirect to logbook, necessary to get optional cookies for that logbook */ redirect(NULL, logbook_enc); xfree(str); return 1; } } /*---- check "hosts deny" ----*/ authorized = 1; if (getcfg(logbook, "Hosts deny", list, sizeof(list))) { strcpy(rem_host_ip, (char *) inet_ntoa(rem_addr)); n = strbreak(list, host_list, MAX_N_LIST, ",", FALSE); /* check if current connection matches anyone on the list */ for (i = 0; i < n; i++) { if (strieq(rem_host, host_list[i]) || strieq(rem_host_ip, host_list[i]) || strieq(host_list[i], "all")) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", strieq(rem_host_ip, host_list[i]) ? rem_host_ip : rem_host, host_list[i]); authorized = 0; break; } if (host_list[i][0] == '.') { if (strlen(rem_host) > strlen(host_list[i]) && strieq(host_list[i], rem_host + strlen(rem_host) - strlen(host_list[i]))) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", rem_host, host_list[i]); authorized = 0; break; } } if (host_list[i][strlen(host_list[i]) - 1] == '.') { strcpy(str, rem_host_ip); if (strlen(str) > strlen(host_list[i])) str[strlen(host_list[i])] = 0; if (strieq(host_list[i], str)) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", rem_host_ip, host_list[i]); authorized = 0; break; } } } } /*---- check "hosts allow" ----*/ if (getcfg(logbook, "Hosts allow", list, sizeof(list))) { strcpy(rem_host_ip, (char *) inet_ntoa(rem_addr)); n = strbreak(list, host_list, MAX_N_LIST, ",", FALSE); /* check if current connection matches anyone on the list */ for (i = 0; i < n; i++) { if (strieq(rem_host, host_list[i]) || strieq(rem_host_ip, host_list[i]) || strieq(host_list[i], "all")) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n", strieq(rem_host_ip, host_list[i]) ? rem_host_ip : rem_host, host_list[i]); authorized = 1; break; } if (host_list[i][0] == '.') { if (strlen(rem_host) > strlen(host_list[i]) && strieq(host_list[i], rem_host + strlen(rem_host) - strlen(host_list[i]))) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n", rem_host, host_list[i]); authorized = 1; break; } } if (host_list[i][strlen(host_list[i]) - 1] == '.') { strcpy(str, rem_host_ip); if (strlen(str) > strlen(host_list[i])) str[strlen(host_list[i])] = 0; if (strieq(host_list[i], str)) { if (get_verbose() >= VERBOSE_INFO) eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n", rem_host_ip, host_list[i]); authorized = 1; break; } } } } if (!authorized) { keep_alive = FALSE; xfree(str); return 0; } if (!logbook[0] && global_cmd[0] && stricmp(global_cmd, "GetConfig") == 0) { download_config(); } else if (stricmp(global_cmd, "gettimedate") == 0) { if (!getcfg(logbook, "Time format", format, sizeof(format))) strcpy(format, DEFAULT_TIME_FORMAT); time(&now); ts = localtime(&now); my_strftime(str, strsize, format, ts); show_http_header(NULL, FALSE, NULL); rsputs(str); rsputs(" "); } else if (strncmp(request, "GET", 3) == 0) { /* extract path and commands */ if (strchr(request, '\r')) *strchr(request, '\r') = 0; if (!strstr(request, "HTTP/1")) { xfree(str); return 0; } *(strstr(request, "HTTP/1") - 1) = 0; /* strip logbook from path */ strlcpy(str, request + 5, strsize); p = str; for (i = 0; *p && *p != '/' && *p != '?'; p++); while (*p && *p == '/') p++; if (strncmp(p, "editor/", 7) == 0) // fix for image request inside CKeditor p += 7; /* decode command and return answer */ decode_get(logbook, p); } else if (strncmp(request, "POST", 4) == 0) { /* extract content length */ if (strstr(request, "Content-Length:")) content_length = atoi(strstr(request, "Content-Length:") + 15); else if (strstr(request, "Content-length:")) content_length = atoi(strstr(request, "Content-length:") + 15); /* extract header length */ if (strstr(request, "\r\n\r\n")) header_length = strstr(request, "\r\n\r\n") - request + 4; else if (strstr(request, "\r\r\n\r\r\n")) header_length = strstr(request, "\r\r\n\r\r\n") - request + 6; else { show_error("Invalid POST header"); xfree(str); return 1; } /* extract boundary */ if (strstr(request, "boundary=")) { strlcpy(boundary, strstr(request, "boundary=") + 9, sizeof(boundary)); if (strchr(boundary, '\r')) *strchr(boundary, '\r') = 0; } /* get logbook from list (needed for attachment dir) */ for (i = 0; lb_list[i].name[0]; i++) if (strieq(logbook, lb_list[i].name)) break; if (!lb_list[i].name[0]) /* must be login page of top group */ decode_post(logbook, NULL, request + header_length, boundary, content_length); else decode_post(logbook, &lb_list[i], request + header_length, boundary, content_length); } else { strencode2(str2, request, sizeof(str2)); sprintf(str, "Unknown request:

%s", str2); show_error(str); } xfree(str); return 1; } /*------------------------------------------------------------------*/ #ifdef HAVE_SSL void send_return(int _sock, SSL * ssl_con, const char *net_buffer) #else void send_return(int _sock, const char *net_buffer) #endif { int length, header_length; char str[NAME_LENGTH]; char *p; #ifndef HAVE_SSL void *ssl_con = NULL; #endif if (!_ssl_flag) ssl_con = NULL; if (return_length != -1) { if (return_length == 0) return_length = strlen_retbuf; if (return_length == 0) return; if (_logging_level > 3) { strlcpy(str, net_buffer, sizeof(str)); sprintf(str, "Return %d bytes", return_length); write_logfile(NULL, str); } length = 0; if ((keep_alive && strstr(return_buffer, "Content-Length") == NULL) || strstr(return_buffer, "Content-Length") > strstr(return_buffer, "\r\n\r\n")) { /*---- add content-length ----*/ p = strstr(return_buffer, "\r\n\r\n"); if (p != NULL) { header_length = (int) (p - return_buffer); length = return_length - header_length - 4; if (header_length + 100 > (int) sizeof(header_buffer)) header_length = sizeof(header_buffer) - 100; memcpy(header_buffer, return_buffer, header_length); sprintf(header_buffer + header_length, "\r\nContent-Length: %d\r\n\r\n", length); send_with_timeout(ssl_con, _sock, header_buffer, strlen(header_buffer)); send_with_timeout(ssl_con, _sock, p + 4, length); if (get_verbose() < VERBOSE_DEBUG) { if (get_verbose() > 0) eprintf("Returned %d bytes\n", length); } else if (get_verbose() >= VERBOSE_DEBUG) { if (strrchr(net_buffer, '/')) strlcpy(str, strrchr(net_buffer, '/') + 1, sizeof(str)); else str[0] = 0; eprintf("==== Return ================================\n"); eputs(header_buffer); if (chkext(net_buffer, ".gif") || chkext(net_buffer, ".jpg") || chkext(net_buffer, ".png") || chkext(net_buffer, ".ico") || chkext(net_buffer, ".pdf") || return_length > 10000) eprintf("\n<%d bytes of \"%s\">\n\n", length, str); else eputs(p + 4); eprintf("\n"); } } else { eprintf("Internal error, no valid header!\n"); keep_alive = FALSE; } } else { if (!keep_alive) { /* no keepalive, so add connection close */ p = strstr(return_buffer, "\r\n\r\n"); if (p != NULL) { header_length = (int) (p - return_buffer); length = return_length - header_length - 4; if (header_length + 100 > (int) sizeof(header_buffer)) header_length = sizeof(header_buffer) - 100; memcpy(header_buffer, return_buffer, header_length); sprintf(header_buffer + header_length, "\r\nConnection: Close\r\n\r\n"); } send_with_timeout(ssl_con, _sock, header_buffer, strlen(header_buffer)); send_with_timeout(ssl_con, _sock, p + 4, length); } else { send_with_timeout(ssl_con, _sock, return_buffer, return_length); } if (get_verbose() < VERBOSE_DEBUG) { if (get_verbose() > 0) eprintf("Returned %d bytes\n", return_length); } else if (get_verbose() == VERBOSE_DEBUG) { if (strrchr(net_buffer, '/')) strlcpy(str, strrchr(net_buffer, '/') + 1, sizeof(str)); else str[0] = 0; eprintf("==== Return ================================\n"); if (chkext(net_buffer, ".gif") || chkext(net_buffer, ".jpg") || chkext(net_buffer, ".png") || chkext(net_buffer, ".ico") || chkext(net_buffer, ".pdf") || return_length > 10000) { if (str[0]) eprintf("\n<%d bytes of \"%s\">\r\n", return_length, str); else eprintf("\n<%d bytes>\r\n", return_length); } else eputs(return_buffer); eprintf("\n\n"); } } } } /*------------------------------------------------------------------*/ BOOL cron_match(char *str, int value, BOOL ignore_star) { int low, high; if (atoi(str) == value) return TRUE; if (!ignore_star && str[0] == '*') return TRUE; /* check range */ if (strchr(str, '-')) { low = atoi(str); high = atoi(strchr(str, '-') + 1); return value >= low && value <= high; } return FALSE; } void check_cron() /* check 'mirror cron' etnry in configuration file minute (0-59) hour (0-23) day of month (1-31) month of year (1-12) day of week (0-6, 0 is Sunday) Each of these patterns might be an asterisk (meaning all legal values) or a list of elements separated by commas. */ { int i, j, n; BOOL min_flag, hour_flag, day_flag, mon_flag, wday_flag; time_t now; char *p, str[256], cron[5][256]; struct tm *ts; static struct tm last_time; char list[60][NAME_LENGTH]; if (!getcfg("global", "mirror cron", str, sizeof(str))) return; for (i = 0; i < 5; i++) strcpy(cron[i], "*"); i = 0; p = strtok(str, " "); while (p) { strcpy(cron[i++], p); p = strtok(NULL, " "); } time(&now); ts = localtime(&now); assert(ts); /* check once every minute */ if (last_time.tm_year && last_time.tm_min != ts->tm_min) { min_flag = hour_flag = day_flag = mon_flag = wday_flag = FALSE; for (i = 0; i < 5; i++) { n = strbreak(cron[i], list, 60, ",", FALSE); for (j = 0; j < n; j++) { /* minutes */ if (i == 0 && cron_match(list[j], ts->tm_min, FALSE)) min_flag = TRUE; /* hours */ if (i == 1 && cron_match(list[j], ts->tm_hour, FALSE)) hour_flag = TRUE; /* day of month */ if (i == 2 && cron_match(list[j], ts->tm_mday, FALSE)) day_flag = TRUE; /* month of year */ if (i == 3 && cron_match(list[j], ts->tm_mon, FALSE)) mon_flag = TRUE; /* weekday */ if (i == 4 && cron_match(list[j], ts->tm_wday, TRUE)) wday_flag = TRUE; } } if (min_flag && hour_flag && ((day_flag && mon_flag) || wday_flag)) { rem_host[0] = 0; write_logfile(NULL, "Cron job started"); /* synchronize all logbooks */ setcfg_topgroup(""); synchronize(NULL, SYNC_CRON); } } memcpy(&last_time, ts, sizeof(struct tm)); } /*------------------------------------------------------------------*/ BOOL _abort = FALSE; BOOL _hup = FALSE; void ctrlc_handler(int sig) { if (sig) _abort = TRUE; } void hup_handler(int sig) { if (sig) _hup = TRUE; } #ifndef OS_WINNT void alarm_handler(int sig) { if (sig) alarm(3); } #endif /*------------------------------------------------------------------*/ #ifdef HAVE_SSL SSL_CTX *init_ssl(void) { char str[256], pwd[256]; SSL_METHOD *meth; SSL_CTX *ctx; SSL_library_init(); SSL_load_error_strings(); meth = (SSL_METHOD *) TLSv1_method(); ctx = SSL_CTX_new(meth); if (getcfg("global", "SSL Passphrase", pwd, sizeof(pwd))) { SSL_CTX_set_default_passwd_cb_userdata(ctx, pwd); } strlcpy(str, resource_dir, sizeof(str)); strlcat(str, "ssl/server.crt", sizeof(str)); if (!file_exist(str)) { eprintf("Cerificate file \"%s\" not found, aborting\n", str); return NULL; } if (SSL_CTX_use_certificate_file(ctx, str, SSL_FILETYPE_PEM) == 0) return NULL; strlcpy(str, resource_dir, sizeof(str)); strlcat(str, "ssl/server.key", sizeof(str)); if (!file_exist(str)) { eprintf("Key file \"%s\" not found, aborting\n", str); return NULL; } if (SSL_CTX_use_PrivateKey_file(ctx, str, SSL_FILETYPE_PEM) == 0) return NULL; if (SSL_CTX_check_private_key(ctx) < 0) return NULL; strlcpy(str, resource_dir, sizeof(str)); strlcat(str, "ssl/chain.crt", sizeof(str)); if (file_exist(str)) { if (SSL_CTX_use_certificate_chain_file(ctx, str) == 0) return NULL; } return ctx; } #endif // HAVE_SSL /*------------------------------------------------------------------*/ void server_loop(void) { int status, i, broken, min, i_min, i_conn, more_requests; char str[1000], logbook[256], logbook_enc[256]; char *pend; int lsock, len, flag, content_length, header_length; struct sockaddr_in serv_addr, acc_addr; struct hostent *phe; fd_set readfds; struct timeval timeout; char *net_buffer = NULL; int net_buffer_size; #ifdef HAVE_SSL SSL_CTX *ssl_ctx = NULL; #endif #ifdef OS_UNIX /* sigaction structs */ struct sigaction ctrlc_handle; struct sigaction ignore_handle; struct sigaction hup_handle; struct sigaction alarm_handle; #endif i_conn = content_length = 0; net_buffer_size = 100000; net_buffer = xmalloc(net_buffer_size); return_buffer_size = 100000; return_buffer = xmalloc(return_buffer_size); pend = NULL; /* determine logging level */ if (getcfg(NULL, "Logging Level", str, sizeof(str))) _logging_level = atoi(str); else _logging_level = 2; /* initialize SSL if requested */ _ssl_flag = 0; if (getcfg("global", "SSL", str, sizeof(str)) && atoi(str) == 1) { #ifdef HAVE_SSL ssl_ctx = init_ssl(); if (ssl_ctx == NULL) { eprintf("Cannot initialize SSL\n"); exit(EXIT_FAILURE); } _ssl_flag = 1; #else eprintf("SSL support not compiled into elogd\n"); exit(EXIT_FAILURE); #endif } /* create a new socket */ lsock = socket(AF_INET, SOCK_STREAM, 0); if (lsock == -1) { eprintf("Cannot create socket\n"); exit(EXIT_FAILURE); } /* bind local node name and port to socket */ memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; /* if no hostname given with the -n flag, listen on any interface */ if (listen_interface[0] == 0) serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); else { /* look up the given hostname. gethostbyname() will take a hostname or an IP address */ phe = gethostbyname(listen_interface); if (!phe) { eprintf("Cannot find address for -n %s\n", listen_interface); exit(EXIT_FAILURE); } if (phe->h_addrtype != AF_INET) { eprintf("Non Internet address for -n %s\n", listen_interface); exit(EXIT_FAILURE); } memcpy(&serv_addr.sin_addr.s_addr, phe->h_addr_list[0], phe->h_length); } serv_addr.sin_port = htons((short) elog_tcp_port); /* switch on reuse of port */ flag = 1; setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(int)); status = bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); if (status < 0) { eprintf("Cannot bind to port %d.\nProbably another instance of this program is already running.\n", elog_tcp_port); exit(EXIT_FAILURE); } /* get local host name */ if (getcfg("global", "URL", str, sizeof(str))) split_url(str, host_name, NULL, NULL, NULL); else { gethostname(host_name, sizeof(host_name)); phe = gethostbyname(host_name); /* if domain name is not in host name, hope to get it from phe */ if (strchr(host_name, '.') == NULL && phe != NULL && strchr(phe->h_name, '.') != NULL) strcpy(host_name, phe->h_name); } /* open configuration file */ getcfg("dummy", "dummy", str, sizeof(str)); /* now, initiate daemon/service */ if (running_as_daemon) { /* Redirect all messages handled with eprintf/efputs to syslog */ redirect_to_syslog(); #ifdef OS_UNIX if (!ss_daemon_init()) { eprintf("Couldn't initiate the daemon; aborting\n"); exit(EXIT_FAILURE); } #endif } /* about to entering the server loop, welcome user with a brief info */ eprintf("%s ", ELOGID); strcpy(str, git_revision()); if (strchr(str, ' ')) *strchr(str, ' ') = 0; eprintf("revision %s\n", str); if (get_verbose() >= VERBOSE_INFO) { getcwd(str, sizeof(str)); if (strchr(config_file, DIR_SEPARATOR) == NULL) eprintf("Config file : %s%c%s\n", str, DIR_SEPARATOR, config_file); else eprintf("Config file : %s\n", config_file); eprintf("Resource dir : %s\n", resource_dir[0] ? resource_dir : str); if (logbook_dir[0] && logbook_dir[0] != DIR_SEPARATOR && logbook_dir[1] != ':') eprintf("Logbook dir : %s%c%s\n", str, DIR_SEPARATOR, logbook_dir); else eprintf("Logbook dir : %s\n", logbook_dir[0] ? logbook_dir : str); } #ifdef OS_UNIX /* create PID file if given as command line parameter or if running under root */ if (geteuid() == 0 || pidfile[0]) { int fd; char buf[20]; struct stat finfo; if (pidfile[0] == 0) strcpy(pidfile, PIDFILE); /* check if file exists */ if (stat(pidfile, &finfo) >= 0) { eprintf("File \"%s\" exists, using \"%s.%d\" instead.\n", pidfile, pidfile, elog_tcp_port); sprintf(pidfile + strlen(pidfile), ".%d", elog_tcp_port); /* check again for the new name */ if (stat(pidfile, &finfo) >= 0) { /* never overwrite a file */ eprintf("Refuse to overwrite existing file \"%s\".\n", pidfile); _exit(EXIT_FAILURE); /* don't call atexit() hook */ } } fd = open(pidfile, O_CREAT | O_RDWR, 0644); if (fd < 0) { sprintf(str, "Error creating pid file \"%s\"", pidfile); eprintf("%s; %s\n", str, strerror(errno)); exit(EXIT_FAILURE); } sprintf(buf, "%d\n", (int) getpid()); if (write(fd, buf, strlen(buf)) == -1) { sprintf(str, "Error writing to pid file \"%s\"", pidfile); eprintf("%s; %s\n", str, strerror(errno)); exit(EXIT_FAILURE); } close(fd); } /* install signal handlers */ ctrlc_handle.sa_handler = ctrlc_handler; sigemptyset(&ctrlc_handle.sa_mask); ctrlc_handle.sa_flags = 0; sigaction(SIGTERM, &ctrlc_handle, NULL); sigaction(SIGINT, &ctrlc_handle, NULL); ignore_handle.sa_handler = SIG_IGN; ignore_handle.sa_flags = 0; sigaction(SIGPIPE, &ignore_handle, NULL); hup_handle.sa_handler = hup_handler; sigemptyset(&hup_handle.sa_mask); hup_handle.sa_flags = 0; sigaction(SIGHUP, &hup_handle, NULL); alarm_handle.sa_handler = alarm_handler; sigemptyset(&alarm_handle.sa_mask); alarm_handle.sa_flags = 0; sigaction(SIGALRM, &alarm_handle, NULL); #ifndef OS_WINNT alarm(3); // prevents blocking send() operations #endif /* give up root privilege */ if (geteuid() == 0) { if (!getcfg("global", "Grp", str, sizeof(str)) || setegroup(str) < 0) { eprintf("Falling back to default group \"elog\"\n"); if (setegroup("elog") < 0) { eprintf("Falling back to default group \"%s\"\n", DEFAULT_GROUP); if (setegroup(DEFAULT_GROUP) < 0) { eprintf("Refuse to run as setgid root.\n"); eprintf("Please consider to define a Grp statement in configuration file\n"); exit(EXIT_FAILURE); } } } else if (get_verbose() >= VERBOSE_INFO) eprintf("Falling back to group \"%s\"\n", str); if (!getcfg("global", "Usr", str, sizeof(str)) || seteuser(str) < 0) { eprintf("Falling back to default user \"elog\"\n"); if (seteuser("elog") < 0) { eprintf("Falling back to default user \"%s\"\n", DEFAULT_USER); if (seteuser(DEFAULT_USER) < 0) { eprintf("Refuse to run as setuid root.\n"); eprintf("Please consider to define a Usr statement in configuration file\n"); exit(EXIT_FAILURE); } } } else if (get_verbose() >= VERBOSE_INFO) eprintf("Falling back to user \"%s\".\n", str); } #endif /* load initial configuration */ check_config(); /* check for CKedit */ strlcpy(str, resource_dir, sizeof(str)); strlcat(str, "scripts", sizeof(str)); strlcat(str, DIR_SEPARATOR_STR, sizeof(str)); strlcat(str, "ckeditor/ckeditor.js", sizeof(str)); ckedit_exist = exist_file(str); if (ckedit_exist) eprintf("CKeditor detected\n"); else eprintf("CKeditor NOT detected\n"); /* check for ImageMagick */ strlcpy(_convert_cmd, "convert", sizeof(_convert_cmd)); strlcpy(_identify_cmd, "identify", sizeof(_convert_cmd)); sprintf(str, "%s -version", _convert_cmd); my_shell(str, str, sizeof(str)); image_magick_exist = (strstr(str, "ImageMagick") != NULL); if (!image_magick_exist) { strlcpy(_convert_cmd, "/usr/bin/convert", sizeof(_convert_cmd)); strlcpy(_identify_cmd, "/usr/bin/identify", sizeof(_convert_cmd)); sprintf(str, "%s -version", _convert_cmd); my_shell(str, str, sizeof(str)); image_magick_exist = (strstr(str, "ImageMagick") != NULL); } if (!image_magick_exist) { strlcpy(_convert_cmd, "/usr/local/bin/convert", sizeof(_convert_cmd)); strlcpy(_identify_cmd, "/usr/local/bin/identify", sizeof(_convert_cmd)); sprintf(str, "%s -version", _convert_cmd); my_shell(str, str, sizeof(str)); image_magick_exist = (strstr(str, "ImageMagick") != NULL); } if (!image_magick_exist) { strlcpy(_convert_cmd, "/opt/local/bin/convert", sizeof(_convert_cmd)); strlcpy(_identify_cmd, "/opt/local/bin/identify", sizeof(_convert_cmd)); sprintf(str, "%s -version", _convert_cmd); my_shell(str, str, sizeof(str)); image_magick_exist = (strstr(str, "ImageMagick") != NULL); } if (image_magick_exist) eprintf("ImageMagick detected\n"); else eprintf("ImageMagick NOT detected. Image scaling will not work.\n"); /* check for keepalive */ if (!use_keepalive) eprintf("Keep-alive disabled\n"); /* build logbook indices */ if (get_verbose() == 0 && !running_as_daemon) eprintf("Indexing logbooks ... "); if (el_index_logbooks() != EL_SUCCESS) exit(EXIT_FAILURE); if (get_verbose() == 0 && !running_as_daemon) eputs("done"); #ifndef HAVE_KRB5 /* check for Kerberos authentication */ getcfg("global", "Authentication", str, sizeof(str)); if (stristr(str, "Kerberos")) { eprintf("Kerberos authentication not compiled into this version of elogd.\n"); exit(EXIT_FAILURE); } #endif #ifndef HAVE_LDAP /* check for Kerberos authentication */ /* NPA change */ getcfg("global", "Authentication", str, sizeof(str)); if (stristr(str, "LDAP")) { eprintf("LDAP authentication not compiled into this version of elogd.\n"); exit(EXIT_FAILURE); } #endif /* listen for connection */ status = listen(lsock, SOMAXCONN); if (status < 0) { eprintf("Cannot listen\n"); exit(EXIT_FAILURE); } if (_ssl_flag) sprintf(str, "SSLServer listening on port %d ...\n", elog_tcp_port); else sprintf(str, "Server listening on port %d ...\n", elog_tcp_port); eprintf("%s", str); if (_logging_level > 0) write_logfile(NULL, str); do { FD_ZERO(&readfds); FD_SET(lsock, &readfds); for (i = 0; i < N_MAX_CONNECTION; i++) if (ka_sock[i] > 0) FD_SET(ka_sock[i], &readfds); timeout.tv_sec = 1; timeout.tv_usec = 0; status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); /* check UNIX signal flags */ if (_abort) break; /* call random number generator on each access to completely randomize it */ rand(); /* close old connections */ for (i = 0; i < N_MAX_CONNECTION; i++) if (ka_sock[i] && (int) time(NULL) - ka_time[i] > 60) { #ifdef HAVE_SSL if (_ssl_flag) { SSL_set_fd(ka_ssl_con[i], ka_sock[i]); SSL_shutdown(ka_ssl_con[i]); SSL_free(ka_ssl_con[i]); ka_ssl_con[i] = NULL; } #endif closesocket(ka_sock[i]); ka_sock[i] = 0; ka_time[i] = 0; } if (status != -1) { // if no HUP signal is received if (FD_ISSET(lsock, &readfds)) { len = sizeof(acc_addr); _sock = accept(lsock, (struct sockaddr *) &acc_addr, (void *) &len); #ifdef HAVE_SSL if (_ssl_flag) { _ssl_con = SSL_new(ssl_ctx); SSL_set_fd(_ssl_con, _sock); if (SSL_accept(_ssl_con) < 0) { if (get_verbose() >= VERBOSE_INFO) eprintf("SSL_accept failed\n"); closesocket(_sock); ka_sock[i_conn] = 0; ka_ssl_con[i_conn] = NULL; _ssl_con = NULL; continue; } } else _ssl_con = NULL; #endif /* find new entry in socket table */ for (i = 0; i < N_MAX_CONNECTION; i++) if (ka_sock[i] == 0) break; /* recycle oldest connection */ if (i == N_MAX_CONNECTION) { for (i = i_min = 0, min = ka_time[0]; i < N_MAX_CONNECTION; i++) if (ka_time[i] < min) { min = ka_time[i]; i_min = i; } #ifdef HAVE_SSL if (_ssl_flag) { SSL_set_fd(ka_ssl_con[i_min], ka_sock[i_min]); SSL_shutdown(ka_ssl_con[i_min]); SSL_free(ka_ssl_con[i_min]); ka_ssl_con[i_min] = NULL; } #endif closesocket(ka_sock[i_min]); ka_sock[i_min] = 0; ka_time[i_min] = 0; i = i_min; } i_conn = i; ka_sock[i_conn] = _sock; ka_time[i_conn] = (int) time(NULL); #ifdef HAVE_SSL ka_ssl_con[i_conn] = _ssl_con; #endif /* save remote host address */ memcpy(&remote_addr[i_conn], &(acc_addr.sin_addr), sizeof(rem_addr)); memcpy(&rem_addr, &(acc_addr.sin_addr), sizeof(rem_addr)); if (getcfg("global", "Resolve host names", str, sizeof(str)) && atoi(str) == 1) { phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET); if (phe != NULL) strcpy(remote_host[i_conn], phe->h_name); else strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr)); } else strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr)); strcpy(rem_host, remote_host[i_conn]); if (get_verbose() == VERBOSE_URL) eprintf("Open connection #%d on socket %d\n", i, _sock); /* start over */ continue; } /* check if open connection received data */ for (i = 0; i < N_MAX_CONNECTION; i++) if (ka_sock[i] > 0 && FD_ISSET(ka_sock[i], &readfds)) break; if (i == N_MAX_CONNECTION) { _sock = 0; } else { i_conn = i; _sock = ka_sock[i_conn]; #ifdef HAVE_SSL _ssl_con = ka_ssl_con[i_conn]; #endif ka_time[i_conn] = (int) time(NULL); memcpy(&rem_addr, &remote_addr[i_conn], sizeof(rem_addr)); strcpy(rem_host, remote_host[i_conn]); } /* turn off keep_alive by default */ keep_alive = FALSE; /* receive data */ if (_sock > 0) { memset(net_buffer, 0, net_buffer_size); len = 0; more_requests = 0; do { /* pipleline loop */ header_length = 0; broken = FALSE; return_length = -1; do { if (!more_requests) { do { FD_ZERO(&readfds); FD_SET(_sock, &readfds); timeout.tv_sec = 6; timeout.tv_usec = 0; status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); if (FD_ISSET(_sock, &readfds)) { #ifdef HAVE_SSL if (_ssl_flag) i = SSL_read(_ssl_con, net_buffer + len, net_buffer_size - len); else #endif i = recv(_sock, net_buffer + len, net_buffer_size - len, 0); if (get_verbose() == VERBOSE_URL) eprintf("Connection #%d received %d bytes on socket %d\n", i_conn, i, _sock); } else break; /* abort if connection got broken */ if (i < 0) { broken = TRUE; break; } /* abort if connection has been closed */ if (i == 0) { broken = TRUE; break; } if (i > 0) len += i; /* check if net_buffer needs to be increased */ if (len == net_buffer_size) { net_buffer = xrealloc(net_buffer, net_buffer_size + 100000); if (net_buffer == NULL) { sprintf(str, "Error: Cannot increase net_buffer, out of memory, net_buffer_size = %d", net_buffer_size); show_error(str); break; } memset(net_buffer + net_buffer_size, 0, 100000); net_buffer_size += 100000; } /* repeat until empty line received (fragmented TCP packets!) */ } while (strstr(net_buffer, "\r\n\r\n") == 0); } if (broken) break; /* if we are in pipelining mode, clear this flag now to force a new recv if the request is not complete */ more_requests = 0; pend = NULL; if (strncmp(net_buffer, "GET", 3) == 0 && strncmp(net_buffer, "POST", 4) != 0) { if (len > 4 && strstr(net_buffer, "\r\n\r\n") != NULL) { pend = strstr(net_buffer, "\r\n\r\n") + 4; break; } if (len > 6 && strstr(net_buffer, "\r\r\n\r\r\n") != NULL) { pend = strstr(net_buffer, "\r\r\n\r\r\n") + 6; break; } } else if (strncmp(net_buffer, "POST", 4) == 0) { if (header_length == 0) { /* extract logbook */ strlcpy(str, net_buffer + 6, sizeof(str)); if (strstr(str, "HTTP")) *(strstr(str, "HTTP") - 1) = 0; strlcpy(logbook, str, sizeof(logbook)); strlcpy(logbook_enc, str, sizeof(logbook)); url_decode(logbook); /* extract content length */ if (strstr(net_buffer, "Content-Length:")) content_length = atoi(strstr(net_buffer, "Content-Length:") + 15); else if (strstr(net_buffer, "Content-length:")) content_length = atoi(strstr(net_buffer, "Content-length:") + 15); /* check for valid content-length */ if (content_length < 0) { broken = TRUE; break; } /* extract header length */ if (strstr(net_buffer, "\r\n\r\n")) header_length = strstr(net_buffer, "\r\n\r\n") - net_buffer + 4; if (strstr(net_buffer, "\r\r\n\r\r\n")) header_length = strstr(net_buffer, "\r\r\n\r\r\n") - net_buffer + 6; if (content_length > _max_content_length) { /* drain socket connection */ do { FD_ZERO(&readfds); FD_SET(_sock, &readfds); timeout.tv_sec = 6; timeout.tv_usec = 0; status = select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); if (FD_ISSET(_sock, &readfds)) { #ifdef HAVE_SSL if (_ssl_flag) i = SSL_read(_ssl_con, net_buffer, net_buffer_size); else #endif i = recv(_sock, net_buffer, net_buffer_size, 0); } else break; } while (i > 0); /* return error */ memset(return_buffer, 0, return_buffer_size); strlen_retbuf = 0; return_length = 0; sprintf(str, loc("Error: Content length (%d) larger than maximum content length (%d)"), content_length, _max_content_length); strcat(str, "
"); strcat(str, loc ("Please increase \"Max content length\" in [global] part of config file and restart elogd")); keep_alive = FALSE; show_error(str); #ifdef HAVE_SSL send_return(_sock, _ssl_con, net_buffer); #else send_return(_sock, net_buffer); #endif break; } } if (header_length > 0 && len >= header_length + content_length) { pend = net_buffer + header_length + content_length; break; } } else if (strstr(net_buffer, "HEAD") != NULL) { /* just return header */ rsprintf("HTTP/1.1 200 OK\r\n"); rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision()); rsprintf("Connection: close\r\n"); rsprintf("Content-Type: text/html\r\n\r\n"); keep_alive = FALSE; return_length = strlen_retbuf + 1; break; } else if (strstr(net_buffer, "OPTIONS") != NULL) { return_length = -1; break; } else { if (strlen(net_buffer) > 0 && get_verbose() >= VERBOSE_INFO) { strcpy(str, "Received unknown HTTP command: "); strencode2(str, net_buffer, sizeof(str)); show_error(str); } break; } } while (1); if (broken) { if (get_verbose() >= VERBOSE_URL) eprintf("TCP connection #%d on socket %d closed\n", i_conn, _sock); keep_alive = FALSE; break; } if (strncmp(net_buffer, "POST", 4) == 0 && len < header_length + content_length) { if (get_verbose() >= VERBOSE_INFO) eprintf("Incomplete POST request\n"); keep_alive = FALSE; break; } /* now process HTTP request and put the result into the return_buffer */ if (process_http_request(net_buffer, i_conn)) { /* send back the return_buffer to the browser */ #ifdef HAVE_SSL send_return(_sock, _ssl_con, net_buffer); #else send_return(_sock, net_buffer); #endif } /* check if the net_buffer contains more than one request (pipelining) */ if (pend && *pend) { memmove(net_buffer, pend, strlen(pend) + 1); more_requests = 1; len -= (pend - net_buffer); } } while (more_requests); if (!keep_alive) { #ifdef HAVE_SSL if (_ssl_flag) { SSL_shutdown(_ssl_con); SSL_free(_ssl_con); } #endif closesocket(_sock); ka_sock[i_conn] = 0; #ifdef HAVE_SSL ka_ssl_con[i_conn] = NULL; #endif } } } #ifdef OS_WINNT /* under windows, check if configuration changed (via stat()) once each access */ check_config(); #else /* under unix, rely on "kill -HUP elogd" */ if (_hup) { /* reload configuration */ check_config(); el_index_logbooks(); _hup = FALSE; } #endif /* check for periodic tasks */ check_cron(); } while (!_abort); eprintf("elogd server aborted.\n"); /* free all allocated memory */ for (i = 0; lb_list[i].name[0]; i++) { if (lb_list[i].el_index) { xfree(lb_list[i].el_index); lb_list[i].el_index = NULL; } if (lb_list[i].n_el_index) { xfree(lb_list[i].n_el_index); lb_list[i].n_el_index = NULL; } } xfree(net_buffer); xfree(return_buffer); free_config(); } /*------------------------------------------------------------------*/ int ss_getchar(BOOL reset) /******************************************************************** Routine: ss_getchar Purpose: Read a single character Input: BOOL reset Reset terminal to standard mode Output: Function value: int 0 for no character available n ASCII code for normal character \********************************************************************/ { #ifdef OS_UNIX static BOOL init = FALSE; static struct termios save_termios; struct termios buf; int i, fd; char c[3]; fd = fileno(stdin); if (reset) { if (init) tcsetattr(fd, TCSAFLUSH, &save_termios); init = FALSE; return 0; } if (!init) { tcgetattr(fd, &save_termios); memcpy(&buf, &save_termios, sizeof(buf)); buf.c_lflag &= ~(ECHO | ICANON | IEXTEN); buf.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON); buf.c_cflag &= ~(CSIZE | PARENB); buf.c_cflag |= CS8; /* buf.c_oflag &= ~(OPOST); */ buf.c_cc[VMIN] = 0; buf.c_cc[VTIME] = 0; tcsetattr(fd, TCSAFLUSH, &buf); init = TRUE; } memset(c, 0, 3); i = my_read(fd, c, 1); if (i == 0) return 0; /* BS/DEL -> BS */ if (c[0] == 127) return 8; return c[0]; #elif defined(OS_WINNT) static BOOL init = FALSE; static int repeat_count = 0; static int repeat_char; HANDLE hConsole; DWORD nCharsRead; INPUT_RECORD ir; OSVERSIONINFO vi; /* find out if we are under W95 */ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) { /* under W95, console doesn't work properly */ int c; if (!kbhit()) return 0; c = getch(); return c; } hConsole = GetStdHandle(STD_INPUT_HANDLE); if (reset) { SetConsoleMode(hConsole, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT); init = FALSE; return 0; } if (!init) { SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT); init = TRUE; } if (repeat_count) { repeat_count--; return repeat_char; } PeekConsoleInput(hConsole, &ir, 1, &nCharsRead); if (nCharsRead == 0) return 0; ReadConsoleInput(hConsole, &ir, 1, &nCharsRead); if (ir.EventType != KEY_EVENT) return ss_getchar(0); if (!ir.Event.KeyEvent.bKeyDown) return ss_getchar(0); if (ir.Event.KeyEvent.wRepeatCount > 1) { repeat_count = ir.Event.KeyEvent.wRepeatCount - 1; repeat_char = ir.Event.KeyEvent.uChar.AsciiChar; return repeat_char; } if (ir.Event.KeyEvent.uChar.AsciiChar) return ir.Event.KeyEvent.uChar.AsciiChar; if (ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY)) return ir.Event.KeyEvent.wVirtualKeyCode; return ss_getchar(0); #endif } int read_password(char *pwd, int size) { int n; char c, str[256]; n = 0; do { c = ss_getchar(0); if (c == 13) break; if (c) { if (c == 8) { if (n > 0) { str[--n] = 0; eprintf("\b \b"); } } else { str[n++] = c; eprintf("*"); } #ifdef OS_WINNT Sleep(10); #endif } } while (1); str[n] = 0; ss_getchar(1); // reset strlcpy(pwd, str, size); return n; } /*------------------------------------------------------------------*/ void create_password(char *logbook, char *name, char *pwd) { int fh, length, i; char *cfgbuffer, str[256], *p; fh = open(config_file, O_RDONLY); if (fh < 0) { /* create new file */ fh = open(config_file, O_CREAT | O_WRONLY, 0640); if (fh < 0) { eprintf("Cannot create \"%s\".\n", config_file); return; } sprintf(str, "[%s]\n%s = %s\n", logbook, name, pwd); write(fh, str, strlen(str)); close(fh); eprintf("File \"%s\" created with password in logbook \"%s\".\n", config_file, logbook); return; } /* read existing file and add password */ length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); cfgbuffer = xmalloc(length + 1); length = my_read(fh, cfgbuffer, length); cfgbuffer[length] = 0; close(fh); fh = open(config_file, O_TRUNC | O_WRONLY, 0640); sprintf(str, "[%s]", logbook); /* check if logbook exists already */ if (strstr(cfgbuffer, str)) { p = strstr(cfgbuffer, str); /* search password in current logbook */ do { while (*p && *p != '\n') p++; if (*p && *p == '\n') p++; if (strncmp(p, name, strlen(name)) == 0) { /* replace existing password */ i = (int) (p - cfgbuffer); write(fh, cfgbuffer, i); sprintf(str, "%s = %s\n", name, pwd); write(fh, str, strlen(str)); eprintf("Password replaced in logbook \"%s\".\n", logbook); while (*p && *p != '\n') p++; if (*p && *p == '\n') p++; /* write remainder of file */ write(fh, p, strlen(p)); xfree(cfgbuffer); cfgbuffer = NULL; close(fh); return; } } while (*p && *p != '['); if (!*p || *p == '[') { /* enter password into current logbook */ p = strstr(cfgbuffer, str); while (*p && *p != '\n') p++; if (*p && *p == '\n') p++; i = (int) (p - cfgbuffer); write(fh, cfgbuffer, i); sprintf(str, "%s = %s\n", name, pwd); write(fh, str, strlen(str)); eprintf("Password added to logbook \"%s\".\n", logbook); /* write remainder of file */ write(fh, p, strlen(p)); xfree(cfgbuffer); cfgbuffer = NULL; close(fh); return; } } else { /* write new logbook entry */ write(fh, cfgbuffer, strlen(cfgbuffer)); sprintf(str, "\n[%s]\n%s = %s\n\n", logbook, name, pwd); write(fh, str, strlen(str)); eprintf("Password added to new logbook \"%s\".\n", logbook); } xfree(cfgbuffer); cfgbuffer = NULL; close(fh); } void cleanup(void) { #ifdef OS_UNIX char str[256]; struct stat finfo; /* regain original uid */ if (setregid(-1, orig_gid) < 0 || setreuid(-1, orig_uid) < 0) eprintf("Cannot restore original GID/UID.\n"); if (pidfile[0] && stat(pidfile, &finfo) >= 0) { if (remove(pidfile) < 0) { sprintf(str, "Cannot remove pidfile \"%s\"\n", pidfile); eprintf("%s; %s\n", str, strerror(errno)); } } #endif if (running_as_daemon) #ifdef OS_UNIX closelog(); #else DeregisterEventSource(hEventLog); #endif } /*------------------------------------------------------------------*/ #ifdef OS_WINNT /* Routines for Windows service management */ // Executable name #define ELOGDAPPNAME "elogd" // Internal service name #define ELOGDSERVICENAME "elogd" // Displayed service name #define ELOGDSERVICEDISPLAYNAME "elogd" SERVICE_STATUS serviceStatus; SERVICE_STATUS_HANDLE serviceStatusHandle = 0; int install_service(void) { OSVERSIONINFO vi; char path[2048], dir[2048], cmd[2080]; SC_HANDLE hservice; SC_HANDLE hsrvmanager; /* check for Windows NT+ */ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) { eprintf("Can install service only under Windows NT/2k/XP.\n"); return -1; } if (GetModuleFileName(NULL, path, sizeof(path)) == 0) { eprintf("Cannot retrieve module file name.\n"); return -1; } strcpy(dir, path); if (strrchr(dir, '\\')) *(strrchr(dir, '\\') + 1) = 0; sprintf(cmd, "\"%s\" -D -c \"%s%s\"", path, dir, CFGFILE); /* Open the default, local Service Control Manager database */ hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hsrvmanager == NULL) { eprintf("Cannot connect to Service Control Manager.\n"); return -1; } /* Create an entry for the elogd service */ hservice = CreateService(hsrvmanager, // SCManager database ELOGDSERVICENAME, // name of service ELOGDSERVICEDISPLAYNAME, // name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type SERVICE_AUTO_START, // start type SERVICE_ERROR_NORMAL, // error control type cmd, // service's binary NULL, // no load ordering group NULL, // no tag identifier "", // dependencies NULL, // LocalSystem account NULL); // no password if (hservice == NULL) { if (GetLastError() == ERROR_SERVICE_EXISTS) eprintf("The elogd service is already registered.\n"); else eprintf("The elogd service could not be registered. Error code %d.\n", GetLastError()); } else { eprintf("The elogd service has been registered successfully.\n"); CloseServiceHandle(hservice); } /* Try to start the elogd service */ hservice = OpenService(hsrvmanager, ELOGDSERVICENAME, SERVICE_ALL_ACCESS); if (hservice == NULL) eprintf("The elogd service could not be accessed. Error code %d.\n", GetLastError()); else { if (!StartService(hservice, 0, NULL)) eprintf("The elogd service could not be started. Error code %d.\n", GetLastError()); else eprintf("The elogd service has been started successfully.\n"); CloseServiceHandle(hservice); } CloseServiceHandle(hsrvmanager); return 1; } int remove_service(int silent) { SC_HANDLE hservice; SC_HANDLE hsrvmanager; SERVICE_STATUS status; /* Open the SCM */ hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hsrvmanager == NULL) { if (!silent) eprintf("Cannot connect to Service Control Manager.\n"); return -1; } hservice = OpenService(hsrvmanager, ELOGDSERVICENAME, SERVICE_ALL_ACCESS); if (hservice == NULL) { if (!silent) eprintf("The elogd service could not be found.\n"); return -1; } /* Try to stop the elogd service */ if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) { while (QueryServiceStatus(hservice, &status)) { if (status.dwCurrentState == SERVICE_STOP_PENDING) Sleep(100); else break; } if (!silent) { if (status.dwCurrentState != SERVICE_STOPPED) { eprintf("The elogd service could not be stopped.\n"); } else eprintf("elogd service stopped successfully.\n"); } } /* Now remove the service from the SCM */ if (!DeleteService(hservice)) { if (!silent) { if (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) eprintf("The elogd service is already marked to be unregistered.\n"); else eprintf("The elogd service could not be unregistered.\n"); } } else if (!silent) eprintf("The elogd service hass been unregistered successfully.\n"); CloseServiceHandle(hservice); CloseServiceHandle(hsrvmanager); return 1; } void WINAPI ServiceControlHandler(DWORD controlCode) { switch (controlCode) { case SERVICE_CONTROL_INTERROGATE: break; case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); _abort = TRUE; return; case SERVICE_CONTROL_PAUSE: break; case SERVICE_CONTROL_CONTINUE: break; default: if (controlCode >= 128 && controlCode <= 255) // user defined control code break; else // unrecognised control code break; } SetServiceStatus(serviceStatusHandle, &serviceStatus); } void WINAPI ServiceMain(DWORD argc, LPSTR * argv) { // initialise service status serviceStatus.dwServiceType = SERVICE_WIN32; serviceStatus.dwCurrentState = SERVICE_STOPPED; serviceStatus.dwControlsAccepted = 0; serviceStatus.dwWin32ExitCode = NO_ERROR; serviceStatus.dwServiceSpecificExitCode = NO_ERROR; serviceStatus.dwCheckPoint = 0; serviceStatus.dwWaitHint = 0; serviceStatusHandle = RegisterServiceCtrlHandler(ELOGDSERVICENAME, ServiceControlHandler); if (serviceStatusHandle) { // service is starting serviceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); // running serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); /* avoid recursive calls to run_service */ running_as_daemon = FALSE; /* Redirect all messages handled with eprintf/efputs to syslog */ redirect_to_syslog(); /* start main server, exit with "_abort = TRUE" */ server_loop(); // service was stopped serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); // service is now stopped serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); } } int run_service(void) { SERVICE_TABLE_ENTRY serviceTable[] = { {ELOGDSERVICENAME, ServiceMain}, {0, 0} }; if (!StartServiceCtrlDispatcher(serviceTable)) return FAILURE; return SUCCESS; } #endif /*------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int i, j, n, fh, tcp_port_cl, sync_flag, silent; char smtp_pwd[80], str[256], logbook[256], clone_url[256], error_str[256], file_name[256]; time_t now; struct tm *tms; struct stat finfo; #ifdef OS_UNIX /* save gid/uid to regain later */ orig_gid = getegid(); orig_uid = geteuid(); pidfile[0] = 0; #endif /* register cleanup function */ atexit(cleanup); tzset(); /* initialize variables */ smtp_pwd[0] = 0; logbook_dir[0] = 0; logbook[0] = clone_url[0] = resource_dir[0] = logbook_dir[0] = 0; silent = tcp_port_cl = sync_flag = 0; use_keepalive = TRUE; running_as_daemon = FALSE; /* initialize random number generator */ srand((unsigned) time(NULL)); /* * Initially, redirect all messages handled with eprintf/efputs to stderr. * Note that we should use eprintf/efputs wrappers for all logging purposes, * but it is OK to use a printf for things like command line parsing till * we switch to daemon mode (if required). */ redirect_to_stderr(); /* evaluate predefined files and directories */ #ifdef CONFIG_PATH strcpy(config_file, CONFIG_PATH); if (config_file[0] && config_file[strlen(config_file) - 1] != DIR_SEPARATOR) strlcat(config_file, DIR_SEPARATOR_STR, sizeof(config_file)); #endif /* default config file */ strlcat(config_file, CFGFILE, sizeof(config_file)); /* parse command line parameters */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == 'D') running_as_daemon = TRUE; else if (argv[i][0] == '-' && argv[i][1] == 'v') { if (i < argc-1 && atoi(argv[i+1]) > 0) { set_verbose(atoi(argv[i+1])); i++; } else set_verbose(VERBOSE_INFO); } else if (argv[i][0] == '-' && argv[i][1] == 'k') use_keepalive = FALSE; else if (argv[i][0] == '-' && argv[i][1] == 'S') silent = TRUE; else if (argv[i][0] == '-' && argv[i][1] == 'x') enable_execute = TRUE; else if (argv[i][0] == '-' && argv[i][1] == 'm') sync_flag = 1; else if (argv[i][0] == '-' && argv[i][1] == 'M') sync_flag = 2; else if (argv[i][1] == 'T') { time(&now); tms = localtime(&now); assert(tms); printf("Actual date/time: %02d%02d%02d_%02d%02d%02d\n", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec); exit(EXIT_SUCCESS); } #ifdef OS_WINNT else if (stricmp(argv[i], "-install") == 0) { install_service(); if (!silent) { printf("Please hit any key ..."); fgets(str, sizeof(str), stdin); } exit(EXIT_SUCCESS); } else if (stricmp(argv[i], "-remove") == 0) { remove_service(silent); if (!silent) { printf("Please hit any key ..."); fgets(str, sizeof(str), stdin); } exit(EXIT_SUCCESS); } #endif else if (argv[i][0] == '-') { if (argv[i][1] == 'C') { if (i + 1 >= argc || argv[i + 1][0] == '-') clone_url[0] = 1; else strlcpy(clone_url, argv[++i], sizeof(clone_url)); } else if (i + 1 >= argc || argv[i + 1][0] == '-') goto usage; else if (argv[i][1] == 'p') tcp_port_cl = atoi(argv[++i]); else if (argv[i][1] == 'c') strlcpy(config_file, argv[++i], sizeof(config_file)); else if (argv[i][1] == 's') strlcpy(resource_dir, argv[++i], sizeof(resource_dir)); else if (argv[i][1] == 'd') strlcpy(logbook_dir, argv[++i], sizeof(logbook_dir)); else if (argv[i][1] == 't') strlcpy(smtp_pwd, argv[++i], sizeof(smtp_pwd)); else if (argv[i][1] == 'l') strlcpy(logbook, argv[++i], sizeof(logbook)); else if (argv[i][1] == 'n') strlcpy(listen_interface, argv[++i], sizeof(listen_interface)); #ifdef OS_UNIX else if (argv[i][1] == 'f') strlcpy(pidfile, argv[++i], sizeof(pidfile)); #endif else { usage:printf("%s\n", ELOGID); printf("usage: elogd [-C ] [-c ] [-D] [-d

] "); printf("[-f ] [-h] [-k] [-l ] [-M] [-m] [-n ] "); printf("[-p ] [-S] [-s ] [-t ] [-v] [-x]\n\n"); printf(" -C clone remote elogd configuration\n"); printf(" -c specify configuration file\n"); printf(" -M synchronize with removing deleted entries\n"); printf(" -m synchronize logbook(s) with remote server\n"); printf(" -D become a daemon\n"); printf(" -d specify logbook root directory\n"); #ifdef OS_UNIX printf(" -f PID file\n"); #endif printf(" -h this help\n"); printf(" -k do not use keep-alive\n"); printf(" -l specify logbook for -r, -w and -m commands\n"); printf(" -n hostname/IP interface to listen on\n"); printf(" -p TCP/IP port\n"); printf(" -s specify resource directory (themes, icons, ...)\n"); printf(" -t create/overwrite SMTP password in config file\n"); printf(" -v verbose output (1:URL, 2:INFO, 3:DEBUG)\n"); printf(" -x enable execution of shell commands\n\n"); #ifdef OS_WINNT printf("Windows service funtions:\n"); printf(" -install install elogd as service and start it\n"); printf(" -remove stop and remove elogd service\n"); #endif exit(EXIT_SUCCESS); } } } #ifdef OS_WINNT { WSADATA WSAData; /* Start windows sockets */ if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0) return 0; } #endif #ifdef OS_WINNT if (running_as_daemon) { /* change to directory of executable */ strcpy(str, argv[0]); for (i = strlen(str) - 1; i > 0; i--) if (str[i] != '\\') str[i] = 0; else break; chdir(str); } #endif /* clone remote elogd configuration */ if (clone_url[0]) { /* check if local config file exists */ fh = open(config_file, O_RDONLY | O_BINARY); if (fh > 0) { close(fh); eprintf("Overwrite local \"%s\"? [y]/n: ", CFGFILE); fgets(str, sizeof(str), stdin); if (str[0] == 'n' || str[0] == 'N') exit(EXIT_FAILURE); } /* contact remote server */ receive_config(NULL, clone_url, error_str); if (error_str[0]) { eputs(error_str); exit(EXIT_FAILURE); } else { printf("\nRemote configuration successfully received.\n\n"); /* adjust config file */ adjust_config(clone_url); /* receive logbook entries after set-up of direcories ... */ } } /* check for configuration file */ fh = open(config_file, O_RDONLY | O_BINARY); if (fh < 0) { eprintf("Cannot open \"%s\": %s\n", config_file, strerror(errno)); exit(EXIT_FAILURE); } close(fh); /* parse contents of config file into internal structure */ check_config(); /* evaluate undefined directories from config file or compiled-in defaults */ if (!resource_dir[0]) if (getcfg("global", "Resource Dir", str, sizeof(str))) strlcpy(resource_dir, str, sizeof(resource_dir)); #ifdef RESOURCE_DIR else strlcpy(resource_dir, RESOURCE_DIR, sizeof(resource_dir)); #endif if (!logbook_dir[0]) if (getcfg("global", "Logbook Dir", str, sizeof(str))) strlcpy(logbook_dir, str, sizeof(logbook_dir)); #ifdef LOGBOOK_DIR else strlcpy(logbook_dir, LOGBOOK_DIR, sizeof(logbook_dir)); #endif /* extract resource directory from configuration file if not given */ if (config_file[0] && strchr(config_file, DIR_SEPARATOR) && !resource_dir[0]) { strcpy(resource_dir, config_file); for (i = strlen(resource_dir) - 1; i > 0; i--) { if (resource_dir[i] == DIR_SEPARATOR) { resource_dir[i] = 0; break; } resource_dir[i] = 0; } } /* do the same for the logbook dir */ if (config_file[0] && strchr(config_file, DIR_SEPARATOR) && !logbook_dir[0]) { strcpy(logbook_dir, config_file); for (i = strlen(logbook_dir) - 1; i > 0; i--) { if (logbook_dir[i] == DIR_SEPARATOR) break; logbook_dir[i] = 0; } strlcat(logbook_dir, "logbooks", sizeof(logbook_dir)); } /* set default logbook dir if not given */ if (!logbook_dir[0]) strcpy(logbook_dir, "logbooks"); /* strip trailing dir separator */ if (logbook_dir[strlen(logbook_dir) - 1] == DIR_SEPARATOR) logbook_dir[strlen(logbook_dir) - 1] = 0; /* check for directories */ if (logbook_dir[0] && stat(logbook_dir, &finfo) < 0) { #ifdef OS_WINNT if (mkdir(logbook_dir) == 0) #else if (mkdir(logbook_dir, 0755) == 0) #endif eprintf("Logbook directory \"%s\" successfully created.\n", logbook_dir); else { eprintf("Cannot create logbook directory \"%s\":%s.\n", logbook_dir, strerror(errno)); exit(EXIT_FAILURE); } } if (resource_dir[0] && stat(resource_dir, &finfo) < 0) { eprintf("Resource directory \"%s\" not found.\n", resource_dir); exit(EXIT_FAILURE); } /* append '/' */ if (resource_dir[0] && resource_dir[strlen(resource_dir) - 1] != DIR_SEPARATOR) strlcat(resource_dir, DIR_SEPARATOR_STR, sizeof(resource_dir)); if (logbook_dir[0] && logbook_dir[strlen(logbook_dir) - 1] != DIR_SEPARATOR) strlcat(logbook_dir, DIR_SEPARATOR_STR, sizeof(logbook_dir)); if (sync_flag) { el_index_logbooks(); if (sync_flag == 2) setparam("confirm", "yes"); if (logbook[0]) { for (i = 0; lb_list[i].name[0]; i++) if (stricmp(lb_list[i].name, logbook)) break; if (!lb_list[i].name[0]) { eprintf("Logbook \"%s\" not defined in configuration file\n", logbook); exit(EXIT_FAILURE); } synchronize(&lb_list[i], SYNC_CLONE); } else synchronize(NULL, SYNC_CLONE); exit(EXIT_SUCCESS); } if (clone_url[0]) { /* force re-read of config file */ check_config_file(TRUE); el_index_logbooks(); /* check for retrieving password files */ for (i = n = 0; lb_list[i].name[0]; i++) if (getcfg(lb_list[i].name, "Password file", str, sizeof(str))) n++; if (n > 0) { eprintf("\nRetrieve remote password files? [y]/n: "); fgets(str, sizeof(str), stdin); if (str[0] != 'n' && str[0] != 'N') for (i = n = 0; lb_list[i].name[0]; i++) { if (lb_list[i].top_group[0]) setcfg_topgroup(lb_list[i].top_group); else setcfg_topgroup(""); if (getcfg(lb_list[i].name, "Password file", file_name, sizeof(file_name))) { /* check if this file has not already been retrieved */ for (j = 0; j < i; j++) { if (lb_list[j].top_group[0]) setcfg_topgroup(lb_list[j].top_group); else setcfg_topgroup(""); if (getcfg(lb_list[j].name, "Password file", str, sizeof(str)) && stricmp(file_name, str) == 0) break; } if (lb_list[i].top_group[0]) setcfg_topgroup(lb_list[i].top_group); else setcfg_topgroup(""); if (j == i) { receive_pwdfile(&lb_list[i], clone_url, error_str); if (error_str[0]) { eputs(error_str); exit(EXIT_FAILURE); } else eprintf("File \"%s\" received successfully.\n", file_name); } } } } eprintf("\nRetrieve remote logbook entries? [y]/n: "); fgets(str, sizeof(str), stdin); if (str[0] != 'n' && str[0] != 'N') /* synchronize all logbooks */ synchronize(NULL, SYNC_CLONE); puts("\nCloning finished. Check " CFGFILE " and start the server normally."); exit(EXIT_SUCCESS); } if (smtp_pwd[0]) { do_crypt(smtp_pwd, str, sizeof(str)); create_password("global", "SMTP Password", str); exit(EXIT_SUCCESS); } /* get listen interface */ if (listen_interface[0] == 0) if (getcfg("global", "interface", str, sizeof(str))) { strlcpy(listen_interface, str, sizeof(listen_interface)); } /* get default port */ if (getcfg("global", "SSL", str, sizeof(str)) && atoi(str) == 1) elog_tcp_port = 443; else elog_tcp_port = 80; /* get port from configuration file */ if (tcp_port_cl != 0) elog_tcp_port = tcp_port_cl; else { if (getcfg("global", "Port", str, sizeof(str))) elog_tcp_port = atoi(str); } /* get optional content length from configuration file */ if (getcfg("global", "Max content length", str, sizeof(str))) _max_content_length = atoi(str); #ifdef OS_WINNT /* if running as a service, server_loop gets called from the service main routine */ if (running_as_daemon) { redirect_to_syslog(); if (!run_service()) { eprintf("Couldn't run the service; aborting\n"); exit(EXIT_FAILURE); } } else server_loop(); #else server_loop(); #endif /* avoid compiler warning */ if (silent == 1) silent = 0; exit(EXIT_SUCCESS); } elog-3.1.3-1/src/locext.c0000755000175000017500000001203512556103751014062 0ustar rogerroger/********************************************************************\ Name: locext.c Created by: Stefan Ritt Copyright 2000 + Stefan Ritt ELOG 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. ELOG 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 ELOG. If not, see . Contents: Extract all localization strings from elogd and put them into eloglang.xxxx $Id$ \********************************************************************/ #include #include #include #include #include #include #include #include #ifdef _MSC_VER #include #include #else #define O_BINARY 0 #include #endif void read_buf(char *filename, char **buf) { int fh, size, i; fh = open(filename, O_RDONLY | O_BINARY); if (fh < 0) { printf("Cannot open file \"%s\"\n", filename); exit(1); } size = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); *buf = malloc(size + 1); assert(*buf); i = read(fh, *buf, size); assert(i == size); (*buf)[size] = 0; close(fh); } /*------------------------------------------------------------------*/ int scan_file(char *infile, char *outfile) { int i, fho, size, first; char *buf, *bufout, *p, *p2, str[1000], line[1000]; read_buf(infile, &buf); p = buf; first = 1; do { p = strstr(p, "loc(\""); if (!p) break; /* check that we did not find "malloc(" */ if (isalpha(*(p - 1))) { p++; continue; } p += 5; p2 = p; while (*p2) { if (*p2 == '"' && *(p2 - 1) != '\\') break; p2++; } size = (int) (p2 - p); if (size >= (int) sizeof(str)) { printf("Error: string too long\n"); free(buf); return 1; } memset(str, 0, sizeof(str)); memcpy(str, p, size < (int) sizeof(str) ? size : (int) sizeof(str)); /* convert \" to " */ for (p2 = str; *p2; p2++) if (*p2 == '\\') strcpy(p2, p2 + 1); /* check if string exists in output file */ sprintf(line, "\n%s =", str); read_buf(outfile, &bufout); p2 = strstr(bufout, line); if (p2 == NULL) { /* append string to output file */ fho = open(outfile, O_CREAT | O_WRONLY | O_APPEND | O_BINARY, 644); if (fho < 0) { printf("Cannot open file \"%s\" for append\n", outfile); return 1; } if (first && !strstr(bufout, "please translate following")) { sprintf(line, "\r\n#\r\n#---- please translate following items and then remove this comment ----#\r\n#\r\n"); write(fho, line, strlen(line)); first = 0; } sprintf(line, "%s = \r\n", str); write(fho, line, strlen(line)); close(fho); printf("Added string: "); puts(str); } free(bufout); } while (1); /* now remove obsolete strings */ read_buf(outfile, &bufout); p = bufout; do { strncpy(line, p, sizeof(line)); p2 = p; if (strchr(line, '\n')) *strchr(line, '\n') = 0; if (strchr(line, '\r')) *strchr(line, '\r') = 0; p += strlen(line); while (*p == '\r' || *p == '\n') p++; if (line[0] == '#' || line[0] == ';') continue; if (strchr(line, '=')) *strchr(line, '=') = 0; if (strlen(line) <= 1) continue; while (line[strlen(line) - 1] == ' ') line[strlen(line) - 1] = 0; /* change " to \" */ for (i = 0; i < (int) strlen(line); i++) if (line[i] == '"') { strcpy(str, line + i); line[i++] = '\\'; strcpy(line + i, str); } sprintf(str, "loc(\"%s\")", line); if (strstr(buf, str)) continue; printf("Removed string: "); puts(line); /* if not found, remove line */ strcpy(p2, p); p = p2; } while (*p); fho = open(outfile, O_CREAT | O_WRONLY | O_BINARY | O_TRUNC, 644); if (fho < 0) { printf("Cannot open file \"%s\" for output\n", outfile); return 1; } write(fho, bufout, strlen(bufout)); close(fho); free(bufout); free(buf); return 0; } /*------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int status; if (argc != 3) { printf("Usage: %s \n", argv[0]); return 1; } status = scan_file(argv[1], argv[2]); return status; } elog-3.1.3-1/src/elogd.h0000644000175000017500000002363412757305354013675 0ustar rogerroger/******************************************************************** Name: elogd.h Created by: Stefan Ritt Copyright 2000 + Stefan Ritt ELOG 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. ELOG 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. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. You should have received a copy of the GNU General Public License along with ELOG. If not, see . Contents: Header file for ELOG program \********************************************************************/ /* Include version from central version file */ #include "elog-version.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include /* Default name of the configuration file. */ #ifndef CFGFILE #define CFGFILE "elogd.cfg" #endif /* Default TCP port for server. */ #ifndef DEFAULT_PORT #define DEFAULT_PORT 80 #endif #ifdef _MSC_VER #define OS_WINNT #define DIR_SEPARATOR '\\' #define DIR_SEPARATOR_STR "\\" #define snprintf _snprintf #include #include #include #include #include #include #include #else #define OS_UNIX #ifdef __APPLE__ #define OS_MACOSX #endif #define TRUE 1 #define FALSE 0 #ifndef O_TEXT #define O_TEXT 0 #define O_BINARY 0 #endif #define DIR_SEPARATOR '/' #define DIR_SEPARATOR_STR "/" #ifndef DEFAULT_USER #define DEFAULT_USER "nobody" #endif #ifndef DEFAULT_GROUP #define DEFAULT_GROUP "nogroup" #endif #ifndef PIDFILE #define PIDFILE "/var/run/elogd.pid" #endif typedef int BOOL; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define closesocket(s) close(s) #ifndef stricmp #define stricmp(s1, s2) strcasecmp(s1, s2) #endif #endif /* OS_UNIX */ /* SSL includes */ #ifdef HAVE_SSL #include #endif /* local includes */ #include "regex.h" #include "mxml.h" #include "strlcpy.h" #define SYSLOG_PRIORITY LOG_NOTICE /* Default priority for syslog facility */ #define TELL(fh) lseek(fh, 0, SEEK_CUR) #ifdef OS_WINNT #define TRUNCATE(fh) chsize(fh, TELL(fh)) #else #define TRUNCATE(fh) ftruncate(fh, TELL(fh)) #endif #define NAME_LENGTH 1500 #define DEFAULT_TIME_FORMAT "%c" #define DEFAULT_DATE_FORMAT "%x" #define DEFAULT_HTTP_CHARSET "ISO-8859-1" #define SUCCESS 1 #define FAILURE 0 #define EL_SUCCESS 1 #define EL_FIRST_MSG 2 #define EL_LAST_MSG 3 #define EL_NO_MSG 4 #define EL_FILE_ERROR 5 #define EL_UPGRADE 6 #define EL_EMPTY 7 #define EL_MEM_ERROR 8 #define EL_DUPLICATE 9 #define EL_INVAL_FILE 10 #define EL_FIRST 1 #define EL_LAST 2 #define EL_NEXT 3 #define EL_PREV 4 #define MAX_GROUPS 32 #define MAX_PARAM 200 #define MAX_ATTACHMENTS 50 #define MAX_N_LIST 100 #define MAX_N_ATTR 100 #define MAX_N_EMAIL 500 #define MAX_REPLY_TO 100 #define CMD_SIZE 10000 #define TEXT_SIZE 250000 #define MAX_PATH_LENGTH 256 #define MAX_CONTENT_LENGTH 10*1024*1024 /* attribute flags */ #define AF_REQUIRED (1<<0) #define AF_LOCKED (1<<1) #define AF_MULTI (1<<2) #define AF_FIXED_EDIT (1<<3) #define AF_FIXED_REPLY (1<<4) #define AF_ICON (1<<5) #define AF_RADIO (1<<6) #define AF_EXTENDABLE (1<<7) #define AF_DATE (1<<8) #define AF_DATETIME (1<<9) #define AF_TIME (1<<10) #define AF_NUMERIC (1<<11) #define AF_USERLIST (1<<12) #define AF_MUSERLIST (1<<13) #define AF_USEREMAIL (1<<14) #define AF_MUSEREMAIL (1<<15) /* attribute format flags */ #define AFF_SAME_LINE 1 #define AFF_MULTI_LINE 2 #define AFF_DATE 4 #define AFF_EXTENDABLE 8 typedef struct { int message_id; char subdir[256]; char file_name[32]; time_t file_time; int offset; int in_reply_to; unsigned char md5_digest[16]; } EL_INDEX; typedef struct { char name[256]; char name_enc[256]; char data_dir[256]; char top_group[256]; EL_INDEX *el_index; int *n_el_index; int n_attr; PMXML_NODE pwd_xml_tree; } LOGBOOK; typedef struct { int message_id; unsigned char md5_digest[16]; } MD5_INDEX; typedef struct LBNODE *LBLIST; typedef struct LBNODE { char name[256]; LBLIST *member; int n_members; int is_top; } LBNODE; typedef struct { LOGBOOK *lbs; int index; char string[256]; int number; int in_reply_to; } MSG_LIST; typedef struct { char user_name[256]; char session_id[32]; char host_ip[32]; time_t time; } SESSION_ID; void show_error(char *error); int is_verbose(void); extern void eprintf(const char *, ...); BOOL enum_user_line(LOGBOOK * lbs, int n, char *user, int size); int get_user_line(LOGBOOK * lbs, char *user, char *password, char *full_name, char *email, BOOL email_notify[1000], time_t * last_access, int *inactive); int get_full_name(LOGBOOK *lbs, char *uname, char *full_name); int set_user_inactive(LOGBOOK * lbs, char *user, int inactive); int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk, BOOL ignore_quotes); int execute_shell(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char att_file[MAX_ATTACHMENTS][256], char *sh_cmd); BOOL isparam(char *param); char *getparam(char *param); void write_logfile(LOGBOOK * lbs, const char *str); BOOL check_login_user(LOGBOOK * lbs, char *user); LBLIST get_logbook_hierarchy(void); BOOL is_logbook_in_group(LBLIST pgrp, char *logbook); BOOL is_admin_user(LOGBOOK * lbs, char *user); BOOL is_admin_user_global(char *user); void free_logbook_hierarchy(LBLIST root); void show_top_text(LOGBOOK * lbs); void show_bottom_text(LOGBOOK * lbs); int set_attributes(LOGBOOK * lbs, char attributes[][NAME_LENGTH], int n); void show_elog_list(LOGBOOK * lbs, int past_n, int last_n, int page_n, BOOL default_page, char *info); int change_config_line(LOGBOOK * lbs, char *option, char *old_value, char *new_value); int read_password(char *pwd, int size); int getcfg(char *group, char *param, char *value, int vsize); int build_subst_list(LOGBOOK * lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH], char attrib[][NAME_LENGTH], BOOL format_date); void highlight_searchtext(regex_t * re_buf, char *src, char *dst, BOOL hidden); int parse_config_file(char *config_file); PMXML_NODE load_password_file(LOGBOOK * lbs, char *error, int error_size); int load_password_files(); BOOL check_login(LOGBOOK * lbs, char *sid); void compose_base_url(LOGBOOK * lbs, char *base_url, int size, BOOL email_notify); void show_elog_entry(LOGBOOK * lbs, char *dec_path, char *command); char *loc(char *orig); void strencode(char *text); void strencode_nouml(char *text); char *stristr(const char *str, const char *pattern); int scan_attributes(char *logbook); int is_inline_attachment(char *encoding, int message_id, char *text, int i, char *att); int setgroup(char *str); int setuser(char *str); int setegroup(char *str); int seteuser(char *str); void strencode2(char *b, const char *text, int size); void load_config_section(char *section, char **buffer, char *error); void remove_crlf(char *buffer); time_t convert_date(char *date_string); time_t convert_datetime(char *date_string); int get_thumb_name(const char *file_name, char *thumb_name, int size, int index); int create_thumbnail(LOGBOOK * lbs, char *file_name); int ascii_compare(const void *s1, const void *s2); int ascii_compare2(const void *s1, const void *s2); void do_crypt(const char *s, char *d, int size); BOOL get_password_file(LOGBOOK * lbs, char *file_name, int size); LOGBOOK *get_first_lbs_with_global_passwd(); /* functions from auth.c */ int auth_verify_password(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size); int auth_change_password(LOGBOOK *lbs, const char *user, const char *old_pwd, const char *new_pwd, char *error_str, int error_size); int auth_verify_password_krb5(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size); int auth_change_password_krb5(LOGBOOK *lbs, const char *user, const char *old_pwd, const char *new_pwd, char *error, int error_size); elog-3.1.3-1/src/auth.c0000644000175000017500000004560312757305354013537 0ustar rogerroger/********************************************************************\ Name: auth.c Created by: Stefan Ritt Copyright 2000 + Stefan Ritt ELOG 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. ELOG 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 ELOG. If not, see . Contents: Authentication subroutines. Currently supported: - password file authentication - kerberos5 password authentication $Id: elog.c 2350 2010-12-23 10:45:10Z ritt $ \********************************************************************/ #include "elogd.h" #ifdef HAVE_KRB5 #include #endif #ifdef HAVE_LDAP #include LDAP *ldap_ld; char ldap_login_attr[64]; char ldap_userbase[256]; char ldap_bindDN[512]; #endif /* HAVE_LDAP */ extern LOGBOOK *lb_list; /*==================================================================*/ /*---- Kerberos5 routines ------------------------------------------*/ #ifdef HAVE_KRB5 int auth_verify_password_krb5(LOGBOOK * lbs, const char *user, const char *password, char *error_str, int error_size) { char *princ_name, str[256], realm[256]; krb5_error_code error; krb5_principal princ; krb5_context context; krb5_creds creds; krb5_get_init_creds_opt options; if (krb5_init_context(&context) < 0) return FALSE; strlcpy(str, user, sizeof(str)); if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { strlcat(str, "@", sizeof(str)); strlcat(str, realm, sizeof(str)); } if ((error = krb5_parse_name(context, str, &princ)) != 0) { strlcpy(error_str, "Kerberos error:
", error_size); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } error = krb5_unparse_name(context, princ, &princ_name); if (error) { strlcpy(error_str, "Kerberos error:
", error_size); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } sprintf(str, "Using %s as server principal for authentication", princ_name); write_logfile(lbs, str); memset(&options, 0, sizeof(options)); krb5_get_init_creds_opt_init(&options); memset(&creds, 0, sizeof(creds)); error = krb5_get_init_creds_password(context, &creds, princ, (char *) password, NULL, NULL, 0, NULL, &options); krb5_free_context(context); if (error && error != KRB5KDC_ERR_PREAUTH_FAILED && error != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) { sprintf(error_str, "Kerberos error %d:
", error); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } if (error) return FALSE; return TRUE; } int auth_change_password_krb5(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, char *error_str, int error_size) { char *princ_name, str[256], realm[256]; int result_code, n; krb5_error_code error; krb5_data result_code_string, result_string; krb5_principal princ; krb5_context context; krb5_creds creds; krb5_get_init_creds_opt options; if (krb5_init_context(&context) < 0) return FALSE; strlcpy(str, user, sizeof(str)); if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { strlcat(str, "@", sizeof(str)); strlcat(str, realm, sizeof(str)); } if ((error = krb5_parse_name(context, str, &princ)) != 0) { strlcpy(error_str, "Kerberos error:
", error_size); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } error = krb5_unparse_name(context, princ, &princ_name); sprintf(str, "Using %s as server principal for authentication", princ_name); write_logfile(lbs, str); memset(&options, 0, sizeof(options)); krb5_get_init_creds_opt_init(&options); krb5_get_init_creds_opt_set_tkt_life(&options, 300); krb5_get_init_creds_opt_set_forwardable(&options, FALSE); krb5_get_init_creds_opt_set_proxiable(&options, FALSE); memset(&creds, 0, sizeof(creds)); error = krb5_get_init_creds_password(context, &creds, princ, (char *) old_pwd, NULL, NULL, 0, "kadmin/changepw", &options); if (error) { strlcpy(error_str, "Kerberos error:
", error_size); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } error = krb5_set_password(context, &creds, (char *) new_pwd, princ, &result_code, &result_code_string, &result_string); if (error) { strlcpy(error_str, "Kerberos error:
", error_size); strlcat(error_str, krb5_get_error_message(context, error), error_size); strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); return FALSE; } if (result_code > 0) { if (result_code_string.length > 0) { strlcpy(error_str, result_code_string.data, error_size); if ((int) result_code_string.length < error_size) error_str[result_code_string.length] = 0; } if (result_string.length > 0) { strlcat(error_str, ": ", error_size); n = strlen(error_str) + result_string.length; strlcat(error_str, result_string.data, error_size); if (n < error_size) error_str[n] = 0; } } krb5_free_data_contents(context, &result_code_string); krb5_free_data_contents(context, &result_string); krb5_free_cred_contents(context, &creds); krb5_get_init_creds_opt_free(context, &options); krb5_free_context(context); if (result_code > 0) return FALSE; return TRUE; } #endif /*---- LDAP routines ------------------------------------------*/ #ifdef HAVE_LDAP int ldap_init(LOGBOOK *lbs, char *error_str, int error_size) { char str[512], ldap_server[256]; int ii, version; // Read Config file if (getcfg(lbs->name, "LDAP server", ldap_server, sizeof(ldap_server))) { strlcpy(str, ldap_server, sizeof(str)); } else { strlcpy(error_str, "LDAP initialization error
", error_size); strlcat(error_str, "
Please check your LDAP configuration.", error_size); strlcat(str, "ERR: Cannot find LDAP server entry!", sizeof(str)); write_logfile(lbs, str); return FALSE; } if (!getcfg(lbs->name, "LDAP userbase", ldap_userbase, sizeof(ldap_userbase))) { strlcpy(error_str, "LDAP initialization error
", error_size); strlcat(error_str, "
Please check your LDAP configuration.", error_size); strlcat(str, ", ERR: Cannot find LDAP userbase (e.g. \'ou=People,dc=example,dc=org\')!", sizeof(str)); write_logfile(lbs, str); return FALSE; } if (!getcfg(lbs->name, "LDAP login attribute", ldap_login_attr, sizeof(ldap_login_attr))) { strlcpy(error_str, "LDAP initialization error
", error_size); strlcat(error_str, "
Please check your LDAP configuration.", error_size); strlcat(str, ", ERR: Cannot find LDAP login attribute (e.g. uid, cn, ...)!", sizeof(str)); write_logfile(lbs, str); return FALSE; } // Initialize/open LDAP connection if(ldap_initialize( &ldap_ld, ldap_server )) { perror("ldap_initialize"); strlcpy(error_str, "LDAP initialization error
", error_size); strlcat(error_str, "
Please check your LDAP configuration.", error_size); return FALSE; } // Use the LDAP_OPT_PROTOCOL_VERSION session preference to specify that the client is LDAPv3 client version = LDAP_VERSION3; ldap_set_option(ldap_ld, LDAP_OPT_PROTOCOL_VERSION, &version); write_logfile(lbs, str); return TRUE; } int auth_verify_password_ldap(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size) { LDAPMessage *result, *err; int bind=0, ii; char str[512]; ldap_ld = NULL; memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); if(!ldap_init(lbs,error_str,error_size)) { strlcpy(error_str, "LDAP initialization error
", error_size); strlcat(error_str, "
Please check your LDAP configuration.", error_size); return FALSE; } // Form LDAP bind DN (distinguished name): // login_attr=user,ldap_userbase, e.g. uid=tuser,ou=People,dc=example,dc=org sprintf(ldap_bindDN,"%s=%s,%s",ldap_login_attr,user,ldap_userbase); strlcat(str, "Connecting as: ", sizeof(str)); strlcat(str, ldap_bindDN, sizeof(str)); write_logfile(lbs, str); // User authentication (bind) bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); if( bind != LDAP_SUCCESS ) { strlcpy(error_str, "LDAP authentication error:
", error_size); strlcat(error_str, ldap_err2string(bind), error_size); strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); strlcpy(str, "LDAP Authentication: FAILED!", sizeof(str)); write_logfile(lbs, str); ldap_unbind(ldap_ld); return FALSE; } strlcpy(str, "LDAP Authentication: Success!", sizeof(str)); ldap_unbind(ldap_ld); write_logfile(lbs, str); return TRUE; } int ldap_adduser_file(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size) { LDAPMessage *result, *entry; char *attribute, **values; char str[512], filter[512]; BerElement *ber; int bind=0, rc=0, i; char lbs_str[256], user_str[256], user_enc[256], fullname[256], usergn[128], usersn[128], useremail[256]; PMXML_NODE node, npwd; struct timeval timeOut = {3,0}; // 3 second connection/search timeout // zerotime.tv_sec = zerotime.tv_usec = 0L; write_logfile(lbs, "New user: getting userdata from LDAP..."); if(!ldap_init(lbs,error_str,error_size)) { return FALSE; } // User authentication (bind) bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); if( bind != LDAP_SUCCESS ) { strlcpy(error_str, "LDAP authentication error:
", error_size); strlcat(error_str, ldap_err2string(bind), error_size); strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); strlcat(str, "LDAP Authentication: FAILED!", sizeof(str)); write_logfile(lbs, str); ldap_unbind(ldap_ld); return FALSE; } // form LDAP filter to find the user; sprintf(filter, "(%s=%s)", ldap_login_attr, user); // below based on: http://www.djack.com.pl/modules.php?name=FAQ&myfaq=yes&xmyfaq=yes&id_cat=7&id=183 (code errors!) // AND on: http://www-archive.mozilla.org/directory/csdk-docs/example.htm // Get user's first name, surname and email from LDAP rc = ldap_search_ext_s( ldap_ld, // LDAP session handle ldap_userbase, // Search Base LDAP_SCOPE_SUBTREE, // Search Scope – everything below o=Acme filter, // Search Filter – only inetOrgPerson objects NULL, // returnAllAttributes – NULL means Yes 0, // attributesOnly – False means we want values NULL, // Server controls – There are none NULL, // Client controls – There are none &timeOut, // search Timeout LDAP_NO_LIMIT, // no size limit &result); if (rc != LDAP_SUCCESS) { strlcat(str, "LDAP search returned error: ", sizeof(str)); strlcat(str, ldap_err2string(rc), sizeof(str)); write_logfile(lbs, str); ldap_unbind(ldap_ld); return FALSE; } for(entry = ldap_first_entry(ldap_ld,result); entry != NULL; entry = ldap_next_entry(ldap_ld,entry) ) { for(attribute = ldap_first_attribute(ldap_ld,entry,&ber); attribute != NULL; attribute = ldap_next_attribute(ldap_ld,entry,ber) ) { // For each attribute, print the attribute name and values. // if((values = ldap_get_values(ldap_ld,entry,attribute)) != NULL ) { for(i=0; values[i] != NULL; i++) { if(strcmp(attribute,"givenName")==0 || strcmp(attribute,"gn")==0) strlcpy(usergn, values[i], sizeof(usergn)); if(strcmp(attribute,"sn")==0 || strcmp(attribute,"surname")==0) strlcpy(usersn, values[i], sizeof(usersn)); if(strcmp(attribute,"mail")==0 || strcmp(attribute,"rfc822Mailbox")==0) strlcpy(useremail, values[i], sizeof(useremail)); } ldap_value_free(values); } ldap_memfree(attribute); } if(ber != NULL) ber_free(ber,0); } ldap_msgfree(result); ldap_unbind(ldap_ld); sprintf(fullname, "%s %s", usergn, usersn); // Add user from LDAP in the local password file // do not allow HTML in user name strencode2(user_enc, user, sizeof(user_enc)); sprintf(lbs_str, "/list/user[name=%s]", user_enc); node = mxml_find_node(lbs->pwd_xml_tree, lbs_str); node = mxml_find_node(lbs->pwd_xml_tree, "/list"); if (!node) { show_error(loc("Error accessing password file")); return 0; } node = mxml_add_node(node, "user", NULL); mxml_add_node(node, "name", user_enc); // add user login from LDAP; do_crypt(password, user_str, sizeof(str)); npwd = mxml_add_node(node, "password", user_str); // add user password; if (npwd) mxml_add_attribute(npwd, "encoding", "SHA256"); // user password is encoded; strencode2(user_str, fullname, sizeof(str)); // add full user name from LDAP; mxml_add_node(node, "full_name", user_str); mxml_add_node(node, "last_logout", "0"); // last logout; mxml_add_node(node, "last_activity", "0"); // last activity; mxml_add_node(node, "email", useremail); // add user email from LDAP; mxml_add_node(node, "inactive", "0"); sprintf(str,"New user: %s, %s added", user_enc, useremail); write_logfile(lbs, str); return TRUE; } /*---- clear ldap_ld and ldap_bindDN ----*/ int ldap_clear () { ldap_ld = NULL; memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); return TRUE; } #endif /* LDAP */ /*---- local password file routines --------------------------------*/ int auth_verify_password_file(LOGBOOK * lbs, const char *user, const char *password, char *error_str, int error_size) { char upwd[256], enc_pwd[256]; get_user_line(lbs, (char *) user, upwd, NULL, NULL, NULL, NULL, NULL); do_crypt(password, enc_pwd, sizeof(enc_pwd)); return strcmp(enc_pwd, upwd) == 0; } int auth_change_password_file(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, char *error_str, int error_size) { char str[256], file_name[256], enc_pwd[256]; PMXML_NODE node; if (lbs == NULL) lbs = get_first_lbs_with_global_passwd(); if (!lbs->pwd_xml_tree) return FALSE; sprintf(str, "/list/user[name=%s]/password", user); node = mxml_find_node(lbs->pwd_xml_tree, str); if (node == NULL) return FALSE; do_crypt(new_pwd, enc_pwd, sizeof(enc_pwd)); mxml_replace_node_value(node, enc_pwd); if (get_password_file(lbs, file_name, sizeof(file_name))) mxml_write_tree(file_name, lbs->pwd_xml_tree); return TRUE; } /*---- common function entry points --------------------------------*/ int auth_verify_password(LOGBOOK * lbs, const char *user, const char *password, char *error_str, int error_size) { char str[256]; BOOL verified; error_str[0] = 0; verified = FALSE; /* otherwise calls with null lbs which make this procedure crash */ if (lbs == NULL) lbs = get_first_lbs_with_global_passwd(); if (lbs == NULL) return FALSE; getcfg(lbs->name, "Authentication", str, sizeof(str)); #ifdef HAVE_KRB5 if (stristr(str, "Kerberos")) verified = auth_verify_password_krb5(lbs, user, password, error_str, error_size); if (verified) return TRUE; #endif #ifdef HAVE_LDAP if (stristr(str, "LDAP")) { verified = auth_verify_password_ldap(lbs, user, password, error_str, error_size); // if user not in password file (external authentication!) and "LDAP register" is allowed (>0), // obtain user info from LDAP and add locally if (verified) { if (get_user_line(lbs, user, NULL, NULL, NULL, NULL, NULL, NULL) == 2) { if (getcfg(lbs->name, "LDAP register", str, sizeof(str)) && atoi(str) > 0) ldap_adduser_file(lbs, user, password, error_str, error_size); } } ldap_clear(); } if (verified) return TRUE; #endif if (str[0] == 0 || stristr(str, "File")) verified = auth_verify_password_file(lbs, user, password, error_str, error_size); return verified; } int auth_change_password(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, char *error_str, int error_size) { int status = 0; char str[256]; error_str[0] = 0; getcfg(lbs->name, "Authentication", str, sizeof(str)); if (str[0] == 0 || stristr(str, "File")) status = auth_change_password_file(lbs, user, old_pwd, new_pwd, error_str, error_size); #ifdef HAVE_KRB5 if (stristr(str, "Kerberos")) status = auth_change_password_krb5(lbs, user, old_pwd, new_pwd, error_str, error_size); #endif return status; } elog-3.1.3-1/src/regex.h0000755000175000017500000004441612556103751013713 0ustar rogerroger/* Definitions for data structures and routines for the regular expression library, version 0.12. Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __REGEXP_LIBRARY_H__ #define __REGEXP_LIBRARY_H__ /* POSIX says that must be included (by the caller) before . */ #ifdef VMS /* VMS doesn't have `size_t' in , even though POSIX says it should be there. */ #include #endif /* The following bits are used to determine the regexp syntax we recognize. The set/not-set meanings are chosen so that Emacs syntax remains the value 0. The bits are given in alphabetical order, and the definitions shifted by one from the previous bit; thus, when we add or remove a bit, only one other definition need change. */ typedef unsigned reg_syntax_t; /* If this bit is not set, then \ inside a bracket expression is literal. If set, then such a \ quotes the following character. */ #define RE_BACKSLASH_ESCAPE_IN_LISTS (1) /* If this bit is not set, then + and ? are operators, and \+ and \? are literals. If set, then \+ and \? are operators and + and ? are literals. */ #define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) /* If this bit is set, then character classes are supported. They are: [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. If not set, then character classes are not supported. */ #define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) /* If this bit is set, then ^ and $ are always anchors (outside bracket expressions, of course). If this bit is not set, then it depends: ^ is an anchor if it is at the beginning of a regular expression or after an open-group or an alternation operator; $ is an anchor if it is at the end of a regular expression, or before a close-group or an alternation operator. This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because POSIX draft 11.2 says that * etc. in leading positions is undefined. We already implemented a previous draft which made those constructs invalid, though, so we haven't changed the code back. */ #define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) /* If this bit is set, then special characters are always special regardless of where they are in the pattern. If this bit is not set, then special characters are special only in some contexts; otherwise they are ordinary. Specifically, * + ? and intervals are only special when not after the beginning, open-group, or alternation operator. */ #define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) /* If this bit is set, then *, +, ?, and { cannot be first in an re or immediately after an alternation or begin-group operator. */ #define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) /* If this bit is set, then . matches newline. If not set, then it doesn't. */ #define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) /* If this bit is set, then . doesn't match NUL. If not set, then it does. */ #define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) /* If this bit is set, nonmatching lists [^...] do not match newline. If not set, they do. */ #define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) /* If this bit is set, either \{...\} or {...} defines an interval, depending on RE_NO_BK_BRACES. If not set, \{, \}, {, and } are literals. */ #define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) /* If this bit is set, +, ? and | aren't recognized as operators. If not set, they are. */ #define RE_LIMITED_OPS (RE_INTERVALS << 1) /* If this bit is set, newline is an alternation operator. If not set, newline is literal. */ #define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) /* If this bit is set, then `{...}' defines an interval, and \{ and \} are literals. If not set, then `\{...\}' defines an interval. */ #define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) /* If this bit is set, (...) defines a group, and \( and \) are literals. If not set, \(...\) defines a group, and ( and ) are literals. */ #define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) /* If this bit is set, then \ matches . If not set, then \ is a back-reference. */ #define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) /* If this bit is set, then | is an alternation operator, and \| is literal. If not set, then \| is an alternation operator, and | is literal. */ #define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) /* If this bit is set, then an ending range point collating higher than the starting range point, as in [z-a], is invalid. If not set, then when ending range point collates higher than the starting range point, the range is ignored. */ #define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) /* If this bit is set, then an unmatched ) is ordinary. If not set, then an unmatched ) is invalid. */ #define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) /* This global variable defines the particular regexp syntax to use (for some interfaces). When a regexp is compiled, the syntax used is stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so don't delete them!) */ /* [[[begin syntaxes]]] */ #define RE_SYNTAX_EMACS 0 #define RE_SYNTAX_AWK \ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ | RE_UNMATCHED_RIGHT_PAREN_ORD) #define RE_SYNTAX_POSIX_AWK \ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) #define RE_SYNTAX_GREP \ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ | RE_NEWLINE_ALT) #define RE_SYNTAX_EGREP \ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ | RE_NO_BK_VBAR) #define RE_SYNTAX_POSIX_EGREP \ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC /* Syntax bits common to both basic and extended POSIX regex syntax. */ #define _RE_SYNTAX_POSIX_COMMON \ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ | RE_INTERVALS | RE_NO_EMPTY_RANGES) #define RE_SYNTAX_POSIX_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this isn't minimal, since other operators, such as \`, aren't disabled. */ #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) #define RE_SYNTAX_POSIX_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_UNMATCHED_RIGHT_PAREN_ORD) /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* [[[end syntaxes]]] */ /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ #ifdef RE_DUP_MAX #undef RE_DUP_MAX #endif #define RE_DUP_MAX ((1 << 15) - 1) /* POSIX `cflags' bits (i.e., information for `regcomp'). */ /* If this bit is set, then use extended regular expression syntax. If not set, then use basic regular expression syntax. */ #define REG_EXTENDED 1 /* If this bit is set, then ignore case when matching. If not set, then case is significant. */ #define REG_ICASE (REG_EXTENDED << 1) /* If this bit is set, then anchors do not match at newline characters in the string. If not set, then anchors do match at newlines. */ #define REG_NEWLINE (REG_ICASE << 1) /* If this bit is set, then report only success or fail in regexec. If not set, then returns differ between not matching and errors. */ #define REG_NOSUB (REG_NEWLINE << 1) /* POSIX `eflags' bits (i.e., information for regexec). */ /* If this bit is set, then the beginning-of-line operator doesn't match the beginning of the string (presumably because it's not the beginning of a line). If not set, then the beginning-of-line operator does match the beginning of the string. */ #define REG_NOTBOL 1 /* Like REG_NOTBOL, except for the end-of-line. */ #define REG_NOTEOL (1 << 1) /* If any error codes are removed, changed, or added, update the `re_error_msg' table in regex.c. */ typedef enum { REG_NOERROR = 0, /* Success. */ REG_NOMATCH, /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes. (In the order listed in the standard.) */ REG_BADPAT, /* Invalid pattern. */ REG_ECOLLATE, /* Not implemented. */ REG_ECTYPE, /* Invalid character class name. */ REG_EESCAPE, /* Trailing backslash. */ REG_ESUBREG, /* Invalid back reference. */ REG_EBRACK, /* Unmatched left bracket. */ REG_EPAREN, /* Parenthesis imbalance. */ REG_EBRACE, /* Unmatched \{. */ REG_BADBR, /* Invalid contents of \{\}. */ REG_ERANGE, /* Invalid range end. */ REG_ESPACE, /* Ran out of memory. */ REG_BADRPT, /* No preceding re for repetition op. */ /* Error codes we've added. */ REG_EEND, /* Premature end. */ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields `buffer', `allocated', `fastmap', `translate', and `no_sub' can be set. After the pattern has been compiled, the `re_nsub' field is available. All other fields are private to the regex routines. */ struct re_pattern_buffer { /* [[[begin pattern_buffer]]] */ /* Space that holds the compiled pattern. It is declared as `unsigned char *' because its elements are sometimes used as array indexes. */ unsigned char *buffer; /* Number of bytes to which `buffer' points. */ unsigned long allocated; /* Number of bytes actually used in `buffer'. */ unsigned long used; /* Syntax setting with which the pattern was compiled. */ reg_syntax_t syntax; /* Pointer to a fastmap, if any, otherwise zero. re_search uses the fastmap, if there is one, to skip over impossible starting points for matches. */ char *fastmap; /* Either a translate table to apply to all characters before comparing them, or zero for no translation. The translation is applied to a pattern when it is compiled and to a string when it is matched. */ char *translate; /* Number of subexpressions found by the compiler. */ size_t re_nsub; /* Zero if this pattern cannot match the empty string, one else. Well, in truth it's used only in `re_search_2', to see whether or not we should use the fastmap, so we don't set this absolutely perfectly; see `re_compile_fastmap' (the `duplicate' case). */ unsigned can_be_null : 1; /* If REGS_UNALLOCATED, allocate space in the `regs' structure for `max (RE_NREGS, re_nsub + 1)' groups. If REGS_REALLOCATE, reallocate space if necessary. If REGS_FIXED, use what's there. */ #define REGS_UNALLOCATED 0 #define REGS_REALLOCATE 1 #define REGS_FIXED 2 unsigned regs_allocated : 2; /* Set to zero when `regex_compile' compiles a pattern; set to one by `re_compile_fastmap' if it updates the fastmap. */ unsigned fastmap_accurate : 1; /* If set, `re_match_2' does not return information about subexpressions. */ unsigned no_sub : 1; /* If set, a beginning-of-line anchor doesn't match at the beginning of the string. */ unsigned not_bol : 1; /* Similarly for an end-of-line anchor. */ unsigned not_eol : 1; /* If true, an anchor at a newline matches. */ unsigned newline_anchor : 1; /* [[[end pattern_buffer]]] */ }; typedef struct re_pattern_buffer regex_t; /* search.c (search_buffer) in Emacs needs this one opcode value. It is defined both in `regex.c' and here. */ #define RE_EXACTN_VALUE 1 /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; /* This is the structure we store register match data in. See regex.texinfo for a full description of what registers match. */ struct re_registers { unsigned num_regs; regoff_t *start; regoff_t *end; }; /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, `re_match_2' returns information about at least this many registers the first time a `regs' structure is passed. */ #ifndef RE_NREGS #define RE_NREGS 30 #endif /* POSIX specification for registers. Aside from the different names than `re_registers', POSIX uses an array of structures, instead of a structure of arrays. */ typedef struct { regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; /* Declarations for routines. */ /* To avoid duplicating every routine declaration -- once with a prototype (if we are ANSI), and once without (if we aren't) -- we use the following macro to declare argument types. This unfortunately clutters up the declarations a bit, but I think it's worth it. */ #if __STDC__ #define _RE_ARGS(args) args #else /* not __STDC__ */ #define _RE_ARGS(args) () #endif /* not __STDC__ */ /* Sets the current default syntax to SYNTAX, and return the old syntax. You can also simply assign to the `re_syntax_options' variable. */ extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); /* Compile the regular expression PATTERN, with length LENGTH and syntax given by the global `re_syntax_options', into the buffer BUFFER. Return NULL if successful, and an error string if not. */ extern const char *re_compile_pattern _RE_ARGS ((const char *pattern, int length, struct re_pattern_buffer *buffer)); /* Compile a fastmap for the compiled pattern in BUFFER; used to accelerate searches. Return 0 if successful and -2 if was an internal error. */ extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); /* Search in the string STRING (with length LENGTH) for the pattern compiled into BUFFER. Start searching at position START, for RANGE characters. Return the starting position of the match, -1 for no match, or -2 for an internal error. Also return register information in REGS (if REGS and BUFFER->no_sub are nonzero). */ extern int re_search _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, int length, int start, int range, struct re_registers *regs)); /* Like `re_search', but search in the concatenation of STRING1 and STRING2. Also, stop searching at index START + STOP. */ extern int re_search_2 _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop)); /* Like `re_search', but return how many characters in STRING the regexp in BUFFER matched, starting at position START. */ extern int re_match _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, int length, int start, struct re_registers *regs)); /* Relates to `re_match' as `re_search_2' relates to `re_search'. */ extern int re_match_2 _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, struct re_registers *regs, int stop)); /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated with malloc, and must each be at least `NUM_REGS * sizeof (regoff_t)' bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ extern void re_set_registers _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends)); /* 4.2 bsd compatibility. */ extern char *re_comp _RE_ARGS ((const char *)); extern int re_exec _RE_ARGS ((const char *)); /* POSIX compatibility. */ extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); extern int regexec _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)); extern size_t regerror _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)); extern void regfree _RE_ARGS ((regex_t *preg)); #endif /* not __REGEXP_LIBRARY_H__ */ /* Local variables: make-backup-files: t version-control: t trim-versions-without-asking: nil End: */ elog-3.1.3-1/src/crypt.c0000644000175000017500000004450412556103751013730 0ustar rogerroger/* SHA256-based Unix crypt implementation. Released into the Public Domain by Ulrich Drepper . Adapted for MS Windows by Stefan Ritt Copyrights Ulrich Drepper Copyright 2000 + Stefan Ritt ELOG 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. ELOG 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 ELOG. If not, see . $Id$ */ #ifdef _MSC_VER #include #include #include #include #define __alignof__(x) sizeof(x) #define ERANGE 34 typedef unsigned int uint32_t; #pragma warning(disable:4996) #else #include #include #include #include #include #include #include #include #include #endif #ifndef MAX #define MAX(x,y) ((x)>(y)?(x):(y)) #define MIN(x,y) ((x)<(y)?(x):(y)) #endif /* Structure to save state of computation between the single steps. */ struct sha256_ctx { uint32_t H[8]; uint32_t total[2]; uint32_t buflen; char buffer[128]; /* NB: always correctly aligned for uint32_t. */ }; #if __BYTE_ORDER == __LITTLE_ENDIAN #define SWAP(n) \ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) #else #define SWAP(n) (n) #endif /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (FIPS 180-2:5.1.1) */ static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* Constants for SHA256 from FIPS 180-2:4.2.2. */ static const uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) { const uint32_t *words = buffer; size_t nwords = len / sizeof(uint32_t); uint32_t a = ctx->H[0]; uint32_t b = ctx->H[1]; uint32_t c = ctx->H[2]; uint32_t d = ctx->H[3]; uint32_t e = ctx->H[4]; uint32_t f = ctx->H[5]; uint32_t g = ctx->H[6]; uint32_t h = ctx->H[7]; /* First increment the byte count. FIPS 180-2 specifies the possible length of the file up to 2^64 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] += len; if (ctx->total[0] < (int) len) ++ctx->total[1]; /* Process all bytes in the buffer with 64 bytes in each round of the loop. */ while (nwords > 0) { uint32_t W[64]; unsigned int t; uint32_t a_save = a; uint32_t b_save = b; uint32_t c_save = c; uint32_t d_save = d; uint32_t e_save = e; uint32_t f_save = f; uint32_t g_save = g; uint32_t h_save = h; /* Operators defined in FIPS 180-2:4.1.2. */ #define Ch(x, y, z) ((x & y) ^ (~x & z)) #define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22)) #define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25)) #define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3)) #define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) /* It is unfortunate that C does not provide an operator for cyclic rotation. Hope the C compiler is smart enough. */ #define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ for (t = 0; t < 16; ++t) { W[t] = SWAP(*words); ++words; } for (t = 16; t < 64; ++t) W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16]; /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ for (t = 0; t < 64; ++t) { uint32_t T1 = h + S1(e) + Ch(e, f, g) + K[t] + W[t]; uint32_t T2 = S0(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } /* Add the starting values of the context according to FIPS 180-2:6.2.2 step 4. */ a += a_save; b += b_save; c += c_save; d += d_save; e += e_save; f += f_save; g += g_save; h += h_save; /* Prepare for the next round. */ nwords -= 16; } /* Put checksum in context given as argument. */ ctx->H[0] = a; ctx->H[1] = b; ctx->H[2] = c; ctx->H[3] = d; ctx->H[4] = e; ctx->H[5] = f; ctx->H[6] = g; ctx->H[7] = h; } /* Initialize structure containing state of computation. (FIPS 180-2:5.3.2) */ static void sha256_init_ctx(struct sha256_ctx *ctx) { ctx->H[0] = 0x6a09e667; ctx->H[1] = 0xbb67ae85; ctx->H[2] = 0x3c6ef372; ctx->H[3] = 0xa54ff53a; ctx->H[4] = 0x510e527f; ctx->H[5] = 0x9b05688c; ctx->H[6] = 0x1f83d9ab; ctx->H[7] = 0x5be0cd19; ctx->total[0] = ctx->total[1] = 0; ctx->buflen = 0; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ static void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ uint32_t bytes = ctx->buflen; size_t pad; unsigned int i; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; memcpy(&ctx->buffer[bytes], fillbuf, pad); /* Put the 64-bit file length in *bits* at the end of the buffer. */ *(uint32_t *) & ctx->buffer[bytes + pad + 4] = SWAP(ctx->total[0] << 3); *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); /* Process last bytes. */ sha256_process_block(ctx->buffer, bytes + pad + 8, ctx); /* Put result from CTX in first 32 bytes following RESBUF. */ for (i = 0; i < 8; ++i) ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); return resbuf; } static void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; memcpy(&ctx->buffer[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 64) { sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); ctx->buflen &= 63; /* The regions in the following copy operation cannot overlap. */ memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen); } buffer = (const char *) buffer + add; len -= add; } /* Process available complete blocks. */ if (len >= 64) { /* To check alignment gcc has an appropriate operator. Other compilers don't. */ #if __GNUC__ >= 2 #define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0) #else #define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0) #endif if (UNALIGNED_P(buffer)) while (len > 64) { sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); buffer = (const char *) buffer + 64; len -= 64; } else { sha256_process_block(buffer, len & ~63, ctx); buffer = (const char *) buffer + (len & ~63); len &= 63; } } /* Move remaining bytes into internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy(&ctx->buffer[left_over], buffer, len); left_over += len; if (left_over >= 64) { sha256_process_block(ctx->buffer, 64, ctx); left_over -= 64; memcpy(ctx->buffer, &ctx->buffer[64], left_over); } ctx->buflen = left_over; } } /* Define our magic string to mark salt for SHA256 "encryption" replacement. */ static const char sha256_salt_prefix[] = "$5$"; /* Prefix for optional rounds specification. */ static const char sha256_rounds_prefix[] = "rounds="; /* Maximum salt string length. */ #define SALT_LEN_MAX 16 /* Default number of rounds if not explicitly specified. */ #define ROUNDS_DEFAULT 5000 /* Minimum number of rounds. */ #define ROUNDS_MIN 1000 /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 /* Table with characters for base64 transformation. */ static const char b64t[64] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static char *sha256_crypt_r(const char *key, const char *salt, char *buffer, int buflen) { #ifdef _MSC_VER unsigned char alt_result[32]; unsigned char temp_result[32]; #else unsigned char alt_result[32] __attribute__ ((__aligned__(__alignof__(uint32_t)))); unsigned char temp_result[32] __attribute__ ((__aligned__(__alignof__(uint32_t)))); #endif struct sha256_ctx ctx; struct sha256_ctx alt_ctx; size_t salt_len; size_t key_len; size_t cnt; char *cp; char *copied_key = NULL; char *copied_salt = NULL; char *p_bytes; char *s_bytes; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; int rounds_custom = 0; /* Find beginning of salt string. The prefix should normally always be present. Just in case it is not. */ if (strncmp(sha256_salt_prefix, salt, sizeof(sha256_salt_prefix) - 1) == 0) /* Skip salt prefix. */ salt += sizeof(sha256_salt_prefix) - 1; if (strncmp(salt, sha256_rounds_prefix, sizeof(sha256_rounds_prefix) - 1) == 0) { const char *num = salt + sizeof(sha256_rounds_prefix) - 1; char *endp; unsigned long int srounds = strtoul(num, &endp, 10); if (*endp == '$') { salt = endp + 1; rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX)); rounds_custom = 1; } } salt_len = MIN(strcspn(salt, "$"), SALT_LEN_MAX); key_len = strlen(key); if ((key - (char *) 0) % __alignof__(uint32_t) != 0) { char *tmp = (char *) alloca(key_len + __alignof__(uint32_t)); key = copied_key = memcpy(tmp + __alignof__(uint32_t) - (tmp - (char *) 0) % __alignof__(uint32_t), key, key_len); } if ((salt - (char *) 0) % __alignof__(uint32_t) != 0) { char *tmp = (char *) alloca(salt_len + __alignof__(uint32_t)); salt = copied_salt = memcpy(tmp + __alignof__(uint32_t) - (tmp - (char *) 0) % __alignof__(uint32_t), salt, salt_len); } /* Prepare for the real work. */ sha256_init_ctx(&ctx); /* Add the key string. */ sha256_process_bytes(key, key_len, &ctx); /* The last part is the salt string. This must be at most 16 characters and it ends at the first `$' character (for compatibility with existing implementations). */ sha256_process_bytes(salt, salt_len, &ctx); /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha256_init_ctx(&alt_ctx); /* Add key. */ sha256_process_bytes(key, key_len, &alt_ctx); /* Add salt. */ sha256_process_bytes(salt, salt_len, &alt_ctx); /* Add key again. */ sha256_process_bytes(key, key_len, &alt_ctx); /* Now get result of this (32 bytes) and add it to the other context. */ sha256_finish_ctx(&alt_ctx, alt_result); /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > 32; cnt -= 32) sha256_process_bytes(alt_result, 32, &ctx); sha256_process_bytes(alt_result, cnt, &ctx); /* Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0 the key. */ for (cnt = key_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) sha256_process_bytes(alt_result, 32, &ctx); else sha256_process_bytes(key, key_len, &ctx); /* Create intermediate result. */ sha256_finish_ctx(&ctx, alt_result); /* Start computation of P byte sequence. */ sha256_init_ctx(&alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; cnt < key_len; ++cnt) sha256_process_bytes(key, key_len, &alt_ctx); /* Finish the digest. */ sha256_finish_ctx(&alt_ctx, temp_result); /* Create byte sequence P. */ cp = p_bytes = alloca(key_len); for (cnt = key_len; cnt >= 32; cnt -= 32) { memcpy(cp, temp_result, 32); cp += 32; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ sha256_init_ctx(&alt_ctx); /* For every character in the password add the entire password. */ for (cnt = 0; (int) cnt < 16 + alt_result[0]; ++cnt) sha256_process_bytes(salt, salt_len, &alt_ctx); /* Finish the digest. */ sha256_finish_ctx(&alt_ctx, temp_result); /* Create byte sequence S. */ cp = s_bytes = alloca(salt_len); for (cnt = salt_len; cnt >= 32; cnt -= 32) { memcpy(cp, temp_result, 32); cp += 32; } memcpy(cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA256 to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { /* New context. */ sha256_init_ctx(&ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha256_process_bytes(p_bytes, key_len, &ctx); else sha256_process_bytes(alt_result, 32, &ctx); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha256_process_bytes(s_bytes, salt_len, &ctx); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha256_process_bytes(p_bytes, key_len, &ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha256_process_bytes(alt_result, 32, &ctx); else sha256_process_bytes(p_bytes, key_len, &ctx); /* Create intermediate result. */ sha256_finish_ctx(&ctx, alt_result); } /* Now we can construct the result string. It consists of three parts. */ strncpy(buffer, sha256_salt_prefix, MAX(0, buflen)); cp = buffer + strlen(buffer); buflen -= sizeof(sha256_salt_prefix) - 1; if (rounds_custom) { #ifdef _MSC_VER int n = _snprintf(cp, MAX(0, buflen), "%s%Iu$", sha256_rounds_prefix, rounds); #else int n = snprintf(cp, MAX(0, buflen), "%s%zu$", sha256_rounds_prefix, rounds); #endif cp += n; buflen -= n; } strncpy(cp, salt, MIN((size_t) MAX(0, buflen), salt_len)); cp = cp + strlen(cp); buflen -= MIN((size_t) MAX(0, buflen), salt_len); if (buflen > 0) { *cp++ = '$'; --buflen; } #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ int n = (N); \ while (n-- > 0 && buflen > 0) \ { \ *cp++ = b64t[w & 0x3f]; \ --buflen; \ w >>= 6; \ } \ } while (0) b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4); b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4); b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4); b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4); b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4); b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4); b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4); b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4); b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4); b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4); b64_from_24bit(0, alt_result[31], alt_result[30], 3); if (buflen <= 0) { errno = ERANGE; buffer = NULL; } else *cp = '\0'; /* Terminate the string. */ /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. We do it in this way to clear correct_words[] inside the SHA256 implementation as well. */ sha256_init_ctx(&ctx); sha256_finish_ctx(&ctx, alt_result); memset(temp_result, '\0', sizeof(temp_result)); memset(p_bytes, '\0', key_len); memset(s_bytes, '\0', salt_len); memset(&ctx, '\0', sizeof(ctx)); memset(&alt_ctx, '\0', sizeof(alt_ctx)); if (copied_key != NULL) memset(copied_key, '\0', key_len); if (copied_salt != NULL) memset(copied_salt, '\0', salt_len); return buffer; } /* This entry point is equivalent to the `crypt' function in Unix libcs. */ char *sha256_crypt(const char *key, const char *salt) { /* We don't want to have an arbitrary limit in the size of the password. We can compute an upper bound for the size of the result in advance and so we can prepare the buffer we pass to `sha256_crypt_r'. */ static char *buffer; static int buflen; int needed = (sizeof(sha256_salt_prefix) - 1 + sizeof(sha256_rounds_prefix) + 9 + 1 + strlen(salt) + 1 + 43 + 1); if (buflen < needed) { char *new_buffer = (char *) realloc(buffer, needed); if (new_buffer == NULL) return NULL; buffer = new_buffer; buflen = needed; } return sha256_crypt_r(key, salt, buffer, buflen); } elog-3.1.3-1/src/mxml.c0000644000175000017500000020256413020302451013527 0ustar rogerroger/********************************************************************\ Name: mxml.c Created by: Stefan Ritt Copyright 2000 + Stefan Ritt Contents: Midas XML Library This is a simple implementation of XML functions for writing and reading XML files. For writing an XML file from scratch, following functions can be used: writer = mxml_open_file(file_name); mxml_start_element(writer, name); mxml_write_attribute(writer, name, value); mxml_write_value(writer, value); mxml_end_element(writer); ... mxml_close_file(writer); To read an XML file, the function tree = mxml_parse_file(file_name, error, sizeof(error)); is used. It parses the complete XML file and stores it in a hierarchical tree in memory. Nodes in that tree can be searched for with mxml_find_node(tree, xml_path); or mxml_find_nodes(tree, xml_path, &nodelist); which support a subset of the XPath specification. Another set of functions is availabe to retrieve attributes and values from nodes in the tree and for manipulating nodes, like replacing, adding and deleting nodes. This file is part of MIDAS XML Library. MIDAS XML Library 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. MIDAS XML Library 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 MIDAS XML Library. If not, see . \********************************************************************/ #include #include #include #include #ifdef _MSC_VER #include #include #include #pragma warning( disable: 4996) /* disable "deprecated" warning */ #else #define TRUE 1 #define FALSE 0 #ifndef O_TEXT #define O_TEXT 0 #define O_BINARY 0 #endif #include #include #include #include #include #ifndef OS_VXWORKS #include #endif #include #endif #include "mxml.h" #ifndef HAVE_STRLCPY #include "strlcpy.h" #endif #define XML_INDENT " " #if defined(__GNUC__) && !defined(__MAKECINT__) # define MXML_GNUC_PRINTF( format_idx, arg_idx ) \ __attribute__((format (printf, format_idx, arg_idx))) # define MXML_GNUC_SCANF( format_idx, arg_idx ) \ __attribute__((format (scanf, format_idx, arg_idx))) # define MXML_GNUC_FORMAT( arg_idx ) \ __attribute__((format_arg (arg_idx))) #else # define MXML_GNUC_PRINTF( format_idx, arg_idx ) # define MXML_GNUC_SCANF( format_idx, arg_idx ) # define MXML_GNUC_FORMAT( arg_idx ) #endif static int mxml_suppress_date_flag = 0; /* suppress writing date at the top of file. */ /* local prototypes */ static PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...) MXML_GNUC_PRINTF(7, 8); static void mxml_encode(char *src, int size, int translate); static void mxml_decode(char *str); static int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent); static int mxml_write_line(MXML_WRITER *writer, const char *line); static int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent); static int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found); static int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found); static void *mxml_malloc(size_t size); static void *mxml_realloc(void *p, size_t size); static void mxml_free(void *p); static void mxml_deallocate(void); /*------------------------------------------------------------------*/ static char *_encode_buffer = NULL; static char *_data_enc = NULL; /*------------------------------------------------------------------*/ void *mxml_malloc(size_t size) { return malloc(size); } /*------------------------------------------------------------------*/ void *mxml_realloc(void *p, size_t size) { return realloc(p, size); } /*------------------------------------------------------------------*/ void mxml_free(void *p) { free(p); } /*------------------------------------------------------------------*/ void mxml_deallocate(void) { if (_encode_buffer != NULL) { mxml_free(_encode_buffer); _encode_buffer = NULL; } if (_data_enc != NULL) { mxml_free(_data_enc); _data_enc = NULL; } } /*------------------------------------------------------------------*/ int mxml_write_line(MXML_WRITER *writer, const char *line) { int len; len = (int)strlen(line); if (writer->buffer) { if (writer->buffer_len + len >= writer->buffer_size) { writer->buffer_size += 10000; writer->buffer = (char *)mxml_realloc(writer->buffer, writer->buffer_size); } strcpy(writer->buffer + writer->buffer_len, line); writer->buffer_len += len; return len; } else { return (int)write(writer->fh, line, len); } return 0; } /*------------------------------------------------------------------*/ /** * open a memory buffer and write XML header */ MXML_WRITER *mxml_open_buffer(void) { char str[256], line[1000]; time_t now; MXML_WRITER *writer; writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER)); memset(writer, 0, sizeof(MXML_WRITER)); writer->translate = 1; writer->buffer_size = 10000; writer->buffer = (char *)mxml_malloc(10000); writer->buffer[0] = 0; writer->buffer_len = 0; /* write XML header */ strcpy(line, "\n"); mxml_write_line(writer, line); time(&now); strcpy(str, ctime(&now)); str[24] = 0; sprintf(line, "\n", str); if (mxml_suppress_date_flag == 0) mxml_write_line(writer, line); /* initialize stack */ writer->level = 0; writer->element_is_open = 0; return writer; } /*------------------------------------------------------------------*/ /** * suppress writing date at the top of file. */ void mxml_suppress_date(int suppress) { mxml_suppress_date_flag = suppress; } /*------------------------------------------------------------------*/ /** * open a file and write XML header */ MXML_WRITER *mxml_open_file(const char *file_name) { char str[256], line[1000]; time_t now; MXML_WRITER *writer; writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER)); memset(writer, 0, sizeof(MXML_WRITER)); writer->translate = 1; writer->fh = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT, 0644); if (writer->fh == -1) { sprintf(line, "Unable to open file \"%s\": ", file_name); perror(line); mxml_free(writer); return NULL; } /* write XML header */ strcpy(line, "\n"); mxml_write_line(writer, line); time(&now); strcpy(str, ctime(&now)); str[24] = 0; sprintf(line, "\n", str); if (mxml_suppress_date_flag == 0) mxml_write_line(writer, line); /* initialize stack */ writer->level = 0; writer->element_is_open = 0; return writer; } /*------------------------------------------------------------------*/ /** * convert '<' '>' '&' '"' ''' into &xx; */ void mxml_encode(char *src, int size, int translate) { char *ps, *pd; static int buffer_size = 1000; assert(size); if (_encode_buffer == NULL) { _encode_buffer = (char *) mxml_malloc(buffer_size); atexit(mxml_deallocate); } if (size > buffer_size) { _encode_buffer = (char *) mxml_realloc(_encode_buffer, size*2); buffer_size = size; } pd = _encode_buffer; for (ps = src ; *ps && (size_t)pd - (size_t)_encode_buffer < (size_t)(size-10) ; ps++) { if (translate) { /* tranlate "<", ">", "&", """, "'" */ switch (*ps) { case '<': strcpy(pd, "<"); pd += 4; break; case '>': strcpy(pd, ">"); pd += 4; break; case '&': strcpy(pd, "&"); pd += 5; break; case '\"': strcpy(pd, """); pd += 6; break; case '\'': strcpy(pd, "'"); pd += 6; break; default: *pd++ = *ps; } } else { switch (*ps) { /* translate only illegal XML characters "<" and "&" */ case '<': strcpy(pd, "<"); pd += 4; break; case '&': strcpy(pd, "&"); pd += 5; break; default: *pd++ = *ps; } } } *pd = 0; strlcpy(src, _encode_buffer, size); } /*------------------------------------------------------------------*/ /** * reverse of mxml_encode, strip leading or trailing '"' */ void mxml_decode(char *str) { char *p; p = str; while ((p = strchr(p, '&')) != NULL) { if (strncmp(p, "<", 4) == 0) { *(p++) = '<'; memmove(p, p+3, strlen(p+3) + 1); } else if (strncmp(p, ">", 4) == 0) { *(p++) = '>'; memmove(p, p+3, strlen(p+3) + 1); } else if (strncmp(p, "&", 5) == 0) { *(p++) = '&'; memmove(p, p+4, strlen(p+4) + 1); } else if (strncmp(p, """, 6) == 0) { *(p++) = '\"'; memmove(p, p+5, strlen(p+5) + 1); } else if (strncmp(p, "'", 6) == 0) { *(p++) = '\''; memmove(p, p+5, strlen(p+5) + 1); } else { p++; // skip unknown entity } } /* if (str[0] == '\"' && str[strlen(str)-1] == '\"') { memmove(str, str+1, strlen(str+1) + 1); str[strlen(str)-1] = 0; }*/ } /*------------------------------------------------------------------*/ /** * set translation of <,>,",',&, on/off in writer */ int mxml_set_translate(MXML_WRITER *writer, int flag) { int old_flag; old_flag = writer->translate; writer->translate = flag; return old_flag; } /*------------------------------------------------------------------*/ /** * start a new XML element, must be followed by mxml_end_elemnt */ int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent) { int i; char line[1000], name_enc[1000]; if (writer->element_is_open) { mxml_write_line(writer, ">\n"); writer->element_is_open = FALSE; } line[0] = 0; if (indent) for (i=0 ; ilevel ; i++) strlcat(line, XML_INDENT, sizeof(line)); strlcat(line, "<", sizeof(line)); strlcpy(name_enc, name, sizeof(name_enc)); mxml_encode(name_enc, sizeof(name_enc), writer->translate); strlcat(line, name_enc, sizeof(line)); /* put element on stack */ if (writer->level == 0) writer->stack = (char **)mxml_malloc(sizeof(char *)); else writer->stack = (char **)mxml_realloc(writer->stack, sizeof(char *)*(writer->level+1)); writer->stack[writer->level] = (char *) mxml_malloc(strlen(name_enc)+1); strcpy(writer->stack[writer->level], name_enc); writer->level++; writer->element_is_open = TRUE; writer->data_was_written = FALSE; return mxml_write_line(writer, line) == (int)strlen(line); } /*------------------------------------------------------------------*/ int mxml_start_element(MXML_WRITER *writer, const char *name) { return mxml_start_element1(writer, name, TRUE); } /*------------------------------------------------------------------*/ int mxml_start_element_noindent(MXML_WRITER *writer, const char *name) { return mxml_start_element1(writer, name, FALSE); } /*------------------------------------------------------------------*/ /** * close an open XML element */ int mxml_end_element(MXML_WRITER *writer) { int i; char line[1000]; if (writer->level == 0) return 0; writer->level--; if (writer->element_is_open) { writer->element_is_open = FALSE; mxml_free(writer->stack[writer->level]); if (writer->level == 0) mxml_free(writer->stack); strcpy(line, "/>\n"); return mxml_write_line(writer, line) == (int)strlen(line); } line[0] = 0; if (!writer->data_was_written) { for (i=0 ; ilevel ; i++) strlcat(line, XML_INDENT, sizeof(line)); } strlcat(line, "stack[writer->level], sizeof(line)); mxml_free(writer->stack[writer->level]); if (writer->level == 0) mxml_free(writer->stack); strlcat(line, ">\n", sizeof(line)); writer->data_was_written = FALSE; return mxml_write_line(writer, line) == (int)strlen(line); } /*------------------------------------------------------------------*/ /** * write an attribute to the currently open XML element */ int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value) { char name_enc[4096], val_enc[4096], line[8192]; if (!writer->element_is_open) return FALSE; strcpy(name_enc, name); mxml_encode(name_enc, sizeof(name_enc), writer->translate); strcpy(val_enc, value); mxml_encode(val_enc, sizeof(val_enc), writer->translate); sprintf(line, " %s=\"%s\"", name_enc, val_enc); return mxml_write_line(writer, line) == (int)strlen(line); } /*------------------------------------------------------------------*/ /** * write value of an XML element, like <[name]>[value] */ int mxml_write_value(MXML_WRITER *writer, const char *data) { static int data_size = 0; if (!writer->element_is_open) return FALSE; if (mxml_write_line(writer, ">") != 1) return FALSE; writer->element_is_open = FALSE; writer->data_was_written = TRUE; if (data_size == 0) { _data_enc = (char *)mxml_malloc(1000); data_size = 1000; } else if ((int)strlen(data)*2+1000 > data_size) { data_size = 1000+(int)strlen(data)*2; _data_enc = (char *)mxml_realloc(_data_enc, data_size); } strcpy(_data_enc, data); mxml_encode(_data_enc, data_size, writer->translate); return mxml_write_line(writer, _data_enc) == (int)strlen(_data_enc); } /*------------------------------------------------------------------*/ /** * write empty line */ int mxml_write_empty_line(MXML_WRITER *writer) { if (writer->element_is_open) { mxml_write_line(writer, ">\n"); writer->element_is_open = FALSE; } if (mxml_write_line(writer, "\n") != 1) return FALSE; return TRUE; } /*------------------------------------------------------------------*/ /** * write a comment to an XML file, enclosed in "" */ int mxml_write_comment(MXML_WRITER *writer, const char *string) { int i; char line[1000]; if (writer->element_is_open) { mxml_write_line(writer, ">\n"); writer->element_is_open = FALSE; } line[0] = 0; for (i=0 ; ilevel ; i++) strlcat(line, XML_INDENT, sizeof(line)); strlcat(line, "\n", sizeof(line)); if (mxml_write_line(writer, line) != (int)strlen(line)) return FALSE; return TRUE; } /*------------------------------------------------------------------*/ /** * shortcut to write an element with a value but without attribute */ int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value) { int i; i = mxml_start_element(writer, name); i += mxml_write_value(writer, value); i += mxml_end_element(writer); return i; } /*------------------------------------------------------------------*/ /** * close a file opened with mxml_open_writer */ char *mxml_close_buffer(MXML_WRITER *writer) { int i; char *p; if (writer->element_is_open) { writer->element_is_open = FALSE; if (mxml_write_line(writer, ">\n") != 2) return NULL; } /* close remaining open levels */ for (i = 0 ; ilevel ; i++) mxml_end_element(writer); p = writer->buffer; mxml_free(writer); return p; } /*------------------------------------------------------------------*/ /** * close a file opened with mxml_open_writer */ int mxml_close_file(MXML_WRITER *writer) { int i; if (writer->element_is_open) { writer->element_is_open = FALSE; if (mxml_write_line(writer, ">\n") != 2) return 0; } /* close remaining open levels */ for (i = 0 ; ilevel ; i++) mxml_end_element(writer); close(writer->fh); mxml_free(writer); return 1; } /*------------------------------------------------------------------*/ /** * create root node of an XML tree */ PMXML_NODE mxml_create_root_node(void) { PMXML_NODE root; root = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1); strcpy(root->name, "root"); root->node_type = DOCUMENT_NODE; return root; } /*------------------------------------------------------------------*/ /** * add a subnode (child) to an existing parent node as a specific position */ PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx) { PMXML_NODE pnode, pchild; int i, j; assert(parent); if (parent->n_children == 0) parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE)); else parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1)); assert(parent->child); /* move following nodes one down */ if (idx < parent->n_children) for (i=parent->n_children ; i > idx ; i--) memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE)); /* correct parent pointer for children */ for (i=0 ; in_children ; i++) { pchild = parent->child+i; for (j=0 ; jn_children ; j++) pchild->child[j].parent = pchild; } /* initialize new node */ pnode = &parent->child[idx]; memset(pnode, 0, sizeof(MXML_NODE)); strlcpy(pnode->name, node_name, sizeof(pnode->name)); pnode->node_type = node_type; pnode->parent = parent; parent->n_children++; if (value && *value) { pnode->value = (char *)mxml_malloc(strlen(value)+1); assert(pnode->value); strcpy(pnode->value, value); } return pnode; } /*------------------------------------------------------------------*/ /** * add a subnode (child) to an existing parent node at the end */ PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value) { return mxml_add_special_node_at(parent, node_type, node_name, value, parent->n_children); } /*------------------------------------------------------------------*/ /** * write value of an XML element, like <[name]>[value] */ PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value) { return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, parent->n_children); } /*------------------------------------------------------------------*/ /** * add a subnode (child) to an existing parent node at the end */ PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx) { return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, idx); } /*------------------------------------------------------------------*/ /** * add a whole node tree to an existing parent node at a specific position */ int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx) { PMXML_NODE pchild; int i, j, k; assert(parent); assert(tree); if (parent->n_children == 0) parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE)); else { pchild = parent->child; parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1)); if (parent->child != pchild) { /* correct parent pointer for children */ for (i=0 ; in_children ; i++) { pchild = parent->child+i; for (j=0 ; jn_children ; j++) pchild->child[j].parent = pchild; } } } assert(parent->child); if (idx < parent->n_children) for (i=parent->n_children ; i > idx ; i--) { /* move following nodes one down */ memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE)); /* correct parent pointer for children */ for (j=0 ; jn_children ; j++) { pchild = parent->child+j; for (k=0 ; kn_children ; k++) pchild->child[k].parent = pchild; } } /* initialize new node */ memcpy(parent->child+idx, tree, sizeof(MXML_NODE)); parent->n_children++; parent->child[idx].parent = parent; /* correct parent pointer for children */ for (i=0 ; in_children ; i++) { pchild = parent->child+i; for (j=0 ; jn_children ; j++) pchild->child[j].parent = pchild; } return TRUE; } /*------------------------------------------------------------------*/ /** * add a whole node tree to an existing parent node at the end */ int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree) { return mxml_add_tree_at(parent, tree, parent->n_children); } /*------------------------------------------------------------------*/ /** * add an attribute to an existing node */ int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value) { if (pnode->n_attributes == 0) { pnode->attribute_name = (char*)mxml_malloc(MXML_NAME_LENGTH); pnode->attribute_value = (char**)mxml_malloc(sizeof(char *)); } else { pnode->attribute_name = (char*)mxml_realloc(pnode->attribute_name, MXML_NAME_LENGTH*(pnode->n_attributes+1)); pnode->attribute_value = (char**)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes+1)); } strlcpy(pnode->attribute_name+pnode->n_attributes*MXML_NAME_LENGTH, attrib_name, MXML_NAME_LENGTH); pnode->attribute_value[pnode->n_attributes] = (char *)mxml_malloc(strlen(attrib_value)+1); strcpy(pnode->attribute_value[pnode->n_attributes], attrib_value); pnode->n_attributes++; return TRUE; } /*------------------------------------------------------------------*/ /** * return number of subnodes (children) of a node */ int mxml_get_number_of_children(PMXML_NODE pnode) { assert(pnode); return pnode->n_children; } /*------------------------------------------------------------------*/ /** * return number of subnodes (children) of a node */ PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx) { assert(pnode); if (idx < pnode->n_children) return &pnode->child[idx]; return NULL; } /*------------------------------------------------------------------*/ int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found); int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found) { /* if at end of path, add this node */ if (*xml_path == 0) { if (*found == 0) *nodelist = (PMXML_NODE *)mxml_malloc(sizeof(PMXML_NODE)); else *nodelist = (PMXML_NODE *)mxml_realloc(*nodelist, sizeof(PMXML_NODE)*(*found + 1)); (*nodelist)[*found] = node; (*found)++; } else { /* if not at end of path, branch into subtree */ return mxml_find_nodes1(node, xml_path+1, nodelist, found); } return 1; } /*------------------------------------------------------------------*/ /** Return list of XML nodes with a subset of XPATH specifications. Following elemets are possible ///..../ Find a node in the tree hierarchy /[idx] Find child #[idx] of node (index starts from 1) /[idx]/ Find subnode of the above /[=] Find a node which has a specific subnode /[=]/ Find subnode of the above /[@=]/ Find a node which has a specific attribute */ int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found) { PMXML_NODE pnode; const char *p1,*p2; char *p3, node_name[256], condition[256]; char cond_name[MXML_MAX_CONDITION][256], cond_value[MXML_MAX_CONDITION][256]; int cond_type[MXML_MAX_CONDITION]; int i, j, k, idx, num_cond; int cond_satisfied,cond_index; size_t len; p1 = xml_path; pnode = tree; /* skip leading '/' */ if (*p1 && *p1 == '/') p1++; do { p2 = p1; while (*p2 && *p2 != '/' && *p2 != '[') p2++; len = (size_t)p2 - (size_t)p1; if (len >= sizeof(node_name)) return 0; memcpy(node_name, p1, len); node_name[len] = 0; idx = 0; num_cond = 0; while (*p2 == '[') { cond_name[num_cond][0] = cond_value[num_cond][0] = cond_type[num_cond] = 0; p2++; if (isdigit(*p2)) { /* evaluate [idx] */ idx = atoi(p2); p2 = strchr(p2, ']'); if (p2 == NULL) return 0; p2++; } else { /* evaluate [<@attrib>/=] */ while (*p2 && isspace((unsigned char)*p2)) p2++; strlcpy(condition, p2, sizeof(condition)); if (strchr(condition, ']')) *strchr(condition, ']') = 0; else return 0; p2 = strchr(p2, ']')+1; if ((p3 = strchr(condition, '=')) != NULL) { if (condition[0] == '@') { cond_type[num_cond] = 1; strlcpy(cond_name[num_cond], &condition[1], sizeof(cond_name[num_cond])); } else { strlcpy(cond_name[num_cond], condition, sizeof(cond_name[num_cond])); } *strchr(cond_name[num_cond], '=') = 0; while (cond_name[num_cond][0] && isspace(cond_name[num_cond][strlen(cond_name[num_cond])-1])) cond_name[num_cond][strlen(cond_name[num_cond])-1] = 0; p3++; while (*p3 && isspace(*p3)) p3++; if (*p3 == '\"') { strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond])); while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1])) cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0; if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\"') cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0; } else if (*p3 == '\'') { strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond])); while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1])) cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0; if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\'') cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0; } else { strlcpy(cond_value[num_cond], p3, sizeof(cond_value[num_cond])); while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1])) cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0; } num_cond++; } } } cond_index = 0; for (i=j=0 ; in_children ; i++) { if (num_cond) { cond_satisfied = 0; for (k=0;kchild[i].name, node_name) == 0) if (mxml_get_attribute(pnode->child+i, cond_name[k]) && strcmp(mxml_get_attribute(pnode->child+i, cond_name[k]), cond_value[k]) == 0) cond_satisfied++; } else { /* search subnode */ for (j=0 ; jchild[i].n_children ; j++) if (strcmp(pnode->child[i].child[j].name, cond_name[k]) == 0) if (strcmp(pnode->child[i].child[j].value, cond_value[k]) == 0) cond_satisfied++; } } if (cond_satisfied==num_cond) { cond_index++; if (idx == 0 || cond_index == idx) { if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found)) return 0; } } } else { if (strcmp(pnode->child[i].name, node_name) == 0) if (idx == 0 || ++j == idx) if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found)) return 0; } } if (i == pnode->n_children) return 1; pnode = &pnode->child[i]; p1 = p2; if (*p1 == '/') p1++; } while (*p2); return 1; } /*------------------------------------------------------------------*/ int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist) { int status, found = 0; status = mxml_find_nodes1(tree, xml_path, nodelist, &found); if (status == 0) return -1; return found; } /*------------------------------------------------------------------*/ /** * Search for a specific XML node with a subset of XPATH specifications. * Return first found node. For syntax see mxml_find_nodes() */ PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path) { PMXML_NODE *node, pnode; int n; n = mxml_find_nodes(tree, xml_path, &node); if (n > 0) { pnode = node[0]; mxml_free(node); } else pnode = NULL; return pnode; } /*------------------------------------------------------------------*/ PMXML_NODE mxml_get_parent(PMXML_NODE pnode) { assert(pnode); return pnode->parent; } /*------------------------------------------------------------------*/ char *mxml_get_name(PMXML_NODE pnode) { assert(pnode); return pnode->name; } /*------------------------------------------------------------------*/ char *mxml_get_value(PMXML_NODE pnode) { assert(pnode); return pnode->value; } /*------------------------------------------------------------------*/ int mxml_get_line_number_start(PMXML_NODE pnode) { assert(pnode); return pnode->line_number_start; } /*------------------------------------------------------------------*/ int mxml_get_line_number_end(PMXML_NODE pnode) { assert(pnode); return pnode->line_number_end; } /*------------------------------------------------------------------*/ char *mxml_get_attribute(PMXML_NODE pnode, const char *name) { int i; assert(pnode); for (i=0 ; in_attributes ; i++) if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, name) == 0) return pnode->attribute_value[i]; return NULL; } /*------------------------------------------------------------------*/ int mxml_replace_node_name(PMXML_NODE pnode, const char *name) { strlcpy(pnode->name, name, sizeof(pnode->name)); return TRUE; } /*------------------------------------------------------------------*/ int mxml_replace_node_value(PMXML_NODE pnode, const char *value) { if (pnode->value) pnode->value = (char *)mxml_realloc(pnode->value, strlen(value)+1); else if (value) pnode->value = (char *)mxml_malloc(strlen(value)+1); else pnode->value = NULL; if (value) strcpy(pnode->value, value); return TRUE; } /*------------------------------------------------------------------*/ /** replace value os a subnode, like value if pnode=parent, and "name"="child", then "value" gets replaced */ int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value) { int i; for (i=0 ; in_children ; i++) if (strcmp(pnode->child[i].name, name) == 0) break; if (i == pnode->n_children) return FALSE; return mxml_replace_node_value(&pnode->child[i], value); } /*------------------------------------------------------------------*/ /** * change the name of an attribute, keep its value */ int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name) { int i; for (i=0 ; in_attributes ; i++) if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, old_name) == 0) break; if (i == pnode->n_attributes) return FALSE; strlcpy(pnode->attribute_name+i*MXML_NAME_LENGTH, new_name, MXML_NAME_LENGTH); return TRUE; } /*------------------------------------------------------------------*/ /** * change the value of an attribute */ int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value) { int i; for (i=0 ; in_attributes ; i++) if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0) break; if (i == pnode->n_attributes) return FALSE; pnode->attribute_value[i] = (char *)mxml_realloc(pnode->attribute_value[i], strlen(attrib_value)+1); strcpy(pnode->attribute_value[i], attrib_value); return TRUE; } /*------------------------------------------------------------------*/ /** * free memory of a node and remove it from the parent's child list */ int mxml_delete_node(PMXML_NODE pnode) { PMXML_NODE parent; int i, j; /* remove node from parent's list */ parent = pnode->parent; if (parent) { for (i=0 ; in_children ; i++) if (&parent->child[i] == pnode) break; /* free allocated node memory recursively */ mxml_free_tree(pnode); if (i < parent->n_children) { for (j=i ; jn_children-1 ; j++) memcpy(&parent->child[j], &parent->child[j+1], sizeof(MXML_NODE)); parent->n_children--; if (parent->n_children) parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children)); else mxml_free(parent->child); } } else mxml_free_tree(pnode); return TRUE; } /*------------------------------------------------------------------*/ int mxml_delete_attribute(PMXML_NODE pnode, const char *attrib_name) { int i, j; for (i=0 ; in_attributes ; i++) if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0) break; if (i == pnode->n_attributes) return FALSE; mxml_free(pnode->attribute_value[i]); for (j=i ; jn_attributes-1 ; j++) { strcpy(pnode->attribute_name+j*MXML_NAME_LENGTH, pnode->attribute_name+(j+1)*MXML_NAME_LENGTH); pnode->attribute_value[j] = pnode->attribute_value[j+1]; } if (pnode->n_attributes > 0) { pnode->attribute_name = (char *)mxml_realloc(pnode->attribute_name, MXML_NAME_LENGTH*(pnode->n_attributes-1)); pnode->attribute_value = (char **)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes-1)); } else { mxml_free(pnode->attribute_name); mxml_free(pnode->attribute_value); } return TRUE; } /*------------------------------------------------------------------*/ #define HERE root, file_name, line_number, error, error_size, error_line /** * used inside mxml_parse_file for reporting errors */ PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...) { char *msg, str[1000]; va_list argptr; if (file_name && file_name[0]) sprintf(str, "XML read error in file \"%s\", line %d: ", file_name, line_number); else sprintf(str, "XML read error, line %d: ", line_number); msg = (char *)mxml_malloc(error_size); if (error) strlcpy(error, str, error_size); va_start(argptr, format); vsprintf(str, (char *) format, argptr); va_end(argptr); if (error) strlcat(error, str, error_size); if (error_line) *error_line = line_number; mxml_free(msg); mxml_free_tree(root); return NULL; } /*------------------------------------------------------------------*/ /** * Parse a XML buffer and convert it into a tree of MXML_NODE's. * Return NULL in case of an error, return error description. * Optional file_name is used for error reporting if called from mxml_parse_file() */ PMXML_NODE mxml_parse_buffer(const char *buf, char *error, int error_size, int *error_line) { char node_name[256], attrib_name[256], attrib_value[1000], quote; const char *p, *pv; int i,j, line_number; PMXML_NODE root, ptree, pnew; int end_element; size_t len; char *file_name = NULL; /* dummy for 'HERE' */ p = buf; line_number = 1; root = mxml_create_root_node(); ptree = root; /* parse file contents */ do { if (*p == '<') { end_element = FALSE; /* found new element */ p++; while (*p && isspace(*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); if (strncmp(p, "!--", 3) == 0) { /* found comment */ pnew = mxml_add_special_node(ptree, COMMENT_NODE, "Comment", NULL); pnew->line_number_start = line_number; pv = p+3; while (*pv == ' ') pv++; p += 3; if (strstr(p, "-->") == NULL) return read_error(HERE, "Unterminated comment"); while (strncmp(p, "-->", 3) != 0) { if (*p == '\n') line_number++; p++; } len = (size_t)p - (size_t)pv; pnew->value = (char *)mxml_malloc(len+1); memcpy(pnew->value, pv, len); pnew->value[len] = 0; pnew->line_number_end = line_number; mxml_decode(pnew->value); p += 3; } else if (*p == '?') { /* found ?...? element */ pnew = mxml_add_special_node(ptree, PROCESSING_INSTRUCTION_NODE, "PI", NULL); pnew->line_number_start = line_number; pv = p+1; p++; if (strstr(p, "?>") == NULL) return read_error(HERE, "Unterminated ?...? element"); while (strncmp(p, "?>", 2) != 0) { if (*p == '\n') line_number++; p++; } len = (size_t)p - (size_t)pv; pnew->value = (char *)mxml_malloc(len+1); memcpy(pnew->value, pv, len); pnew->value[len] = 0; pnew->line_number_end = line_number; mxml_decode(pnew->value); p += 2; } else if (strncmp(p, "!DOCTYPE", 8) == 0 ) { /* found !DOCTYPE element , skip it */ p += 8; if (strstr(p, ">") == NULL) return read_error(HERE, "Unterminated !DOCTYPE element"); j = 0; while (*p && (*p != '>' || j > 0)) { if (*p == '\n') line_number++; else if (*p == '<') j++; else if (*p == '>') j--; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); p++; } else { /* found normal element */ if (*p == '/') { end_element = TRUE; p++; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); } /* extract node name */ i = 0; node_name[i] = 0; while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<') node_name[i++] = *p++; node_name[i] = 0; if (!*p) return read_error(HERE, "Unexpected end of file"); if (*p == '<') return read_error(HERE, "Unexpected \'<\' inside element \"%s\"", node_name); mxml_decode(node_name); if (end_element) { if (!ptree) return read_error(HERE, "Found unexpected ", node_name); /* close previously opened element */ if (strcmp(ptree->name, node_name) != 0) return read_error(HERE, "Found , expected ", node_name, ptree->name); ptree->line_number_end = line_number; /* go up one level on the tree */ ptree = ptree->parent; } else { if (ptree == NULL) return read_error(HERE, "Unexpected second top level node"); /* allocate new element structure in parent tree */ pnew = mxml_add_node(ptree, node_name, NULL); pnew->line_number_start = line_number; pnew->line_number_end = line_number; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); while (*p != '>' && *p != '/') { /* found attribute */ pv = p; while (*pv && !isspace((unsigned char)*pv) && *pv != '=' && *pv != '<' && *pv != '>') pv++; if (!*pv) return read_error(HERE, "Unexpected end of file"); if (*pv == '<' || *pv == '>') return read_error(HERE, "Unexpected \'%c\' inside element \"%s\"", *pv, node_name); /* extract attribute name */ len = (size_t)pv - (size_t)p; if (len > sizeof(attrib_name)-1) len = sizeof(attrib_name)-1; memcpy(attrib_name, p, len); attrib_name[len] = 0; mxml_decode(attrib_name); p = pv; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); if (*p != '=') return read_error(HERE, "Expect \"=\" here"); p++; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); if (*p != '\"' && *p != '\'') return read_error(HERE, "Expect \" or \' here"); quote = *p; p++; /* extract attribute value */ pv = p; while (*pv && *pv != quote) pv++; if (!*pv) return read_error(HERE, "Unexpected end of file"); len = (size_t)pv - (size_t)p; if (len > sizeof(attrib_value)-1) len = sizeof(attrib_value)-1; memcpy(attrib_value, p, len); attrib_value[len] = 0; mxml_decode(attrib_value); /* add attribute to current node */ mxml_add_attribute(pnew, attrib_name, attrib_value); p = pv+1; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); } if (*p == '/') { /* found empty node, like , just skip closing bracket */ p++; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) return read_error(HERE, "Unexpected end of file"); if (*p != '>') return read_error(HERE, "Expected \">\" after \"/\""); p++; } if (*p == '>') { p++; /* check if we have sub-element or value */ pv = p; while (*pv && isspace((unsigned char)*pv)) { if (*pv == '\n') line_number++; pv++; } if (!*pv) return read_error(HERE, "Unexpected end of file"); if (*pv == '<' && *(pv+1) != '/') { /* start new subtree */ ptree = pnew; p = pv; } else { /* extract value */ while (*pv && *pv != '<') { if (*pv == '\n') line_number++; pv++; } if (!*pv) return read_error(HERE, "Unexpected end of file"); len = (size_t)pv - (size_t)p; pnew->value = (char *)mxml_malloc(len+1); memcpy(pnew->value, p, len); pnew->value[len] = 0; mxml_decode(pnew->value); p = pv; ptree = pnew; } } } } } /* go to next element */ while (*p && *p != '<') { if (*p == '\n') line_number++; p++; } } while (*p); return root; } /*------------------------------------------------------------------*/ /** * parse !ENTYTY entries of XML files and replace with references. * Return 0 in case of no errors, return error description. * Optional file_name is used for error reporting if called from mxml_parse_file() */ int mxml_parse_entity(char **buf, const char *file_name, char *error, int error_size, int *error_line) { char *p; char *pv; char delimiter; int i, j, k, line_number, status; char *replacement; char entity_name[MXML_MAX_ENTITY][256]; char entity_reference_name[MXML_MAX_ENTITY][256]; char *entity_value[MXML_MAX_ENTITY]; int entity_type[MXML_MAX_ENTITY]; /* internal or external */ int entity_line_number[MXML_MAX_ENTITY]; int nentity; int fh, length, len; char *buffer; int ip; /* counter for entity value */ char directoryname[FILENAME_MAX]; char filename[FILENAME_MAX]; int entity_value_length[MXML_MAX_ENTITY]; int entity_name_length[MXML_MAX_ENTITY]; PMXML_NODE root = mxml_create_root_node(); /* dummy for 'HERE' */ for (ip = 0; ip < MXML_MAX_ENTITY; ip++) entity_value[ip] = NULL; line_number = 1; nentity = -1; status = 0; if (!buf || !(*buf) || !strlen(*buf)) return 0; strcpy(directoryname, file_name); mxml_dirname(directoryname); /* copy string to temporary space */ buffer = (char *) mxml_malloc(strlen(*buf) + 1); if (buffer == NULL) { read_error(HERE, "Cannot allocate memory."); status = 1; goto error; } strcpy(buffer, *buf); p = strstr(buffer, "!DOCTYPE"); if (p == NULL) { /* no entities */ status = 0; goto error; } pv = strstr(p, "["); if (pv == NULL) { /* no entities */ status = 1; goto error; } p = pv + 1; /* search !ENTITY */ do { if (*p == ']') break; if (*p == '<') { /* found new entity */ p++; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (strncmp(p, "!--", 3) == 0) { /* found comment */ p += 3; if (strstr(p, "-->") == NULL) { read_error(HERE, "Unterminated comment"); status = 1; goto error; } while (strncmp(p, "-->", 3) != 0) { if (*p == '\n') line_number++; p++; } p += 3; } else if (strncmp(p, "!ENTITY", 7) == 0) { /* found entity */ nentity++; if (nentity >= MXML_MAX_ENTITY) { read_error(HERE, "Too much entities"); status = 1; goto error; } entity_line_number[nentity] = line_number; pv = p + 7; while (*pv == ' ') pv++; /* extract entity name */ p = pv; while (*p && isspace((unsigned char)*p) && *p != '<' && *p != '>') { if (*p == '\n') line_number++; p++; } if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*p == '<' || *p == '>') { read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p); status = 1; goto error; } pv = p; while (*pv && !isspace((unsigned char)*pv) && *pv != '<' && *pv != '>') pv++; if (!*pv) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*pv == '<' || *pv == '>') { read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]); status = 1; goto error; } entity_name[nentity][0] = '&'; i = 1; entity_name[nentity][i] = 0; while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<' && i < 253) entity_name[nentity][i++] = *p++; entity_name[nentity][i++] = ';'; entity_name[nentity][i] = 0; if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*p == '<') { read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]); status = 1; goto error; } /* extract replacement or SYSTEM */ while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*p == '>') { read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]); status = 1; goto error; } /* check if SYSTEM */ if (strncmp(p, "SYSTEM", 6) == 0) { entity_type[nentity] = EXTERNAL_ENTITY; p += 6; } else { entity_type[nentity] = INTERNAL_ENTITY; } /* extract replacement */ while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*p == '>') { read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]); status = 1; goto error; } if (*p != '\"' && *p != '\'') { read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]); status = 1; goto error; } delimiter = *p; p++; if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } pv = p; while (*pv && *pv != delimiter) pv++; if (!*pv) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } if (*pv == '<') { read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]); status = 1; goto error; } len = (int)((size_t) pv - (size_t) p); replacement = (char *) mxml_malloc(len + 1); if (replacement == NULL) { read_error(HERE, "Cannot allocate memory."); status = 1; goto error; } memcpy(replacement, p, len); replacement[len] = 0; mxml_decode(replacement); if (entity_type[nentity] == EXTERNAL_ENTITY) { strcpy(entity_reference_name[nentity], replacement); } else { entity_value[nentity] = (char *) mxml_malloc(strlen(replacement)); if (entity_value[nentity] == NULL) { read_error(HERE, "Cannot allocate memory."); status = 1; goto error; } strcpy(entity_value[nentity], replacement); } mxml_free(replacement); p = pv; while (*p && isspace((unsigned char)*p)) { if (*p == '\n') line_number++; p++; } if (!*p) { read_error(HERE, "Unexpected end of file"); status = 1; goto error; } } } /* go to next element */ while (*p && *p != '<') { if (*p == '\n') line_number++; p++; } } while (*p); nentity++; /* read external file */ for (i = 0; i < nentity; i++) { if (entity_type[i] == EXTERNAL_ENTITY) { if ( entity_reference_name[i][0] == DIR_SEPARATOR ) /* absolute path */ strcpy(filename, entity_reference_name[i]); else /* relative path */ sprintf(filename, "%s%c%s", directoryname, DIR_SEPARATOR, entity_reference_name[i]); fh = open(filename, O_RDONLY | O_TEXT, 0644); if (fh == -1) { line_number = entity_line_number[i]; read_error(HERE, "%s is missing", entity_reference_name[i]); status = 1; goto error; } else { length = (int)lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); if (length == 0) { entity_value[i] = (char *) mxml_malloc(1); if (entity_value[i] == NULL) { read_error(HERE, "Cannot allocate memory."); close(fh); status = 1; goto error; } entity_value[i][0] = 0; } else { entity_value[i] = (char *) mxml_malloc(length); if (entity_value[i] == NULL) { read_error(HERE, "Cannot allocate memory."); close(fh); status = 1; goto error; } /* read complete file at once */ length = (int)read(fh, entity_value[i], length); entity_value[i][length - 1] = 0; close(fh); /* recursive parse */ if (mxml_parse_entity(&entity_value[i], filename, error, error_size, error_line) != 0) { status = 1; goto error; } } } } } /* count length of output string */ length = (int)strlen(buffer); for (i = 0; i < nentity; i++) { p = buffer; entity_value_length[i] = (int)strlen(entity_value[i]); entity_name_length[i] = (int)strlen(entity_name[i]); while (1) { pv = strstr(p, entity_name[i]); if (pv) { length += entity_value_length[i] - entity_name_length[i]; p = pv + 1; } else { break; } } } /* re-allocate memory */ *buf = (char *) mxml_realloc(*buf, length + 1); if (*buf == NULL) { read_error(HERE, "Cannot allocate memory."); status = 1; goto error; } /* replace entities */ p = buffer; pv = *buf; do { if (*p == '&') { /* found entity */ for (j = 0; j < nentity; j++) { if (strncmp(p, entity_name[j], entity_name_length[j]) == 0) { for (k = 0; k < (int) entity_value_length[j]; k++) *pv++ = entity_value[j][k]; p += entity_name_length[j]; break; } } } *pv++ = *p++; } while (*p); *pv = 0; error: if (buffer != NULL) mxml_free(buffer); for (ip = 0; ip < MXML_MAX_ENTITY; ip++) if (entity_value[ip] != NULL) mxml_free(entity_value[ip]); return status; } /*------------------------------------------------------------------*/ /** * parse a XML file and convert it into a tree of MXML_NODE's. * Return NULL in case of an error, return error description */ PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size, int *error_line) { char *buf, line[1000]; int fh, length; PMXML_NODE root; if (error) error[0] = 0; fh = open(file_name, O_RDONLY | O_TEXT, 0644); if (fh == -1) { sprintf(line, "Unable to open file \"%s\": ", file_name); strlcat(line, strerror(errno), sizeof(line)); strlcpy(error, line, error_size); return NULL; } length = (int)lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buf = (char *)mxml_malloc(length+1); if (buf == NULL) { close(fh); sprintf(line, "Cannot allocate buffer: "); strlcat(line, strerror(errno), sizeof(line)); strlcpy(error, line, error_size); return NULL; } /* read complete file at once */ length = (int)read(fh, buf, length); buf[length] = 0; close(fh); if (mxml_parse_entity(&buf, file_name, error, error_size, error_line) != 0) { mxml_free(buf); return NULL; } root = mxml_parse_buffer(buf, error, error_size, error_line); mxml_free(buf); return root; } /*------------------------------------------------------------------*/ /** * write complete subtree recursively into file opened with mxml_open_document() */ int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent) { int i; mxml_start_element1(writer, tree->name, indent); for (i=0 ; in_attributes ; i++) if (!mxml_write_attribute(writer, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i])) return FALSE; if (tree->value) if (!mxml_write_value(writer, tree->value)) return FALSE; for (i=0 ; in_children ; i++) if (!mxml_write_subtree(writer, &tree->child[i], (tree->value == NULL) || i > 0)) return FALSE; return mxml_end_element(writer); } /*------------------------------------------------------------------*/ /** * write a complete XML tree to a file */ int mxml_write_tree(const char *file_name, PMXML_NODE tree) { MXML_WRITER *writer; int i; assert(tree); writer = mxml_open_file(file_name); if (!writer) return FALSE; for (i=0 ; in_children ; i++) if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */ if (!mxml_write_subtree(writer, &tree->child[i], TRUE)) return FALSE; if (!mxml_close_file(writer)) return FALSE; return TRUE; } /*------------------------------------------------------------------*/ PMXML_NODE mxml_clone_tree(PMXML_NODE tree) { PMXML_NODE clone; int i; clone = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1); /* copy name, node_type, n_attributes and n_children */ memcpy(clone, tree, sizeof(MXML_NODE)); clone->value = NULL; mxml_replace_node_value(clone, tree->value); clone->attribute_name = NULL; clone->attribute_value = NULL; for (i=0 ; in_attributes ; i++) mxml_add_attribute(clone, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]); clone->child = NULL; clone->n_children = 0; for (i=0 ; in_children ; i++) mxml_add_tree(clone, mxml_clone_tree(mxml_subnode(tree, i))); return clone; } /*------------------------------------------------------------------*/ /** * print XML tree for debugging */ void mxml_debug_tree(PMXML_NODE tree, int level) { int i, j; for (i=0 ; iname); for (i=0 ; ivalue); for (i=0 ; inode_type); for (i=0 ; iline_number_start); for (i=0 ; iline_number_end); for (j=0 ; jn_attributes ; j++) { for (i=0 ; iattribute_name+j*MXML_NAME_LENGTH, tree->attribute_value[j]); } for (i=0 ; iparent); for (i=0 ; in_children); for (i=0 ; in_children ; i++) mxml_debug_tree(tree->child+i, level+1); if (level == 0) printf("\n"); } /*------------------------------------------------------------------*/ /** * free memory of XML tree, must be called after any * mxml_create_root_node() or mxml_parse_file() */ void mxml_free_tree(PMXML_NODE tree) { int i; /* first free children recursively */ for (i=0 ; in_children ; i++) mxml_free_tree(&tree->child[i]); if (tree->n_children) mxml_free(tree->child); /* now free dynamic data */ for (i=0 ; in_attributes ; i++) mxml_free(tree->attribute_value[i]); if (tree->n_attributes) { mxml_free(tree->attribute_name); mxml_free(tree->attribute_value); } if (tree->value) mxml_free(tree->value); /* if we are the root node, free it */ if (tree->parent == NULL) mxml_free(tree); } /*------------------------------------------------------------------*/ /* void mxml_test() { char err[256]; PMXML_NODE tree, tree2, node; tree = mxml_parse_file("c:\\tmp\\test.xml", err, sizeof(err)); tree2 = mxml_clone_tree(tree); printf("Orig:\n"); mxml_debug_tree(tree, 0); printf("\nClone:\n"); mxml_debug_tree(tree2, 0); printf("\nCombined:\n"); node = mxml_find_node(tree2, "cddb"); mxml_add_tree(tree, node); mxml_debug_tree(tree, 0); mxml_free_tree(tree); } */ /*------------------------------------------------------------------*/ /** mxml_basename deletes any prefix ending with the last slash '/' character present in path. mxml_dirname deletes the filename portion, beginning with the last slash '/' character to the end of path. Followings are examples from these functions path dirname basename "/" "/" "" "." "." "." "" "" "" "/test.txt" "/" "test.txt" "path/to/test.txt" "path/to" "test.txt" "test.txt "." "test.txt" Under Windows, '\\' and ':' are recognized ad separator too. */ void mxml_basename(char *path) { char str[FILENAME_MAX]; char *p; char *name; if (path) { strcpy(str, path); p = str; name = str; while (1) { if (*p == 0) break; if (*p == '/' #ifdef _MSC_VER || *p == ':' || *p == '\\' #endif ) name = p + 1; p++; } strcpy(path, name); } return; } void mxml_dirname(char *path) { char *p; #ifdef _MSC_VER char *pv; #endif if (!path || strlen(path) == 0) return; p = strrchr(path, '/'); #ifdef _MSC_VER pv = strrchr(path, ':'); if (pv > p) p = pv; pv = strrchr(path, '\\'); if (pv > p) p = pv; #endif if (p == 0) /* current directory */ strcpy(path, "."); else if (p == path) /* root directory */ sprintf(path, "%c", *p); else *p = 0; return; } /*------------------------------------------------------------------*/ /** * Retieve node at a certain line number */ PMXML_NODE mxml_get_node_at_line(PMXML_NODE tree, int line_number) { int i; PMXML_NODE pn; if (tree->line_number_start == line_number) return tree; for (i=0 ; in_children ; i++) { pn = mxml_get_node_at_line(&tree->child[i], line_number); if (pn) return pn; } return NULL; } elog-3.1.3-1/src/mxml.h0000644000175000017500000001367213020302451013534 0ustar rogerroger/********************************************************************\ Name: mxml.h Created by: Stefan Ritt Copyright 2000 + Stefan Ritt Contents: Header file for mxml.c This file is part of MIDAS XML Library. MIDAS XML Library 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. MIDAS XML Library 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 MIDAS XML Library. If not, see . \********************************************************************/ /*------------------------------------------------------------------*/ #ifndef _MXML_H_ #define _MXML_H_ #define MXML_NAME_LENGTH 64 #define ELEMENT_NODE 1 #define TEXT_NODE 2 #define PROCESSING_INSTRUCTION_NODE 3 #define COMMENT_NODE 4 #define DOCUMENT_NODE 5 #define INTERNAL_ENTITY 0 #define EXTERNAL_ENTITY 1 #define MXML_MAX_ENTITY 500 #define MXML_MAX_CONDITION 10 #ifdef _MSC_VER #define DIR_SEPARATOR '\\' #else #define DIR_SEPARATOR '/' #endif typedef struct { int fh; char *buffer; int buffer_size; int buffer_len; int level; int element_is_open; int data_was_written; char **stack; int translate; } MXML_WRITER; typedef struct mxml_struct *PMXML_NODE; typedef struct mxml_struct { char name[MXML_NAME_LENGTH]; // name of element <[name]>[value] int node_type; // type of node XXX_NODE char *value; // value of element int n_attributes; // list of attributes char *attribute_name; char **attribute_value; int line_number_start; // first line number in XML file, starting from 1 int line_number_end; // last line number in XML file, starting from 1 PMXML_NODE parent; // pointer to parent element int n_children; // list of children PMXML_NODE child; } MXML_NODE; /*------------------------------------------------------------------*/ /* make functions callable from a C++ program */ #ifdef __cplusplus extern "C" { #endif #ifndef EXPRT #if defined(EXPORT_DLL) #define EXPRT __declspec(dllexport) #else #define EXPRT #endif #endif void mxml_suppress_date(int suppress); MXML_WRITER *mxml_open_file(const char *file_name); MXML_WRITER *mxml_open_buffer(void); int mxml_set_translate(MXML_WRITER *writer, int flag); int mxml_start_element(MXML_WRITER *writer, const char *name); int mxml_start_element_noindent(MXML_WRITER *writer, const char *name); int mxml_end_element(MXML_WRITER *writer); int mxml_write_comment(MXML_WRITER *writer, const char *string); int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value); int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value); int mxml_write_value(MXML_WRITER *writer, const char *value); int mxml_write_empty_line(MXML_WRITER *writer); char *mxml_close_buffer(MXML_WRITER *writer); int mxml_close_file(MXML_WRITER *writer); int mxml_get_number_of_children(PMXML_NODE pnode); PMXML_NODE mxml_get_parent(PMXML_NODE pnode); PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx); PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path); int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist); char *mxml_get_name(PMXML_NODE pnode); char *mxml_get_value(PMXML_NODE pnode); int mxml_get_line_number_start(PMXML_NODE pnode); int mxml_get_line_number_end(PMXML_NODE pnode); PMXML_NODE mxml_get_node_at_line(PMXML_NODE tree, int linenumber); char *mxml_get_attribute(PMXML_NODE pnode, const char *name); int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value); PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value); PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx); PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value); PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx); PMXML_NODE mxml_clone_tree(PMXML_NODE tree); int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree); int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx); int mxml_replace_node_name(PMXML_NODE pnode, const char *new_name); int mxml_replace_node_value(PMXML_NODE pnode, const char *value); int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value); int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name); int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value); int mxml_delete_node(PMXML_NODE pnode); int mxml_delete_attribute(PMXML_NODE, const char *attrib_name); PMXML_NODE mxml_create_root_node(void); PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size, int *error_line); PMXML_NODE mxml_parse_buffer(const char *buffer, char *error, int error_size, int *error_line); int mxml_parse_entity(char **buf, const char* file_name, char *error, int error_size, int *error_line); int mxml_write_tree(const char *file_name, PMXML_NODE tree); void mxml_debug_tree(PMXML_NODE tree, int level); void mxml_free_tree(PMXML_NODE tree); void mxml_dirname(char* path); void mxml_basename(char *path); #ifdef __cplusplus } #endif #endif /* _MXML_H_ */ /*------------------------------------------------------------------*/ elog-3.1.3-1/src/strlcpy.c0000644000175000017500000000551313020302451014245 0ustar rogerroger/********************************************************************\ Name: strlcpy.c Created by: Stefan Ritt Copyright 2000 + Stefan Ritt Contents: Contains strlcpy and strlcat which are versions of strcpy and strcat, but which avoid buffer overflows This file is part of MIDAS XML Library. MIDAS XML Library 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. MIDAS XML Library 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 MIDAS XML Library. If not, see . \********************************************************************/ #include #include #include "strlcpy.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless size == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ #ifndef STRLCPY_DEFINED size_t strlcpy(char *dst, const char *src, size_t size) { char *d = dst; const char *s = src; size_t n = size; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (size != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++); } return (s - src - 1); /* count does not include NUL */ } /*-------------------------------------------------------------------*/ /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless size <= strlen(dst)). * Returns strlen(src) + MIN(size, strlen(initial dst)). * If retval >= size, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t size) { char *d = dst; const char *s = src; size_t n = size; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = size - dlen; if (n == 0) return (dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return (dlen + (s - src)); /* count does not include NUL */ } /*-------------------------------------------------------------------*/ #endif // STRLCPY_DEFINED elog-3.1.3-1/src/strlcpy.h0000644000175000017500000000270513020302451014252 0ustar rogerroger/********************************************************************\ Name: strlcpy.h Created by: Stefan Ritt Copyright 2000 + Stefan Ritt Contents: Header file for strlcpy.c This file is part of MIDAS XML Library. MIDAS XML Library 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. MIDAS XML Library 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 MIDAS XML Library. If not, see . \********************************************************************/ #ifndef _STRLCPY_H_ #define _STRLCPY_H_ // some version of gcc have a built-in strlcpy #ifdef strlcpy #define STRLCPY_DEFINED #endif #ifdef __cplusplus extern "C" { #endif #ifndef EXPRT #if defined(EXPORT_DLL) #define EXPRT __declspec(dllexport) #else #define EXPRT #endif #endif #ifndef STRLCPY_DEFINED size_t EXPRT strlcpy(char *dst, const char *src, size_t size); size_t EXPRT strlcat(char *dst, const char *src, size_t size); #endif #ifdef __cplusplus } #endif #endif /*_STRLCPY_H_ */ elog-3.1.3-1/elogd.plist0000644000175000017500000000065012556103750013774 0ustar rogerroger Label ch.psi.elogd ProgramArguments /usr/local/sbin/elogd -c /usr/local/elog/elogd.cfg RunAtLoad elog-3.1.3-1/elogd.cfg0000755000175000017500000000063412556103750013405 0ustar rogerroger[global] port = 8080 [demo] Theme = default Comment = General Linux Tips & Tricks Attributes = Author, Type, Category, Subject Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other Options Category = General, Hardware, Software, Network, Other Extendable Options = Category Required Attributes = Author, Type Page Title = ELOG - $subject Reverse sort = 1 Quick filter = Date, Type elog-3.1.3-1/themes/0000755000175000017500000000000013076344742013117 5ustar rogerrogerelog-3.1.3-1/themes/default/0000755000175000017500000000000013076344742014543 5ustar rogerrogerelog-3.1.3-1/themes/default/first.png0000755000175000017500000000040612556103751016376 0ustar rogerrogerPNG  IHDR͕-DtIMEbw pHYsN N }gAMA a$PLTE@@3ff333ffff|YtRNS@fXIDATxڽ10Ph_KVLyJV0z T͡NP2K%A0;GU#6U&~ d8qUIENDB`elog-3.1.3-1/themes/default/entry.png0000755000175000017500000000063012556103751016407 0ustar rogerrogerPNG  IHDR ex)tEXtDescriptionBuilt with GIF Movie Gear 2.6O"]tIMEbw pHYsN N }gAMA a`PLTE... PPP'%===]Yδ e&tRNS@fyIDATxM P*l+|[Nl'7mR!<@˟Jr ;ZSÞӹ;fo1:#F9OŽVSHhٗY?y?2(;AJ9(I NIENDB`elog-3.1.3-1/themes/default/compact.css0000755000175000017500000000105412556103751016701 0ustar rogerroger/* compact formatting */ body { margin:0px; } .gtab a { padding-left:1px; padding-right:1px; } .sgtab a { padding-left:1px; padding-right:1px; } .ltab a { padding-left:1px; padding-right:1px; } .sltab a { padding-left:1px; padding-right:1px; } .menuframe { padding:1px; } .attribhead { padding:1px; } .attribname { padding:1px; } .attribvalue { padding:1px; } .attribvalue2 { padding:1px; } .messagelist { padding:1px; } .messageframe { padding:1px; } .errormsg { padding:1px; } .notifymsg { padding:1px; } elog-3.1.3-1/themes/default/previous.png0000755000175000017500000000033512556103751017124 0ustar rogerrogerPNG  IHDR͕-DtIMEbw pHYsN N }gAMA aPLTE@@ff7 tRNS@fDIDATx1 CQfh\[ZSUt+-cXc <$CyAдRM6aaqr]IENDB`elog-3.1.3-1/themes/default/cal.png0000755000175000017500000000032712556103751016010 0ustar rogerrogerPNG  IHDRRtIMEz= pHYsN N }gAMA aPLTEJMJ֥MJJM$"PtRNS@f8IDATxc`%PdPKKKCe(;!156D0P0u6)A(xIENDB`elog-3.1.3-1/themes/default/reply.png0000755000175000017500000000041212556103751016377 0ustar rogerrogerPNG  IHDR extIMEbw pHYsN N }gAMA a0PLTE^7s1gPۀY.[ XݛUܓDB ?R(tRNS@fPIDATxڍK0DKA(xJh4c[1i'Ћ.AYВ4](AWF^Gyޏ_iwIENDB`elog-3.1.3-1/themes/default/cal_next.png0000755000175000017500000000047612556103751017053 0ustar rogerrogerPNG  IHDRRtIMEz= pHYsN N }gAMA a0PLTEFXalfNz~sڂ޹|IDATxc``w+5gVG$*Amg&2_V9a cîi K,#  ~1 }x+wd(0Z޽ 0$q[30110B1 sB4##`6o100 103X2@8f0u Gcgc L \, ^9'oIs/fe @/C2`/__8JG>e60F t&0ҏ_ L= L_10s11=e3;pzob@ 30Wff/ (ra!/̌A?$13|{ >Ielog-3.1.3-1/themes/default/attachment.png0000644000175000017500000000057412556103751017402 0ustar rogerrogerPNG  IHDRk\1tIME7Z) pHYs  ~gAMA a IDATxڝSE@/^E^B!tz-":DAomqڍ+&bN93;0&W,['?^6IENDB`elog-3.1.3-1/themes/default/icons/0000755000175000017500000000000013076344742015656 5ustar rogerrogerelog-3.1.3-1/themes/default/icons/elc_underline.png0000755000175000017500000000030412556103751021167 0ustar rogerrogerPNG  IHDR7tIMEbA pHYsN N }gAMA aPLTEưI8IDATxc` &8 &8@4` ;&Ldp}uQIENDB`elog-3.1.3-1/themes/default/icons/eld_italic.png0000755000175000017500000000031612556103751020453 0ustar rogerrogerPNG  IHDR7tIME ,:{ pHYsN N }gAMA aPLTEބƼlp"BIDATxc` &8 &,cQŪ90a4q08BEIYY!s8 ‡ `IFUIENDB`elog-3.1.3-1/themes/default/icons/elc_bold.png0000755000175000017500000000027712556103751020133 0ustar rogerrogerPNG  IHDR7tIME[( pHYsN N }gAMA aPLTEưI3IDATxc` &8 &8`$(8OpgpPYTsIENDB`elog-3.1.3-1/themes/default/icons/icon6.png0000755000175000017500000000056112556103751017402 0ustar rogerrogerPNG  IHDRyhtEXtDescriptionIcon05.gif Cool (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net<tIME* pHYsN N }gAMA aPLTEH0`ktRNS@fYIDATxc```g``T2v 1Ē]BHv,g3 EaGQ0+HiGf0 ' " j;HΪIENDB`elog-3.1.3-1/themes/default/icons/elc_pleased.png0000755000175000017500000000123012556103751020616 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:32:13 +0100Y5q.tIME 4y pHYsN N }gAMA aIDATxŔKAS@"ئt6X!" J*U l,x&\d@R9ΰ3;cw&ooAӽG+7-G4 6M]}̽3Ygbw#h/{=|%4rIkFuuu6!]+ XZa@ @^HT,_3oy{S@d:Z*`D:;yp3ѡ'U՜)O鈢3LEQEox|u*f*-K*2F}H&IvK~0_eY%IT4?0yt:Ñ%IaX L?={usch$3@t{TE<$$Ć|kxmq[/ڀ Λ;߳zf(/=@aZJPùѮ Mvt>P9q%ouۿ ~ϊ!0 TaٜNg<ňS oۍd5IENDB`elog-3.1.3-1/themes/default/icons/elc_arrow.png0000755000175000017500000000067512556103751020347 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:00:37 +0100rH|tIME "2 pHYsN N }gAMA aIDATxԱN0غ@ߧ/Zva`J'JXRRBlUIbQ qC,+ΧNVDQ^B/QMAO6qw#;G@W7JN>'o:Dh<0 ] t;|DJɷZKR*KղS41ƀBP*/f_}%y#@H鱒疙QT2ix Mv.5qp`.I)eZwz5^&Zeq,J)R|411*IENDB`elog-3.1.3-1/themes/default/icons/eld_underline.png0000755000175000017500000000030312556103751021167 0ustar rogerrogerPNG  IHDR7tIME . ? pHYsN N }gAMA aPLTEބƼlp"7IDATxc` &8 & WC4`;$2b/z&l(u¿{IENDB`elog-3.1.3-1/themes/default/icons/crying.png0000755000175000017500000000111012556103751017646 0ustar rogerrogerPNG  IHDR;֕JtIME  pHYsN N }gAMA aIDATxڍSKAvo;BLc @$ j! M#鬒*htSnwvwvgo7sFqA PPP===... !Built with GIF Movie Gear 2.6! ,u@p8$ry( d B@E8Aa04k .``$iӚ4qB s  I  MnIizaMBA;elog-3.1.3-1/themes/default/icons/bulb.png0000755000175000017500000000070512556103751017310 0ustar rogerrogerPNG  IHDRqtEXtDescriptionicon3.gif for use in UBB (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.netRЦtIME%uU pHYsnu>gAMA aIDATxڕR;@ x:K*3#XJ.!GNJbX) d7l3%"MŌ6(B%w3s:%Q(-@chx~%la]⪞{o) P-3@6-YjH,.cmZ[[L>Եl]u{mYꤎukO%mce.qz:~UU%I-=V-!ƫ@>*FIENDB`elog-3.1.3-1/themes/default/icons/eld_pleased.png0000755000175000017500000000123112556103751020620 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:32:20 +0100RlPtIME :H pHYsN N }gAMA aIDATxڭKAun3] !HZbE8HLFX# brcw!]~{(,xJ#"V<) i=om۶N+ʿQGt`"z qs{*24ەaucc- #q\=erJ"0}RFzf[i(^Y#֮Sȕ9B!1J]?ɦ_ Z|H^.ApN338ƅmBb<c,x|8cmyFU 0pz:ꀂ,~};qGb9  6=_6Mri RÕ @ÒIδ0` hxBKtSćx*éTFjK&kA0xnClؠ{tIME #5SG pHYsN N }gAMA aIDATxڭ;hAo'shD+,l-#&9@@)La!*(  -N )A cw3;˚?~|T*,qc?#:8j"7 {i;JzFFv>%$c,'Nw(cҔ;!qn?jEoo)}z@JׄCBQqRA殯g,n/8H3SLvTn̟FOW6?2ƄZ(刯IENDB`elog-3.1.3-1/themes/default/icons/icon11.png0000755000175000017500000000052712556103751017460 0ustar rogerrogerPNG  IHDRydtEXtDescriptionIcon10.gif (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.netw-tIME,fX pHYsN N }gAMA aPLTE3 #tRNS@fLIDATxc```g``T2661 c~#CH $"d(c2! @QI18$d2.WlXIENDB`elog-3.1.3-1/themes/default/icons/elc_image.png0000755000175000017500000000035612556103751020273 0ustar rogerrogerPNG  IHDR7tIME?Κ pHYsN N }gAMA aPLTE^1,_IDATxuA _^ KHƭӦR!fp"NĉSz (T9alMGGR_f ,;DKe*IENDB`elog-3.1.3-1/themes/default/icons/eld_tongue.png0000755000175000017500000000125712556103751020514 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:17 +0100B>tIME #a pHYsN N }gAMA aIDATxڭSkAmET9 Wl3U "*4AHI-;ngؙrk.>fgy3o޷ #c_j![9 =u0s4;s2 ( #}|+W/1̟ٽAQ#)x$},>` Orz(,O@ޡ# G&!F#&l>iK;18 Y1/~bXs[ ll ,, 2 |?c+ׯ_}6hlN4;ؐ5K6gXL3xGɫIENDB`elog-3.1.3-1/themes/default/icons/eld_quote.png0000755000175000017500000000050212556103751020340 0ustar rogerrogerPNG  IHDRRtIME -:)G pHYsN N }gAMA aIDATx͓ Eq\3F921#قBRY(ȡGym9v<[h)PJy?џ],<`N/҉1.˒g[wq~4agSuT/\PXBLtB8SиaYnx_%ך!BL6,;ZPa mۆVzӮxsIENDB`elog-3.1.3-1/themes/default/icons/icon5.gif0000644000175000017500000000053012556103751017353 0ustar rogerrogerGIF89a`H0fH3!hicon5.gif for use in UBB (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net! ,i`%VTiRcJ) 8lL*PRHdRZ,] )z3#0w~%^_2Ippq"x ~:x#'&)!;elog-3.1.3-1/themes/default/icons/eld_anchor.png0000644000175000017500000000074712556103751020465 0ustar rogerrogerPNG  IHDRRtIME **0pA pHYsN N }gAMA avIDATxڥT͊`ɠ}>Z0VSC$-۸HW \ 7X- EmD\m:39w(e\.hDLtpfV9oQ*(㑜eZZf69 ڲ,0v],qC21Vad"r7  Ah&r/@i2N H. Hbq]. Y4l6E$ll!vkJ%eYItF3T@lZ~ӣTHEL\fز{yavLdzl6~&UUU=ͤR׏|=#@T?W#% 1 IENDB`elog-3.1.3-1/themes/default/icons/elc_frown.png0000755000175000017500000000126512556103751020344 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:30:44 +0100ЇtIME ˤd pHYsN N }gAMA a IDATxŔOHQwfޮAH.=)D" BP& zT`Y/tYK`!ʾ7ヰquЗaf~߿7FdG6gD2;F.tҀmGt:wWagvM_] v),t[8"Ա!! Ӽ72,Cpɶ;*b3BZ~,JmP;#Vq㥋9D,79ez*&/jy&bjsd|qp03yeW)ofq1J)BH)duhn'd!=pU8D9 =h2g !(eգC7ކ J9gQ a)˲t}|z#B-1N-oh9d;c߃*Qu<0V(EJJi yX aqލAr{.TR(2ZeYDfSz_aPI{>IENDB`elog-3.1.3-1/themes/default/icons/elc_list.png0000755000175000017500000000027412556103751020163 0ustar rogerrogerPNG  IHDR7tIME/C^ pHYsN N }gAMA aPLTE-IDATxc` &8 & ʊ[ P?j/L.XuWIENDB`elog-3.1.3-1/themes/default/icons/eld_url.png0000755000175000017500000000042612556103751020012 0ustar rogerrogerPNG  IHDR7tIME / pHYsN N }gAMA aPLTEބx4]~IDATxڥ DٸaS;#pAK@*~fHb*&dO\\IKs󳠻EKξ@[--^8֭vz'&Q>wU`bbHH;ﱠ'DDP租99<z:yŖgIENDB`elog-3.1.3-1/themes/default/icons/eld_crying.png0000755000175000017500000000124612556103751020504 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:28:01 +0100Ԭ/tIME H pHYsN N }gAMA aIDATxڭS;KQQhB4"!Mdce4,,H@ IA /H6!꺂E+sw< 2L\Ν9|wn&A*JLQ(7A>O#GOc1_,qX.%>'#˺e⢶RZf*M5M[`d[֣Hr$&Bm}?0949W;g{Rn_v+dz<=IOkv,;Ӿڕ.X]8xzpF}AOuﺮ\J99IVJ5RF0 F)gLrw֤F`Uu6; /$iU NXE#catR\FXX#( I[_^4Z&uۺ60 4t$gme4 GWCË hyu̻Z`SdgA);_dn~[oxȫj&rLC7;H3IENDB`elog-3.1.3-1/themes/default/icons/thmbdown.png0000755000175000017500000000104312556103751020202 0ustar rogerrogerPNG  IHDR;֕JtIMEÌ pHYsN N }gAMA aIDATxڕM(DQo| RVJl(,4W)҂Y$;lf,lXH̻7{x&&N{;.%S6z>4)T|d2J}$9ޒlm'`~( EBAE@P #rׅC*T}teXEhijE e S*;IJlUlk s 6plc#vDI9cJk#r05hHBcGИ MU?z(..p[KpaLCGtCqqgpbXVEOk,^Qui9:ۉt2 OS-ȁ89]{R򰮴kf?Yya9}1]`=9p^=@~eDjIENDB`elog-3.1.3-1/themes/default/icons/icon12.png0000755000175000017500000000052712556103751017461 0ustar rogerrogerPNG  IHDR extIME-h pHYsN N }gAMA aEPLTE$' /21*003/33:=#I2Ycq7{4[ЄStRNS@fIDATxUK!EAE58)]gE&Dm[oASo%ֽCPl"]Y*V[ni)Sqǝ`tdˁ{C~Y"3#݇5*  (IENDB`elog-3.1.3-1/themes/default/icons/icon9.gif0000644000175000017500000000032412556103751017360 0ustar rogerrogerGIF89a!^Icon07.gif Sad (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net! ,7XZN+@kjh߀MYbG 5&۾_kѦzE%k٠8MhHH;elog-3.1.3-1/themes/default/icons/yawn.png0000755000175000017500000000112512556103751017337 0ustar rogerrogerPNG  IHDR;֕JtIME  pHYsN N }gAMA aIDATxڅ=hAq ADV,l$ "i!"Qr)@ W*"ѐ;vggwvgvƷsfp y0 v~?o<ԛy9,wOb?~c1nN1(Au4@9!>< ] ep,wlެDhۘ.G̀4; $1`3rft@&:R[^P"7pΑ&|69O#lI }+ӯȳ(.3U)I+[zx S"s03c {>,Es`3 V6w-_#8e^޵bQCG.9V¢v<ͅT!Iܮ/W;g_ +k <"V ?IENDB`elog-3.1.3-1/themes/default/icons/eld_mad.png0000755000175000017500000000127312556103751017752 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:30:01 +0100tIME &' pHYsN N }gAMA aIDATxڭMh@7V=,Z 'HHNx⡄=i),XPԋ "zփPö=TQP[$m@%vW${ɤ(>RiMbX,h(da0rox_ 6۲R/~Im,7Jma{um{Ni~a -n鉈Bٺ=͍O.њ2iU{EX:yw>ۗ{yd> &9&w[o,|&8]_)WT-0.']语Rry#1\J)RM"b$%RZ W ֌ݲ\_?tf^ JI6Fn Z;0A 7vdCD'tQ㣇jq J-\8'OBl3/֗Lrjz%f]OMu'P 7as|_#^ 7nΑcQ:W/eZGx5'?RB0~Yս4%JgKe*O1Cuj2 (J(~718`4MvGw~i/nc܁B#.q8##GӟTV 2"9*=a: -FjoKndcmM С1$8@u/șHIENDB`elog-3.1.3-1/themes/default/icons/eld_heading.png0000644000175000017500000000030412556103751020577 0ustar rogerrogerPNG  IHDR7tIME98= pHYsN N }gAMA aPLTEބƔ5IDATxc` &8 &8## ,ΊDSg] ` <kIENDB`elog-3.1.3-1/themes/default/icons/happy.png0000755000175000017500000000112112556103751017476 0ustar rogerrogerPNG  IHDR;֕JtIME ޸K pHYsN N }gAMA aIDATxڕSMkQ=ĊX"! HDq(*ąTEMJD44!ywӌ {=7a-~m6ƧiScp;i5j@j! -\ܡjDv뭏P:F/06UE'gP8G|u e&Tƹ4ьK1@A}?~ dl528~)Wʢp7wZKH!+ФƠ/ĕC2rKUVI'( ˳794IpRM}<L߽e<'I!r]GYV(N&+MSqzfQ `q%Lia~^OSr>m6J}I4 ¤M$It8%+:0x'ZmdYT*w[~?/ϛt]/$eH3 !⛒z+VՐBMIENDB`elog-3.1.3-1/themes/default/icons/eld_original.png0000644000175000017500000000102712556103751021007 0ustar rogerrogerPNG  IHDRRtIME '" pHYsN N }gAMA aIDATxc`HmDTTdp(q>`F޲/? `"/jU1W ϱeÔ=|䝘\@vYNSRO*zreϾ<|;V_|GP%0~ q\=$pٺOyE"7/<+ᅪOs`ˊu]]6F" ={KNgL_>12C||聍=\,U<@ n史jhg?w \?ݫ/'񻼼lZp>-g|d@@,$̌ %QLCݻGpEEEF`dIENDB`elog-3.1.3-1/themes/default/icons/eld_image.png0000755000175000017500000000036012556103751020267 0ustar rogerrogerPNG  IHDR7tIME +4Ǹ pHYsN N }gAMA aPLTEބ:aIDATxڝA!Ĩ5e[)F R79'die= hgqȯw?{ny|;-v3 ۇ̂J+IENDB`elog-3.1.3-1/themes/default/icons/eld_happy.png0000755000175000017500000000126012556103751020326 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:31:35 +0100jtIME  pHYsN N }gAMA aIDATxڭSMk@LVUi ,"BAAP ^*D_Z/,Hb,a$[Qm-468 GQ$DV=4?ӏHr4/u5VJe tPP]c'*6"J͚;?paiwpUoވƐ2]]&b--%I EIڧʢf}z:j1V(0lHaS@9m2-HcY̰csbʊ7 aQ S>)$64LXf:4:v~ȃ[vZdzuֆ M ^e艫riLT,Jz\< G|RBwؿYUV@J՞nkkcn)~Y]]= \GApb;guZz#btt9Q,"PUee e֏ ou:zcq[BIʵYIENDB`elog-3.1.3-1/themes/default/icons/elc_table.png0000644000175000017500000000031312556103751020266 0ustar rogerrogerPNG  IHDR7tIME̖d pHYsN N }gAMA aPLTEcccLIDATxc`HmDTTdp(q>ؔ!4 5~s|gdda뗘04;##XmʇVz.%b/ Gm}v]p++#gz.a#3Jg+[ϟLa9SrFK>:""hn@y:ٳ>{b+ɔ4w~a;'DYDҐ`C pf0ߟ?o8i66FB=Xb%L<_R ''3A#ǴkXP?//QF0GRRD.dPݻw#Oc՟MIENDB`elog-3.1.3-1/themes/default/icons/elc_delatt.png0000644000175000017500000000124712556103751020463 0ustar rogerrogerPNG  IHDRRtIME 4# pHYsN N }gAMA a6IDATxc?eHݿl#-[$Y ;vgg3ucTKK`jn57 WQQp l> Bg'܈7oLp6== HA8iwu{b?ۍ!\}Dq[!<y]gf8<(zQ#F8^>`jRzHnAAV4;kˀ!1ziՓGU %D&C#gn O>ЛL[w ahfi9$5p$:"EJ #&Bd`TKf))EFj A\ .Fl6Ekb)1I s E% %D"4T#fA(:}vBvϢmB) %G@,u4 Ŀ6.,l2 *-kƝ.C5pX knViG_ }>֖zX)wC~)x(c՝xċ=xS754W15=\@T `<"YV}x< ~ |TUɼ +T(`C!VRp8)!>;55< #rq(Ҽ* x~pPIENDB`elog-3.1.3-1/themes/default/icons/thmbup.png0000755000175000017500000000107012556103751017657 0ustar rogerrogerPNG  IHDR;֕JtIME# pHYsN N }gAMA aIDATxڕKAǿ;^-ؓ=P<Ջ"XjElPmK[ZA B !)!Ћ_Ћ6NNj@of}}!B.{8\Oåk-MDqF436.#se3qL3bŝf5+.-!A#PUBmؖGkĽEFث}utV}ϧ3yb\%]!PcPF><**V#cyldMTyCcT~}O^l9>bU F?>"HT 4rA!2 }/myѰ;`*M`gCn宬쿵n~nd,T8{3IXJ l;^&BҔ}cV¹tS̏$\X%aIENDB`elog-3.1.3-1/themes/default/icons/eld_rotright.png0000644000175000017500000000132512556103751021046 0ustar rogerrogerPNG  IHDRR*tEXtCreation TimeSa 2 Feb 2008 14:07:20 +0100~PtIME %rK pHYsN N }gAMA a.IDATxc`HmDTTdp(q>ؔ!4 Vjެ !bbbG)@-/vdqL 'zdRĽ{Hu2PTTde8`OSA2+TH$j[L9D1?+ug" ^M.eU8#M;l @! ,A  \&LP 4Pp` >\` J~! 8` B^x@ .A  H@ LX`ҥ^.HBUr P8P@,,D &\PR 1 lĀfh/ >ؼ9s@;elog-3.1.3-1/themes/default/icons/eld_frown.png0000755000175000017500000000127412556103751020345 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:30:49 +0100P9tIME U\ pHYsN N }gAMA aIDATxڭMhA7T{h CK{D^ H H *R Z(-hx D "{C"iԋPK"&%3پu6_ooÈmA xrFdYx"d28+otmۍnP;gev]_m'ةr#%yJܣ"5k3_gQRJ=|Q 1~khuwq㧋5rإr[JI΃s[T4tIME :) pHYsN N }gAMA aIDATxTKQwFhBҟ!HCHC!J [, :l]ƊXl.2Ǽ_f:;C_7}? ð5,ˆWT6E>ю'r9Yi GۘuVݻLQnv3&tFd;B}`qSPbK*ۥ]=7ilӏ0[Hӵ f¹BJ @" HO=.iFXz; ?ٜ"ą+㹱e ژind)qjt}yaqȽ)B,pTq'0LiRǷW[2c,udkw2ng֨omNxUaIENDB`elog-3.1.3-1/themes/default/icons/elc_clock.png0000644000175000017500000000200612556103751020273 0ustar rogerrogerPNG  IHDR7+tEXtCreation TimeMi 11 Apr 2007 08:32:58 +0100V.tIME &< pHYsN N }gAMA a7PLTEʅ[hofr{x=O\ &?=TfatzZlrNcp0I-?ż'@H^lݱֲVgm,HO_d +Hഝ}ƞ7Ux.KbJetگ縂 .P7^}ޱsȟi<]ug}h@avٮII;JJ;Ҩ޲%IkJk^uxglr??2""Υ3`JlRcdj}}dyyass[iiSOO?[[I߲uyf0^]|Mbhȟ֫٭ڮܰجЦ}?g:Tȩݺ߼޺ӲL^aKm5\\eaԽw ,K-Dñ.AJ>a{p2Aļ͟1@D>]wé=O_>GGyQWS0ANɆhwbs|u IDATxc` &8 &@߾c{׮[a[nC_p%K-_r5ig̜5̜;o>BO4yTxm]}CcSsKKkS[{GgLBFܷ&_PI ILJ{9m]\,\=<4042615337khjiIk#2r  3+;'7Fc 6 WS$eIENDB`elog-3.1.3-1/themes/default/icons/icon11.gif0000644000175000017500000000033412556103751017432 0ustar rogerrogerGIF89a3![Icon10.gif (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net! ,Bhj^+PzAsCX!FuP/ή}7sɼM@TZ`d!+Xi*@0ш$;elog-3.1.3-1/themes/default/icons/elc_original.png0000644000175000017500000000110612556103751021004 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 11 Feb 2008 17:12:43 +0100睩tIME D pHYsN N }gAMA aIDATxc?eHݿl#-[$Y /^P& CMSX^%$15ZSTE24b**9l>NL]. L\F[<~G 5#kȋ?,'))v'sU=s9|ig >/CD>#oɏG8 pu@l]z߼p}zŹ0eg:.#sPyyN Մ=%'ųtG|XY /EؘEE!l>>6܀6<@ n史jhg?w \?ݫ/'񻼼lZp>-gdA2H03DY0Ldzb5<IENDB`elog-3.1.3-1/themes/default/icons/tongue.png0000755000175000017500000000112312556103751017660 0ustar rogerrogerPNG  IHDR;֕JtIME' pHYsN N }gAMA aIDATxڅS=hAvo.Z$X>UI#bE  JlR4L4W**'h!wNNaN̼}ߛyka1뼀9ieC8n+<W0vxnBBrImX-<[mV[_p^4i RJѨpI`;Ex$QT+~@$W0I %3]~TWl|v 펍sBPYS3ɾaH^Q7H1C7>1(i0c8E6]PNd%g< sQ(Op:<ׅ9!) 9AB>cCN'#,>{;T"a64S8xtJQL@NfqzY}KtCl!ǚIsoOu{aowg̼6n9~tlp{6⬫!m/)$XS<[3F+IENDB`elog-3.1.3-1/themes/default/icons/pleased.png0000755000175000017500000000110012556103751017767 0ustar rogerrogerPNG  IHDR;֕JtIME pHYsN N }gAMA aIDATxڕS1KAvNI@P@0߈]X"Q $E4 hsH84zf专aޛ0:̈tqpy5( 43M(uZMh$<߇y𹅭怪{h*:8TR:0FCCwGe}۰'s;!eFg׃fBL4cN"$ 1G hsJh㷨NR YS Aw<ԙQW4ٞK=fXcj@ ||6,=[IY(DaHF <"Ɖ؆U1&J3l=CB 7+k?)-:Į ɣ|sT~_wrU|~aФՁi^, SWOZ}*^UП?D*GdIENDB`elog-3.1.3-1/themes/default/icons/icon10.gif0000644000175000017500000000034212556103751017430 0ustar rogerrogerGIF89af33!cIcon09.gif Laughing (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net! ,@xzn+A2XYMV $|kO`y@h<^5bBJhI/;elog-3.1.3-1/themes/default/icons/elc_quote.png0000755000175000017500000000035312556103751020343 0ustar rogerrogerPNG  IHDR7tIME< pHYsN N }gAMA aPLTEbϑYIDATxڕ[0z#K&@3Yȑ5xX jgxYosvvη>ȗ7hs T6.LIENDB`elog-3.1.3-1/themes/default/icons/biggrin.png0000755000175000017500000000076412556103751020012 0ustar rogerrogerPNG  IHDR;֕JtIME29U pHYsN N }gAMA aIDATxڕS1KPVk ѩv]\".AtD .CAP:V)tҼ$m4l.ww w.H_ DBi'*9h~}^~ q88-b{?PeiX1Q#9VrRntEM,a1uA[FZdS?=T1 2,`0S_Ae $B $B DctYf<B6=w"#KFeZ1dͼ(3%bN2鍬(ur,_ӖrjG񱉔=m{IDR%L|tN%*;<?خ}K|ЩCo6u Wpq_ Ls t%IENDB`elog-3.1.3-1/themes/default/icons/eld_list.png0000755000175000017500000000027312556103751020163 0ustar rogerrogerPNG  IHDR7tIME ,3 pHYsN N }gAMA aPLTEބEfX,IDATxc` &8 &8+++nq#P?j/#6 zbXIENDB`elog-3.1.3-1/themes/default/icons/icon1.png0000755000175000017500000000062712556103751017400 0ustar rogerrogerPNG  IHDR ex)tEXtDescriptionBuilt with GIF Movie Gear 2.6O"]tIME(aœ pHYsN N }gAMA a`PLTE... PPP'%===]Yδ e&tRNS@fyIDATxM P*l+|[Nl'7mR!<@˟Jr ;ZSÞӹ;fo1:#F9OŽVSHhٗY?y?2(;AJ9(I NIENDB`elog-3.1.3-1/themes/default/icons/icon7.png0000755000175000017500000000061012556103751017376 0ustar rogerrogerPNG  IHDRyqtEXtDescriptionicon7.gif for use in UBB (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net_w4tIME* pHYsN N }gAMA aPLTE3fo<tRNS@fjIDATxEͱ c8R؟Fx X`H쾼4z$n.0: ,|܍шH3NPw[|`4ڎsB`[r_} nIENDB`elog-3.1.3-1/themes/default/icons/elc_italic.png0000755000175000017500000000031612556103751020452 0ustar rogerrogerPNG  IHDR7tIME#tu pHYsN N }gAMA aPLTEưIBIDATxڵA DѺ(i6CDk"j+1U̫=ɉٙg>]WtIENDB`elog-3.1.3-1/themes/default/icons/eld_bold.png0000755000175000017500000000036412556103751020131 0ustar rogerrogerPNG  IHDR7+tEXtCreation TimeMo 16 Mai 2005 11:05:31 +0100J{vtIME ;`ݧp pHYsN N }gAMA aPLTEބƼlp"1IDATxc` &8 & 0cA$(8Oa0a a}IENDB`elog-3.1.3-1/themes/default/icons/paper.png0000755000175000017500000000076012556103751017474 0ustar rogerrogerPNG  IHDR;֕JtIME"υ pHYsN N }gAMA aIDATxڍ;KAϮQHDD*P&Z( DD BbڤXB1g'f͚,{gسߜH Cr@IEyx0GAT?C(A M<؝qF-LMa$Idoxm@ &'NVj!H6c`+K׻緪ݾQ +}M@%@ FO9Md6lMݠ>:Ç}bWoIENDB`elog-3.1.3-1/themes/default/icons/icon13.png0000755000175000017500000000055212556103751017460 0ustar rogerrogerPNG  IHDR extIME-h pHYsN N }gAMA a]PLTE>0/" Թʋu)Ҝ䯻;ҮhϮoL̯vz?C7!MraPx3!<^?9a@RkJJvqfgX.I@RX{{%g!YK> C* 劎[{V١wnyU0]IENDB`elog-3.1.3-1/themes/default/icons/icon10.png0000755000175000017500000000055312556103751017456 0ustar rogerrogerPNG  IHDRyltEXtDescriptionIcon09.gif Laughing (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.netAartIME+/ pHYsN N }gAMA aPLTE3f3 1tRNS@fUIDATxc```g``TRRR1D %~FA%E!C$ HXh d0 + * B@vAml -6@IENDB`elog-3.1.3-1/themes/default/icons/elc_wink.png0000755000175000017500000000125412556103751020157 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:10 +0100tIME ".`-1 pHYsN N }gAMA aIDATxŔOHA߮4BJB;t <  O)DMQ݅B^]</DG;͟]m#t}wcvٙ|f$I 8ZvjD\ƞd/zsa|ZTNFv) },rq @=.|_orso*"ӦMH/$Ir6석}v iMy),ǎ#Wo~ߍ?j .#^;+߬?~321122OG++c4qέXXXy,~a߿񢕹*ԑSͧC'. ;ݺ]E^[`OטM=#8 В>rrq } t<>3|/_|/|ln_XQ.4n1 8wa _?"LLؔ?0V/(շw[r_>I] ԀiGoı @K0ʏAKs_@}hO< r{F L#T~Yѽ0xIENDB`elog-3.1.3-1/themes/default/icons/icon13.gif0000644000175000017500000000170012556103751017432 0ustar rogerrogerGIF89acu)1ػ;>0SBҮhLz?{ϮoXWG'eʋ̯vpAiX5γ~Ҝra<Թm/" cQ-C7!M !,@@ A!,H@.`H@A<@I&0@@$@@0tH4 '?dՃ:ذA)֮ @@T)b C 04hF?=807;elog-3.1.3-1/themes/default/icons/cool.png0000755000175000017500000000106612556103751017321 0ustar rogerrogerPNG  IHDR;֕JtIMExj pHYsN N }gAMA aIDATxڕS=KQ=F? BDD,Em,D&MPa%!db%6N6[*"Zvf|};oC$7Μw9ܧ!5"ܕaVqrDZQn28G>2C4,aSY"LTVA17*BJZ%g68$e+t9\߽@G$ j>3Qb_yt%nVQaݎޕl\e*X<~t?iH_˜cXYx聹'-ȷhob_x?MV{ mIENDB`elog-3.1.3-1/themes/default/icons/icon3.gif0000644000175000017500000000041212556103751017350 0ustar rogerrogerGIF89a`H0ppp!hicon3.gif for use in UBB (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net!  ,KI+t< Dy0ppq xn@fC$GX4DTxR+#"Zt IZS[fKkS;elog-3.1.3-1/themes/default/icons/confused.png0000755000175000017500000000101312556103751020163 0ustar rogerrogerPNG  IHDRtIME'fӀ pHYsN N }gAMA aIDATxڽ=KAg/-RDAF>VAA%~`V*6+;EXvםJ.(ݰϼ!kNzk0ì°_@.=PJB(+#9& '0fKJX 0ppG; ú)D6 LթI}8'zn쭍ke-W[ 0ZkPƁ3{KMrKc`,e23;Ueݳ@'[ jbF`tZ+:4Ҥ8HD7@D] jn`JnfOWp ӳub} wʓ/8,40h[Ynºf{T/-~m /k5EӿlIENDB`elog-3.1.3-1/themes/default/icons/question.png0000755000175000017500000000104612556103751020232 0ustar rogerrogerPNG  IHDR;֕JtIME%kkg pHYsN N }gAMA aIDATxڅ=KAߙ;Ҥ $TB@AE2 4bb%@ Q4+H+H"*wg&gF轻$v93;3P=dE/?u~/{B/1.x~YG}F± `1SJ6ٯy3Ys {rŶ_ b|fX\mqmU\0;@eY'OװX*!-('=(CDq$I)Z;'ex'8MY~%q'mt!%^=0Uh wBvX8PUJ@ۈg,KTSYOίa5V}$[7ѓ 6uyA4W仠s2oKw/<kusIENDB`elog-3.1.3-1/themes/default/icons/elc_cool.png0000755000175000017500000000122412556103751020140 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:26:54 +0100н>tIME 'u pHYsN N }gAMA aIDATxc?eHݿl#-[$Y /^P\VVVcǎf Dg?}x]ϟ?YY1ӈ }76vߌLLk|VVX bB3ڝ[?|?}h>b +l/+/A:O߿0@ݻsV}*~ ZZZ?ٴ%ER¸ YBׯ_|7)3q =W&m'_>b<&Ы_> _?*۷?|'”ϟYXYY"ߟ?@S; |>դ_>}3#0_˗%Co{1fT/?211Ǧ,0?~`q)@K* b. $?@/_;qk@w {(dM:\)ym8r x"2KIENDB`elog-3.1.3-1/themes/default/icons/elc_biggrin.png0000755000175000017500000000113312556103751020624 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 11:57:34 +0100m%tIME ;;@ pHYsN N }gAMA aIDATxc?eHݿl#-[$Y /^P\VVVcǎf Dg?}x]ϟ?YY1ӈ }76vߌLLk|VVX bB3ڝ[?XۿrE+sUSͧXLd$bi`-#''n@}3÷o_}篟?OFo߾pkF?H [L t~$!d g~$.@g3D)?#`8`;_P"٫o"$_>I] ԀiGo߰:%Xe@\N\:Q~@ ִ 2\'C{*Gpib]-Roh╳>IENDB`elog-3.1.3-1/themes/default/icons/eld_table.png0000644000175000017500000000030212556103751020265 0ustar rogerrogerPNG  IHDR7tIME ׃ pHYsN N }gAMA aPLTEބƼlp"6IDATxc` &8 & WV8:8kSQ32a h얳IENDB`elog-3.1.3-1/themes/default/icons/icon4.png0000755000175000017500000000040212556103751017372 0ustar rogerrogerPNG  IHDRytIME)Ŭ pHYsN N }gAMA aPLTE'""RLz ,&f0{tRNS@faIDATx-ʁ  ( #thOXj?+N'8'0zU8A!3# K;FNk6-IҥLrIENDB`elog-3.1.3-1/themes/default/icons/icon2.gif0000644000175000017500000000160212556103751017351 0ustar rogerrogerGIF89a1^XB[ gs.ۀYݛUP7ܓDhQ!,_!Hh`  3@` B:yA*BP `@@@ōr,AJ3b PJ laDF ;elog-3.1.3-1/themes/default/icons/elc_yawn.png0000755000175000017500000000125012556103751020161 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:10 +0100tIME " Mp pHYsN N }gAMA aIDATxŔ=hAog.BD$&lm'BH E? -"$.NAR&řpw|oY8f͝^cc޼Y'B8G^gD\;N;Ft<[Vin>(##JA[xYS Al׾j}6T)T(8[ͬ4&c'!MN<<}q6 ( >Tk߇+ɉQV΍D;3o!2=H( ȿն(%g[s1&.r 1B(gO>k)RFkh]t2J!32d.eo.b.r zI:l*BgtheqNpaIJV/l1#P~XY:_~-7 %',%M•d2k` 撂IɆ BeZP]rIz~QX0I'3IENDB`elog-3.1.3-1/themes/default/icons/icon5.png0000755000175000017500000000066312556103751017404 0ustar rogerrogerPNG  IHDR exqtEXtDescriptionicon5.gif for use in UBB (C) 1999 Philipp Esselbach (ple@gmx.net) http://www.ntgamepalace.iscool.net~tIME)Ŭ pHYsN N }gAMA a?PLTE30HHf` #tRNS@fnIDATxE @QQu(vor'Gc?FyXO>1d9[]smf"юE"nU)EMŰRB1Ƈ{VC?Ptn)IENDB`elog-3.1.3-1/themes/default/icons/smile.png0000755000175000017500000000113512556103751017473 0ustar rogerrogerPNG  IHDR;֕JtIME > pHYsN N }gAMA aIDATxڕSMkA}AK~\! ! B B!AVxA0\E!Lʹwܰ @MwW^0#|k?q||x&0fF%*Pja pA +wXv R&G!DDh-(bf#NG}xVZ:n.E(3GϺh}돱q;!hMei^{.kXzz펁R"p{ms #"6`+1Bȁs{.RH2A{%`"DZWL9JIe>}3z<#s$qƐh?}|ٗ!8p;Q6oHՂh .5<LXpe&fC/Pphee &4#ݹ//ߜ-!m҂@N>4bwO\e$7k\)=-k<_>bBvȌe<T"rU_ϟQ+0߾O_~ׯ_}ׯ߿ׯ_XXY_@o@ϟ@Dm?YXXR[,VǏo?|'”ϟYXYY-@a0 g@_¤/?*G`rї/AQ,mb$_>I] d`܀71b `,D̅p/9?ġzi-   u #RJ41p%H7NITAMIENDB`elog-3.1.3-1/themes/default/icons/elc_smile.png0000755000175000017500000000125212556103751020316 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:10 +0100tIME #.y6 pHYsN N }gAMA aIDATxŔkAo'sh -A&" + t$}"iDNC؝3;͊\6? kg>}oެq7sCh4QVl"aV;JfvZ۟xCXdLt] {N.PU4 )] FQvF X;8IKQ&7K筵N! UH:qFJtn9"8"M/rjZK)gc12Fa.bPlh"WCrVTc8P/t2p%E.e_t!{hùBR(3[^Q ?;̼~RLoB1 Tu6Q~nmNFy4- $0ЇB 0SzEQһSVcb èT*Wez m=nKL3L$I׿~$VT,+;An@͆n,[_AQԮۀgIENDB`elog-3.1.3-1/themes/default/icons/elc_astonished.png0000755000175000017500000000122412556103751021345 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:29:12 +0100,^tIME ,z pHYsN N }gAMA aIDATxT=KAޝ;5RK kVB,$F! !$i%E"ڤ"ڜV؝ُwWevvݙgg֌/LpVmzZ:eKP˫çG5 C'aJʋ@AAV7u,k< ~|oH r\(䂘Rͥ>}z]ߕJɅ({f;_ @D7 Pӯ g4B(j+6Bp:#TJuQDnq, +кxD}1 |= `.8Bs8̄OtЕqSukq=kp8:|g2(5@`pjv]+e.;[[ph`9ӻi$mɗ_|A@)9:h~n{{Y֥AɯIENDB`elog-3.1.3-1/themes/default/icons/eld_line.png0000644000175000017500000000033112556103751020127 0ustar rogerrogerPNG  IHDRRtIME-w> pHYsN N }gAMA ahIDATxc`HmDTTdp(q>5e 1:;F;w[ G@`ddD)))@|=JPTTdF1'wIENDB`elog-3.1.3-1/themes/default/icons/elc_confused.png0000755000175000017500000000121612556103751021013 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:25:10 +0100K tIME .w pHYsN N }gAMA aIDATxڥT;KAHE 8Q,-T>8 QQ A[ TVvp BLڀ{vGcG"d|ff(lusŘxu|>__A~Kz8V4F4eYHʖr=Fd,$ʿB'esA;™+0@l=gYÎ̾s]>s9cX=΁2r4΅ ]s\ A'Alg :?3Z+D<0=SӗI:fH. ca^"ZFwPTcuH4W" 7H(@ ܤuAa  aJ!q!g%\B!6"T:mb{>u:?t n7FFmAˤ4hmqԑs= 2fT}:Iq.T aL"Ʉ\IW9D~VŢփضK|+-IENDB`elog-3.1.3-1/themes/default/icons/eld_larger.png0000644000175000017500000000164212556103751020462 0ustar rogerrogerPNG  IHDRR*tEXtCreation TimeSa 2 Feb 2008 14:04:37 +0100_DCtIME ޔP pHYsN N }gAMA aIDATxڥKSam6se2\bέm)AD``Y$ deVB_`S@SF(54ێs޷xtA~}ޗ v=0 tvv7RWW9Lå^I/'S3LeV˦%}6k742sCx5v3E+8Z$ Us¾e; bd /GȆ6IG=^UlܧRQ:f}>'c hc?gDNQ7uJ(arsLajCcLiI ×԰X 4XO3Z ݙZb1^2MJGd2+ff.A(:ݼQ.4%0 f|M/^=]] 6.LUBϟleeЬnosH,-_}>CSSb-%tBM7,_h{ڣ,_N6ܮoӵj)T]ɇ{Ea=u&)W'X"Rpɢ4[o׳{~ˡ@<6D%y+M@RNxT&Qy>xu_.R&zV,Mn/f~IENDB`elog-3.1.3-1/themes/default/icons/eld_delatt.png0000644000175000017500000000133412556103751020461 0ustar rogerrogerPNG  IHDRR*tEXtCreation TimeSa 2 Feb 2008 14:05:44 +0100tIME 8L pHYsN N }gAMA a5IDATxc`HmDTTdp(q %pzv6ݶhS--ed0U|00xvww}l p#53=LO u==p=@8iwu{ XLr/??+oP?.u+vs?66FSĽ0K{z޽䊊o22CZ[EE/] A?W y3t˗/] )PphhɁNA[H 5 ` ,:04V8M|L| d5k< _Tkj|A]L_a7 wd asׇܽq"zؚ5o23BDx; B{;M(//~E")vw)ਫ{//` "jHdH[Td;geн{0E`0@Ѓ߼Ȧ?LgB@^w@QQXziIENDB`elog-3.1.3-1/themes/default/icons/eld_arrow.png0000755000175000017500000000070612556103751020343 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:00:50 +0100qutIME ,0' pHYsN N }gAMA aIDATxӱN0ĺ@_#Ƙ`DJea`` Eb܁V* lIbVҥj`6NJ(%i!˲_iVI;wA6b9!Nަ&~9yRF^/"ulϟ7 RU"X)X C5ʚGB<9C. 5kafHTjTe2ܓy.ËCϹl^?,9`{?.|zL~i#:di[9ˎ *‡8 OZIENDB`elog-3.1.3-1/themes/default/icons/icon8.png0000755000175000017500000000056712556103751017412 0ustar rogerrogerPNG  IHDR extIME* pHYsN N }gAMA aWPLTE$.TH2L9D1+ug?+#"j[`OSA@" 8#Z>8l ^MM;eUdN7tRNS@fIDATx5Y DY  EE9T#Zb{A/mԁ]v#W4rѶE s)ȾxDh&4c&ClXkB{lv`Xa0STF1g:IENDB`elog-3.1.3-1/themes/default/icons/elc_email.png0000755000175000017500000000043712556103751020300 0ustar rogerrogerPNG  IHDR7tIME +ƣ pHYsN N }gAMA aPLTEdIDATxuQ Da_`fG_wƛ; /ojӡE9p%?tȡAT bh{{C mMϹz|s 2IENDB`elog-3.1.3-1/themes/default/icons/eld_clock.png0000644000175000017500000000174412556103751020304 0ustar rogerrogerPNG  IHDR7+tEXtCreation TimeMo 16 Mai 2005 11:05:31 +0100J{vtIME & /K pHYsN N }gAMA a"PLTEބƱ=O\ &?=TfatzZlrNcp0I-?ż'@H^lݱֲVgm,HO_d +Hഝ}ƞ7Ux.KbJetگ縂 .P7^}ޱsȟi<]ug}h@avٮII;JJ;Ҩ޲%IkJk^uxglr??2""Υ3`JlRcdj}}dyyass[iiSOO?[[I߲uyf0^]|Mbhȟ֫٭ڮܰجЦ}?g:Tȩݺ߼޺ӲL^aKm5\\eaԽw ,K-Dñ.AJ>a{p2Aļ͟1@D>]wé=O_>GGyQWS0ANɆhwbs|ur^IDATxc` &8 &H|MlE_|U׬]E|sΛ`K"'L4y L4m xKk[{G'ttu+*kjka9ynE%0ظĤԴ,x`PpHhXxDhoTt L?&nfnaiemckkhg w)\\VN^AQIYYDEUM])ED%$eÍnL.bMӇIENDB`elog-3.1.3-1/themes/default/icons/eld_astonished.png0000755000175000017500000000123112556103751021344 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:29:19 +0100+ WtIME 2,G pHYsN N }gAMA aIDATxڭS=kAޝ;?H XX[[BkS "B́mlV!? mƴ c؝@85w̾33㔎,(^V!&2E(CWPɿ=,ߡ?7~7}Jt{N௮@e9jq'7KAvմf.뺧MЍx5=#hچ=5ӽ?0Ae 4MC!a#ZE*w C(6,ӄW}~ qgs0MF&!ð60c2zvnFT0Ŗ@R]W+>}_cZTo wSElWl؅<wGoܼ z ED(9%6,B2%c|zBr)!=}Kc*IV6<)(BxxXJ\ו%j>]PHQԘ:8*pK%9;8?xz/ExIENDB`elog-3.1.3-1/themes/default/icons/wink.png0000755000175000017500000000114212556103751017330 0ustar rogerrogerPNG  IHDR;֕JtIME(W# pHYsN N }gAMA aIDATxڕS=hAv(i,"D16[[ B A! H   1 (c%*jm@PmTDF I؛L ogvޛ7,A'`ck?~?zXxpSnlu`mͦA=.lD(]ܸŪv_ߠtAZ%+Q:GÕU^Lv)8urh8&^./MR0 ͹eD C)n:8Fke%WKnaHtIME ":z pHYsN N }gAMA a IDATxڭMh@7V(q(Ȃ< E"B/E(Ȃ  =؃aEXPAAPVD{CV/=*"ē-̛L⋑,[e2{S4M)Z?#4e!3o zQlEg}sEApEX\7LUþ< 8"O/:!Q7vH*?I4.7W2-Vw+?|b+3ݚ9K鋰mtyuW 5ZӸwnU$^|-0~k\IyGql.MJ !Hqa:Z)%8QoUM)źQC!08HQVR.0YԼqG!}q0\,-LcvZ((ܽٷ /=0A,Lqq2tzO=AQ*N]\D$3@Vonq,9GY˺_k޿kVvL{xuJ jUDn?\ \IENDB`elog-3.1.3-1/themes/default/icons/eld_yawn.png0000755000175000017500000000125512556103751020167 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:17 +0100B>tIME :6Z1 pHYsN N }gAMA aIDATxڭ=hAwg.BDDA* $@ ? -"$$XuTD+Ƅc{fgw}5>f?fo*lqFj5xiZ Qݧ=m6FIou61}qyGsYCcɠ$CFȶ΀P.kҮOG,EQ'؍LN>?sp L3Q6Yy~66:aMu{˭Q~צ_g?]gQRCo9ɳ/1N:2F-!ܙ>?xY .JA L1&`ϊ•J^,:$Rr7 n8#SSk9 RbqogW%>B ̻K/8g:Ȍ% gj11SiqvcIAsBRaB1&{wNkVT*6UDjJ.jIENDB`elog-3.1.3-1/themes/default/icons/elc_crying.png0000755000175000017500000000124012556103751020475 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:27:53 +0100kPtIME 8< pHYsN N }gAMA aIDATxTKQ~3":"!Jҕm"B[yA"f- Ckj 7br<}Ώ0y{[qםs9-\s4̀8WmFP.c\²bXNm;t1;uZNJj5Si^(aZ֣L"=E[)=%Bo 0064p:0muq!uh~p/023 YA_n|;tWNu)w_|s.0 /G{Kc7JQ a7#`r$g O<)a4%ka@_rQ4'/Ll Deׂ1|?V*`~=Y a~XB`T"mLu6K0 4t"^Bef&ӻi-a9RQ5=}ereZ_(b>UIENDB`elog-3.1.3-1/themes/default/icons/icon12.gif0000644000175000017500000000171212556103751017434 0ustar rogerrogerGIF89a_hDJ]hn{1/cY732/2*qI0430 {3[$' :=#!, ء` @Bv`AÄ `0C ,p0G> 8vذ 8XPA DȠ vp 4xacX0t{0R\"KlfYY2z`bs]{ l̮}ܕ}u> 4$VSg؝zcRz\*kJ 8؝ZHBBIBӀ4"CRzWRr]ƼD<-<\'/8 5\#23܆ip)p/?}4U4H;>=}w|&9RsN5H@)^-\k9TpM J0śѬgX8M`_r[nܪ UB!DIENDB`elog-3.1.3-1/themes/default/icons/elc_rotleft.png0000644000175000017500000000124512556103751020663 0ustar rogerrogerPNG  IHDRRtIME 8(} pHYsN N }gAMA a4IDATxc?eHݿl#-[$Y /^P&" .5~s|߯_b,B쌌 `ʇVz.%b/ Gm}v]p++#z,>zς=R\T!F-f SWɟ?òsEy"?6 c1^"Bљ3T%E$?Yu,aw1v:JL/_gsBb͔K3dSSӤ?|ut}G?gbcc9w Q+z_~2J^Fѷooa n o䥸=-,qO|b #Kr Q?7dib'^?zPc GRop IENDB`elog-3.1.3-1/themes/default/icons/elc_tongue.png0000755000175000017500000000125112556103751020505 0ustar rogerrogerPNG  IHDRR+tEXtCreation TimeMo 16 Mai 2005 12:33:10 +0100tIME #Q45 pHYsN N }gAMA aIDATxTKQwy0Csҽ2'AOz !SD?`BP3ҡE A@@uXTllD{cޛP츚{˛|>o8; ˲l\!B]Ev& JhBlHJ8B`Q͋ 4D9ȅR;{0KͥfVJV\Kχތ19ڻ{͖I:!h$MRfI SHm:1U@d-rP(ӻi.Xat 1~xq{;}ct9q4$ӏ?oH: w IENDB`elog-3.1.3-1/themes/default/tiny.css0000755000175000017500000000207112556103751016236 0ustar rogerroger/* tiny formatting */ body { margin:0px; } td { font-size:8pt; } .tabs { font-size:8pt; } /* unselected and selected group tabs */ .gtab a { padding-left:1px; padding-right:1px; } .sgtab a { padding-left:1px; padding-right:1px; } .ltab a { padding-left:1px; padding-right:1px; } .sltab a { padding-left:1px; padding-right:1px; } .title1 { font-size:10pt; } .title2 { font-size:10pt; } .title3 { font-size:10pt; } .menu1 { font-size:8pt; } .menu2a { font-size:8pt; } .menu2b { font-size:8pt; } .menu3 { font-size:8pt; } .menu4 { font-size:8pt; } .attribhead { padding:1px; font-size:8pt; } .attribname { padding:1px; } .attribvalue { padding:1px; } .attribvalue2 { padding:1px; } .messagelist { font-size:8pt; padding:1px; } .messageframe { padding:1px; } .messagepre { font-size:8pt; } .bytes { font-size:8pt; } .summary { font-size:8pt; } .form1 { padding:1px; } .form2 { padding:1px; } /* error message */ .errormsg { font-size:10pt; } .notifymsg { padding:1px; font-size:10pt; } elog-3.1.3-1/themes/default/delete.png0000755000175000017500000000043612556103751016514 0ustar rogerrogerPNG  IHDR(-StIMEz= pHYsN N }gAMA a9PLTEYY5500[[LLGG ffnqtRNS@f[IDATxU 2TqWˀ7 =DR@ODi]r=zVm~QyO3lhb;dn S ɣ\Q1IENDB`elog-3.1.3-1/themes/default/lock.png0000755000175000017500000000034212556103751016176 0ustar rogerrogerPNG  IHDRytIMEbw pHYsN N }gAMA aPLTE(_tRNS@fIIDATxc```RR```f``PT3 Ba)) J d F aa" (  2IENDB`elog-3.1.3-1/themes/default/all_entry.png0000755000175000017500000000170112556103751017237 0ustar rogerrogerPNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<SIDATxbTUg20 4*ba(PD/DO( 񛁁qFby߿2|?@k@&B,@_xmFGM!_2:`6###п [/|gH A kS  /3?&߯_1~Z%W aXd%æM;^z'kC&1' ,2 ~| ϐt2#CDD_`F/'ö112 Ȕ??~00J3k 04Xd0@ aY9*L#y oE`4.?+;ÿ;IB~u _?m6(]󇁅0f  !  fx/0]]{{;eV2~& Ž@X/ d`2d`b`af@@a_@/21_3}_7Э@`._ Y2p04bd Y!!P?p(S9A y;X~3ˋ_m?A=H?h.L@B2 e `"@1! mh:{ܺIENDB`elog-3.1.3-1/themes/default/clock.png0000755000175000017500000000172512556103751016347 0ustar rogerrogerPNG  IHDRa+tEXtCreation TimeMo 18 Apr 2005 19:44:46 +0100l tIME)Y pHYs  d_gAMA a-IDATxemlSev۽Qpڀ"/.mF2M@qD̄XIG2BHD% |`fdc+51*ʘ9]fIy}s9;lZJ'=I4W^+S^I`Bem?=gw?@1~CA)=˥,z7b/(wh?$rW[Kz*2'? |?ۇ>û(L3ѵ[7{>m{ 456Aaame9r9drk?ئhOPs'#@U E; t6GGNڧ_ ; $yء" VO ūwxCb'2Q"eqCoHd$02;DZBŤ﬽i92 S躛W$1/&6P&zpA4!PYy/e˂hN TWWp8T}EHTtiݶX'`VQRu^]Mhh*8#57UwGyowoKpߘOC\$#~~G._wօʁ.@o|N<#[-Q~5g= R{`aǑw}$5+a\Cͥ&5Z>kA?8NyY1KX/ڭ  FD|bIENDB`elog-3.1.3-1/themes/default/edit.png0000755000175000017500000000042512556103751016175 0ustar rogerrogerPNG  IHDR(-StIMEbw pHYsN N }gAMA a!PLTEtRNS@fjIDATxU[QDid/8I9Gyt|u, >"Sx=":UL\0fx${yj|ЭEn`צs܍VIENDB`elog-3.1.3-1/themes/default/favicon.png0000755000175000017500000000075612556103751016704 0ustar rogerrogerPNG  IHDRh6tIME %ÜU pHYsnu>gAMA a}IDATxڕR;KAٻ !'`)j' ZXj "S 6"hbow{X~7Suf@?D4EUitem+8 d8)HIKzӛ_ON$եzsgXɾaX~}0 ,h͌ժx"HiztJ.enz/ˆU`d&3_*&|㬘i>>_v&|I`\ù:aN\:r`^tf \ȹT[{E{0;5r(g\FȤM[@>ӭɔ "P8͑H& ^ID)G΂\IENDB`elog-3.1.3-1/themes/default/elog.css0000755000175000017500000003331512556103751016206 0ustar rogerroger/* Standard CSS file for elog. Please do NOT MODIFY this file directly. Use the CSS = option in elogd.cfg instead and overwrite settings from this file. */ body { margin:3px; color:black; background-color:white; font-family:verdana,tahoma,sans-serif; /* background-image:url(elog.gif); */ } /* standard link colors and decorations */ a:link { color:#0000FF; text-decoration:none } a:visited { color:#800080; text-decoration:none } a:hover { color:#0000FF; text-decoration:underline } a:active { color:#0000FF; text-decoration:underline } a:focus { color:#0000FF; text-decoration:underline } td { color:black; font-size:12px; } /* frame table */ .frame { width:100%; } /* printable frame table */ .pframe { width:600px; } /* standard formatting for logbook tabs */ .tabs { font-family:sans-serif; font-size:10pt; background-color:white; } /* logbook selection page */ .selframe { width:60%; background-color:#486090; border:1px solid #486090; font-size:12pt; } .seltitle { border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; background-color:#CCCCFF; color:#486090; text-align:center; } .selexp { border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; background-color:#CCCCFF; color:#486090; text-align:left; font-size:10pt; } .selspace { width:2%; border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#EEEEEE; } .selgroup { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#FFCCFF; padding:3px; text-align:left; font-weight:bold; font-size:14pt; } .sellogbook { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; padding:3px; text-align:left; font-weight:bold; } .selcomment { font-size:8pt; } .selentries { background-color:#E0E0A0; border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; text-align:center; font-size:10pt; } /* unselected and selected group tabs */ .gtab a { background-color:#B0E0B0; padding-left:5px; padding-right:5px; } .gtab { background-color:#B0E0B0; border-right:1px solid #409040; } .sgtab a { color:white; padding-left:5px; padding-right:5px; } .sgtab { background-color:#486090; color:white; border-right:1px solid #084070; } .sgtab a:visited { color:white; } /* bug for IE */ /* unselected and selected logbook tabs */ .ltab a { background-color:#E0E0E0; padding-left:5px; padding-right:5px; } .ltab { background-color:#E0E0E0; border-top:1px solid white; border-left:1px solid white; border-right:1px solid gray; } .sltab a { background-color:#486090; color:white; padding-left:5px; padding-right:5px; } .sltab { background-color:#486090; color:white; border-left:1px solid #E0E0E0; border-right:1px solid #084070; } .sltab a:visited { color:white; } /* bug for IE */ /* logbook title, left, middle and right cell */ .title1 { padding:5px; background-color:#486090; border-bottom:1px solid black; border-left:1px solid #E0E0E0; color:white; font-size:medium; font-family:sans-serif; text-align:left; } .title1 a:visited { color:#A0FFA0; } .title1 a:link { color:#A0FFA0; } .title2 { background-color:#486090; border-bottom:1px solid black; color:white; font-size:medium; font-family:sans-serif; text-align:center; } .title3 { border-bottom:1px solid black; border-right:1px solid black; background-color:#486090; text-align:right; width:100px; } /* main menu row */ .menuframe { border:1px solid #486090; border-top:1px solid white; border-left:1px solid white; background-color:#E0E0E0; height:29px; } .menu1 { text-align:left; font-size:10pt; vertical-align:middle; } .menu1a { text-align:center; width:110px; font-size:10pt; font-weight:bold; vertical-align:middle; } .menu2a { text-align:left; font-size:10pt; } .menu2b { text-align:right; font-size:10pt; } .menu3 { text-align:left; font-size:8pt; font-weight:bold; } .menu4 { text-align:right; font-size:10pt; vertical-align:middle; } .menucenter { border:1px solid #486090; border-top:1px solid white; border-left:1px solid white; background-color:#E0E0E0; text-align:center; font-size:10pt; } .toolframe { border:1px solid #486090; border-top:1px solid white; border-left:1px solid white; background-color:#E0E0E0; padding:2px; } /* frame table in listings */ .listframe { border:1px solid #0000FF; border-top:1px solid white; border-left:1pc solid white; background-color:#486090; border:0px; } /* title row in listing */ .listtitle { border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; background-color:#CCCCFF; text-align:center; width:0%; } .listtitle2 { border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; background-color:#DDDDFF; text-align:center; width:100%; } .listtitle3 { border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; background-color:#FFCCFF; text-align:center; width:0%; } /* odd and even lines in message listing */ .list1 { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; text-align:center; } .list2 { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFFFB0; text-align:center; } .list1h { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; text-align:center; font-weight:bold; } .list2h { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFFFB0; text-align:center; font-weight:bold; } .listdraft { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFD0D0; text-align:center; font-weight:bold; } .listatt { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#B0FFB0; text-align:center; } .draftsel { background-color:#DDEEBB; padding:5px; border-bottom:1px solid #486090; } /* attachment line */ .attachment { border-left:1px solid gray; border-right:1px solid gray; border-bottom:1px solid gray; background-color:#FFFFB0; text-align:left; } /* threaded listing */ .thread { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; text-align:left; } .threadreply { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFFFB0; text-align:left; } .threadh { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; text-align:left; font-weight:bold } .threadreplyh { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFFFB0; text-align:left; font-weight:bold } .threaddraft { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFD0D0; text-align:left; font-weight:bold } /* attribute names and values on single message page */ .attribhead { background-color:#E0E0A0; border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; padding:2px; font-size:10pt; font-family:sans-serif; } .attribname { width:150px; background-color:#CCCCFF; border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; padding:3px; } .attribvalue { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; padding:3px; } .attribvalue2 { border:1px solid #808040; border-top:1px solid white; border-left:1px solid white; background-color:#FFFFB0; padding:3px; } /* message cell in listing */ .messagelist { background-color:white; border-right:1px solid gray; border-left:1px solid gray; border-bottom:1px solid gray; font-family:'lucida console',courier,monospace; font-size:10pt; padding:5px; } /* message display in single message page */ .messageframe { background-color:white; border:1px solid #486090; border-top:0px; padding:3px; } .messagepre { font-family:'lucida console',courier,monospace; } /* frame around attachments */ .attachmentframe { border:1px solid grey; border-top:1px solid lightgrey; border-left:1px solid lightgrey; background-color:white; padding:5px; } /* maximal attachment img width */ .attachmentframe img { max-width:100%; } /* attachment size */ .bytes { font-family:'lucida console',courier,monospace; font-size:10pt; } /* summary display (threaded mode expand) */ .summary { border:1px solid black; border-top:1px solid white; border-left:1px solid white; background-color:white; font-family:'lucida console',courier,monospace; font-size:8pt; } /* input forms */ .form1 { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; padding:3px; } .form2 { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; padding:3px; } /* error message */ .errormsg { border:1px solid #486090; padding:5px; background-color:#FFB0B0; text-align:center; font-size:medium; } /* notification (warning) */ .notifymsg { padding:5px; border:1px solid #806000; border-top:1px solid white; border-left:1px solid white; background-color:#FFD070; text-align:center; font-size:small; } .notifyleft { padding:5px; border:1px solid #806000; border-top:1px solid white; border-left:1px solid white; background-color:#FFD070; text-align:left; font-size:medium; } /* dialog boxes (login, delete, etc.) */ .dlgframe { width:50%; background-color:#486090; border:1px solid #486090; margin-top:2cm; } .dlgerror { background-color:red; padding:10px; font-size:small; text-align:center; font-size:medium; color:white; } .dlgtitle { background-color:#486090; color:white; font-size:medium; text-align:center; padding:5px; } .dlgtitle a:visited { color:#A0FFA0; } .dlgtitle a:link { color:#A0FFA0; } .dlgform { background-color:#DDEEBB; padding:5px; text-align:center; } /* login page */ .login_frame { width:200px; border:1px solid #D0D0D0; border-radius:25px; overflow:hidden; margin-top:2cm; -webkit-box-shadow: 10px 10px 20px 0px rgba(128,128,128,1); -moz-box-shadow: 10px 10px 20px 0px rgba(128,128,128,1); box-shadow: 10px 10px 20px 0px rgba(128,128,128,1); } .login_form { background-color:#DDEEBB; padding:8px; padding-left:25px; padding-right:25px; text-align:center; } .login_form_bottom { border-bottom-left-radius:24px; border-bottom-right-radius:24px; background-color:#DDEEBB; padding:8px; padding-bottom:20px; padding-left:25px; padding-right:25px; text-align:center; } .login_title { border-top-left-radius:24px; border-top-right-radius:24px; background-color:#486090; color:white; font-size:18px; text-align:center; padding:10px; } .login_submit { font-size:18px; padding:10px; padding-top:5px; padding-bottom:5px; background-color:rgb(69,181,69); color:white; border-radius:8px; } .overlay_wrapper { position: relative; top:0; left:0; z-index:10; background:none; } .overlabel { pointer-events: none; position: absolute; top: -5px; left: 5px; z-index: 1; color: #999; font-size: 18px; } .login_input { font-size: 18px; padding: 3px; width:200px; } .calwindow { margin:0px; color:black; background-color:white; font-family:sans-serif; } .caltitle { background-color:#4682B4; padding:3px; font-size:medium; text-align:center; font-weight:bold; color:white; } .calhead { background-color:#468284; color:white; padding:0px; font-size:12px; text-align:center; font-weight:bold; } .calday { background-color:#CCFFCC; padding:0px; font-size:16px; font-weight:bold; text-align:center; cursor:pointer; } .calcurday { background-color:#4040A0; padding:0px; font-size:16px; text-align:center; font-weight:bold; color:white; cursor:pointer; } .calsel { background-color:#208020; padding:0px; font-size:16px; text-align:center; font-weight:bold; color:white; cursor:pointer; } .calsat { background-color:#90C090; padding:0px; font-size:16px; font-weight:bold; text-align:center; cursor:pointer; } .calsun { background-color:#90C090; padding:0px; font-size:16px; font-weight:bold; text-align:center; color:red; cursor:pointer; } .uploadwindow { margin:0px; color:black; background-color:white; font-family:sans-serif; padding:0px; } .uploadtext { background-color:#CCCCFF; border:1px solid #0000FF; border-top:1px solid white; border-left:1px solid white; padding:3px; } .uploadvalue { border:1px solid #308000; border-top:1px solid white; border-left:1px solid white; background-color:#DDEEBB; padding:3px; } .quotetable { width:98%; background-color:black; border:0px; padding:0px; } .quotetitle { background-color:#486090; padding:3px; font-size:12px; font-weight:bold; color:white; } .quote { background-color:#FFFFB0; padding:10px; } .holder { background-color: white; border:6px dashed #ccc; height:50px; line-height:50px; margin:5px; color:#999; font-size:2em; text-align:center; } /* ELOG home page link at bottom of page */ .bottomlink { font-family:sans-serif; font-size:xx-small; } elog-3.1.3-1/themes/default/up.png0000755000175000017500000000041312556103751015671 0ustar rogerrogerPNG  IHDRa~etIMEz= pHYsN N }gAMA a-PLTEff333f3ff33f̙fhtRNS@fTIDATxڥ E!e1Y9s/ ψeEz VURճîtP. u*N!V)|,p' _IENDB`elog-3.1.3-1/themes/default/cal_prev.png0000755000175000017500000000047212556103751017045 0ustar rogerrogerPNG  IHDRRtIMEz= pHYsN N }gAMA a0PLTEFXagmOztځݙ|}IDATxc``{ݽ{t);+gt.`໻pɶUUj0Й"Txj:6 d =' @=g\XO&1Hs&D)Y{>Z9sʤഴtUB&΢ V0S IENDB`elog-3.1.3-1/themes/default/stop.png0000755000175000017500000000050712556103751016236 0ustar rogerrogerPNG  IHDR(-StIMEbw pHYsN N }gAMA a"}+Rsê$(bǡ2f)*O^"! Z!!TOSZ /dev/null 2>&1 RETVAL=$? if [ $RETVAL -eq 0 ] ; then touch /var/lock/subsys/elogd echo_success else echo_failure fi echo ;; stop) echo "" if [ -f /var/run/elogd.pid ] ; then echo -n "Stoping elogd: " /bin/kill `cat /var/run/elogd.pid` rm -f /var/lock/subsys/elogd rm -f /var/run/elogd.pid echo_success echo else echo -n "No elogd running?" echo fi ;; status) status -p /var/run/elogd.pid /usr/local/sbin/elogd RETVAL=$? ;; restart|reload) $0 stop sleep 1 $0 start ;; *) echo "Usage: $0 {start|stop|status|restart}" exit 1 esac exit 0 elog-3.1.3-1/contrib/0000755000175000017500000000000013076344742013272 5ustar rogerrogerelog-3.1.3-1/contrib/README0000755000175000017500000000713112556103750014151 0ustar rogerrogerNAME elog2sql - copy an elog logbook to a MySQL database SYNOPSIS parsecfg.pl - translate the elogd.cfg file to a MySQL database template parselog.pl - translate a elog logbook to a MySQL insert template file DESCRIPTION elog2sql was created to help translate logbooks created by the program "elog" (http://midas.psi.ch/elog/) from the native elog flat file format to a MySQL database. I found elog to be one of the easiest, yet feature-rich web-based programs for maintaining journals and logbooks; The program is fast, the administration and setup are easy and simple, and the features are extremly well suited for the application. However, I was bothered by all-in-one (web server and application) design of elog, rather then the typical use of Apache/PHP/MySQL platform for such an application. IMHO, having elog on such a platform will allow easier and simpler integration of new functions and extensions for elog. The long-term goal is to create a version of elog functionality based on the LAMP platform. Therefore, I created a set of perl scripts that will allow the translation of elog logbooks into a MySQL database. The design and implementation of these scripts are a simple one, and allow the one-time copying of a set of logbooks. The elog2sql toolkit consists of two scripts. The first script, parsecfg.pl, reads a elogd.cfg, and creates a sql file that will create a set of db tables corresponding to elog logbooks. The second script, parselog.pl, takes a set of elog logfiles, and creates a sql file that will enter the logbook data into the database. The result is a copy of the elog logbook that can used as desired inside the framework of MySQL. Attachments are handled by inserting an entry of the attachment name into an seperate attachment table. This allows multiple attachments per entry. USAGE 1) Create a MySQL database (example: elog) 2) Create the sql database templates parsecfg.pl < elogd.cfg >elog.sql 3) Create the MySQL tables mysql -u user -p elog journal.sql 5) Load the table into MySQL mysql -u user -p elog -a from= -a cc= -f attachment-1 -f attachment-2 -f ... attachment-1 is the body of the message and attachment-2... are the actual MIME attachments. Set the elogd configuration to display attachments, so that the message body is immediately visible. Defaults: -h localhost -p 8080 -l emails If no `-l logbook' flag is specified, then the entry is sent to the `emails' logbook, so make sure that logbook exists. Save this in, e.g, ~/bin/mailelog, and make sure it is executable (`chmod +x mailelog') and on your path (bash: `export PATH=$HOME/bin:$PATH' or csh/tcsh: `setenv PATH $HOME/bin:$PATH') Bugs: multi-message files don't work. can't add other attributes. if the logbook doesn't have attributes subject, from, cc, they are quietly lost. -- Sridhar Anandakrishnan elog-3.1.3-1/contrib/parselog.pl0000755000175000017500000001263412556103750015446 0ustar rogerroger#!/usr/bin/perl # Copyright 2000 + Stefan Ritt # # ELOG 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. # # ELOG 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 ELOG. If not, see . =head1 NAME parselog.pl - create a sql db input file from a elog logbook =head1 SYNOPSIS B I I I =head1 DESCRIPTION reads the *.log files (elog logbook) on stdin and writes to stdout the sql statements for inputing the logbook data into an MySQL database. You will need to create the MySQL database first by using the parsecfg.pl command file. Default entries are created by ID, Date, Reply_to, In_reply_to, and Notes (the text of the logbook entry). Other attributes are translated as needed. Note that any attribute name that contains "Date" is translated to the MySQL datetime variable type, and the script will attempt to convert the attribute value to the datetime format. Attachments entries are handled by created a separate table "<$table_prefix>attachments", which can be indexed by the logbook_name and entry id. =head1 OPTIONS =over 4 =item I Required Option: The name to use of the database table. Should be the same name as used by elog for a logbook, and created by parsecfg.pl. =item Globals See the "config.inc" file for global parameters. Currently, the default data type, and table_prefix are assignable options in this file. =back =head1 FILES =over 4 =item I<*.log> The log files in a elog logbook directory =item I A mysql output command file, suitable to be used as input into "mysql", as in, mysql -u <> -p <> << I =back =head1 BUGS this is a quick and dirty programming job, so I expect this to be a brittle program. Use at your own risk, and check the resulting output before using mysql. Elog is sloppy on logbook formats - Extra attributes could remain in a elog logbook file if the user created a logbook, and then deleted attributes later on. These extra attribute entries will break the sql file, and you will need to delete them from the logbook or from the sql file manually. Attribute names containing "Date" are assumed to contain datetime information and are automatically converted to MySQL datetime format. This may not be the correct action and will cause things to break. =head1 LICENSE The author issues this code under the GPL. (See GPL.txt) =head1 AUTHOR dave@davidfannin.com =cut use Time::ParseDate ; use Date::Format ; require "config.inc" ; if ($#ARGV == 0) { $table_name=$ARGV[0] ; } else { print "usage: parselog.pl \n" ; print "you are missing the logbook name\n" ; exit(1) ; }; $openentry = 0 ; sub tokenize($) { my $t = shift ; $t =~ s/^\s+// ; # get rid of leading ws $t =~ s/\s+$// ; # get rid of trailing ws $t =~ s/\s+/_/g ; #replace spaces with _ return $t ; } sub quote_sql($) { my $qstr = shift ; # escape special chars $qstr =~ s/(['"\\\0])/\\$1/g ; return "'".$qstr."'" ; } sub entry($) { my $e = shift ; if ($openentry) { emit_entry() ; } ; $debug && print "#entry:$e\n"; $e=tokenize($e) ; $current_entry=$e ; $openentry=1 ; } sub attribute($$) { my $name = shift ; my $value = shift ; $name=tokenize($name) ; $debug && print "#attribute:$name:$value\n" ; if ( $name eq "Attachment" ) { if ( $value == "" ) { return ; } push @attachment, "$current_entry;$value" ; return ; }; return if ( $name eq "Id" ) ; return if ( $name eq "Encoding" ) ; if ( $name =~ /Date/ ) { $value=time2str("%Y-%m-%d %X",parsedate($value)) ; } $attribute{$name}=$value ; } sub encoding { $encoding = ""; ; while () { /^\$\@MID\@\$: (\d+)$/ && return ; $encoding .= $_ ; } } sub option($$) { my $on = shift ; my $ov = shift ; @ol=split(/,/,$ov) ; foreach $olv (@ol) { $olv=tokenize($olv); $debug && print "#$current_section:options:$on:$olv\n"; } } sub moption($$) { my $mon = shift ; my $mov = shift ; $debug && print "#$current_section:moptions:$mon:$mov\n"; } sub ioption($$) { my $ion = shift ; my $iov = shift ; $debug && print "#$current_section:ioptions:$ion :$iov\n"; } sub roption($$) { my $ron = shift ; my $rov = shift ; $debug && print "#$current_section:roptions:$ron:$rov\n"; } sub emit_entry { print "\n\nINSERT INTO $table_prefix$table_name \n" ; print " SET id = ($current_entry),\n" ; foreach $name (keys %attribute) { print " $name = (".quote_sql($attribute{$name})."),\n" ; } print " Note = (".quote_sql($encoding).");\n"; } sub emit_attachment { while($value = pop(@attachment)) { ($id,$filename)= split(/;/,$value) ; print "\n\nINSERT INTO ".$table_prefix."attachment \n" ; print " SET logname = (\"$table_prefix$table_name\"),\n" ; print " pid = ($id),\n" ; print " filename = (\"$filename\");\n" ; } } while() { chomp ; /^Encoding:/ && encoding() ; /^\$\@MID\@\$: (\d+)$/ && entry($1) ; /^([\w\s]+): (.*)$/ && attribute($1,$2) ; } if ($openentry) { emit_entry() ; } ; emit_attachment() ; elog-3.1.3-1/contrib/config.inc0000755000175000017500000000227612556103750015236 0ustar rogerroger# elog2sql # global configuration vars # Copyright 2000 + Stefan Ritt # # ELOG 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. # # ELOG 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 ELOG. If not, see . # # set the database table names to "$table_prefix" # This is used in case in only allowed one database # and want to avoid table naming conflicts. # normally, keep the default to ("") $table_prefix = "" ; # # set the default element type for mysql # (each elogd attribute is set to this type) # $default_element_type = "varchar(100)" ; # # set the debug flat : set to 1 if you want to see debugging output. # $debug = 0 ; # # Do not change the variables after this line # $version = "0.99" ; elog-3.1.3-1/contrib/elogsubmit.txt0000755000175000017500000000703112556103750016203 0ustar rogerrogerI have created a javascript to be used as a browser link that allows a one step cut and paste from a web browser into a elog logbook. The intended application is allow a user to do a text selection in a web browser, then click on a bookmark that automagically pastes the selected text, the current browser page url, and the current browser page title into a pre-defined elog logbook. I do some research where I would like to save some text from a webpage, but also have a record of where the webpage came from. However, you should find that you can extend this script in a varity of ways for your own application. The script is a simple one: it uses javascript in a saved bookmark to get your selected text, title, and url, and then creates a new browser window with a elog form, and print the document variables into the form, and then submits the form to elog. The key advantage to this approach is that you can use the "post" command, rather than "get", to submit to the text section of an elog logbook. The only way I found now to submit to elog via a bookmark is using the "get" command, and it doesn't allow entry of the "text" field, only attribute fields. The second major advantage to using POST is that you can submit a much large quanity of information ; However, some checking on this leads me to believe that the limit is browser and server depended, so YMMV. However, a great discussion on the limits of browsers can be found here: http://www.squarefree.com/bookmarklets/browsers.html . One of the major limits is that IE6.0 browsers have a maxium of 508 bytes per bookmark - This book runs over 800 bytes, so I suspect tha IE6+ will not allow it. I tested the link with Mozilla and Firebird 0.7. This script will need to edited for you to use with your elog logbook. The script should be fairly self-explainitory, if you are used to html forms and have some exposure to javascript. You will need to modifiy the following fields: 1) in form action = http://ELOG_URL/LOGBOOK/?cmd=New change the link to point to your specific logbook to be used for entry. 2) the attribute fields need match up with the ones in your logbook. The ones listed in the template are Author, Email, Title, and URL. If you have fixed fields (like Author and Email), then you can predefine these fields as shown. I have the page title used as the entry for Title, and the page url is use as the URL attribute. Finally, I have the text selection used as the entry for the Text field. You can add additional fields by creating a new segment in the script. For those more clever than me, you can concatinate the title, url and selection to paste into the Text area as well. 3) once you have a edited version of the script (make sure you keep it as a single line), you can then create a new bookmark in your browser, and then paste the script into the properties->location field (for Mozilla/Firebird) or the properties->url field (IE). Give it a good name like "post to elog" 4) once saved, you can then go a web page, select some text, and then go to your bookmarks and click on the bookmark. It should then create a new window in elog with a completed logbook entry. some notes: 1) again, this may not work on IE6+ browsers due to M$ limitations. 2) You may have to be logged in already to elog for this work - I have not tested the interaction using a password protected elog 3) You can only post to a single elog logbook - You'll need to have multiple bookmarks for multiple logbooks. -- Fred Hooper elog-3.1.3-1/contrib/parsecfg.pl0000755000175000017500000001234512556103750015423 0ustar rogerroger#!/usr/bin/perl # Copyright 2000 + Stefan Ritt # # ELOG 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. # # ELOG 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 ELOG. If not, see . =head1 NAME parsecfg.pl - create a sql db template from a elog.cfg file =head1 SYNOPSIS B I I =head1 DESCRIPTION reads the elogd.cfg (elog config file) on stdin and writes to stdout the mysql statements for creating of database template for that elog configuration. A sql template is created for each logbook, with each attribute being assigned to a sql column. Default columns are automatically created for ID, Date, Reply_to, In_reply_to and the entry text ("Notes"). Additional columns are created, one for each attribute. Attachments are handled by created a separate table "<$table_prefix>attachments", which can be indexed by the logbook_name and entry id. =head1 OPTIONS =over 4 =item Globals See the "config.inc" file for global parameters. Currently, the default data type, and table_prefix are assignable options in this file. =back =head1 FILES =over 4 =item I a well-formed elogd.cfg file =item I A mysql output command file, suitable to be used as input into "mysql", as in, mysql -u <> -p <> << I =back =head1 BUGS this is a quick and dirty programming job, so I expect this to be a brittle program. Use at your own risk, and check the resulting output before using mysql. =head1 LICENSE The author issues this code under the GPL. (See GPL.txt) =head1 AUTHOR dave@davidfannin.com =cut require "config.inc" ; # do not change these values, for development only $opentable = 0 ; # # trims whitespace, and removes blanks between words # sub tokenize($) { my $t = shift ; $t =~ s/^\s+// ; # get rid of leading ws $t =~ s/\s+$// ; # get rid of trailing ws $t =~ s/\s+/_/g ; #replace spaces with _ return $t ; } # # starts a new logbook # sub section($) { my $s = shift ; if ($opentable && ($current_section ne "global")) { emit_table() ; } ; $debug && print "#section:$s\n"; $s=tokenize($s) ; $current_section=$s ; $opentable=1 ; } # # reads a logbook's comment line # sub comment($) { my $c = shift ; $debug && print "#$current_section:comment:$c\n"; $comment{'$current_section'}=$c ; } # # parses the attribute lines # sub attribute($) { my $a = shift ; @al=split(/,/,$a) ; foreach $av (@al) { $av=tokenize($av) ; if ($av eq "Id") { next ; } $debug && print "#$current_section:attribute:$av\n" ; push @attribute,$av ; } } # # parses the option lines (currently not used) # sub option($$) { my $on = shift ; my $ov = shift ; @ol=split(/,/,$ov) ; foreach $olv (@ol) { $olv=tokenize($olv); $debug && print "#$current_section:options:$on:$olv\n"; } } # # parses the moption lines (currently not used) # sub moption($$) { my $mon = shift ; my $mov = shift ; $debug && print "#$current_section:moptions:$mon:$mov\n"; } # # parses the ioption lines (currently not used) # sub ioption($$) { my $ion = shift ; my $iov = shift ; $debug && print "#$current_section:ioptions:$ion :$iov\n"; } # # parses the roption lines (currently not used) # sub roption($$) { my $ron = shift ; my $rov = shift ; $debug && print "#$current_section:roptions:$ron:$rov\n"; } # # writes out the sql code for a logbook (table) # sub emit_table { print "\n\nCREATE TABLE `$table_prefix$current_section` (\n" ; print " `id` int(10) NOT NULL auto_increment,\n" ; print " `Reply_to` int(10) default NULL ,\n" ; print " `In_reply_to` int(10) default NULL,\n" ; print " `Date` datetime NOT NULL,\n" ; while ($a=pop(@attribute)) { if ($a =~ /Date/) { $type="datetime" ; } else { $type=$default_element_type ; } print " `$a` $type default NULL,\n" ; } print " `Note` text NOT NULL,\n" ; print " UNIQUE INDEX id (`id`)\n" ; print ") TYPE=MyISAM;\n" ; } # # writes out the sql code for an attachment table # sub emit_attachment { print "\nCREATE TABLE `".$table_prefix."attachment` (\n"; print " `id` int(10) NOT NULL auto_increment,\n"; print " `logname` varchar(255) ,\n"; print " `pid` int(10) ,\n"; print " `filename` varchar(255) ,\n"; print " UNIQUE INDEX id (`id`)\n"; print ") TYPE=MyISAM\n;" ; } # # main() # # # loop on input, parsing out the relavant sections # while(<>) { chomp ; /^\s*\[(\w+)\]\s*$/ && section($1) ; /^\s*(Comment)\s*=(.*)/ && comment($2); /^\s*(Attributes)\s*=(.*)/ && attribute($2); /^\s*(Options)\s*(\w+)\s*=(.*)/ && option($2,$3); /^\s*(MOptions)\s*(\w+)\s*=(.*)/ && moptions($2,$3); /^\s*(IOptions)\s*(\w+)\s*=(.*)/ && ioptions($2,$3); /^\s*(ROptions)\s*(\w+)\s*=(.*)/ && roptions($2,$3); } # # close any open tables, if ($opentable && ($current_section ne "global")) { emit_table() ; } ; emit_attachment() ; elog-3.1.3-1/contrib/elog2sql.txt0000755000175000017500000000261012556103750015557 0ustar rogerrogerelog2sql was created to help translate logbooks created by the program ``elog'' from the native elog flat file format to a MySQL database. I had a need to have the elog data in a database, and it appears from the forum that several others had a similar need. I created a set of perl scripts that will allow the translation of elog logbooks into a MySQL database. The design and implementation of these scripts are a simple one, and allow the one-time copying of a set of logbooks. The elog2sql toolkit consists of two scripts. The first script, parsecfg.pl, reads a elogd.cfg, and creates a sql file that will create a set of db tables corresponding to elog logbooks. The second script, parselog.pl, takes a set of elog logfiles, and creates a sql file that will enter the logbook data into the database. The result is a copy of the elog logbook that can used as desired inside the framework of MySQL. Attachments are handled by inserting an entry of the attachment name into an seperate attachment table. This allows multiple attachments per entry. You can download the elog2sql program archive at http://www.davidfannin.com/elog2sql/elog2sql.tar.gz . It contains the scripts and basic documentation. You can read the man page at http://www.davidfannin.com/elog2sql/index.html I have also uploaded a copy of the archive here. email me for questions or comments. -- Fred Hooper elog-3.1.3-1/contrib/elogsubmit.js0000755000175000017500000000176512556103750016010 0ustar rogerrogerjavascript:var t=document.title;var u=document.location.href;var s=document.getSelection();var d=window.open('','d');with(d.document){write('









');void(close())} elog-3.1.3-1/contrib/mailelog.pl0000755000175000017500000000727312556103750015426 0ustar rogerroger#!/usr/bin/perl -w # Copyright 2000 + Stefan Ritt # # ELOG 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. # # ELOG 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 ELOG. If not, see . =head1 NAME doelog - save a mime message to elog =head1 SYNOPSIS doelog ... someprocess | doelog - =head1 DESCRIPTION Takes one or more files from the command line that contain MIME messages, and explodes their contents out into /tmp. The parts are sent to elog as attachments. Modified mimeexplode of the MIME::Tools in perl This was written as an example of the MIME:: modules in the MIME-parser package I wrote. It may prove useful as a quick-and-dirty way of splitting a MIME message if you need to decode something, and you don't have a MIME mail reader on hand. =head1 COMMAND LINE OPTIONS None yet. =head1 AUTHOR sak@essc.psu.edu =cut BEGIN { unshift @INC, ".." } # to test MIME:: stuff before installing it! require 5.001; use strict; use vars qw($Msgno $cmd); use MIME::Parser; use Getopt::Std; ## these should be options too? ## base elog cmd $cmd = "~/elog -h localhost -p 8080 "; #------------------------------------------------------------ # dump_entity - dump an entity's file info #------------------------------------------------------------ sub dump_entity { my $ent = shift; my @parts = $ent->parts; my $file; die "too many attachments\n" if ($#parts>10); if (@parts) { # multipart... map { dump_entity($_) } @parts; } else { # single part...append to elog cmd $file = $ent->bodyhandle->path; $cmd .= "-f \"$file\" "; ## print $cmd, "\n"; ## print " Part: ", $ent->bodyhandle->path, ## " (", scalar($ent->head->mime_type), ")\n"; } } #------------------------------------------------------------ # main #------------------------------------------------------------ sub main { my $file; my $entity; my $subject; my $logbook; our($opt_l); # Sanity: ## (-w ".") or die "cwd not writable, you naughty boy..."; ## check if user wants a particular logbook ## fix to add host and port? getopts('l:'); if($opt_l) { $logbook=$opt_l;} else {$logbook="emails";} $cmd .= "-l $logbook "; # Go through messages: @ARGV or unshift @ARGV, "-"; while (defined($file = shift @ARGV)) { # Create a new parser object: my $parser = new MIME::Parser; # Optional: set up parameters that will affect how it extracts # documents from the input stream: $parser->output_under("/tmp"); # Parse an input stream: open FILE, $file or die "couldn't open $file"; $entity = $parser->read(\*FILE) or print STDERR "Couldn't parse MIME in $file; continuing...\n"; close FILE; ## get the subject, assumes all logbooks have a subject ## attribute - not necessarily true. Mine do... chomp($subject = $entity->head->get('Subject', 0)); $cmd .= "-a subject=\"$subject\" "; print $cmd, "\n"; # Congratulations: you now have a (possibly multipart) MIME entity! dump_entity($entity) if $entity; ### $entity->dump_skeleton if $entity; ### print $cmd, "\n"; exec $cmd; } 1; } exit (&main ? 0 : -1); #------------------------------------------------------------ 1; elog-3.1.3-1/resources/0000755000175000017500000000000013076344742013644 5ustar rogerrogerelog-3.1.3-1/resources/eloghelp_zh_CN-GB2312.html0000644000175000017500000000354212556103751020207 0ustar rogerroger ELOG -- Ӽ¼İ
ELOG -- Ӽ¼İ

Ӽ¼ (The Electronic Logbook, ELog) ͨ Web ҳϢĴ洢Ͳҡļ Elog ϵͳṩһ ¼ (logbooks) Ƿֱ𱣴ڷĶλϡ

ժҪ

Ĭ£ʾǼ¼¼¼ij¼ʾü¼ȫݡ ͨťֱ鿴һǰһһһ¼

½ ťڴµļ¼ļ޸ģ ťڱ༭һѾڵļ¼ظ ťڶһѴڵļ¼һظͻظʼơ

ťڴ򿪲ѯҳ棬ҳݹʾ¼еļ¼ÿһǿյ붼һĹǡ͡(AND) Ĺϵûѡʾ¼ем¼

Ϣ

ϸݣǹ Elog ÷μ ELOG ҳ.


ߣS. Ritt, 28 August 2001 ķ룺 ϼ 2005109
elog-3.1.3-1/resources/eloglang.slovak0000644000175000017500000010622313076344200016646 0ustar rogerroger# # Slovak translation by Branislav Gardon # New = Nový Edit = Úprava Delete = Zmazať Reply = Odpovedať Find = Hladať Last day = Posledný den Last 10 = Posledných 10 Config = Konfigurácia Change password = Zmena hesla Logout = Odhlásenie Help = Pomoc Back = Späť Submit = Odovzdať First = Prvý Last = Posledný Previous = Predchádzajúci Next = Ďalší Copy to = Kopírovať do Move to = Presunúť do Save = Uložiť Cancel = Zrušiť Wrong password = Nesprávne heslo Please login = Prosím prihláste sa Username = Prihlasovacie meno Password = Heslo Old password = Staré heslo New password = Nové heslo ELOG change password = ELOG zmena hesla Several logbooks are defined on this host = Na tomto hoste je definovaných niekoľko denníkov Please select the one to connect to = Zvoľte jedno k pripojeniu Change password for user = Zmena hesla pre užívateľa Logged in as = Prihlasený ako Entry date = Dátum príspevku with = s This is the last entry = Toto je posledný príspevok This is the first entry = Toto je prvý príspevok Email sent to = Email odoslaný na Email notification suppressed = Upozornenie emailom zakázané please select = prosím zvoľte Suppress Email notification = Zakázať upozornenie emailom Resubmit as new entry = Potvrď ako nový záznam Attachment = Príloha Reply to this = Odpovedať na In reply to = V odpovedi na Fields marked with = Pole označené s are required = sú vyžadované Please check that it exists and elogd has write access = Prosím uistite sa, že existuje a elogd ma právo na zapisovanie Error: Attribute %s not supplied = Chyba: Atribút %s nie je zásobený Please go back and enter the %s field = Prosím choďte späť a zadajte %s pole Please use your browser's back button to go back = Prosím použite tlačítko späť vášho prehliadača k návratu ELOG find = ELOG nájsť Find = Nájsť Search = Hľadať Reset Form = Nulovať formulár Summary only = Iba zhrnutie Show attachments = Ukáž prílohy Printable output = Tlačový výstup Sort in reverse order = Zoraď v opačnom poradí Start date = Počiatočný dátum End date = Koncový dátum Year = Rok Text = Text Search all logbooks = Prehľadaj všetky denníky Last %d days = Posledných %d dní Last %d entries = Posledných %d záznamov No entries found = Žiadne záznamy neboli nájdené A new entry has been submitted on %s = Nový záznam bol potvrdený na %s Logbook = Denník Yes = Áno No = Nie Error deleting message: status = Chyba mazania správy: status Error: Command "%s" not allowed = Chyba: Príkaz "%s" nie je povolený Cannot open file %s = Nemôžem otvoriť súbor %s Cannot write to %s = Nemôžem zapísať do %s No SMTP host defined in [global] section of configuration file = Nie je definovaný žiadny SMTP host v sekci [global] konfiguračného súboru Display threads = Zobraz vlákno Logbook is empty = Denník je prázdny Download = Stiahnúť and all its replies = a všetky jeho odpovede Message ID = ID správy Not logged in = Neprihlásený Login = Prihlásenie Page %d of %d = Strana %d z %d all entries = všetky záznamy Login name = Prihlasovacie meno Full name = Celé meno ELOG user config = ELOG konfigurácia užívateľa ELOG new user = ELOG nový užívateľ Admin = Admin Select user = Označ užívateľa Remove user = Vymaž užívateľa New user = Nový užívateľ Retype new password = Zadaj nové heslo New passwords do not match, please retype = Nové heslo sa nezhoduje, prosím zadaj znovu Retype password = Zadaj znovu heslo exists already = už existuje Register as new user = Registruj sa ako nový užívateľ A new ELOG user has been registered on %s = Nový ELOG užívateľ bol registrovaný na %s Email = Email User "%s" registered on logbook "%s" = Užívateľ "%s" registrovaný v denníku "%s" User [%s] has been deleted = Užívateľ [%s ]bol zmazaný Hit following URL to activate that account = Zadajte následujúcu URL k aktivácii tohto účtu ELOG registration = ELOG registrácia Your ELOG account has been activated on host = Váš ELOG účet bol aktivovaný na hoste You can access it at = Možte pristupovať na Are you sure to delete these messages? = Naozaj chcete zmazať túto správu? Select = Označte All entries = Všetky záznamy Day = Deň Week = Týždeň Month = Mesiac Show last = Ukáž posledný Goto page = Choď na stránku All = Všetko Display = Zobraz entries per page = položiek na stránku Toggle all = Odobrať všetko Selected entries = Označené záznamy Collapse = Zbaliť Expand = Rozbaliť Full = Plné Summary = Súhrn Threaded = Vláknitý Filters = Filtre Mode = Mód Options = Voľby Error: start date after end date = Chyba: počiatočný dátum po konečný dátum and all their replies = a všetky ich odpovede Upload = Nahrať Requested = Požadovaný A new ELOG user wants to register on "%s" = Nový užívateľ sa chce registrovať na "%s" Search text also in attributes = Hľadaj text aj v atribútoch Date = Dátum ID = ID Forgot password? = Zabudli ste heslo? ELOG password recovery = ELOG obnova hesla No Email address registered with user name "%s" = Žiadna emailová adresa registrovaná s uživateľským menom "%s" Error sending Email via "%s" = Chybné zaslanie emailu cez "%s" Forgot = Zabudol User "%s" registered on host "%s" = Užívateľ "%s" registrovaný na hoste "%s" Enter your user name or email address = Zadajte vaše užívateľské meno alebo emailovú adresu Password recovery for ELOG %s = Obnova hesla pre ELOG %s Host = Host Your ELOG account has been activated = Váš ELOG účet bol aktivovaný Maximum number of attachments reached = Maximální počet príloh prekročený on = na Entry is currently edited by = Záznam je práve menený recipients = príjimatelia Suppress shell execution = Potlačiť vykonávanie shell programu Update = Aktualizácia by = od Change %s = Zmeň %s Add new option here = Pridaj novú voľbu Expand all = Rozbaľ všetko Add %s = Pridať %s Synchronization = Synchronizácia Error: Value %s not allowed for boolean attributes = Chyba: Hodnota %s nie je dovolená pre boolean atribút Error: Attribute option %s not existing = Chyba: Atribút voľba %s neexistuje Last submission = Posledné odovzdanie Synchronize = Synchronizuj No mirror server defined in configuration file = V konfiguračnom súbore nie sú definované žiadne zrkadlované servre user = užívateľ Maximum number of replies (%d) exceeded = Maximálny počet odpovedí (%d) prekročený Entry can only be edited %1.2lg hours after creation = Záznamy možu býť menené iba %1.2lg hodín po vytvorení Display full entries = Zobraz plné záznamy No entry selected for deletion = Nie sú označené žiadne záznamy pre mazanie Cannot read entry from local logbook = Nie je možné čítať žiadne záznamy z lokálneho denníku Cannot connect to host %s, port %d = Nie je možné pripojenie k hostu %s, portu %d Cannot receive "%s" = Nie je možné prijať"%s" Received wrong entry id "%d" = Prijaté chybné ID záznamu "%d" Entry #%d is locked on remote server = Záznam #%d je zamknutý na vzdialenom serveri Cannot save remote entry locally = Nie je možné uložiť vzdialenú položku lokálne local = lokálny remote = vzdialený Please delete %s or %s entry to resolve conflict = Prosím zmažte %s alebo %s záznam pre vyriešenie konfliktu Error sending local entry = Zaslaná chyba lokálneho záznamu Local entry submitted = Lokálna chyba potvrdená Error deleting remote entry = Chyba mazania vzdialeného záznamu New entry cannot be written to directory "%s" = Nie je možné zapísať novú položku do adresára "%s" Entry %s cannot be read from logbook "%s" = Záznam %s nie je možné čítať z denníku %s This entry has been deleted = Tento záznam bol zmazaný Entries = Záznamy Cannot connect to remote server "%s" = Nie je možné prippojenie k vzdialenému serveru "%s" Remote server is not an ELOG server = Vzdialený server nie je ELOG Server Incorrect remote ELOG server version %s = Nesprávna verzia %s vzdialeného servera Error accessing remote logbook = Chyba prístupu k vzdialenému denníku Invalid HTTP header = Neplatná HTTP hlavička No user name supplied to access remote logbook = Nie sú zadané žiadne uživateľské mená k vzdialenému prístupu denníka Synchronizing logbook %s with server "%s" = Synchronizácia denníku %s so serverom "%s" Configuration has been changed locally and remotely = Konfigurácia zmenená lokálne aj vzdialene Please merge manually to resolve conflict = Pripojte ručne k vyriešeniu konfliktu Logbook "%s" does not exist on remote server = Logbook "%s" neexistuje na vzdialenom serveri Entry has been changed locally and remotely = Záznam bol zmenený lokálne aj vzdialene Entry deleted locally = Záznam bol zmazaný lokálne Changed local entry ID to %d = Zmenené lokálne ID záznamu na %d Entry deleted remotely = Záznam bol zmazaný vzdialene All entries identical = Všetky záznamy sú rovnaké Invalid mirror_id or entry_date = Neplatné zrkadlové_id alebo zaznamový_dátum Synchronize all logbooks = Synchronizuj všetky denníky Calendar = Kalendár Remote entry received = Vzdialený záznam prijatý Pick a date = Vložte dátum Please select at least one '%s' =Prosím označte nejmenej jednu '%s' Please select a '%s' = Prosím označte '%s' Please enter month for attribute '%s' = Prosím zvoľte mesiac pre atribút '%s' Please enter day for attribute '%s' = Prosím zvoľte den pre atribút '%s' Please enter year for attribute '%s' = Prosím zvoľte rok pre atribút '%s' Please enter attribute '%s' = Prosím zvoľte atribút '%s' Entry time = Čas vloženia Start = Začiatok End = Koniec From = Z After = Po to = do Before = Pred Previous Year = Minulý rok Next Year = Budúci rok Please enter numeric value for '%s' = Prosím vložte číselnú hodnotu pre '%s' Error: Attribute %s must be numeric = Chyba: Atribút %s musí byť číslo 3 Months = 3 Mesiace 6 Months = 6 Mesiacov Error sending local message = Chyba zasielnia miestnej správy Error receiving message = Chyba príjmu správy Are you sure to delete this entry? = Chcete naozaj zmazať tento záznam? ELOG CSV import = ELOG CSV import Import = Import CSV filename = CSV meno súboru Field separator = Oddeľovač polí Edit entry = Upraviť záznam Delete entry = Zmazať záznam CSV Import = CSV Import Derive attributes from CSV file = Odvodzovať atribúty z CSV súboru Comma = Čiarka Semicolon = Bodkočiarka Preview import = Náhľad importu CSV import preview of %s = Náhľad CSV importu z%s Too many attributes in CSV file = Príliš veľa atribútov v CSV súbore %d entries successfully imported = %d záznam bol úspešne importovaný No 'Attributes' option present in %s = Voľba 'Attributes' nie je dostsupná v %s CSV ("," separated) = CSV ("," oddeľovačom) CSV (";" separated) = CSV (";"oddeľovačom) Auto detect = Automatické zisťovanie text = text Column header '%s' must be present in CSV file = Stĺpec hlavičky sa musí nachádzať v CSV súbore '%s' Fill text body = Vyplňte telo textu Please re-enter filename = Prosím zadajte znovu meno súbora Last x = Posledných x Activate = Aktivuj Entry has been modified = Záznam bol modifikovaný No attachment file specified = Nie sú špecifikované žiadne prílohy Delete this logbook = Zmaž tento denník Rename this logbook = Premenuj tento denník Create new logbook = Vytvor nový denník Syntax error in config file = Chyba syntaxu v konfiguračnom súbore Email notification = Emailové upozornenia Are you sure to delete logbook "%s"? = Určite chcete zmazať denník "%s"? Logbook "%s" exists already, please choose different name = Denník "%s" už existuje, prosím zvoľte iné meno Rename logbook = Premenuj denník Enter new logbook name = Vložte nové meno denníku Logbook name = Meno denníku Use existing logbook as template = Použi existujúcí denník ako šablónu none = žiadny URL is redirected to: = URL je presmerovaná na: Remote config should be received = Vzdialená konfigurácia by mala byť prijatá Local entry should be submitted = Lokálny záznam by mal byť odovzdaný Remote entry should be received = Vzdialený záznam by mal byť prijatý Local entry = Lokálny záznam %s should be deleted = %s mal by byť zmazaný Entry should be deleted locally = Záznam by mal byť zmazaný lokálne Local entry ID should be changed to %d = Lokálne ID záznamu by malo byť zmenené na %d Remote entry = Vzdialený záznam Entry should be deleted remotely = Záznam by mal byť zmazaný vzdialene Click here to delete %d entries = Kliknite tu pre zmazanie %d záznamu Click here to delete this entry = Kliknite tu pre zmazanie tohto záznamu Maximum number of attribute options exceeded = Maximálny počet volieb atribútov bol prekročený Please increase MAX_N_LIST in elogd.c and recompile = Prosím zväčši MAX_N_LIST v elogd.c a prekompiluj You can "steal" the lock by editing this entry = Môžte "ukradnúť" zámok upravením tohto záznamu Several logbooks groups are defined on this host = Na tomto hoste je definovaných nikoľko skupín denníkov Please select one to list the logbooks in that group = Prosím vyberte jeden z denníkov v tejto skupine No logbook defined on this server = Na tomto serveri nie sú definované žiadne denníky Goto ELOG home page = Choďte na ELOG domovskú stránku Please enter "%s" = Prosím vložte "%s" Change config file = Zmena v konfiguračného súboru Please check that it exists and elogd has write access and disk is not full = Prosím skontrolujte či existuje a či elogd ma právo na zapisovanie a disk nie je plný Show = Ukáž Hide = Skry Hide all = Skry všetko Show all = Ukáž všetko This logbook requires authentication = Tento denník vyžaduje autentifikáciu Attachment file "%s" empty or not found = Pripojený súbor "%s" je prázdny alebo nebol nájdený Case sensitive = Rozlišovať malé a veľké písmena List = Zoznam Date must be between 1970 and 2037 = Dátum musí byť medzi 1970 und 2037 up = hore down = dole stop = zastaviť Entry = Záznam name may not contain blanks = meno nemôže byť prázdne regular expressions = regulárny výraz Text fields are treated as %s = Textové polia sú upravené ako %s Subscribe to logbooks = Prihlásiť sa k odberu denníka enable automatic email notifications = povoliť automatické emailové upozornenia Set all = Nastaviť všetko Set none = Nenastaviť nič Insert current time = Vlož aktuálny čas Please enter hour for attribute '%s' = Prosím zadajte hodinu pre atribút '%s' Please enter minute for attribute '%s' = Prosím zadajte minútu pre atribút '%s' Please enter second for attribute '%s' = Prosím zadajte sekundu pre atribút '%s' No admin user has been defined in configuration file = V konfiguračnom súbore nie ja definovaný žiadny administrátor Duplicate = Duplikát Cannot lookup server name = Nie je možné zistiť meno servera Cannot connect to server = Nie je možné pripojenie k serveru %s wrote = %s napísal Quote = Citát Please enter numeric value for year of attribute '%s' = Prosím zadajte číselnú hodnotu pre rok atribútu '%s' Preview = Náhľad FONT = PÍSMO SIZE = VEĽKOSŤ COLOR = FARBA smiling = usmievavý happy = šťastný winking = blikajúcí big grin = široký úsmev crying = plakajúci cool = kľudný frowning = zamračený confused = zmätený mad = šialený pleased = spokojný tongue = jazyk yawn = znudený Encoding = kódovanie User "%s" not found in password file = Užívateľ "%s" nebol nájdený v súbore hesiel Cannot write to file %s =Nie je možné zapísať do súboru %s Invalid user name "%s" or password for remote logbook = Neplanté užívateľské meno "%s"alebo heslo pre vzdialený denník Hide attachments = Skry prílohy Show only new entries = Ukáž len nové záznamy Show all entries = Ukáž všetky záznamy New entries since = Nové záznamy od An old ELOG entry has been updated = Starý ELOG záznam bol aktualizovaný A new ELOG entry has been submitted = Nový ELOG záznam bol odovzdaný Invalid user name or password = Neplatné užívateľské meno alebo heslo Upload image = Nahrávanie obrázku Please enter filename or URL = Prosím zadajte meno súboru alebo URL Maximum allowed file size is = Maximálna povolená veľkosť súboru je Enter filename or URL = Zadajte meno súbora alebo URL Image uploaded successfully = Obrázok bol nahraný úspešne Image "%s" uploaded successfully = Obrázok "%s" bol nahraný úspešne HelpELCode = PomocELCode Cannot retrieve file from URL "%s" = Nie je možné získať súbor z URL "%s" Enter name of hyperlink = Zadajte meno hyperlinky Enter URL of hyperlink = Zadajte URL hyperlinky astonished = užasnutý Error in regular expression "%s" = Chyba v regulárnom výraze "%s" File not found at URL "%s" = Súbor nebol nájdený na URL "%s" Entry size too large for email notification = Záznam je príliš veľký pre emailové upozornenie Please specify a valid email address = Prosím zadajte platnú emailovú adresu Cannot send email notification to "%s" = Nie je možné poslať emailové upozornenie "%s" Error: Command "%s" is not allowed for user "%s" = Chyba: Príkaz "%s" nie je povolený pre užívateľov "%s" Shell execution not enabled via -x flag = Shell príkaz nebol povolený cez -x flag Enter heading level (1, 2 or 3) = Zadajte úroveň nadpisu (1, 2 oder 3) User name may not contain blanks = Užívateľské meno nesmie byť prázdne Empty password not allowed = Prázdne heslo nie je povolené Password may not contain blanks = Heslo nesmie obsahovať medzery Anonymous = Anonym Activation notice has been sent to %s = Oznámenie o aktivácii bolo zaslané na %s Your request has been forwarded to the administrator = Vaša požiadavka bola zaslaná administrátorovi You will be notified by email upon activation of your new account = O aktivácii vášho nového účtu budete informovaný emailom Select period = Označte obdobie Last week = Posledný týždeň Last month = Posledný mesiac Last 3 Months = Posledné 3 mesiace Last 6 Months = Posledných 6 mesiacov Last Year = Posledný rok Enter %s = Enter Select %s = Označ Bold text CTRL+B = Hrubé Italics text CTRL+I = Šikmé Underlined text CTRL+U = Podčiarknuté Centered text = na stred textu Insert hyperlink = Vložte hyperlinku Insert email = Vložte email Insert image CTRL+M = Vložte obrázok CTRL+M Insert quote = Vložte quotu Insert list CTRL+L = Vložte zoznam CTRL+L Insert table = Vložte tabuľku Insert heading CTRL+H = Volžte nadpis CTRL+H Insert code CTRL+O = Vložte kód CTRL+O Show the smiley bar = Ukázať ponuku smajlov Insert current time/date = Vložte aktuálny čas/dátum Entry is locked on local server and therefore skipped =Záznam je uzamknutý na lokálnom serveri a preto bol vynechadný Keep me logged in on this computer = Zostať prihlásený na tomto počítači for the next %d minutes = pre ďaľšie %d minúty for the next hour = pre ďaľšiu hodinu for the next %d hours = pre ďaľšie%dhodiny for the next %d days = pre ďaľšie %d dni keep original values = ponechaj pôvodnú hodnotu Entry is locked = Záznam je uzamknutý Edit anyhow = Úpravy v každom prípade Insert horizontal line = Vlož riadok horizontálne Insert anchor point = Vložte kotviaci bod keep original text = ponechaj pôvodný text unspecified = nešpecifikované To subscribe to any logbook, click on 'Config' in that logbook = Ak sa chcete prihlásiť k odberu akéhokoľvek denníka, kliknite na 'Config' v tomto denníku ELOG XML import = ELOG XML import Derive attributes from XML file = Odvodiť atribúty z XML súboru XML filename = XML súbor Invalid date format = Neplatný formát dátumu XML file does not contain %s element = XML súbor neobsahuje %s prvok XML import preview of %s = XML náhľad importu %s XML Import = XML Import A old entry has been updated on %s = starý záznam bol aktualizovaný %s First entry, Ctrl-Home = Prvý záznam, Ctrl-Home Previous entry, Ctrl-PgUp = Predošlý záznam, Ctrl-PgUp Next entry, Ctrl-PgDn = Ďaľší záznam, Ctrl-PgDn Last entry, Ctrl-End = Posledný záznam, Ctrl-End ELOG import = ELOG import Please choose format to import: = Prosím zvoľte formát importu Enter text = Vložte text or until I log out = alebo pokiaľ sa neodhlásim Make smaller = Zmenšiť Original size = Pôvodná veľkosť Make larger = Zväčšiť Rotate left = Otočiť v ľavo Rotate right = Otočiť v pravo Delete attachment = Vymazať prílohu Cannot create thumbnail, please check ImageMagick installation = Nie je možné vytvoriť náhľad, prosím skontrolujte inštaláciu ImageMagick Attachments = Príloha Error: Content length (%d) larger than maximum content length (%d) = Chyba:Dĺžka obsahu (%d) väčšia ako maximálna dĺžka obsahu (% d) ELOG error = ELOG chyba Only user %s can edit this entry = Iba užívateľ %s može zmenit tento záznam Export to = Export do Do not ignore first line = Neignorujte prvý riadok Last %d hours = Posledné %d hodiny Invalid URL = Neplatná URL Attribute "%s" is not allowed in config file = Atribút "%s" nie je povolený v konfigurčnom súbore Only user %s can delete this entry = Iba užívateľ %s može zmazať tento záznam File system full, ELOG cannot continue to work = Súborový systém je plný, ELOG nemôže pokračovať v práci 3 Days = 3 dni Last 3 Days = Posledné 3 dni Really submit this entry? = Skutočne odovzdať tento záznam? Account activation for ELOG logbook "%s" = Aktivácia účtu pre ELOG denníka "%s" Account activation for ELOG on host "%s" = Aktivácia účtu pre ELOG na hoste "%s" Please click the URL below to activate following ELOG account = Prosím kliknite na nasledujúcu URL pre aktiváciu nasledujúceho ELOG účtu Activation URL = Aktivácia URL An email has been sent to <%s> = Email bol odoslaný <%s> Use that email to activate your account = Použite tento e-mail na aktiváciu vášho účtu Registration request for ELOG logbook "%s" = Žiadosť o registráciu pre ELOG denníka "% s" Registration request for ELOG on host "%s" = Žiadosť o registráciu pre ELOG na hoste "% s" Are you sure you want to deactivate your own account? = Ste si istí, že chcete deaktivovať svoj účet? Active = Aktívny Invalid activation code = Neplatný aktivačný kód Error activating user = Chyba aktivácie užívateľa A password recovery email for user "%s" has been sent to %s = Email pre obnovenie hesla pre používateľa "% s" bol odoslaný na adresu% s User "%s" has no access to this logbook = Užívateľ "% s" nemá prístup k tomuto denníku Your account has been activated = Váš účet bol deaktivovaný Please subscribe to logbooks if you want to receive automatic email notifications = Prosím, prihláste sa do denníkov, ak chcete dostávať automatické upozornenia emailom This is an automatically generated account recovery email for host %s = Toto je automaticky generovaný emailový účet pre host% s Please click on following link to recover your account = Prosím, kliknite na nasledujúci odkaz pre obnovu svojho účtu Email address "%s" not registered = Emailová adresa "%s" nie je registrovaná User name "%s" not registered = Užívateľské meno "% s" nie je registrované Error accessing password file = Chyba pri prístupe k súboru s heslami # #---- please translate following items and then remove this comment ----# # Shell execution not enabled via -x flag = Cannot lookup server name = Cannot connect to server = Entry size too large for email notification = Change %s = %s wrote = Quote = Logged in as = Not logged in = Goto ELOG home page = ELOG error = Back = Please use your browser's back button to go back = Anonymous = Config = Change password = ELOG change password = Wrong password = New passwords do not match, please retype = Change password for user = Old password = New password = Retype new password = Submit = keep original values = Year = Pick a date = Insert current time = Pending draft available = %d pending drafts available = Draft entry created on %s by %s = Edit = Create new entry = Entry is currently edited by = Entry is locked = Edit anyhow = Cancel = Maximum number of replies (%d) exceeded = Only user %s can edit this entry = Entry can only be edited %1.2lg hours after creation = user = on = Entry has been modified = Really submit this entry? = Please select at least one '%s' = Please select a '%s' = Please enter month for attribute '%s' = Please enter day for attribute '%s' = Please enter year for attribute '%s' = Please enter hour for attribute '%s' = Please enter minute for attribute '%s' = Please enter second for attribute '%s' = Please enter attribute '%s' = Please enter numeric value for '%s' = Please enter numeric value for year of attribute '%s' = Really delete this entry? = No attachment file specified = If you leave this page you will lose your unsaved changes = Edited = Enter name of hyperlink = Enter URL of hyperlink = Enter heading level (1, 2 or 3) = Update = Save = Preview = Delete = Draft saved at = Fields marked with = are required = Entry time = please select = Add new option here = Toggle all = Add %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert horizontal line = Insert anchor point = Insert code CTRL+O = Show the smiley bar = FONT = SIZE = COLOR = Insert current time/date = smiling = happy = winking = big grin = crying = cool = frowning = confused = astonished = mad = pleased = tongue = yawn = keep original text = Encoding = Suppress Email notification = Suppress shell execution = Resubmit as new entry = Attachment = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Maximum number of attachments reached = Upload = Drop attachments here... = ELOG find = Search = Reset Form = Mode = Display full entries = Summary only = Summary = Display threads = Export to = CSV ("," separated) = CSV (";" separated) = CSV (";" separated) + Text = Options = Show attachments = Printable output = Sort in reverse order = Search all logbooks = Display = entries per page = Filters = regular expressions = Text fields are treated as %s = Entry date = Start = Show last = All time = Day = 3 Days = Week = Month = 3 Months = 6 Months = End = unspecified = Text = Search text also in attributes = Case sensitive = Admin = Delete this logbook = Rename this logbook = Create new logbook = Cannot open file %s = Cannot write to %s = Syntax error in config file = Please enter "%s" = Login name = Full name = Please specify a valid email address = User name may not contain blanks = Empty password not allowed = Password may not contain blanks = exists already = Error accessing password file = No SMTP host defined in [global] section of configuration file = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Logbook = Host = Email = Activation URL = Activate = Cannot send email notification to "%s" = ELOG registration = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = A new ELOG user wants to register on "%s" = A new ELOG user has been registered on %s = Hit following URL to activate that account = No admin user has been defined in configuration file = Requested = Cannot write to file %s = ELOG user config = Are you sure you want to deactivate your own account? = Select user = User [%s] has been deleted = Active = Subscribe to logbooks = enable automatic email notifications = Set all = Set none = Remove user = New user = Change config file = Invalid activation code = Error activating user = Your ELOG account has been activated = Your ELOG account has been activated on host = You can access it at = To subscribe to any logbook, click on 'Config' in that logbook = No Email address registered with user name "%s" = Password recovery for ELOG %s = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = ELOG password recovery = Email notification = Error sending Email via "%s" = User name "%s" not registered = Enter your user name or email address = Forgot = ELOG new user = name may not contain blanks = Password = Retype password = Entry can only be deleted %1.2lg hours after creation = No = Yes = Error deleting message: status = No entry selected for deletion = Only user %s can delete this entry = Are you sure to delete these messages? = and all their replies = Are you sure to delete this entry? = and all its replies = Are you sure to delete logbook "%s"? = Rename logbook = Enter new logbook name = Logbook name = Use existing logbook as template = none = ELOG CSV import = Import = Field separator = Auto detect = Comma = Semicolon = Derive attributes from CSV file = Do not ignore first line = Preview import = text = Column header '%s' must be present in CSV file = Fill text body = CSV filename = Please re-enter filename = ELOG XML import = Derive attributes from XML file = XML filename = CSV import preview of %s = CSV Import = Too many attributes in CSV file = Invalid date format = %d entries successfully imported = XML file does not contain %s element = XML import preview of %s = XML Import = Date = Cannot connect to remote server "%s" = Remote server is not an ELOG server = Incorrect remote ELOG server version %s = URL is redirected to: = Invalid HTTP header = No user name supplied to access remote logbook = Error accessing remote logbook = Cannot read entry from local logbook = Cannot connect to host %s, port %d = Download = Cannot receive "%s" = Received wrong entry id "%d" = Entry #%d is locked on remote server = Cannot save remote entry locally = Please login = No mirror server defined in configuration file = Synchronizing logbook %s with server "%s" = Remote config should be received = Configuration has been changed locally and remotely = Please merge manually to resolve conflict = Entry is locked on local server and therefore skipped = Error sending local entry = Local entry submitted = Local entry should be submitted = Error receiving message = Remote entry received = Remote entry should be received = local = remote = Entry has been changed locally and remotely = Please delete %s or %s entry to resolve conflict = Error sending local message = Local entry = %s should be deleted = Entry should be deleted locally = Entry deleted locally = Changed local entry ID to %d = Local entry ID should be changed to %d = Remote entry = Entry should be deleted remotely = Entry deleted remotely = Error deleting remote entry = Click here to delete %d entries = Click here to delete this entry = All entries identical = Synchronization = Entry = Reply = ID = Draft = Edit entry = Delete entry = Full = Threaded = Hide attachments = Collapse = Expand = Show only new entries = Show all entries = Select period = All entries = Last day = Last 3 Days = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Select %s = Last = Next = Enter text = Enter %s = Entries = Goto page = Previous = All = Selected entries = Copy to = Move to = Error: start date after end date = Error in regular expression "%s" = Message ID = Last %d days = Last %d hours = Last %d entries = all entries = Page %d of %d = Last x = Select = New entries since = Restrict search to last = days = Start date = End date = From = After = to = Before = up = down = Attachments = No entries found = An old ELOG entry has been updated = A new ELOG entry has been submitted = A old entry has been updated on %s = A new entry has been submitted on %s = recipients = No 'Attributes' option present in %s = Error: Attribute %s not supplied = Please go back and enter the %s field = Error: Attribute %s must be numeric = Error: Value %s not allowed for boolean attributes = Maximum number of attribute options exceeded = Please increase MAX_N_LIST in elogd.c and recompile = Error: Attribute option %s not existing = Date must be between 1970 and 2037 = New entry cannot be written to directory "%s" = Please check that it exists and elogd has write access and disk is not full = Invalid mirror_id or entry_date = Please check that it exists and elogd has write access = Entry %s cannot be read from logbook "%s" = First = Invalid URL = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = Logbook is empty = This entry has been deleted = with = This is the last entry = This is the first entry = Email notification suppressed = Email sent to = stop = You might however then overwrite each other's modifications = This is a draft message, edit and submit it to make it permanent = In reply to = Reply to this = Show = Hide = Hide all = Show all = Cannot create thumbnail, please check ImageMagick installation = Login = Invalid user name or password = Username = Keep me logged in on this computer = for the next %d days = for the next %d minutes = for the next hour = for the next %d hours = or until I log out = Forgot password? = Register as new user = File system full, ELOG cannot continue to work = This logbook requires authentication = by = Several logbooks groups are defined on this host = Please select one to list the logbooks in that group = No logbook defined on this server = Several logbooks are defined on this host = Please select the one to connect to = Synchronize all logbooks = Last submission = Expand all = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Calendar = Previous Year = Next Year = Upload image = Please enter filename or URL = Maximum allowed file size is = Enter filename or URL = Image uploaded successfully = Insert Timestamp = Activation notice has been sent to %s = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = New = Duplicate = List = Error: Command "%s" not allowed = Help = HelpELCode = Find = Last 10 = ELOG import = Please choose format to import: = Synchronize = Logout = Cannot retrieve file from URL "%s" = File not found at URL "%s" = Error: Content length (%d) larger than maximum content length (%d) = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.dutch0000755000175000017500000005663013076344200016467 0ustar rogerroger# # Dutch translation by djek@xs4all.nl # 31-01-2008 # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Nieuw Edit = Wijzig Delete = Wis Reply = Antwoord Find = Zoek Last day = Laatste dag Last 10 = Laatste 10 Config = Configuratie Change password = Wachtwoord wijzigen Logout = Afmelden Help = Help Back = Terug Submit = Verzend First = Eerste Last = Laatste Previous = Vorige Next = Volgende Copy to = Kopiëren naar Move to = Verplaats naar Save = Opslaan Cancel = Annuleren Wrong password = Verkeerde wachtwoord Please login = Aanmelden Username = Gebruikersnaam Password =     Wachtwoord Old password =  Oude wachtwoord New password = Nieuw wachtwoord ELOG change password = ELOG wachtwoord wijzigen Several logbooks are defined on this host = Meerdere logboeken aanwezig op deze machine Please select the one to connect to = Selecteer er een om aan te melden Change password for user = Wachtwoord wijzigen voor gebruiker Logged in as = Aangemeld als Entry date = Startdatum with = met This is the last entry = Dit is het laatste bericht This is the first entry = Dir is het eerste bericht Email sent to = E-mail verzonden naar Email notification suppressed = Email verzending onderdrukt please select = Kies Suppress Email notification = Email verzending onderdrukken Resubmit as new entry = Als nieuwe bericht verzenden Attachment = Bijlage Reply to this = Hierop antwoorden In reply to = In antwoord op Fields marked with = Velden gemarkeerd met are required = zijn verplicht Please check that it exists and elogd has write access = Controleer of het bestaat en of elog schrijfrechten heeft Error: Attribute %s not supplied = Fout: Attribuut %s niet ingevuld Please go back and enter the %s field = Ga terug en vul het veld %s in Please use your browser's back button to go back = Gebruik de terug-knop van je browser om terug te gaan ELOG find = ELOG zoeken Find = zoek Search = Zoeken Reset Form = Schoon formulier Summary only = Alleen de samenvatting Show attachments = Toon de bijlagen Printable output = Afdrukbare weergave Sort in reverse order = Sorteer in omgekeerde volgorde Start date = Startdatum End date = Einddatum Year = Jaar Text = Tekst Search all logbooks = Alle Logboeken doorzoeken Last %d days = Laatste %d dagen Last %d entries = Laatste %d berichten No entries found = Geen berichten gevonden A new entry has been submitted on %s = Een nieuwe bericht is verzonden op %s Logbook = Logboek Yes = Ja No = Nee Error deleting message: status = Fout bij wissen: Status Error: Command "%s" not allowed = Fout: Commando "%s" niet toegestaan Cannot open file %s = Bestand %s kan niet worden geopend Cannot write to %s = Kan niet schrijven naar bestand %s No SMTP host defined in [global] section of configuration file = Geen SMTP Host vermeld in [global] van het configuratiebestand Display threads = Toon draden Logbook is empty = logboek is leeg Download = Download and all its replies = en al de antwoorden Message ID =Bericht ID Not logged in = Niet ingelogd Login = Inloggen Page %d of %d = Pagina %d van de %d all entries = alle berichten Login name = Aanmeldnaam Full name = Gebruikersnaam ELOG user config = ELOG gebruikers configuratie ELOG new user = ELOG Nieuwe gebruiker Admin = Administratie Select user = Gebruiker selecteren Remove user = Gebruiker Verwijderen New user = Nieuwe gebruiker aanmaken Retype new password = Nieuw wachtwoord opnieuw opgeven New passwords do not match, please retype = Wachtwoorden verschillen, AUB opnieuw opgeven Retype password = Wachtwoord opnieuw opgeven exists already = bestaat al Register as new user = Als nieuwe gebruiker aanmelden A new ELOG user has been registered on %s = Een nieuwe gebruiker heeft zich aangemeld op %s Email = E-mail User "%s" registered on logbook "%s" = Gebruiker "%s" geregistreerd in logboek "%s" User [%s] has been deleted = Gebruiker [%s] is gewist Hit following URL to activate that account = Klik op de volgende URL om de gebruikers account te activeren ELOG registration = ELOG Registratie Your ELOG account has been activated on host = Uw ELOG account is geactiveerd op You can access it at = U kan toegang krijgen op Are you sure to delete these messages? = Weet u zeker dat u deze berichten wilt wissen? Select = Kies All entries = Alle berichten Day = Dag Week = Week Month = Maand Show last = Toon laatste Goto page = Ga naar pagina All = Alle Display = Toon entries per page = berichten per pagina Toggle all = Toon alles Selected entries = Geselecteerde berichten Collapse = Samenvatten Expand = Uitbreiden Full = Volledig Summary = Samenvatting Threaded = Berichtdraad Filters = Filter Mode = Modus Options = Opties Error: start date after end date = Fout: Begindatum begint na de einddatum and all their replies = en alle antwoorden Upload = Upload Requested = Verzocht A new ELOG user wants to register on "%s" = Een nieuwe Elog gebruiker wil zich registreren voor "%s" Search text also in attributes = Zoek ook in attributen Date = Datum ID = ID Forgot password? = Wachtwoord vergeten? ELOG password recovery = ELOG Wachtwoord Herstel No Email address registered with user name "%s" = Voor gebruiker "%s" is geen e-mail adres geregistreerd Error sending Email via "%s" = Fout bij het versturen van e-mail via "%s" Forgot = vergeten User "%s" registered on host "%s" = Gebruiker "%s" is op computer "%s" geregistreerd Enter your user name or email address = Geef je gebruikersnaam of je email adres op Password recovery for ELOG %s = Wachtwoord Herstel voor ELOG %s Host = Computer Your ELOG account has been activated = Het Elog account is geactiveerd Maximum number of attachments reached = Maximaal aantal bijlagen bereikt on = op Entry is currently edited by = Bericht wordt momenteel gewijzigd door recipients = Ontvanger Suppress shell execution = Onderdruk commando uitvoer Update = Ververs by = van Change %s = %s wijzigen Add new option here = Hier nieuwe optie opgeven Expand all = Alles uitbreiden Add %s = Voeg %s toe Synchronization = Synchronisatie Error: Value %s not allowed for boolean attributes = Fout: Waarde %s niet toegestaan voor booleaanse attributen Error: Attribute option %s not existing = Fout: Attribuut optie %s bestaat niet Last submission = Laatste toevoeging Synchronize = Synchroniseer No mirror server defined in configuration file = Geen mirror server gedefinieerd in het configuratie bestand user = gebruiker Maximum number of replies (%d) exceeded = Maximum aantal antwoorden (%d) overschreden Entry can only be edited %1.2lg hours after creation = Bericht kan alleen binnen %1.2lg na aanmaak worden gewijzigd Display full entries = Toon volledige berichten No entry selected for deletion = Geen bericht geselecteerd om te wissen Cannot read entry from local logbook = Kan niet van het lokale logboek lezen Cannot connect to host %s, port %d = Kan geen verbinding maken met %s, poort %d Cannot receive "%s" = Kan "%s" niet ontvangen Received wrong entry id "%d" = Verkeerd bericht met id "%d" ontvangen Entry #%d is locked on remote server = Bericht is #%d is geblokkeerd op de andere server Cannot save remote entry locally = Kan op andere server gemaakte bericht niet lokaal opslaan local = lokaal remote = op afstand Please delete %s or %s entry to resolve conflict = Verwijder bericht %s of %s om conflict op te lossen Error sending local entry = Fout bij het lokaal versturen Local entry submitted = Lokaal bericht verstuurd Error deleting remote entry = Fout bij het wissen bericht op afstand New entry cannot be written to directory "%s" = Nieuw bericht kan niet worden weggeschreven naar directory "%s" Entry %s cannot be read from logbook "%s" = Bericht %s kan niet worden gelezen van logboek This entry has been deleted = Dit bericht is gewist Entries = Berichten Cannot connect to remote server "%s" = Kan geen verbinding maken met server op afstand "%s" Remote server is not an ELOG server = Server op afstand is geen ELOG server Incorrect remote ELOG server version %s = Incorrecte versie van ELOG server op afstand %s Error accessing remote logbook = Fout bij het benaderen van het logboek op afstand Invalid HTTP header = Ongeldige HTTP header No user name supplied to access remote logbook = Geen gebruikersnaam opgegeven om het logboek op andere server te benaderen. Synchronizing logbook %s with server "%s" = Nu Synchroniseert logboek %s met server "%s" Configuration has been changed locally and remotely = Configuratie is lokaal en op andere server gewijzigd Please merge manually to resolve conflict = Voeg handmatig samen om conflicten op te lossen Logbook "%s" does not exist on remote server = Logboek "%s" bestaat niet op de andere server Entry has been changed locally and remotely = Bericht is lokaal en andere server gewijzigd Entry deleted locally = Bericht lokaal gewist Changed local entry ID to %d = Lokaal bericht ID veranderd in %d Entry deleted remotely = Bericht op andere server gewist All entries identical = Alle berichten identiek Invalid mirror_id or entry_date = Ongeldige mirror_id of entry_date Synchronize all logbooks = Synchroniseer alle logboeken Calendar = Kalender Remote entry received = Bericht van andere server ontvangen Pick a date = Kies een datum Please select at least one '%s' = Kies minstens een '%s' Please select a '%s' = Kies een '%s' Please enter month for attribute '%s' = Geef een maand voor attribuut '%s' Please enter day for attribute '%s' = Geef een dag voor attribuut '%s' Please enter year for attribute '%s' = Geef een jaar voor attribuut '%s' Please enter attribute '%s' = Geef een attribuut '%s' Entry time = Geef de tijd op Start = Start End = Eind After = Na to = aan Before = Voor Previous Year = Vorig Jaar Next Year = Volgend jaar Please enter numeric value for '%s' = Geef een numerieke waarde voor '%s' Error: Attribute %s must be numeric = Fout Attribuut %s moet numeriek zijn 3 Months = 3 Maanden 6 Months = 6 Maanden Error sending local message = Fout bij versturen lokaal bericht Error receiving message = Fout bij ontvangen bericht Are you sure to delete this entry? = Weet je zeker dat je dit bericht wilt wissen? CSV import preview of %s = CSV import voorbeeld van %s Import = Importeer CSV filename = CSV bestandsnaam Field separator = Veldscheidingssteken Edit entry = Wijzig bericht Delete entry = Wis bericht CSV Import = CSV Import Derive attributes from CSV file = Haal attributen uit CSV bestand Comma = Komma Semicolon = Punt Komma Preview import = Import voorbeeld ELOG CSV import = ELOG CSV import Too many attributes in CSV file = Te veel attributen in CSV bestand %d entries successfully imported = %d berichten succesvol geïmporteerd No 'Attributes' option present in %s = Geen 'Attributen' optie aanwezig in %s CSV ("," separated) = CSV (";" gescheiden) CSV (";" separated) = CSV (";" gescheiden) Auto detect = Auto ontdek Text = Tekst Column header '%s' must be present in CSV file = Kolomkop '%s' moet aanwezig zijn in CSV bestand Fill text body = Vul tekst gedeelte Please re-enter filename = Geef bestandsnaam opnieuw op Last x = Laatste x Activate = Activeer Entry has been modified = Bericht is gewijzigd No attachment file specified = Geen bijlage opgegeven Delete this logbook = Wis dit logboek Rename this logbook = Hernoem dit logboek Create new logbook = Maak nieuw logboek Syntax error in config file = Syntax fout in configuratiebestand Email notification = Email berichtgeving Are you sure to delete logbook "%s"? = Weet je zeker dat je logboek "%s" wilt wissen? Logbook "%s" exists already, please choose different name = Logboek "%s" bestaat al, kies een andere naam Rename logbook = Hernoem logboek Enter new logbook name = Geef nieuw logboek naam op Logbook name = Logboek naam Use existing logbook as template = Gebruik bestaande logboek als sjabloon none = geen URL is redirected to: = URL wordt doorgestuurd naar: Remote config should be received = Configuratie op andere server moet worden ontvangen Local entry should be submitted = Lokaal bericht moet worden opgeslagen Remote entry should be received = Bericht op andere server moet worden ontvangen Local entry = Lokaal bericht %s should be deleted = %s moet worden gewist Entry should be deleted locally = Bericht moet lokaal worden gewist Local entry ID should be changed to %d = Lokaal bericht ID moet worden gewijzigd in %d Remote entry = Bericht op andere server Entry should be deleted remotely = Bericht moet worden gewist op andere server Click here to delete this entry = Klik hier om dit bericht te wissen Click here to delete %d entries = Klik hier om %d berichten te verwijderen Maximum number of attribute options exceeded = Maximum aantal attribuut opties overschreden Please increase MAX_N_LIST in elogd.c and recompile = Verhoog MAX_N_LIST in elogd.c en compileer opnieuw You can "steal" the lock by editing this entry = Je kan het slot op dit bericht "stelen" door dit bericht te wijzigen Several logbooks groups are defined on this host = Verschillende logboek groepen zijn op deze server gedefinieerd Please select one to list the logbooks in that group = Kies er een om de logboeken in die groep te zien No logbook defined on this server = Geen logboek gedefinieerd op deze server Goto ELOG home page = Ga naar de ELOG home pagina Please enter "%s" = Vul "%s" in Change config file = Configuratie bestand veranderen Please check that it exists and elogd has write access and disk is not full = Controleer of het bestaat en dat naar elogd geschreven kan worden en dat de schijf niet vol is Show = Laat zien Hide = Verberg Hide all = Alles Verbergen Show all = Alles laten zien This logbook requires authentication = Dit logboek vereist authenticatie Attachment file "%s" empty or not found = Bijlage bestand "%s" leeg of niet gevonden Case sensitive = Hoofdlettergevoelig List = Lijst Date must be between 1970 and 2037 = Datum moet tussen 1970 en 2037 liggen up = op down = neer stop = stop Entry = bericht name may not contain blanks = Naam mag geen spaties bevatten regular expressions = reguliere expressie Text fields are treated as %s = Tekst velden worden beschouwd als %s Subscribe to logbooks = Abonneer op logboeken enable automatic email notifications = Zet email kennisgeving aan Set all = Zet alles Set none = Zet niets Please enter hour for attribute '%s' = Geef uur op voor attribuut '%s' Please enter minute for attribute '%s' = Geef minuten op voor attribuut '%s' Please enter second for attribute '%s' = Geef secondes op voor attribuut '%s' No admin user has been defined in configuration file = Er is geen admin gebruiker opgegeven in configuratie bestand Duplicate = Kopieer Cannot lookup server name = Kan server naam niet vinden Cannot connect to server = Kan niet verbinden met server %s wrote = %s schreef Quote = Citaat Insert current time = Huidige tijd invoegen Please enter numeric value for year of attribute '%s' = Geef jaar in cijfers op voor attribuut '%s' Preview = Voorbeeld FONT = LETTERTYPE SIZE = GROOTTE COLOR = KLEUR smiling = lachend happy = vrolijk winking = knipogend big grin = grote grijns crying = huilend cool = cool frowning = fronsend confused = verward mad = gek pleased = tevreden tongue = tong yawn = gaap Encoding = codering User "%s" not found in password file = Gebruiker "%s" niet gevonden in wachtwoord bestand Cannot write to file %s = Kan niet schrijven naar bestand %s Invalid user name "%s" or password for remote logbook = Ongeldige gebruikersnaam "%s" of wachtwoord voor logboek op afstand Hide attachments = Verberg bijlagen Show only new entries = Toon alleen nieuwe berichten Show all entries = Toon alle berichten New entries since = Nieuwe berichten sinds An old ELOG entry has been updated = Een oud ELOG bericht is ververst A new ELOG entry has been submitted = Een nieuw ELOG bericht is bezorgd Invalid user name or password = Ongeldige gebruikersnaam of wachtwoord Upload image = Afbeelding uploaden Please enter filename or URL = Geef een bestandsnaam of URL Maximum allowed file size is = Maximum bestandsgrootte is Enter filename or URL = Geef een bestandsnaam of URL Image uploaded successfully = Afbeelding met succes geüpload Image "%s" uploaded successfully = Afbeelding "%s" met succes geüpload HelpELCode = HelpELCode Cannot retrieve file from URL "%s" = Kan bestand niet ophalen van URL "%s" Enter name of hyperlink = Geef naam van de hyperlink Enter URL of hyperlink = Geef URL van de hyperlink astonished = verbaast Error in regular expression "%s" = Fout in reguliere expressie "%s" File not found at URL "%s" = Kan bestand niet gevonden bij URL "%s" Entry size too large for email notification = bericht te groot voor email melding Please specify a valid email address = Geef een geldig emailadres op Cannot send email notification to "%s" = Kan geen email melding sturen naar "%s" text = tekst From = Van Error: Command "%s" is not allowed for user "%s" = Fout: Commando "%s" is niet toegestaan voor gebruiker "%s" Shell execution not enabled via -x flag = Shell uitvoering niet aangezet met -x flag Enter heading level (1, 2 or 3) = Start bij kopniveau (1, 2 of 3) User name may not contain blanks = Gebruikersnaam mag geen spaties bevatten Empty password not allowed = Leeg wachtwoord is niet toegestaan Password may not contain blanks = Wachtwoord mag geen spaties bevatten Anonymous = Anoniem Activation notice has been sent to %s = Activeringsbericht is gestuurd naar %s Your request has been forwarded to the administrator = Uw verzoek is doorgestuurd naar de beheerder You will be notified by email upon activation of your new account = U krijgt via email een bericht als uw account is geactiveerd. Select period = Selecteer periode Last week = Afgelopen week Last month = Afgelopen maand Last 3 Months = Afgelopen 3 maanden Last 6 Months = Afgelopen 6 maanden Last Year = Afgelopen jaar Enter %s = %s invoeren Select %s = Selecteer %s Bold text CTRL+B = Vette tekst CTRL+B Italics text CTRL+I = Schuine tekst CTRL+I Underlined text CTRL+U = Onderstreepte tekst CTRL+U Centered text = Gecentreerde tekst Insert hyperlink = hyperlink invoegen Insert email = email invoegen Insert image CTRL+M = afbeelding invoegen CTRL+M Insert quote = citaat invoegen Insert list CTRL+L = lijst invoegen CTRL+L Insert table = tabel invoegen Insert heading CTRL+H = kop invoegen CTRL+H Insert code CTRL+O = code invoegen CTRL+O Show the smiley bar = Toon smiley balk Insert current time/date = Huidige tijd/datum invoegen Entry is locked on local server and therefore skipped = Bericht is geblokkeerd op de lokale server en dus overgeslagen Keep me logged in on this computer = Hou me ingelogd op deze computer for the next %d minutes = voor de komende %d minuten for the next hour = voor het komende uur for the next %d hours = voor de komende %d uren for the next %d days = voor de komende %d dagen keep original values = behoud de originele waarden Entry is locked = bericht is geblokkeerd Edit anyhow = toch wijzigen Insert horizontal line = Horizontale lijn invoegen Insert anchor point = Ankerpunt invoegen keep original text = behoud originele tekst unspecified = ongespecificeerd To subscribe to any logbook, click on 'Config' in that logbook = Om op een logboek te abonneren, klik op 'Configuratie' van dat logboek ELOG XML import = ELOG XML import Derive attributes from XML file = Leid attributen af van het XML bestand XML filename = XML bestand Invalid date format = ongeldige datum opmaak XML file does not contain %s element = XML bestand bevat niet het %s element XML import preview of %s = XML import voorbeeld van %s XML Import = XML import A old entry has been updated on %s = Een oud bericht is vernieuwd op %s First entry, Ctrl-Home = Eerste bericht, Ctrl-Home Previous entry, Ctrl-PgUp = Vorige bericht, Ctrl-PgUp Next entry, Ctrl-PgDn = Volgende bericht, Ctrl-PgDn Last entry, Ctrl-End = Laatste bericht, Ctrl-End ELOG import = ELOG import Please choose format to import: = Kies het formaat om te importeren Enter text = Voer tekst in or until I log out = of tot ik afmeld Make smaller = Verkleinen Original size = Oorspronkelijke grootte Make larger = Vergroten Rotate left = Links draaien Rotate right = Rechts draaien Delete attachment = Verwijder bijlage Cannot create thumbnail, please check ImageMagick installation = Kan geen voorbeeld maken, controleer de ImageMagick installatie Attachments = Bijlagen Error: Content length (%d) larger than maximum content length (%d) = Fout: Berichtgrootte (%d) is groter dan de maximum grootte (%d) ELOG error = ELOG fout Only user %s can edit this entry = Alleen gebruiker %s kan dit bericht wijzigen Export to = Exporteer naar Do not ignore first line = Negeer de eerste regel niet Last %d hours = Laatste %d uur Invalid URL = Ongeldige URL Attribute "%s" is not allowed in config file = Attribuut "%s" is niet toegestaan in het configuratiebestand Only user %s can delete this entry = Alleen gebruiker %s kan dit bericht verwijderen File system full, ELOG cannot continue to work = Bestandsysteem is vol, ELOG kan niet verder 3 Days = 3 Dagen Last 3 Days = Laatste 3 Dagen Really submit this entry? = Dit bericht echt verzenden? Account activation for ELOG logbook "%s" = Account activering voor ELOG logboek "%s" Account activation for ELOG on host "%s" = Account activering voor ELOG op server"%s" Please click the URL below to activate following ELOG account = Klik op de URl hieronder om het volgende account te activeren Activation URL = Activerings URL An email has been sent to <%s> = Er is een email verstuurd naar <%s> Use that email to activate your account = Gebruik deze email om je account te activeren Registration request for ELOG logbook "%s" = Registratie verzoek voor ELOG logboek "%s" Registration request for ELOG on host "%s" = Registratie verzoek voor ELOG op server"%s" Are you sure you want to deactivate your own account? = Weet je zeker dat je je account wilt opheffen? Active = Actief Invalid activation code = Ongeldige activeringscode Error activating user = Fout bij activeren van gebruiker A password recovery email for user "%s" has been sent to %s = Een wachtwoord herstel email voor gebruiker "%s" is gestuurd naar %s User "%s" has no access to this logbook = Gebruiker "%s" heeft geen toegang tot dit logboek Your account has been activated = Je account is geactiveerd Please subscribe to logbooks if you want to receive automatic email notifications = Abonneer je op logboeken als je automatisch meldingen wilt ontvangen This is an automatically generated account recovery email for host %s = Dit is een automatisch gegenereerd bericht voor het herstellen van het account voor server %s Please click on following link to recover your account = Klik op de volgende link om je account te herstellen Email address "%s" not registered = Email adres "%s" is niet geregistreerd User name "%s" not registered = Gebruikersnaam "%s" is niet geregistreerd # #---- please translate following items and then remove this comment ----# # CSV (";" separated) + Text = Error accessing password file = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.ru_CP12510000644000175000017500000005430313076344200016671 0ustar rogerroger# Russian translation by Alexander Boykov # # To use Russian translation, add the following lines to [global]: # Charset = windows-1251 # Language = ru_CP1251 # # Translation for elogd.c 1.526 # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Edit = Delete = Reply = Download = Find = Select = Last day = Move to = Copy to = Config = Admin = Login = CSV Import = CSV Logout = Help = Date = Text = Back = Submit = New user = Save = Requested = Activate = Activate Please login = Username = Password = ID = ID Logbook = Goto ELOG home page = ELOG Upload = Cancel = Update = Search = Last 10 = 10 Change %s = %s Delete this logbook = Rename this logbook = Create new logbook = Remove user = Forgot = Synchronize = Page %d of %d = %d %d Logged in as = Last x = Full = Summary = Threaded = Previous = Last = First = Next = Message ID = ID Entry time = Reply to this = Please enter attribute '%s' = "%s" No attachment file specified = Entry has been modified = are required = Fields marked with = Add %s = %s please select = Suppress Email notification = Resubmit as new entry = Attachment = Email sent to = Forgot password? = ? Register as new user = Collapse = Expand = In reply to = A new entry has been submitted on %s = %s ELOG user config = Select user = Login name = Full name = All entries = Entries = ELOG password recovery = Enter your user name or email address = Several logbooks are defined on this host = Please select the one to connect to = Last submission = Last submission by = by This logbook requires authentication = List = Not logged in = Please use your browser's back button to go back = "" , ELOG change password = Wrong password = New passwords do not match, please retype = , Change password for user = Old password = New password = Retype new password = Year = Pick a date = user = on = Maximum number of replies (%d) exceeded = (%d) Entry can only be edited %1.2lg hours after creation = %1.2lg Please select at least one '%s' = "%s" Please select a '%s' = "%s" Please enter month for attribute '%s' = "%s" Please enter day for attribute '%s' = "%s" Please enter year for attribute '%s' = "%s" Please enter numeric value for '%s' = "%s" Add new option here = Suppress shell execution = Maximum number of attachments reached = ELOG find = Reset Form = Mode = Display full entries = Summary only = Display threads = CSV ("," separated) = CSV ( ",") CSV (";" separated) = CSV ( ";") Options = Show attachments = Printable output = Sort in reverse order = Search all logbooks = Display = entries per page = Filters = Entry date = Start = Show last = Day = Week = Month = 3 Months = 3 6 Months = 6 End = Search text also in attributes = Case sensitive = Cannot open file %s = %s Cannot write to %s = %s Syntax error in config file = Please enter "%s" = "%s" exists already = No SMTP host defined in [global] section of configuration file = SMTP [global] Your ELOG account has been activated = Your ELOG account has been activated on host = You can access it at = A new ELOG user wants to register on "%s" = "%s" User "%s" registered on logbook "%s" = "%s" "%s" User "%s" registered on host "%s" = "%s" "%s" A new ELOG user has been registered on %s = %s Host = Email = - Hit following URL to activate that account = , User [%s] has been deleted = [%s] Change password = Change config file = No Email address registered with user name "%s" = "%s" Password recovery for ELOG %s = %s Email notification = Error sending Email via "%s" = "%s" ELOG new user = Retype password = No = Yes = Error deleting message: status = : No entry selected for deletion = Are you sure to delete these messages? = , ? and all their replies = Are you sure to delete this entry? = , ? and all its replies = Are you sure to delete logbook "%s"? = , "%s"? Logbook "%s" exists already, please choose different name = "%s" , Rename logbook = Enter new logbook name = Logbook name = Use existing logbook as template = none = ELOG CSV import = CSV Import = Field separator = Auto detect = Comma = Semicolon = Derive attributes from CSV file = CSV Preview import = text = Column header '%s' must be present in CSV file = '%s' CSV Fill text body = CSV filename = CSV Please re-enter filename = CSV import preview of %s = CSV %s Too many attributes in CSV file = CSV %d entries successfully imported = %d Cannot connect to remote server "%s" = "%s" Remote server is not an ELOG server = ELOG Incorrect remote ELOG server version %s = %s ELOG URL is redirected to: = URL : Invalid HTTP header = HTTP No user name supplied to access remote logbook = Error accessing remote logbook = Cannot read entry from local logbook = Cannot connect to host %s, port %d = %s, %d Cannot receive "%s" = "%s" Received wrong entry id "%d" = "%d" Entry #%d is locked on remote server = #%d Cannot save remote entry locally = No mirror server defined in configuration file = Synchronizing logbook %s with server "%s" = %s "%s" Remote config should be received = Configuration has been changed locally and remotely = Please merge manually to resolve conflict = Logbook "%s" does not exist on remote server = "%s" Error sending local entry = Local entry submitted = Local entry should be submitted = Error receiving message = Remote entry received = Remote entry should be received = local = remote = Entry has been changed locally and remotely = Please delete %s or %s entry to resolve conflict = %s %s, Error sending local message = Local entry = %s should be deleted = %s Entry should be deleted locally = Entry deleted locally = Changed local entry ID to %d = %d Local entry ID should be changed to %d = %d Remote entry = Entry should be deleted remotely = Entry deleted remotely = Error deleting remote entry = Click here to delete %d entries = %d Click here to delete this entry = All entries identical = Synchronization = Entry is currently edited by = Edit entry = Delete entry = Goto page = All = Selected entries = Toggle all = Error: start date after end date = : Last %d days = %d Last %d entries = %d all entries = Start date = End date = From = After = to = Before = No entries found = recipients = No 'Attributes' option present in %s = 'Attributes' %s Error: Attribute %s not supplied = : %s Please go back and enter the %s field = %s Error: Attribute %s must be numeric = : %s Error: Value %s not allowed for boolean attributes = : %s Maximum number of attribute options exceeded = Please increase MAX_N_LIST in elogd.c and recompile = .. MAX_N_LIST elogd.c Error: Attribute option %s not existing = : %s Date must be between 1970 and 2037 = 1970 2037 New entry cannot be written to directory "%s" = "%s" Please check that it exists and elogd has write access and disk is not full = , , elogd Invalid mirror_id or entry_date = mirror_id entry_date Please check that it exists and elogd has write access = , elogd Entry %s cannot be read from logbook "%s" = %s "%s" Logbook is empty = This entry has been deleted = with = This is the last entry = This is the first entry = Email notification suppressed = You can "steal" the lock by editing this entry = , Show = Hide = Hide all = Show all = Several logbooks groups are defined on this host = Please select one to list the logbooks in that group = No logbook defined on this server = Synchronize all logbooks = Expand all = ELOG registration = Calendar = Previous Year = Next Year = Error: Command "%s" not allowed = : "%s" Attachment file "%s" empty or not found = "%s" up = down = stop = Entry = name may not contain blanks = regular expressions = Subscribe to logbooks = Cannot connect to server = %s wrote = %s Insert current time = Preview = FONT = SIZE = COLOR = Encoding = User "%s" not found in password file = "%s" Hide attachments = Show only new entries = Show all entries = New entries since = Invalid user name or password = Upload image = Duplicate = Cannot lookup server name = Quote = Cannot write to file %s = %s Invalid user name "%s" or password for remote logbook = "%s" An old ELOG entry has been updated = A new ELOG entry has been submitted = Please enter filename or URL = Maximum allowed file size is = Enter filename or URL = Image uploaded successfully = Image "%s" uploaded successfully = "%s" HelpELCode = ELCode Cannot retrieve file from URL "%s" = "%s" Select period = Last week = Last month = Last 3 Months = 3 Last 6 Months = 6 Last Year = Select %s = %s Bold text CTRL+B = CTRL+B Italics text CTRL+I = CTRL+I Underlined text CTRL+U = CTRL+U Centered text = Insert hyperlink = Insert list CTRL+L = CTRL+L Insert table = Insert heading CTRL+H = CTRL+H Insert code CTRL+O = CTRL+O Show the smiley bar = Insert current time/date = / Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = %d for the next hour = for the next %d hours = %d for the next %d days = %d keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Error in regular expression "%s" = "%s" File not found at URL "%s" = "%s" keep original text = unspecified = ELOG XML import = XML Invalid date format = XML filename = XML XML Import = XML First entry, Ctrl-Home = ,Ctrl-Home Previous entry, Ctrl-PgUp = , Ctrl-PgUp Next entry, Ctrl-PgDn = , Ctrl-PgDn Last entry, Ctrl-End = , Ctrl-End ELOG import = Please choose format to import: = : Enter text = Enter name of hyperlink = Enter URL of hyperlink = enable automatic email notifications = Set all = Please specify a valid email address = User name may not contain blanks = Empty password not allowed = Password may not contain blanks = Anonymous = Your request has been forwarded to the administrator = Insert email = - Insert image CTRL+M = CTRL+M Insert quote = A old entry has been updated on %s = %s Enter %s = %s You will be notified by email upon activation of your new account = Activation notice has been sent to %s = %s Entry size too large for email notification = Text fields are treated as %s = %s Set none = Please enter hour for attribute '%s' = '%s' Please enter minute for attribute '%s' = '%s' Please enter second for attribute '%s' = '%s' No admin user has been defined in configuration file = Please enter numeric value for year of attribute '%s' = '%s' Error: Command "%s" is not allowed for user "%s" = : "%s" "%s" Shell execution not enabled via -x flag = XML file does not contain %s element = XML %s To subscribe to any logbook, click on 'Config' in that logbook = , 'Config' confused = mad = pleased = crying = cool = smiling = happy = winking = big grin = frowning = tongue = yawn = astonished = Enter heading level (1, 2 or 3) = (1, 2 3) Insert anchor point = Derive attributes from XML file = XML XML import preview of %s = XML %s or until I log out = Cannot send email notification to "%s" = "%s" # #---- please translate following items and then remove this comment ----# # Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_english.html0000755000175000017500000000370712556103751020047 0ustar rogerroger ELOG Electronic Logbook Help
ELOG Electronic Logbook Help

The Electronic Logbook (ELog) can be used to store and retrieve messages through a Web interface. Depending on the configuration, the ELog system can host one or more logbooks which are stored in separate sections on the server.

Quick overview

Per default, a page containing the the last entries from a logbook is displayed. By clicking on an entry, its complete contents is displayed. One can use the browser buttons to display the first, previous, next and last message.

The New button creates a new entry. With the Edit button one can edit an existing message, if this is allowed in the configuration file. The Reply button creates a reply to an existing message, similar like a reply to an email.

The Find button opens a query page, where messages from a logbook can be displayed based on filter rules. Each non-empty field works like an additional filter, which is and-ed with the other rules. If no filter is selected, all messages from a logbook are displayed.

More information

For more information, especially about the configuration of ELog, refer to the ELOG home page.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloghelp_italian.html0000755000175000017500000000407612556103751020037 0ustar rogerroger Aiuto per il Registro Elettronico ELOG
Aiuto per il Registro Elettronico ELOG

Il Registro Elettronico (ELog) pu essere usato per consultare dei messaggi attraverso un'interfaccia Web. A seconda della configurazione, il server ELOG pu ospitare uno o pi registri che sono immagazzinati in diverse sezioni.


Un rapido sguardo

Per impostazione predefinita viene visualizzato l'ultimo elemento di un registro. Usando i pulsanti di scorrimento si possono visualizzare il primo, il precedente, il prossimo e l'ultimo messaggio.

Il pulsante Nuovo consente di creare un nuovo elemento. Con il pulsante Modifica si pu modificare un messaggio esistente, se ci permesso dalla configurazione attiva. Il pulsante Rispondi consente di creare una risposta ad un messaggio esistente, come normalmente si fa per una e-mail.

Il pulsante Cerca apre una pagina di interrogazione, nella quale inserire i criteri di ricerca. Ogni campo non vuoto si comporta come un filtro, e tutti quelli non vuoti vengono considerati insieme. Se non si seleziona nessun filtro vengono visualizzati tutti i messaggi del registro.


Pi informazioni


Per informazioni addizionali, specialmente per la configurazione di ELOG, fate riferimento alla Home Page di ELOG.



S. Ritt, 28 Agosto 2001
elog-3.1.3-1/resources/eloglang.danish0000755000175000017500000005340713076344200016625 0ustar rogerroger# Danish translation by Miljan Dedic # 04-06-2004 # Updated by Tony Albers # 19-02-2010 # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Ny Edit = ndr Delete = Slet Reply = Svar Find = Find Last day = Sidste Dag Last 10 = Sidste 10 Config = Konfigurer Change password = ndre Password Logout = Log Ud Help = Hjlp Back = Tilbage Submit = Send First = Frste Last = Sidste Previous = Forrige Next = Nste Copy to = Kopier til Move to = Flyt til Save = Gem Cancel = Annuller Wrong password = Forkert kendeord Please login = Log Ind Username = Bruger Navn Password = Kendeord Old password = Gamle kendeord New password = Nyt kendeord ELOG change password = ndre ELOG kendeord Several logbooks are defined on this host = Flere logbger er defineret p denne host Please select the one to connect to = Vlg hvilken du vil have adgang til Change password for user = ndr kendeord til bruger Logged in as = Du er logget ind som Entry date = Dato for indlg with = Med This is the last entry = Dette er det sidste indlg This is the first entry = Dette er det frste indlg Email sent to = Email sendt til Email notification suppressed = Send ikke Email please select = Vlg venligst Suppress Email notification = Notificr ikke med Email Attachment = Vedhft Reply to this = Svar p denne In reply to = Som svar p Fields marked with = Felter markeret med are required = Er pkrvet Please check that it exists and elogd has write access = Kontroller venligst om det eksisterer,og om elogd har skriveadgang Error: Attribute %s not supplied = Fejl: Attribut %s ikke udfyldt Please go back and enter the %s field = G tilbage og udfyld %s feltet Please use your browser's back button to go back = Brug tilbage knappen p din browser ELOG find = ELOG sg Find = Find Search = Sg Reset Form = Nulstil Form Summary only = Kun Beskrivelse Show attachments = Vis vedhftede filer Printable output = Vis Print Sort in reverse order = Sorter i omvendt rkkeflge Start date = Start dato End date = Slut dato Year = r Text = Tekst Search all logbooks = Sg i alle log bger Last %d days = Sidste %d dage Last %d entries = Sidste %d indlg No entries found = Ingen indlg fundet A new entry has been submitted on %s = Et nyt indlg af %s Logbook = Logbog Yes = Ja No = Nej Error deleting message: status = Fejl ved sletning: Status Error: Command "%s" not allowed = Fejl: kommando "%s" ikke tilladt Cannot open file %s =kan ikke bne filen %s Cannot write to %s = kan ikke skrive til %s No SMTP host defined in [global] section of configuration file = Ingen SMTP host defineret i [global] sektionen af konfigurationsfilen Display threads = Vis trde Logbook is empty = Logbog er tom Download = Hent and all its replies = Og alle Svar Message ID = Besked-ID Not logged in = Ikke logget ind Login = Login Page %d of %d = Side %d af %d all entries = alle indlg Login name = Brugernavn Full name = Fulde navn ELOG user config = ELOG bruger Konfig ELOG new user = Ny ELOG bruger Admin = admin Select user = Vlg bruger Remove user = Fjern bruger New user = Ny bruger Retype new password = Gentag det nye kendeord New passwords do not match, please retype = kendeord passer ikke, prv igen Retype password = Gentag kendeord exists already = Bruger eksisterer allerede Register as new user = Opret som ny bruger A new ELOG user has been registered on %s = En ny bruger er blevet registreret p %s Email = Email User "%s" registered on logbook "%s" = Bruger "%s" i Logbog "%s" registreret User [%s] has been deleted = Bruger [%s] er blevet slettet Hit following URL to activate that account = Tryk p flgende URL for at aktivere bruger konto ELOG registration = ELOG Registrering Your ELOG account has been activated on host = Din ELOG Konto er aktiveret You can access it at = Du kan opn adgang p Are you sure to delete these messages? = Er du sikker p at du vil slette disse beskeder? Select = Vlg All entries = Alle indlg Day = Dag Week = Uge Month = Mned Show last = Vis Sidste(n) Goto page = G til All = Alle Display = Vis entries per page = Indlg per side Toggle all = Vis alle Selected entries = Vlg Indlg Collapse = Fold Sammen Expand = Fold Ud Full = Fuld Summary = Beskrivelse Threaded = Vis som Trde Filters = Filtrer Mode = Modus Options = Funktioner Error: start date after end date = Fejl: Start Dato efter Slut Dato and all their replies = Og alle deres svar Upload = Overfr til server Requested = Rekvirer A new ELOG user wants to register on "%s" = Ny Bruger nsker At Registrere p "%s" logbogen Search text also in attributes = Sg ogs text i attributerne Date = Dato ID = ID Forgot password? = Glemt kendeord? ELOG password recovery = Gendan ELOG kendeord No Email address registered with user name "%s" = Bruger "
har ikke registreret mail adresse Error sending Email via "%s" = Fejl ved afsendelse af e mail via "%s" Forgot = Glemt User "%s" registered on host "%s" = Bruger "%s" p host "%s" registreret Enter your user name or email address = Skriv venligst Bruger navn eller e mail adresse Password recovery for ELOG %s = Genskab kendeord for ELOG %s Host = Host Your ELOG account has been activated = Din ELOG Konto er aktiveret Maximum number of attachments reached = Max antal vedhftninger net on = Til Entry is currently edited by = Indlg er ved at blive redigeret af recipients = Modtagere Suppress shell execution = Ignorer kommando shell Update = Opdater by = Af Change %s = rediger %s Add new option here = Tilfj ny Funktion Expand all = Udvid Alle Add %s = Tilfj %s Synchronization = Synkronisering Error: Value %s not allowed for boolean attributes = Fejl: vrdi %s ikke tilladt for bool'ske Attributer Error: Attribute option %s not existing = Fejl: Attribut Funktion %s eksisterer ikke Last submission = Sidste Indlg Synchronize = Synkroniser No mirror server defined in configuration file = ingen mirror-Server defineret i konfigurations filen user = Bruger Maximum number of replies (%d) exceeded = Max antal svar (%d) overskredet Entry can only be edited %1.2lg hours after creation = Indlg kan kun redigeres %1.2lg timer efter oprettelse Display full entries = Vis alt No entry selected for deletion = Intet indlg markeret til sletning Cannot read entry from local logbook = Kan ikke lse fra lokal logbog Cannot connect to host %s, port %d = Kan ikke forbinde til %s, Port %d Cannot receive "%s" = Kan ikke modtage "%s" Received wrong entry id "%d" = Modtog forkert ID "%d" Entry #%d is locked on remote server = Indlg #%d er lst p remote server Cannot save remote entry locally = Kan ikke gemme remote indlg lokalt local = lokal remote = remote Please delete %s or %s entry to resolve conflict = Slet venligst indlg %s eller %s indlg for at lse problem Error sending local entry = Fejl ved afsending af lokalt indlg Local entry submitted = Lokalt indlg indlagt Error deleting remote entry = Fejl ved sleting af remote indlg New entry cannot be written to directory "%s" = Nyt indlg kan ikke skrives til bibliotek "%s" Entry %s cannot be read from logbook "%s" = Indlg %s kan ikke lses fra logbog "%s" This entry has been deleted = Dette indlg er slettet Entries = Indlg Cannot connect to remote server "%s" = Kan ikke forbinde til remote server "%s" Remote server is not an ELOG server = Remote server er ikke en ELOG server Incorrect remote ELOG server version %s = Forkert Version %s fra remote server Error accessing remote logbook = Fejl ved adgang til remote server Invalid HTTP header = Forkert HTTP header No user name supplied to access remote logbook = Intet bruger navn angivet til remote logbog Synchronizing logbook %s with server "%s" = Synkroniser %s med server "%s" Configuration has been changed locally and remotely = Konfigurationen er ndret lokalt og remote Please merge manually to resolve conflict = Sammensmelt manuelt for at lse konflikt Logbook "%s" does not exist on remote server = Logbog "%s" eksisterer ikke p remote server Entry has been changed locally and remotely = Indlg er ndret lokalt og remote Entry deleted locally = Indlg slettet lokalt Changed local entry ID to %d = lokalt indlg ID til %d Entry deleted remotely = Indlg slettet remote All entries identical = Alle indlg identiske Invalid mirror_id or entry_date = Ugyldigt mirror_id eller indlg_dato Synchronize all logbooks = Synkroniser alle logbger Calendar = Kalender Remote entry received = Remote indlg modtaget Pick a date = Vlg dato Please select at least one '%s' = Vlg mindst en '%s' Please select a '%s' = Vlg en '%s' Please enter month for attribute '%s' = Vlg mned for attribut '%s' Please enter day for attribute '%s' = Vlg dag for attribut '%s' Please enter year for attribute '%s' = Vlg r for attribut '%s' Please enter attribute '%s' = Vlg attribut '%s' Entry time = Dato/Tid Start = Start End = Slut From = Fra After = Efter to = Til Before = fr Previous Year = Forrige r Next Year = Nste r Please enter numeric value for '%s' = Indst numerisk vrdi for '%s' Error: Attribute %s must be numeric = Felj: Attribut %s skal vre en numerisk vrdi 3 Months = 3 mneder 6 Months = 6 mneder Error sending local message = Fejl ved adsendelse af lokalt besked Error receiving message = Fejl ved modtagelse af besked Are you sure to delete this entry? = Er du sikker p at du vil slette dette indlg? ELOG CSV import = ELOG CSV Import Import = Import CSV filename = CSV filnavn Field separator = Felt Separator Edit entry = Rediger Felt Delete entry = Slet Felt CSV Import = CSV Import Derive attributes from CSV file = Hent attributer fra CSV fil Comma = Komma Semicolon = Semikolon Preview import = Forhndsvis Import CSV import preview of %s = CSV import forhndsvisning af %s Too many attributes in CSV file = For mange attributter i CSV fil %d entries successfully imported = %d indlg successfuldt importeret No 'Attributes' option present in %s = Ingen 'Attributter' funktioner tilstede i %s CSV ("," separated) = CSV ("," separeret) CSV (";" separated) = CSV (";" separaret) Auto detect = Auto detektion Resubmit as new entry = send som nyt indlg text = tekst Column header '%s' must be present in CSV file = Kolonneoverskrift '%s' skal vre tilstede i CSV filen Fill text body = Brdteks Please re-enter filename = Genindtast venligst filnavnet New = Ny Last x = Sidste x Activate = Aktiver Entry has been modified = Indlgget er blevet modificeret No attachment file specified = Der er ikke specificeret en fil til vedhftning Delete this logbook = Slet denne logbog Rename this logbook = Omdb denne logbog Create new logbook = Opret ny logbog Syntax error in config file = Indtastningsfejl i konfigurationsfilen Email notification = Email notifikation Are you sure to delete logbook "%s"? = Er du sikker p at du vil slette logbogen ''%s''? Logbook "%s" exists already, please choose different name = Logbogen ''%s'' findes allerede, vlg venligst et andet navn Rename logbook = Omdb logbog Enter new logbook name = Indtast nyt navn p logbog Logbook name = Logbog navn Use existing logbook as template = Brug eksisterende logbog som skabelon none = ingen URL is redirected to: = URL er henvist til Remote config should be received = Fjernkonfiguration skulle blive modtaget Local entry should be submitted = Lokalt indlg br afsendes Remote entry should be received = Fjernindlg br modtages Local entry = Lokalt indlg %s should be deleted = %s br slettes Entry should be deleted locally = Indlg br slettes lokalt Local entry ID should be changed to %d = Lokalt indlgs ID br ndres til %d Remote entry = Fjernindlg Entry should be deleted remotely = Indlg br slettes p fjernlokation Click here to delete this entry = Tryk her for at slette dette indlg Maximum number of attribute options exceeded = Maximalt tilladt antal atributmuligheder overskredet Please increase MAX_N_LIST in elogd.c and recompile = Forg venligst MAX_N_LIST i elogd.c og kompiler igen You can "steal" the lock by editing this entry = Du kan ''stjle'' lsen ved at modoficere dette indlg Several logbooks groups are defined on this host = Der er adskillige logbogsgrupper defineret p dette system Please select one to list the logbooks in that group = Vlg venligst en gruppe for at se dens logbger No logbook defined on this server = Der er ikke defineret en logbog p dette system Goto ELOG home page = G til ELOG's hjemmeside Please enter "%s" = Indtast venligst ''%s'' Change config file = ndr konfigurationsfil Click here to delete %d entries = Tryk her for at slette %d indlg Please check that it exists and elogd has write access and disk is not full = Kontroller venligst at elogd har skriveadgang og at der er ledig plads p harddisken Show = Vis Hide = Skjul Hide all = Skjul alle Show all = Vis alle This logbook requires authentication = Denne logbog behver opmrksomhed Attachment file "%s" empty or not found = Vedhftet fil ''%s'' er tom eller kunne ikke findes Case sensitive = Forskel p store og sm bogstaver List = Liste Date must be between 1970 and 2037 = Dato skal vre mellem 1970 og 2037 up = op down = ned stop = stands Entry = Indlg name may not contain blanks = Der m ikke vre mellemrum i navnet regular expressions = regulre udtryk Text fields are treated as %s = Teksfelter behandles som %s Subscribe to logbooks = Abonnr p logbger enable automatic email notifications = aktiver emailnotifikationer Set all = St alle Set none = St ingen Please enter hour for attribute '%s' = Indtast venligst timetal for attribut '%s' Please enter minute for attribute '%s' = Indtast venligst minuttal for attribut '%s' Please enter second for attribute '%s' = Indtast venligst sekundtal for attribut '%s' No admin user has been defined in configuration file = Der er ikke defineret en administrator i konfigurationsfilen Duplicate = Dupliker Cannot lookup server name = Servernavn kan ikke sls op Cannot connect to server = Der kan ikke forbindes til serveren %s wrote = %s skrev Quote = Citat Insert current time = Indtast nuvrende tidspunkt Please enter numeric value for year of attribute '%s' = Indtast venligst numerisk vrdi for rstal i attributten '%s' Preview = Forhndsvisning FONT = FONT SIZE = STRRELSE COLOR = FARVE smiling = smiler happy = lykkelig winking = blinker big grin = griner crying = grder cool = klig frowning = vrnger ansigt confused = forvirret mad = gal pleased = glad tongue = rkker tunge yawn = gaber Encoding = Formattering User "%s" not found in password file = Brugeren ''%s'' blev ikke fundet i password filen Cannot write to file %s = Kan ikke skrive til filen %s Invalid user name "%s" or password for remote logbook = Ugyldigt brugernavn eller kodeord til fjernlogbog Hide attachments = Skjul vedhftninger Show only new entries = Vis kun nye indlg Show all entries = Vis alle indlg New entries since = Nye indlg siden An old ELOG entry has been updated = Et gammelt ELOG indlg er blevet opdateret A new ELOG entry has been submitted = Et nyt ELOG indlg er blevet publiceret Invalid user name or password = Ugyldigt brugernavn eller kodeord Upload image = Send billede Please enter filename or URL = Indtast venligst filnavn eller URL Maximum allowed file size is = Den strst tilladte filstrrelse er Enter filename or URL = Indtast filnavn eller URL Image uploaded successfully = Billedet er korrekt overfrt Image "%s" uploaded successfully = Billedet ''%s'' er korrekt overfrt HelpELCode = HjlpELKode Cannot retrieve file from URL "%s" = Filen kan ikke hentes fra URL ''%s'' Enter name of hyperlink = Indtast hyperlink navn Enter URL of hyperlink = Indtast hyperlink URL adresse astonished = forblffet Error in regular expression "%s" = Fejl i regulrt udtryk ''%s'' File not found at URL "%s" = Filen blev ikke fundet p URL ''%s'' Entry size too large for email notification = Indlggets strrelse er for stort til email-notifikation Please specify a valid email address = Specificer venligst en gyldig email adresse Cannot send email notification to "%s" = Kan ikke sende email-notifikation til ''%s'' Error: Command "%s" is not allowed for user "%s" = Fejl: Kommando ''%s'' er ikke tilladt for bruger ''%s'' Shell execution not enabled via -x flag = Kommandofortolkerudfrelse er ikke slet til med -x flaget Enter heading level (1, 2 or 3) = Indtast overskiftsniveau (1, 2 eller 3) User name may not contain blanks = Der m ikke vre mellemrum i brugernavnet Empty password not allowed = Tomme kodeord er ikke tilladt Password may not contain blanks = Kodeord m ikke indeholde mellemrum Anonymous = Anonym Activation notice has been sent to %s = Notifiakation om aktivering er sendt til %s Your request has been forwarded to the administrator = Din foresprgsel er sendt videre til en administrator You will be notified by email upon activation of your new account = Du vil f besked i en email nr din konto er aktiveret Select period = Vlg periode Last week = Sidste uge Last month = Sidste mned Last 3 Months = Sidste 3 mneder Last 6 Months = Sidste 6 mneder Last Year = Sidste r Enter %s = Indtast %s Select %s = Vlg %s Bold text CTRL+B = Fed tekst CTRL+B Italics text CTRL+I = Kursiv tekst CTRL+I Underlined text CTRL+U = Understreget tekst CTRL+U Centered text = Centreret tekst Insert hyperlink = Indsat hyperlink Insert email = Indtast email Insert image CTRL+M = Indst billede CTRL+M Insert quote = Indst citat Insert list CTRL+L = Indst liste CTRL+L Insert table = Indst tabel Insert heading CTRL+H = Indst overskrift CTRL+H Insert code CTRL+O = Indst kode CTRL+O Show the smiley bar = Vis smileymenu Insert current time/date = Indtast nuvrende tidspunkt/dato Entry is locked on local server and therefore skipped = Indlgget er lst p det lokal system og springes derfor over Keep me logged in on this computer = Lad mig blive ved at vre logget ind p denne maskine for the next %d minutes = i de nste %d minutter for the next hour = i den nste time for the next %d hours = i de nste %d timer for the next %d days = i de nste %d dage keep original values = gem oprindelige vrdier Entry is locked = Indlg er lst Edit anyhow = Ret alligevel Insert horizontal line = Indst horisontal linje Insert anchor point = Indst ankerpunkt keep original text = behold oprindelig tekst unspecified = uspecificeret To subscribe to any logbook, click on 'Config' in that logbook = For at abonnere p en logbog, vlg 'konfigurer' i denne logbog ELOG XML import = ELOG XML import Derive attributes from XML file = Afled attributter fra XML fil XML filename = XML filnavn Invalid date format = Ugyldigt datoformat XML file does not contain %s element = XML filen indeholder ikke elementet %s XML import preview of %s = XML import forhndsvisning af %s XML Import = XML Import A old entry has been updated on %s = Et gammelt indlg er blevet opdateret den %s First entry, Ctrl-Home = Frste indlg, Ctrl-Home Previous entry, Ctrl-PgUp = Foregende indlg, Ctrl-PgUp Next entry, Ctrl-PgDn = Nste indlg, Ctrl-PgDn Last entry, Ctrl-End = Sidste indlg, Ctrl-End ELOG import = ELOG import Please choose format to import: = Vlg det format der skal importeres Enter text = Indtast tekst or until I log out = indtil jeg logger ud Make smaller = Gr mindre Original size = Oprindelig strrelse Make larger = Gr strre Rotate left = Rotr til venstre Rotate right = Rotr til hjre Delete attachment = Slet vedhftning Cannot create thumbnail, please check ImageMagick installation = Kan ikke danne oversigtsbilleder, kontroller at ImageMagick er korrekt installeret Attachments = Vedhftninger Error: Content length (%d) larger than maximum content length (%d) = Fejl: Lngde af indhold (%d) er strre end maksimalt tilladt (%d) ELOG error = ELOG fejl Only user %s can edit this entry = Kun brugeren %s kan rette i dette indlg Export to = Eksporter til Do not ignore first line = Ignorer ikke frste linje Last %d hours = Sidste %d timer Invalid URL = Ugyldig URL Attribute "%s" is not allowed in config file = Attributten ''%s'' er ikke tilladt i konfigurationsfilen Only user %s can delete this entry = Kun bruger %s kan slette dette indlg File system full, ELOG cannot continue to work = Filsystemet er lbet fuld, ELOG kan ikke fortstte # #---- please translate following items and then remove this comment ----# # 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_japanese.html0000755000175000017500000000422312556103751020176 0ustar rogerroger ELOG Electronic Logbook Help
ELOG GNgjNXOubN@wv

GNgjNXOubN (ELog(ҒFuC[OvƔ܂B)) gpƁAbZ[W̕ۊǂȂтɏo͂EFuC^[tF[XčsȂƂł܂B ݒύX邱ƂɂElogVXe͈‚邢͕OubNǗ邱Ƃł܂B ̃OubN̓T[o[ԁX̃ZNVɕĕۊǂ܂B

Tv

ftHg̐ݒł́AŌ̋L܂ły[W\܂B LNbNƁAS\܂B{p̃{^gp邱ƂɂĤŐL ̋LŌ̋Lƕ\邱Ƃł܂B

VK{^ƂɂVKL쐬ł܂B̋LҏW邽߂ɂ ҏW{^ĂBe̕ҏW”\ɂ邩ǂ͊‹ݒt@CŐݒł܂B ԐM{^͊̋LɕԐMۂɂgBdq[ŕԐMsȂ̂ lɋLɕԐM邱Ƃł܂B

{^ɂ茟p̃y[WJ܂Bɂ胍OubN̑{ĂL y[Wɕ\邱Ƃł܂BtB[hɉ܂Ăꍇ́ÃtB[h Ƃ̘_aƂ܂BĂȂΑSẴbZ[W\܂B

ڂ

ELOG̊‹ݒȂǁAڂɂ‚܂ẮA ELOG z[y[WQƂB

{ŎgpɍۂẮAR[hZbgShift_JISw肵ĂB


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.japanese0000755000175000017500000004247113076344200017144 0ustar rogerroger# # Japanese translation by Satoshi Mihara (mihara@icepp.s.u-tokyo.ac.jp) # Shift_JISR[hgpĂB # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # Change %s = %s ύX Logged in as = OC Not logged in =@OCႢ܂ Please use your browser's back button to go back = Õy[Wɖ߂ɂ̓uEÚu߂vĂB Back = ߂ Config = ݒ ELOG change password = pX[hύX Wrong password = pX[hႢ܂ New passwords do not match, please retype = VKpX[hႢ܂.x͂Ă Change password for user = pX[hύXA[U Old password = ݂̃pX[h New password = VKpX[h Retype new password = VKpX[h̍ē Submit = e user = [U[ on = I Maximum number of replies (%d) exceeded = őԐM”\i%dj𒴉߂Ă܂ Entry can only be edited %1.2lg hours after creation = L͓e@%1.2lg@ԂҏWł܂ Please select at least one '%s' = ŒЂƂ‚'%s'IĂ Please select a '%s' = '%s'IĂ Please enter attribute '%s' = '%s'@͂Ă Update = XV Fields marked with = tB[h}[N are required = Kvł Entry date = e Add %s = %s Add new option here = ɐVIvVĂ please select = IĂ Suppress Email notification = EmailʒmsȂȂ Suppress shell execution = VFR}hsȂ Resubmit as new entry = VLƂēe Attachment = Yt Delete = 폜 Maximum number of attachments reached = őYt”\t@C𒴉߂܂ Upload = XV ELOG find = ELOG Search = Reset Form = Zbg Mode = [h Display full entries = SĂ̋L\ Summary only = LTv̂ Summary = Tv Display threads = Xbh\ Options = IvV Show attachments = Yt\ Printable output = o Sort in reverse order = tɃ\[g Search all logbooks = SẴOubN Display = \ entries per page = FPy[Wɕ\L Filters = tB^[ Start date = Jn Year = N Pick a date = tI Show last = ͈̔͂̍ŋ߂̋L Day = Week = T Month = End date = I Text = { Search text also in attributes = ̃eLXg Admin = Ǘ Save = ۑ Cancel = LZ Cannot open file %s = t@C%sJ܂ Cannot write to %s = t@C%sɏ߂܂ Login name = OC exists already =@ɓeĂ܂ Your ELOG account has been activated = ELOGAJEgLɂȂ܂ Your ELOG account has been activated on host = ELOGAJEgLɂȂ܂.zXg You can access it at = ̏ꏊŃANZXł܂ A new ELOG user wants to register on "%s" = VKELOG[U["%s"ւ̓o^v܂ User "%s" registered on logbook "%s" = [U["%s"OubN"%s"ɓo^܂ User "%s" registered on host "%s" = [U["%s"zXg"%s"ɓo^܂ A new ELOG user has been registered on %s = VKELOG[U[%sɓo^܂ Logbook = OubN Host = zXg Full name = tl[ Email = dq[ Hit following URL to activate that account = AJEgLɂɂ͎URLNbNĂ Requested = v ELOG user config = ELOG[U[ݒ Select user = [U[I User [%s] has been deleted = [U[[%s]폜܂ Change password = pX[hύX Remove user = [U[폜 New user = VK[U[ No Email address registered with user name "%s" = [U["%s"ɂ͓dq[o^Ă܂ Password recovery for ELOG %s = ELOG %s ɑ΂pX[h𕜋 ELOG password recovery = ELOGpX[h̕ Error sending Email via "%s" = "%s"@dq[𑗂ۂɃG[܂ Enter your user name or email address = [U[dq[AhX͂Ă Forgot = Forgot ELOG new user = ELOGVK[U Password = pX[h Retype password = pX[hē No = Yes = ͂ Error deleting message: status = L폜ɃG[܂ No entry selected for deletion = 폜LIĂ܂ Are you sure to delete these messages? = ̋L폜Ă낵łH and all their replies = ԐML폜܂B낵łH and all its replies = ԐML폜܂B낵łH Cannot connect to remote server "%s" = [gT[o[ "%s"ɐڑł܂ Remote server is not an ELOG server = [gT[o[ELOGT[o[ł͂܂ Incorrect remote ELOG server version %s =@[gELOGT[o[̃o[WقȂ܂. %s Error accessing remote logbook = [gOubNɃANZXۂɃG[܂ Invalid HTTP header = HTTPwb_[܂ No user name supplied to access remote logbook = [gOubNɃANZX邽߂̃[U[Kvł Cannot read entry from local logbook = [JOubN̋Lǂ߂܂ Cannot connect to host %s, port %d = zXg %s ̃|[g %d ɐڑł܂ Cannot receive "%s" = "%s"󂯎邱Ƃł܂ Received wrong entry id "%d" = sȃGgԍ "%d"ł Entry #%d is locked on remote server = Gg #%d ̓[gT[o[ɃbNĂ܂ Cannot save remote entry locally = [gGg[Jɕۑł܂ No mirror server defined in configuration file = ‹ݒt@CɃ~[T[o[`Ă܂ Synchronizing logbook %s with server "%s" = OubN %s T[o[ "%s"ƓĂ܂ Configuration has been changed locally and remotely = ‹ݒ肪[Jyу[gŕύXĂ܂ Please merge manually to resolve conflict = RtNg݂܂.蓮ʼnĂ Logbook "%s" does not exist on remote server = OubN "%s"̓[gT[o[ɑ݂܂ Remote entry received = [g̋LM܂ local = [J remote = [g Entry has been changed locally and remotely = L̓[Jyу[gŕύX܂ Please delete %s or %s entry to resolve conflict = 邽߂ %s@܂ %s ̂ꂩ̋L폜Ă Entry deleted locally = L̓[Jɍ폜܂ Error sending local entry = [JL𑗐MɃG[܂ Local entry submitted = [JLM܂ Changed local entry ID to %d = [JLID %d ɕύX܂ Entry deleted remotely = L[gō폜܂ Error deleting remote entry = [gL̍폜ɃG[܂ All entries identical = SĂ̋Lł Synchronization = Entry is currently edited by = L͌ݕҏWł. ҏW ID = ID Date = t Full = S Threaded = Xbh Collapse = ܂肽 Expand = WJ All entries = SĂ̋L Goto page = y[Wֈړ Previous = ŐL Next = ̋L All = S Selected entries = IꂽL Toggle all = ׂĂgO Copy to = Rs[ Move to = ړ Error: start date after end date = G[FJnŏIɂ܂ Last day = ŏI Last %d days = Ō %d Last %d entries = Ō %d L all entries = SĂ̋L Page %d of %d = %d y[WiS %d y[Wj Select = I No entries found = L‚܂ł No SMTP host defined in [global] section of configuration file = ‹ݒt@C[global]SMTPT[o[`Ă܂ A new entry has been submitted on %s = VKL %s ɓe܂ recipients = l Error: Attribute %s not supplied = G[F%s̑܂ Please go back and enter the %s field = ߂ %s̃tB[hɓ͂Ă Error: Value %s not allowed for boolean attributes = G[F%s̒l̓u[ɂ͎󂯓܂ Error: Attribute option %s not existing = G[FIvV %s݂͑܂ New entry cannot be written to directory "%s" = VKLfBNg "%s"ɏނƂł܂ Please check that it exists and elogd has write access = elogd݌Ă邱ƂmFĂ Invalid mirror_id or entry_date = mirror_id entry_date ̒lĂ܂ Entry %s cannot be read from logbook "%s" = %s ̋LOubN "%s" ǂݏoƂł܂ Last = Ō First = ŏ Logbook is empty = L܂ This entry has been deleted = ̋L͍폜܂ with = with This is the last entry = ͍Ō̋Lł This is the first entry = ͍ŏ̋Lł Email notification suppressed = dq[ʒmsȂȂ Email sent to = dq[t Message ID = LID In reply to =@ԐML Reply to this = ̋LɕԐM Login = OC Please login = OCĂ Username = [U[ Forgot password? = pX[hYꂽ Register as new user = VK[U[Ƃēo^ by = e Several logbooks are defined on this host = ̃zXgɂ͕̃OubN`Ă܂ Please select the one to connect to = ڑ̂‘Ił Synchronize all logbooks = SẴOubN𓯊 Entries = ̋L Last submission = Ō̓e Expand all = SĂWJ ELOG registration = ELOGo^ Calendar = J_[ New = VK Edit = ҏW Reply = ԐM Error: Command "%s" not allowed = G[FR}h"%s"͋‚Ă܂B Help = wv Find = Last 10 = Ō10 Download = _E[h Synchronize = Logout = OAEg Please enter month for attribute '%s' = '%s'Ɍ͂ĂB Please enter day for attribute '%s' = '%s'ɓ͂ĂB Please enter year for attribute '%s' = '%s'ɔN͂ĂB Please enter numeric value for '%s' = '%s'ɂ͔p͂ĂB Entry time = o^ Start = Jn End = I After = ȍ~ to = ܂ Before = ȑO Error: Attribute %s must be numeric = G[F%s̒l͔p͂ĂB Previous Year = ON Next Year = N 3 Months = 3 6 Months = 6 Error sending local message = [JL̑MɃG[܂ Error receiving message = bZ[W̎MɃG[܂ Are you sure to delete this entry? = ̋L폜Ă낵ł? Import = C|[g CSV filename = CSVt@C Field separator = tB[h Edit entry = LҏW Delete entry = L폜 CSV Import = CSVC|[g ELOG CSV import = ELOG@CSṼC|[g Derive attributes from CSV file = CSVt@C瑮擾 Comma = R} Semicolon = Z~R Preview import = C|[g̃vr[ CSV import preview of %s = %sCSVC|[gvr[ Too many attributes in CSV file = CSVt@C̑܂ %d entries successfully imported = %d‚̋LC|[g܂ No 'Attributes' option present in %s = %sɑIvV܂ # #---- please translate following items and then remove this comment ----# # CSV ("," separated) = CSV (";" separated) = Auto detect = From = text = Column header '%s' must be present in CSV file = Fill text body = Please re-enter filename = Last x = Activate = Entry has been modified = No attachment file specified = Delete this logbook = Rename this logbook = Create new logbook = Syntax error in config file = Email notification = Are you sure to delete logbook "%s"? = Logbook "%s" exists already, please choose different name = Rename logbook = Enter new logbook name = Logbook name = Use existing logbook as template = none = URL is redirected to: = Remote config should be received = Local entry should be submitted = Remote entry should be received = Local entry = %s should be deleted = Entry should be deleted locally = Local entry ID should be changed to %d = Remote entry = Entry should be deleted remotely = Click here to delete this entry = Maximum number of attribute options exceeded = Please increase MAX_N_LIST in elogd.c and recompile = You can "steal" the lock by editing this entry = Several logbooks groups are defined on this host = Please select one to list the logbooks in that group = No logbook defined on this server = Goto ELOG home page = Please enter "%s" = Change config file = Click here to delete %d entries = Please check that it exists and elogd has write access and disk is not full = Show = Hide = Hide all = Show all = This logbook requires authentication = Attachment file "%s" empty or not found = Case sensitive = List = Date must be between 1970 and 2037 = up = down = stop = Entry = name may not contain blanks = regular expressions = Text fields are treated as %s = Subscribe to logbooks = enable automatic email notifications = Set all = Set none = Please enter hour for attribute '%s' = Please enter minute for attribute '%s' = Please enter second for attribute '%s' = No admin user has been defined in configuration file = Duplicate = Cannot lookup server name = Cannot connect to server = %s wrote = Quote = Insert current time = Please enter numeric value for year of attribute '%s' = Preview = FONT = SIZE = COLOR = smiling = happy = winking = big grin = crying = cool = frowning = confused = mad = pleased = tongue = yawn = Encoding = User "%s" not found in password file = Cannot write to file %s = Invalid user name "%s" or password for remote logbook = Hide attachments = Show only new entries = Show all entries = New entries since = An old ELOG entry has been updated = A new ELOG entry has been submitted = Invalid user name or password = Upload image = Please enter filename or URL = Maximum allowed file size is = Enter filename or URL = Image uploaded successfully = Image "%s" uploaded successfully = HelpELCode = Cannot retrieve file from URL "%s" = Enter name of hyperlink = Enter URL of hyperlink = astonished = Error in regular expression "%s" = File not found at URL "%s" = Entry size too large for email notification = Please specify a valid email address = Cannot send email notification to "%s" = Error: Command "%s" is not allowed for user "%s" = Shell execution not enabled via -x flag = Enter heading level (1, 2 or 3) = User name may not contain blanks = Empty password not allowed = Password may not contain blanks = Anonymous = Activation notice has been sent to %s = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Error accessing password file = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_swedish.html0000644000175000017500000000431612556103751020056 0ustar rogerroger ELOG Elektronisk Loggbok Hjlp
ELOG Elektronisk Loggbok Hjlp

Den elektroniska loggboken (ELog) kan anvndas till att spara och hmta meddelanden (kallas poster) via ett webb-grnssnitt. ELog systemet kan, beroende p den aktuella konfigurationen, innehlla flera olika loggbcker vilka sparas i separata sektioner p ELog servern.

Kort versikt

Som standard, visas sidan med de senaste posterna i en loggbok. Genom att klicka p en post visas dess fullstndiga innehll. Man kan anvnda webblsarens knappar fr att visa frsta, fregende, nsta och sista post.

Med knappen Ny kan en ny post skapas. Med knappen ndra, kan man editera en existerande post, om detta r tilltet enligt ELog systemets konfigurationsfil. Svara knappen, anvnds fr att svara p en existerande post, p liknande stt som nr man svarar p ett e-post meddelande.

Sk knappen ppnar en sksida, med vars hjlp poster frn en loggbok kan visas efter att ha sorterats enligt angivna filtreringsregler. Varje icke-tomt flt fungerar som ett ytterligare filter, vilket lggs till redan existerande filter genom ett logiskt OCH. Om inget filter anges visas alla poster i loggboken.

Mera information

Fr mera information, speciellt gllande konfigureringen av ELog, se ELOG systemets hemsida.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloghelp_turkish.html0000755000175000017500000000431412556103751020102 0ustar rogerroger ELOG Elektronik Kayıt Defteri Yardımı
ELOG Elektronik Kayıt Defteri Yardımı

Elektronik Kayıt Defteri(ELog), web arabirimi içerisinden mesajları depolamak ve erişmek için kullanılır. Yapılandırmaya bağlı olarak, Elog sistemi bir veya birden fazla, sunucu üzerinde ayrı bölümlerde depolanan, kayıt defterlerini host edebilir

Genel Açıklama

Varsayılan olarak, sayfa kayıt defterinden görüntülenen son girdileri ihtiva eder. Bir girdinin üzerini tıkladığınızda, tüm içerik görüntülenir. Gezginin düğmeleri kullanılarak ilk, önceki, sonraki ve son mesaj görüntülenebilir.

Yeni düğmesi yeni bir girdi yaratır. Düzenle düğmesi ile eğer yapılandırma dosyası içinde izin verildiyse, mevcut olan bir mesajı düzenleyebilirsiniz. Yanıtla düğmesi mevcut olan mesaja e-postadaki yanıtlama gibi cevap yaratır.

Bul düğmesi, filtre kurallarına göre kayıt defterinden görüntüleyebilen bir sorgu sayfası açar, Her bir alan, eklenen diğer kurallar ile ilave filtre gibi çalışır. Eğer hiç filtre seçilmezse, kayıt defterindeki tüm mesajlar görüntülenir.

Daha fazla bilgi

Daha fazla bilgi için, özellikle Elog'un ayarlanması hakkında, ELOG anasayfasına bakın.


S. Ritt, 28 Ağustos 2001
Bu belge İngilizce'den Türkçe'ye Devrim Esentürk tarafından çevrilmiştir. Temmuz 2005
elog-3.1.3-1/resources/eloghelp_indonesia.html0000644000175000017500000000412112560137216020351 0ustar rogerroger Bantuan Logbook Elektronik ELOG
Bantuan Logbook Elektronik ELOG

Buku catatan/Logbook elektronik(ELog) dapat digunakan untuk menyimpan dan mengambil pesan melalui antarmuka Web. Tergantung pada konfigurasi, sistem Elog dapat menjadi tempat satu atau lebih logbook/buku catatan yang disimpan dalam bagian terpisah pada server.

Gambaran singkat

Standarnya, halaman yang memuat catatan terakhir dari buku catatan akan ditampilkan. Dengan mengklik pada catatan, isi lengkap ditampilkan. Dapat menggunakan tombol browser untuk menampilkan yang pertama, sebelumnya, berikutnya dan pesan terakhir.

Tombol Baru/New menciptakan catatan baru. Dengan tombol Edit seseorang dapat mengedit pesan yang sudah ada. Tombol Balas membuat balasan ke pesan yang ada, sama seperti membalas email.

Tombol Cari membuka halaman permintaan, di mana pesan dari buku catatan dapat ditampilkan berdasarkan aturan filter/penyaringan. Setiap lajur yang tidak kosong bekerja seperti saringan/filter tambahan, dan terkombinasi dengan aturan yang lain. Jika tidak ada filter yang dipilih, semua pesan dari buku catatan akan ditampilkan.

Informasi lebih lanjut

Untuk informasi lebih lanjut, terutama tentang konfigurasi Elog, dapat merujuk ke Halaman ELOG.


S. Ritt, 28 Agustus 2001
elog-3.1.3-1/resources/elcode_zh_CN-GB2312.html0000644000175000017500000002250012556103750017635 0ustar rogerroger ELCode

ELCode


ELCode һıǣڸʽһ ELOG ¼ HTML ﷨ƣ 򵥡ǰڷ [ ] ֮䣬 < >һЩ ڸııʽ塢СɫһЩǶ URL ͼƬ ELCode Ǻ BBCode ƣʱҲΪ vB code

ıʽ


ELCode ڻıʽ

[b]Hello[/b] ɴı Hello
[u]Hello[/u] ɴ»ߵı Hello
[i]Hello[/i] бı Hello
[color=red]Hello[/color]
[color=#FF0000]Hello[/color]
ı Hello ָ֪ɫƣ white, red, green, lightgreen һʮƵ RGB ֵͷλ (00-FF) ʾɫ֣ڶֱʾɫֱʾɫ
[size=5]Hello[/size] ı Hello ıĴСرʾΧ 1 29 һЩӣ size=1, size=2, size=3, size=4, size=5, size=6.
[font=comic]Hello[/font] ı塣һЩʾ comic, courier, arial, tahoma, times, verdana
עЩʾûʲô仯ӢĶԡ
[center]Hello[/center]
ʹıĶ롣

ǿǶף

[size=5][color=red][b]Hello[/b][/color][/size]

Hello ע⣬ǵǶױ밴ȷ˳Уд

[b][color=red]Ǵģ[/b][/color]

ǴģΪ [b] ȿʼģҲӦ [/color] ʶ֮󣬱

[b][color=red]ȷ[/color][/b]

رַܱ


ʱ㲢 ELCode DZ̳ͣһЩǵʾʱ ΪЩǣҪڱǰϷб "\" \[b] [b]


Цü̵ĴһЩͼ󣬱 :) ʾˡ elog ﱻԶתСͼ󣬱



:) :( ;)
:)) :D ?)
;( :] :O
8)8o X(
:P


÷ʽοĺͲοģ

  • [quote]ԭʼı[/quote] ʽһЩıһ οã磺

    Quote:
    ԭʼı

  • [quote="Mr. Bean"]ԭʼı[/quote] ʽһ Щıɴοã磺

    Mr. Bean wrote:
    ԭʼı

    עʹòοñҪñʶο "Mr. Bean"

̶ı


Ϊʹ Courier ̶ȵı

[code]Hello[/code]

ܻڼбߺƱԷĶʱã

Item                Price     Availability
====                =====     ============

Apples               0.50     1000
Grapefruits          2.50     100
Sun-dried Tomatos   12.50     20

versus

Item Price Availability
==== ===== ============

Apples 0.50 1000
Grapefruits 2.50 100
Sun-dried Tomatos 12.50 20

Lists


һûбʹñ [list][/list]

[list]
[*]Ocean
[*]Forest
[*]Desert
[/list]

  • Ocean
  • Forest
  • Desert

бУָÿĿǰʹʲôʶΪɴֵб ʹ [list=1][/list]:

[list=1]
[*]Ocean
[*]Forest
[*]Desert
[/list]

  1. Ocean
  2. Forest
  3. Desert

Ϊɰĸбʹ [list=a][/list]

[list=a]
[*]Ocean
[*]Forest
[*]Desert
[/list]

to produce

  1. Ocean
  2. Forest
  3. Desert

õѡ [list=A] ʹôдĸ[list=I] ʹ ĸ֡


ӻͳһʽԴַ (URL) öַʽɣ

  • κ﷨ȷ URL ԶתӣҪϿͷ http:// midas.psi.ch/elog midas.psi.ch/elog http://www.cern.ch http://www.cern.ch.

  • Ϊʽָһ URL [url]www.cern.ch[/url]

  • ҪһЩͬ URL ıָ [url=http://www.cern.ch]Text[/url]

    [url=http://www.cern.ch]CERN[/url]

    ɣ

    CERN

  • ʼĵַơǿ [email]john.doe@domain.org[/email] д߼򵥵 john.dow@domain.org ת john.doe@domain.org

Ƕͼ


ַʽıǶͼ

  • ͼñ [img]URL[/img] Ƕ뵽ıУ˴ URL ָһϿõͼע⣬Щͨ Web ɷʵͼ ԡûһ Web 㱾Ӳϵͼ󲻿Դⲿʡ Ƕ elog ͼ꣺

    [img]http://midas.psi.ch/elogs/elog.png[/img]

  • һͼΪ elog ¼ĸķʽ

    [img]elog:/1[/img]

    ˴ elog:/1 ʾһelog:/2ʾڶԴơ ַʽοĸͼǾͲڼ¼Ľβˡ

ELOG ҳ
elog-3.1.3-1/resources/elogd-zh_CN-UTF8.cfg0000644000175000017500000000137712556103751017145 0ustar rogerroger[global] port = 8080 Language = zh_CN-UTF8 charset = utf-8 [电子记录本演示] Theme = default Comment = 实验记录本演示 Attributes = 作者, 类别 Options 类别 = 仪器调试, 实验设置, 值班安排, 数据采集, 数据分析, 结果讨论, 其它 Required Attributes = 作者, 类别 Page Title = ELOG - $subject Reverse sort = 1 Quick filter = Date, Type [demo] Theme = default Comment = General linux tips & tricks Attributes = Author, Type, Category, Subject Options Type = Routine, Software Installation, Problem Fixed, Configuration, Other Options Category = General, Hardware, Software, Network, Other Extendable Options = Category Required Attributes = Author, Type Page Title = ELOG - $subject Reverse sort = 1 Quick filter = Date, Type elog-3.1.3-1/resources/eloghelp_czech.html0000644000175000017500000000417112556103751017503 0ustar rogerroger Nápověda k ELOG Electronic Logbook
Nápověda k ELOG Electronic Logbook

The Electronic Logbook (ELog) lze použít k ukládání a vybírání zpráv pomocí Webového rozhraní. V závislosti na nastavení může ELog obsluhovat jeden, nebo i více logbooků uložených v oddělených sekcích serveru.

Stručný souhrn

Standardně stránka zobrazuje poslední záznamy obsažené v logbooku. Kliknutím na záznam se zobrazí jeho kompletní obsah. Použitím tlačítek přehrávače ( vpravo nahoře ) zobrazíte první, předchozí, následnou a poslední zprávu.

Odkaz New ( Nový ) vytvoří nový záznam.

Pomocí odkazu Edit ( Úprava ) editujete existující zprávu, pokud je tato funkce povolena v konfiguračním souboru.

Pomocí odkazu Reply ( Odepsat ) vytvoříte odpověď k existující zprávě, podobně jako u emailu.

Odkaz Find ( Hledat ) otevírá dotazovací stránku, pomocí které si můžete zobrazit filtrované zprávy. Každé vyplněné pole funguje jako další filtr spojený s dalšími pomocí logického součinu. Pokud není vybrán žádný filtr zobrazí se všechny zprávy z logbooku.

Další informace

Pro další informace, specielně o konfiguraci se podívejte na ELOG home page.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.bulgarian0000755000175000017500000004714113076344200017321 0ustar rogerroger# To use Bulgarian translation, add the following lines to [global]: # Charset = windows-1251 # Language = bulgarian # # Translation for elogd.c 1.526 # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Edit = Delete = Reply = Download = Find = Select = Last day = Move to = Copy to = Config = Admin = Login = CSV Import = CSV Logout = Help = Date = Text = Back = Submit = New user = Save = Requested = Activate = Please login = , Username = Password = ID = ID Logbook = Goto ELOG home page = ELOG Upload = Cancel = Update = Search = Last 10 = 10 Change %s = %s Delete this logbook = Rename this logbook = Create new logbook = Remove user = Forgot = Synchronize = Page %d of %d = %d %d Logged in as = Last x = Full = Summary = Threaded = Previous = Last = First = Next = Message ID = ID Entry time = Reply to this = ̀ Please enter attribute '%s' = , "%s" No attachment file specified = Entry has been modified = are required = Fields marked with = Add %s = %s please select = , Suppress Email notification = . Resubmit as new entry = Attachment = Email sent to = . Forgot password? = ? Register as new user = Collapse = Expand = In reply to = A new entry has been submitted on %s = %s ELOG user config = ELOG Select user = Login name = Full name = All entries = Entries = ELOG password recovery = ELOG Enter your user name or email address = . Several logbooks are defined on this host = Please select the one to connect to = , Last submission = by = This logbook requires authentication = List = Not logged in = Please use your browser's back button to go back = , Back , ELOG change password = ELOG Wrong password = New passwords do not match, please retype = , , Change password for user = Old password = New password = Retype new password = Year = Pick a date = user = on = Maximum number of replies (%d) exceeded = (%d) Entry can only be edited %1.2lg hours after creation = %1.2lg Please select at least one '%s' = , "%s" Please select a '%s' = , "%s" Please enter month for attribute '%s' = , "%s" Please enter day for attribute '%s' = , "%s" Please enter year for attribute '%s' = , "%s" Please enter numeric value for '%s' = , "%s" Add new option here = Suppress shell execution = Maximum number of attachments reached = ELOG find = ELOG Reset Form = Mode = Display full entries = Summary only = Display threads = CSV ("," separated) = CSV ( ",") CSV (";" separated) = CSV ( ";") Options = Show attachments = Printable output = Sort in reverse order = Search all logbooks = Display = entries per page = Filters = Entry date = Start = Show last = Day = Week = Month = 3 Months = 3 6 Months = 6 End = Search text also in attributes = Case sensitive = / Cannot open file %s = %s Cannot write to %s = %s Syntax error in config file = Please enter "%s" = , "%s" exists already = No SMTP host defined in [global] section of configuration file = SMTP [global] Your ELOG account has been activated = ELOG Your ELOG account has been activated on host = ELOG You can access it at = A new ELOG user wants to register on "%s" = ELOG "%s" User "%s" registered on logbook "%s" = "%s" "%s" User "%s" registered on host "%s" = "%s" "%s" A new ELOG user has been registered on %s = ELOG %s Host = Email = . Hit following URL to activate that account = URL, User [%s] has been deleted = [%s] Change password = Change config file = No Email address registered with user name "%s" = . "%s" Password recovery for ELOG %s = ELOG %s Email notification = . Error sending Email via "%s" = . "%s" ELOG new user = ELOG Retype password = No = Yes = Error deleting message: status = : No entry selected for deletion = Are you sure to delete these messages? = , ? and all their replies = Are you sure to delete this entry? = , ? and all its replies = Are you sure to delete logbook "%s"? = , "%s"? Logbook "%s" exists already, please choose different name = "%s" , Rename logbook = Enter new logbook name = Logbook name = Use existing logbook as template = none = ELOG CSV import = ELOG CSV Import = Field separator = Auto detect = Comma = Semicolon = Derive attributes from CSV file = CSV Preview import = text = Column header '%s' must be present in CSV file = "%s" CSV Fill text body = CSV filename = CSV Please re-enter filename = , CSV import preview of %s = CSV %s Too many attributes in CSV file = CSV %d entries successfully imported = %d Cannot connect to remote server "%s" = "%s" Remote server is not an ELOG server = ELOG Incorrect remote ELOG server version %s = %s ELOG URL is redirected to: = URL- : Invalid HTTP header = HTTP No user name supplied to access remote logbook = Error accessing remote logbook = Cannot read entry from local logbook = Cannot connect to host %s, port %d = %s, %d Cannot receive "%s" = "%s" Received wrong entry id "%d" = "%d" Entry #%d is locked on remote server = #%d Cannot save remote entry locally = No mirror server defined in configuration file = Synchronizing logbook %s with server "%s" = %s "%s" Remote config should be received = Configuration has been changed locally and remotely = Please merge manually to resolve conflict = , , Logbook "%s" does not exist on remote server = "%s" Error sending local entry = Local entry submitted = Local entry should be submitted = Error receiving message = Remote entry received = Remote entry should be received = local = remote = Entry has been changed locally and remotely = Please delete %s or %s entry to resolve conflict = , %s %s, Error sending local message = Local entry = %s should be deleted = %s Entry should be deleted locally = Entry deleted locally = Changed local entry ID to %d = %d Local entry ID should be changed to %d = %d Remote entry = Entry should be deleted remotely = Entry deleted remotely = Error deleting remote entry = Click here to delete %d entries = , %d Click here to delete this entry = , All entries identical = Synchronization = Entry is currently edited by = Edit entry = Delete entry = Goto page = All = Selected entries = Toggle all = Error: start date after end date = : Last %d days = %d Last %d entries = %d all entries = Start date = End date = From = After = to = Before = No entries found = recipients = No 'Attributes' option present in %s = "Attributes" %s Error: Attribute %s not supplied = : %s Please go back and enter the %s field = , %s Error: Attribute %s must be numeric = : %s Error: Value %s not allowed for boolean attributes = Error: %s Maximum number of attribute options exceeded = Please increase MAX_N_LIST in elogd.c and recompile = , MAX_N_LIST elogd.c Error: Attribute option %s not existing = : %s Date must be between 1970 and 2037 = 1970 2037 New entry cannot be written to directory "%s" = "%s" Please check that it exists and elogd has write access and disk is not full = , , elogd Invalid mirror_id or entry_date = mirror_id entry_date Please check that it exists and elogd has write access = , , elogd Entry %s cannot be read from logbook "%s" = %s "%s" Logbook is empty = This entry has been deleted = with = This is the last entry = This is the first entry = Email notification suppressed = . You can "steal" the lock by editing this entry = "" Show = Hide = Hide all = Show all = Several logbooks groups are defined on this host = Please select one to list the logbooks in that group = , , No logbook defined on this server = Synchronize all logbooks = Expand all = ELOG registration = ELOG Calendar = Previous Year = Next Year = Error: Command "%s" not allowed = : "%s" Attachment file "%s" empty or not found = "%s" # #---- please translate following items and then remove this comment ----# # up = down = stop = Entry = name may not contain blanks = regular expressions = Text fields are treated as %s = Subscribe to logbooks = enable automatic email notifications = Set all = Set none = Please enter hour for attribute '%s' = Please enter minute for attribute '%s' = Please enter second for attribute '%s' = No admin user has been defined in configuration file = Duplicate = Cannot lookup server name = Cannot connect to server = %s wrote = Quote = Insert current time = Please enter numeric value for year of attribute '%s' = Preview = FONT = SIZE = COLOR = smiling = happy = winking = big grin = crying = cool = frowning = confused = mad = pleased = tongue = yawn = Encoding = User "%s" not found in password file = Cannot write to file %s = Invalid user name "%s" or password for remote logbook = Hide attachments = Show only new entries = Show all entries = New entries since = An old ELOG entry has been updated = A new ELOG entry has been submitted = Invalid user name or password = Upload image = Please enter filename or URL = Maximum allowed file size is = Enter filename or URL = Image uploaded successfully = Image "%s" uploaded successfully = HelpELCode = Cannot retrieve file from URL "%s" = Enter name of hyperlink = Enter URL of hyperlink = astonished = Error in regular expression "%s" = File not found at URL "%s" = Entry size too large for email notification = Please specify a valid email address = Cannot send email notification to "%s" = Error: Command "%s" is not allowed for user "%s" = Shell execution not enabled via -x flag = Enter heading level (1, 2 or 3) = User name may not contain blanks = Empty password not allowed = Password may not contain blanks = Anonymous = Activation notice has been sent to %s = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_danish.html0000755000175000017500000000367312556103751017666 0ustar rogerroger ELOG Elektronisk Logbog Hjlp
ELOG Elektronisk Logbog Hjlp

Den Elektroniske Logbog (ELog) kan bruges til at gemme og hente beskeder gennem en Web brugerflade. Afhnging af opstning, kan ELog systemet agere vrt, for en eller Flere logbger som er gemt i separate sektioner p serveren.

Kort Oversigt

Som standard, vises siden med de sidste indlg  fra en logbog. Ved at klikke p et indlg, vises alt indhold. Man kan bruge browser knapperne for at vise det frste, forrige, nste og sidste indlg.

Med knappen Ny oprettes et nyt indlg. Med knappen ndre , kan man ndre et eksisterende indlg, hvis dette er tilladt i konfigurations filen. Besvar knappen  besvarer et eksisterende indlg, p samme mde som i en e-mail.

Find knappen bner en sge side, hvor indlg fra en logbog kan vises, sorteret p regler.Et udfyldt felt virker som et yderligere filter, som tilfjes og lggesind, o.s.v. Hvis intet filter er valgt, vises alle indlg fra en given logbog.

Mere information

For mere information, specielt omkring opstning af  ELog, g til  ELOG hjemmesiden.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloghelp_slovak.html0000644000175000017500000000433212556103751017705 0ustar rogerroger Nápoveda k ELOG Electronic Logbook
Nápověda k ELOG Electronic Logbook

Elektornický denník (ELog) je možné použiť na ukladanie a načítanie záznamov prostredníctvom webového rozhrania. V závislosti na konfigurácii môže ELOG systém hostovať jeden alebo viacero denníkov , ktoré sú uložené v samostatných sekciách na serveri.

Rýchly prehľad

Standartne stránka zobrazuje posledné záznamy obsiahnuté v denníku. Kliknutím na záznam sa zobrazí jeho kompletný obsah. Použitím tlačítok prehrávača ( vpravo nahor ) zobrazíte prvú, predchádzajúcu, nasledujúcu a poslednú správu.

Odkaz New ( Nový ) vytvorí nový záznam.

Pomocou odkazu Edit ( Úprava ) editujete existujúci záznam, pokiaľ je táto funkcia povolená v konfiguračnom súbore.

Pomocou odkazu Reply ( Odpovedať ) vytvoríte odpoveď k existujúcemu záznamu, podobne ako pri emaily.

Odkaz Find ( Hľadať ) otvára dotazovaciu stránku, pomocou kterej si môžte zobraziť filtrované záznamy. Každé vyplnené pole funguje ako ďaľší filter spojený s dalšími, pomocou logického súčinu. Ak nie je vybraný žiaden filter zobrazia sa všetky záznamy z denníku.

Ďalšie informácie

Pre ďalšie informácie, hlavne ohlľadom konfiguracie sa pozrite na ELOG domovskú stránku.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.italian0000755000175000017500000004774713076344200017012 0ustar rogerroger# # Italian translation by francesco@netlabor.it and AlberT@SuperAlberT.it # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Nuovo Edit = Modifica Delete = Elimina Reply = Rispondi Find = Cerca Last day = Ultimo giorno Last 10 = Ultimi 10 Config = Configurazione Change password = Modifica password Logout = Esci Help = Aiuto Back = Indietro Submit = Invia First = Primo Last = Ultimo Previous = Precedente Next = Successiva Copy to = Copia in Move to = Sposta in Save = Salva Cancel = Cancella Wrong password = Password errata Please login = Inserisci i dati per l'accesso Username = Nome utente Password = Password Old password = Vecchia password New password = Nuova password ELOG change password = ELOG cambia password Several logbooks are defined on this host = Su questo server sono presenti diversi registri Please select the one to connect to = Per favore seleziona quello a cui connetterti Change password for user = Cambia la password dell'utente Logged in as = Utente: Entry date = Data inserimento with = con This is the last entry = Questo l'ultimo elemento This is the first entry = Questo il primo elemento Email sent to = Email inviata a Email notification suppressed = Disattivato l'invio di e-mail please select = seleziona Suppress Email notification = Disattiva l'invio di e-mail Resubmit as new entry = Reinvia come nuovo elemento Attachment = Allegato Reply to this = Rispondi a questo In reply to = In risposta a Fields marked with = I campi segnati con are required = sono obbligatori Please check that it exists and elogd has write access = Per favore controlla che esista e che ELOGD abbia diritti di accesso in scrittura Error: Attribute %s not supplied = Errore: Attributo %s non fornito Please go back and enter the %s field = Per favore torna indietro ed inserisci il campo %s Please use your browser's back button to go back = Per favore usa il pulsante indietro del tuo browser ELOG find = ELOG Cerca Find = Trova Search = Cerca Reset Form = Azzera tutto Summary only = Solo sommario Show attachments = Mostra gli allegati Printable output = Versione stampabile Sort in reverse order = Ordina inversamente Start date = Data inizio End date = Data fine Year = Anno Text = Testo Search all logbooks = Cerca in tutti i registri Last %d days = Ultimi %d giorni Last %d entries = Ultimi %d elementi No entries found = Nessun elemento trovato A new entry has been submitted on %s = Un nuovo elemento stato inserito in %s Logbook = Registro Yes = Si No = No Error deleting message: status = Errore nel cancellare il messaggio: stato Error: Command "%s" not allowed = Errore: Comando "%s" non permesso Cannot open file %s = Non posso aprire il file %s Cannot write to %s = Non posso scrivere su %s No SMTP host defined in [global] section of configuration file = Nessun server SMTP definito nella sezione [global] del file di configurazione Display threads = Mostra le discussioni Logbook is empty = Il registro vuoto Download = Scarica and all its replies = e tutte le sue risposte Message ID = ID messaggio Not logged in = Non entrato Login = Entra Page %d of %d = Pagina %d di %d all entries = tutti gli elementi Login name = Nome utente Full name = Nome completo ELOG user config = ELOG configurazione utente ELOG new user = ELOG nuovo utente Admin = Amministratore Select user = Scegli l'utente Remove user = Cancella l'utente New user = Crea nuovo utente Retype new password = Riscrivi la nuova password New passwords do not match, please retype = La nuova password non coincide, per favore riscrivila Retype password = Riscrivi la password exists already = esiste gi Register as new user = Registrati come nuovo utente A new ELOG user has been registered on %s = Un nuovo utente ELOG stato registrato su %s Email = Email User "%s" registered on logbook "%s" = L'utente "%s" si registrato su "%s" User [%s] has been deleted = L'utente [%s] stato cancellato Hit following URL to activate that account = Clicca il seguente indirizzo per attivare quell'account ELOG registration = ELOG Registrazione Your ELOG account has been activated on host = Il tuo account ELOG stato attivato sul server You can access it at = Puoi accedervi da Are you sure to delete these messages? = Sei sicuro di voler cancellare questi messaggi? Select = Seleziona All entries = Tutti gli elementi Day = Giorno Week = Settimana Month = Mese Show last = Mostra l'ultimo Goto page = Vai alla pagina All = Tutti gli elementi Display = Mostra entries per page = elementi per pagina Toggle all = Inverti Selected entries = Elementi selezionati Collapse = Restringi Expand = Espandi Full = Completo Summary = Sommario Threaded = Ad albero Filters = Filtri Mode = Modo Options = Opzioni Error: start date after end date = Errore: la data iniziale posteriore a quella finale and all their replies = e tutte le loro risposte Upload = Carica Requested = Richiesto A new ELOG user wants to register on "%s" = Un nuovo utente ELOG vuole registrarsi su "%s" Search text also in attributes = Ricerca il testo anche negli attributi Date = Data ID = ID Forgot password? = Dimenticata la password? ELOG password recovery = ELOG ripristino password No Email address registered with user name "%s" = Nessun indirizzo e-mail registrato con il nome utente "%s" Error sending Email via "%s" = Errore nell'nvio della e-mail con "%s" Forgot = Dimenticata User "%s" registered on host "%s" = Utente "%s" registrato sul server "%s" Enter your user name or email address = Inserisci il tuo nome utente o l'indirizzo e-mail Password recovery for ELOG %s = Ripristino password per ELOG %s Host = Server Your ELOG account has been activated = Il tuo account ELOG stato attivato Maximum number of attachments reached = Raggiunto il massimo numero di allegati consentito on = su Entry is currently edited by = L'elemento ora modificato da recipients = destinatari Suppress shell execution = Disattiva esecuzione da shell Maximum number of replies (%d) exceeded = Superato il numero massimo di risposte (%d) Update = Aggiorna Change %s = Cambia %s Add new option here = Aggiungi qui una nuova opzione Add %s = Aggiungi %s Synchronization = Sincronizzazione Error: Value %s not allowed for boolean attributes = Errore: valore %s non consentito per elementi booleani Error: Attribute option %s not existing = Errore: l'opzione %s dell'elemento non esiste Last submission = Ultima immissione Expand all = Espandi tutto Synchronize = Sincronizza No mirror server defined in configuration file = Nessun server gemello definito nel file di configurazione user = utente Entry can only be edited %1.2lg hours after creation = L'elemento pu essere modificato solo per %1.2lg ore dopo la creazione Display full entries = Mostra gli elementi completi No entry selected for deletion = Nessun elemento selezionato per la cancellazione Cannot read entry from local logbook = Non posso leggere l'elemento dal registro Cannot connect to host %s, port %d = Non posso connettermi al computer %s, porta %d Cannot receive "%s" = Non posso ricevere "%s" Received wrong entry id "%d" = Ho ricevuto l'id "%d" dell'elemento errato Entry #%d is locked on remote server = L'elemento #%d bloccato sul server remoto Cannot save remote entry locally = Non posso salvare l'elemento remoto in locale local = locale remote = remoto Please delete %s or %s entry to resolve conflict = Per favore cancella %s o %s per risolvere il conflitto Error sending local entry = Errore nell'invio dell'elemento locale Local entry submitted = Elemento locale inviato Error deleting remote entry = Errore nel cancellare l'elemento remoto New entry cannot be written to directory "%s" = Il nuovo elemento non pu essere scritto nella cartella "%s" Entry %s cannot be read from logbook "%s" = L'elemento %s non pu essere letto dal registro "%s" This entry has been deleted = Questo elemento stato cancellato by = da Entries = Elementi Cannot connect to remote server "%s" = Non posso connettermi al server remoto "%s" Remote server is not an ELOG server = Il server remoto non e' un server ELOG Incorrect remote ELOG server version %s = La versione %s del server ELOG remoto non e' corretta Error accessing remote logbook = Errore nell'accesso al registro remoto Invalid HTTP header = Intestazione HTTP non valida No user name supplied to access remote logbook = Non e' stato fornito alcun nome utente per l'accesso al registro remoto Synchronizing logbook %s with server "%s" = Sincronizzazione registro %s con il server %s Configuration has been changed locally and remotely = La configurazione e' stata modificata localmente ed in remoto Please merge manually to resolve conflict = Per favore modificate manualmente per risolvere il conflitto Logbook "%s" does not exist on remote server = Il registro "%s" non esiste sul server remoto Entry has been changed locally and remotely = L'elemento e' stato modificato localmente ed in remoto Entry deleted locally = Elemento cancellato localmente Changed local entry ID to %d = Modificato l'ID dell'elemento locale in %d Entry deleted remotely = Elemento cancellato in remoto All entries identical = Tutti gli elementi sono identici Invalid mirror_id or entry_date = mirror_id o entry_date invalidi Synchronize all logbooks = Sincronizza tutti i registri Calendar = Calendario Remote entry received = Ricevuto elemento remoto Pick a date = Seleziona una data Please select at least one '%s' = Per favore seleziona almeno uno '%s' Please select a '%s' = Per favore seleziona un '%s' Please enter month for attribute '%s' = Per favore inserisci il mese per l'attributo '%s' Please enter day for attribute '%s' = Per favore inserisci il giorno per l'attributo '%s' Please enter year for attribute '%s' = Per favore inserisci l'anno per l'attributo '%s' Please enter attribute '%s' = Per favore inserisci l'attributo '%s' Entry time = Ora inserimento Start = Inizio End = Fine After = Dopo to = a Before = Prima Previous Year = Anno prima Next Year = Anno dopo Please enter numeric value for '%s' = Per favore inserisci un valore numerico per '%s' Error: Attribute %s must be numeric = Errore: %s l'attributo deve essere numerico 3 Months = 3 Mesi 6 Months = 6 Mesi Error sending local message = Errore nell'invio del messaggio locale Error receiving message = Errore nella ricezione del messaggio Are you sure to delete this entry? = Sei sicuro di voler cancellare questo elemento? Import = Importa CSV filename = Nome del file CSV Field separator = Separatore di campo Edit entry = Modifica elemento Delete entry = Cancella elemento CSV Import = Importa CVS ELOG CSV import = Importa CVS ELOG Derive attributes from CSV file = Deduci gli attributi dal file CSV Comma = Virgola Semicolon = Punto e virgola Preview import = Anteprima dell'importazione CSV import preview of %s = Anteprima dell'importazione CSV di %s Too many attributes in CSV file = Troppi attributi nel file CSV %d entries successfully imported = %d elementi importati con successo No 'Attributes' option present in %s = Nessuna opzione "Attributes" presente in %s CSV ("," separated) = CSV (separati con ",") CSV (";" separated) = CSV (separati con ";") Auto detect = seleziona automaticamente From = Da text = testo Column header '%s' must be present in CSV file = Nel file CSV deve essere presente la colonna di intestazione '%s' Fill text body = Riempi il testo del corpo Please re-enter filename = Re-immettere il nome del file Last x = Ultimi x Activate = Abilita Entry has been modified = L'elemento stato modificato No attachment file specified = Non stato specificato nessun allegato Delete this logbook = Cancella questo logbook Rename this logbook = Rinomina questo logbook Create new logbook = Crea un nuovo logbook Syntax error in config file = Errore di sintassi nel file di configurazione Email notification = Notifica via email Are you sure to delete logbook "%s"? = Sicuro di voler cancellare il logbook '%s'? Logbook "%s" exists already, please choose different name = Il logbook "$s" gi presente, scegli un altro nome Rename logbook = Rinomina il logbook Enter new logbook name = Inserisci un nuovo nome per il logbook Logbook name = Nom del logbook Use existing logbook as template = Utilizza un logbook esistente come modello none = nessuno URL is redirected to: = La URL limitata a: Remote config should be received = La configrazione remota dovrebbe essere ricevuta Local entry should be submitted = L'elemento locale dovrebbe essere inviato Remote entry should be received = L'elemento remoto dovrebbe essere ricevuto Local entry = Elemento locale %s should be deleted = %s dovrebbe essere cancellato Entry should be deleted locally = L'elemento dovrebbe essere cancellato localmente Local entry ID should be changed to %d = L'ID dell'elemento locale dovrebbe essere modificato in %d Remote entry = Elemento remoto Entry should be deleted remotely = L'elemento dovrebbe essere rimosso in remoto Click here to delete this entry = Fai click qui per cancellare questo elemento Maximum number of attribute options exceeded = Superato il numero massimo di opzioni per un attributo Please increase MAX_N_LIST in elogd.c and recompile = Aumentare MAX_N_LIST in elogd.c e ricompilare You can "steal" the lock by editing this entry = possibile aggirare il blocco modificando questo elemento Several logbooks groups are defined on this host = Su questo host sono definiti diversi gruppi di logbook Please select one to list the logbooks in that group = Selezionarne uno per mostrare i logbook nel gruppo No logbook defined on this server = Nessun logbook presente su questo server Goto ELOG home page = Vai alla Home Page di ELOG Please enter "%s" = Inserisci "%s" Change config file = Modifica il file di configurazione Click here to delete %d entries = Fai click qui per cancellare %d elementi Please check that it exists and elogd has write access and disk is not full = Controlla che esista, che elogd abbia i permessi per scrivere e che il disco non sia pieno Show = Mostra Hide = Nascondi Hide all = Nascondi tutti Show all = Mostra tutti This logbook requires authentication = Questo logbook richiede una autenticazione Attachment file "%s" empty or not found = Il file allegato "%s" vuoto o non stato trovato Case sensitive = Sensibile alle maiuscole List = Elenca Date must be between 1970 and 2037 = La data deve essere compresa tra il 1970 ed il 2037 up = su down = gi stop = ferma Entry = Elemento name may not contain blanks = il nome non pu contenere spazi regular expressions = espressioni regolari Text fields are treated as %s = I campi di testo sono considerati come %s Subscribe to logbooks = Iscriviti ai logbook enable automatic email notifications = abilita la notifica automatica via email Set all = Imposta tutti Set none = Imposta nessuno Please enter hour for attribute '%s' = Inserisci l'ora per l'attributo '%s' Please enter minute for attribute '%s' = Inserisci i minuti per l'attributo '%s' Please enter second for attribute '%s' = Inserisci i secondi per l'attributo '%s' No admin user has been defined in configuration file = Nel file di configurazione non stato definito nessun utente di amministrazione Duplicate = Duplica # #---- please translate following items and then remove this comment ----# # Cannot lookup server name = Cannot connect to server = %s wrote = Quote = Insert current time = Please enter numeric value for year of attribute '%s' = Preview = FONT = SIZE = COLOR = smiling = happy = winking = big grin = crying = cool = frowning = confused = mad = pleased = tongue = yawn = Encoding = User "%s" not found in password file = Cannot write to file %s = Invalid user name "%s" or password for remote logbook = Hide attachments = Show only new entries = Show all entries = New entries since = An old ELOG entry has been updated = A new ELOG entry has been submitted = Invalid user name or password = Upload image = Please enter filename or URL = Maximum allowed file size is = Enter filename or URL = Image uploaded successfully = Image "%s" uploaded successfully = HelpELCode = Cannot retrieve file from URL "%s" = Enter name of hyperlink = Enter URL of hyperlink = astonished = Error in regular expression "%s" = File not found at URL "%s" = Entry size too large for email notification = Please specify a valid email address = Cannot send email notification to "%s" = Error: Command "%s" is not allowed for user "%s" = Shell execution not enabled via -x flag = Enter heading level (1, 2 or 3) = User name may not contain blanks = Empty password not allowed = Password may not contain blanks = Anonymous = Activation notice has been sent to %s = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.french0000644000175000017500000006352113076344200016617 0ustar rogerroger# # French translation by Fred Pacquier # (for 3.1.1+) # A more recent version may be available at : # https://bitbucket.org/ritt/elog/src/b4d2a375a1c1991daa94427455f57665f7497df0/resources/eloglang.french?at=master&fileviewer=file-view-default New = Crer Edit = Modifier Delete = Supprimer Restore = Restaurer Reply = Rpondre Find = Chercher Last day = Dernier jour Last 10 = 10 Dernires entres Config = Configuration Change password = Changer le mot de passe Logout = Se dconnecter Help = Aide Back = Retour Submit = Valider First = Premire Last = Dernire Previous = Prcdente Next = Suivante Copy to = Copier vers Move to = Dplacer vers Save = Enregistrer Cancel = Annuler Wrong password = Mot de passe incorrect Please login = Connexion Username = Utilisateur Password = Mot de passe ELOG change password = Changer le mot de passe ELOG Several logbooks are defined on this host = Il y a plusieurs registres sur ce serveur Please select the one to connect to = Choisissez celui auquel vous souhaitez vous connecter Change password for user = Changer le mot de passe de l'utilisateur Logged in as = Connect sous Entry date = Date de cration with = avec This is the last entry = Ceci est la dernire entre This is the first entry = Ceci est la premire entre Email sent to = Ml envoy Email notification suppressed = Notification ml dsactive please select = choisissez Suppress Email notification = Dsactiver la notification ml. Resubmit as new entry = Positionner comme une nouvelle entre Attachment = Attachement Reply to this = Rponse In reply to = En rponse Fields marked with = Les champs marqus are required = sont obligatoires Please check that it exists and elogd has write access = Vrifier qu'il existe bien et qu'elogd a le droit d'y crire Error: Attribute %s not supplied = Erreur : l'attribut %s n'est pas spcifi Please go back and enter the %s field = Revenez en arrire et renseignez le champ %s Please use your browser's back button to go back = Utilisez le bouton "page prcdente" de votre navigateur pour revenir en arrire ELOG find = Recherche ELOG Find = Chercher Search = Lancer la recherche Reset Form = Rinitaliser le formulaire Summary only = Rsum seulement Show attachments = Montrer les attachements Printable output = Sortie pour impression Sort in reverse order = Tri invers Start date = Date de dbut End date = Date de fin Year = Anne Text = Texte Search all logbooks = Chercher dans tous les registres Last %d days = %d Derniers jours Last %d entries = %d Dernires entres No entries found = Aucune entre trouve A new entry has been submitted on %s = Une nouvelle entre a t cre le %s Logbook = Registre Yes = Oui No = Non Error deleting message: status = Erreur lors de la suppression de l'entre : Status Error: Command "%s" not allowed = Erreur : commande "%s" non autorise Cannot open file %s = Erreur d'ouverture du fichier %s Cannot write to %s = Erreur d'criture dans le fichier %s No SMTP host defined in [global] section of configuration file = Aucun serveur SMTP n'est dfini dans la section [global] du fichier de configuration Display threads = Afficher les fils de discussion Logbook is empty = Ce registre est vide Download = Tlcharger and all its replies = et toutes les rponses Message ID = Numro de message Not logged in = Non identifi Login = S'identifier Page %d of %d = Page %d sur %d all entries = Toutes les entres Login name = Nom de connexion Full name = Nom complet ELOG user config = Configuration d'un utilisateur ELOG ELOG new user = Cration d'un utilisateur ELOG Admin = Administration Select user = Slectionner un utilisateur Remove user = Supprimer l'utilisateur courant New user = Crer un nouvel utilisateur Retype new password = Re-saisir le nouveau mot de passe New passwords do not match, please retype = Les nouveaux mots de passe ne sont pas identiques, merci de les re-saisir Retype password = Re-saisir le mot de passe exists already = existe dj Register as new user = S'enregistrer comme nouvel utilisateur A new ELOG user has been registered on %s = Un nouvel utilisteur ELOG a t enregistr sur %s Email = Ml. User "%s" registered on logbook "%s" = L'utilisateur "%s" est enregistr dans le registre "%s" User [%s] has been deleted = L'utilisateur [%s] a t supprim Hit following URL to activate that account = Cliquer sur l'URL suivante pour activer ce compte ELOG registration = Enregistrement ELOG Your ELOG account has been activated on host = Votre compte ELOG a t activ sur You can access it at = Vous pouvez y accder par Are you sure to delete these messages? = Confirmez-vous la suppression de ces messages ? Select = Slectionner All entries = Toutes les entres Day = un jour Week = une semaine Month = un mois Show last = Afficher les entres sur Goto page = Aller la page All = Tous Display = Afficher entries per page = entres par page Toggle all = Basculer tout Selected entries = Entres slectionnes Collapse = Rduire Expand = Dvelopper Full = Dtail Summary = Rsum Threaded = Arborescence Filters = Filtres Mode = Mode Options = Options Error: start date after end date = Erreur : la date de dbut est postrieure la date de fin and all their replies = et toutes leurs rponses Upload = Envoyer Requested = Demand A new ELOG user wants to register on "%s" = Un nouvel utilisateur ELOG souhaite s'inscrire "%s" Search text also in attributes = Rechercher galement le texte dans les attributs Date = Date ID = ID Forgot password? = Mot de passe oubli ? ELOG password recovery = Rcupration de mot de passe ELOG No Email address registered with user name "%s" = Il n'y a pas d'adresse ml. enregistre pour le nom d'utilisateur "%s" Forgot = Oubli User "%s" registered on host "%s" = Utilisateur "%s" enregistr sur l'ordinateur "%s" Enter your user name or email address = Indiquez votre nom d'utilisateur ou votre adresse ml. Password recovery for ELOG %s = Rcupration de mot de passe sur ELOG %s Host = Ordinateur Your ELOG account has been activated = Votre compte ELOG a t activ Maximum number of attachments reached = Le nombre maximal d'attachements a t atteint on = sur Entry is currently edited by = Entre actuellement en cours de modification par Old password = Ancien mot de passe Maximum number of replies (%d) exceeded = Nombre maximum de rponses (%d) dpass Update = Actualiser Add %s = Ajouter %s Add new option here = Ajouter une nouvelle option ici Suppress shell execution = Empcher l'excution de commandes Change %s = Modifier %s Error sending Email via "%s" = Erreur lors de l'envoi d'un ml. via "%s" Synchronization = Synchronisation recipients = destinataires Error: Value %s not allowed for boolean attributes = Erreur : la valeur %s n'est pas valide pour les attributs boolens Error: Attribute option %s not existing = Erreur : l'option %s n'existe pas pour l'attribut Last submission = Dernire entre Expand all = Tout dvelopper Synchronize = Synchroniser No mirror server defined in configuration file = Aucun serveur miroir n'est dfini dans la configuration New password = Nouveau mot de passe user = utilisateur Entry can only be edited %1.2lg hours after creation = Cette entre ne peut tre modifie que %1.2lg heures aprs sa cration Display full entries = Afficher les entres compltes No entry selected for deletion = Aucune entre n'est slectionne pour supprssion Cannot read entry from local logbook = Impossible de lire l'entre depuis le registre local Cannot connect to host %s, port %d = Impossible de se connecter l'hte %s sur le port %d Cannot receive "%s" = Impossible de recevoir "%s" Received wrong entry id "%d" = L'identifiant de l'entre reue est erronn (%d) Entry #%d is locked on remote server = L'entre #%d est verrouille sur le serveur distant Cannot save remote entry locally = Impossible d'enregistrer l'entre distante en local local = local(e) remote = distant(e) Please delete %s or %s entry to resolve conflict = Il faut supprimer l'une des deux entres %s ou %s pour rsoudre le conflit Error sending local entry = Erreur lors de l'envoie de l'entre locale Local entry submitted = L'entre locale a t transmise Error deleting remote entry = Erreur lors de la suppression de l'entre locale New entry cannot be written to directory "%s" = La nouvelle entre ne peut tre crite dans le rpertoire "%s" Entry %s cannot be read from logbook "%s" = L'entre %s ne peut tre lue dans le registre "%s" This entry has been deleted = Cette entre a t supprime by = par Entries = Entres Cannot connect to remote server "%s" = Impossible de se connecter au serveur distant "%s" Remote server is not an ELOG server = Le serveur distant n'est pas un serveur ELOG Incorrect remote ELOG server version %s = La version du serveur ELOG distant est incorrecte (%s) Error accessing remote logbook = Erreur lors de l'accs au registre distant Invalid HTTP header = En-tte HTTP invalide No user name supplied to access remote logbook = Aucun nom d'utilisateur n'a t fourni pour l'accs au registre distant Synchronizing logbook %s with server "%s" = Synchronisation du registre %s avec le serveur distant "%s" Configuration has been changed locally and remotely = Les configurations locale et distante ont t modifies Please merge manually to resolve conflict = Il faut fusionner manuellement pour rsoudre le conflit Logbook "%s" does not exist on remote server = Le registre "%s" n'existe pas sur le serveur distant Entry has been changed locally and remotely = Les entres locale et distante ont t modifies Entry deleted locally = Entre locale supprime Changed local entry ID to %d = Identifiant de l'entre locale modifi en %d Entry deleted remotely = Entre distante supprime All entries identical = Toutes les entres sont identiques Invalid mirror_id or entry_date = Valeur de mirror_id ou entry_date incorrecte Synchronize all logbooks = Synchroniser tous les registres Calendar = Calendrier Remote entry received = Entre distante reue Pick a date = Choisissez une date Please select at least one '%s' = Veuillez slectionner au moins une valeur de '%s' Please select a '%s' = Veuillez slectionner une valeur de '%s' Please enter month for attribute '%s' = Veuillez slectionner un mois pour l'attribut '%s' Please enter day for attribute '%s' = Veuillez slectionner un jour pour l'attribut '%s' Please enter year for attribute '%s' = Veuillez slectionner une anne pour l'attribut '%s' Please enter attribute '%s' = Veuillez renseigner l'attribut '%s' Entry time = Heure de l'entre Start = Dbut End = Fin After = Aprs to = Before = Avant Previous Year = Anne prcdente Next Year = Anne suivante Please enter numeric value for '%s' = Veuillez saisir une valeur numrique pour '%s' Error: Attribute %s must be numeric = Erreur: l'attribut %s doit tre numrique 3 Months = 3 mois 6 Months = 6 mois Error sending local message = Erreur lors de l'envoi du message local Error receiving message = Erreur la rception du message Are you sure to delete this entry? = Confirmez-vous la suppression de cette entre ? Import = Importation CSV filename = nom du fichier CSV Field separator = sparateur de champs Edit entry = Modifier l'entre Delete entry = Supprimer l'entre CSV Import = Importation CSV ELOG CSV import = Importation CSV ELOG Derive attributes from CSV file = Crer les attributs partir du fichier CSV Comma = virgule Semicolon = point-virgule Preview import = Aperu avant importation Too many attributes in CSV file = Trop d'attributs dans le fichier CSV %d entries successfully imported = %d entres importes avec succs No 'Attributes' option present in %s = Il n'y a pas d'option 'Attributs' dans %s CSV ("," separated) = CSV (sparateur ",") CSV (";" separated) = CSV (sparateur ";") Auto detect = Dtection automatique CSV import preview of %s = Prvisualiser l'importation CSV de %s text = texte Column header '%s' must be present in CSV file = L'en-tte de la colonne '%s' doit tre prsent dans le fichier CSV Fill text body = Remplir le champ texte Please re-enter filename = Veuillez re-saisir le nom du fichier Last x = x derniers Activate = Activer Entry has been modified = L'entre a t modifie No attachment file specified = Aucun fichier joint n'a t dfini Delete this logbook = Supprimer ce registre Rename this logbook = Renommer ce registre Create new logbook = Crer un nouveau registre Syntax error in config file = Erreur de syntaxe dans le fichier de configuration Email notification = Avertissement par ml Are you sure to delete logbook "%s"? = Etes-vous sr(e) de vouloir supprimer le registre "%s" ? Logbook "%s" exists already, please choose different name = Le registre "%s" existe dj, merci de choisir un autre nom Rename logbook = Renommer le registre Enter new logbook name = Saisissez le nom du nouveau registre Logbook name = Nom du registre Use existing logbook as template = Utiliser un registre existant comme modle none = aucun URL is redirected to: = URL redirige vers : Remote config should be received = Recevoir la configuration distante Local entry should be submitted = Emettre l'entre locale Remote entry should be received = Recevoir l'entre distante Local entry = Entre locale %s should be deleted = Supprimer %s Entry should be deleted locally = Supprimer l'entre localement Local entry ID should be changed to %d = Changer l'identifiant de l'entre locale en %d Remote entry = Entre distante Entry should be deleted remotely = Supprimer l'entre distante Click here to delete this entry = Cliquer ici pour supprimer cette entre Maximum number of attribute options exceeded = Le maximum autoris pour les options d'attributs est dpass Please increase MAX_N_LIST in elogd.c and recompile = Veuillez augmenter la valeur MAX_N_LIST dans elogd.c et recompiler You can "steal" the lock by editing this entry = Vous pouvez vous approprier le verrou en modifiant cette entre Several logbooks groups are defined on this host = Plusieurs groupes de registres sont dfinis sur ce serveur Please select one to list the logbooks in that group = Veuillez en slectionner un pour en voir le contenu No logbook defined on this server = Aucun registre n'est dfini sur ce serveur Goto ELOG home page = Aller la page d'accueil ELOG Please enter "%s" = Veuillez saisir "%s" Change config file = Modifier le fichier de configuration Click here to delete %d entries = Cliquer ici pour supprimer %d entres Please check that it exists and elogd has write access and disk is not full = Veuillez vrifier qu'il existe, que elogd a les droits en criture et que le disque n'est pas plein Show = Montrer Hide = Cacher Hide all = Cacher tout Show all = Montrer tout This logbook requires authentication = Ce registre ncessite une authentification Attachment file "%s" empty or not found = Fichier joint "%s" vide ou non trouv Case sensitive = Tient compte de la casse List = Liste From = De Date must be between 1970 and 2037 = La date doit tre comprise entre 1970 et 2037 up = haut down = bas stop = arrter Entry = Entre name may not contain blanks = le nom de doit pas contenir d'espace regular expressions = expressions rgulires Text fields are treated as %s = Les champs texte sont traits en tant que Subscribe to logbooks = S'abonner aux registres enable automatic email notifications = activer les notifications par ml. Set all = Activer tout Set none = Dsactiver tout Insert current time = Rgler sur l'heure courante Please enter hour for attribute '%s' = Entrez l'heure pour l'attribut '%s' Please enter minute for attribute '%s' = Entrez les minutes pour l'attribut '%s' Please enter second for attribute '%s' = Entrez les secondes pour l'attribut '%s' No admin user has been defined in configuration file = Aucun utilisateur n'est dfini comme administrateur dans le fichier de configuration Duplicate = Dupliquer Cannot lookup server name = Impossible de rsoudre le nom du serveur Cannot connect to server = Impossible de se connecter au serveur %s wrote = %s a crit Quote = Citer Please enter numeric value for year of attribute '%s' = Indiquez une valeur numrique pour l'anne de l'attribut '%s' Preview = Prvisualisation FONT = POLICE SIZE = TAILLE COLOR = COULEUR astonished = surpris smiling = souriant happy = heureux winking = clin d'oeuil big grin = grand sourire crying = plure cool = tranquille frowning = fronce les sourcils confused = perdu mad = furieux pleased = content tongue = tire la langue yawn = baille Encoding = Encodage User "%s" not found in password file = Utilisateur "%s" non trouv dans le fichier des comptes Cannot write to file %s = Impossible d'crire dans le fichier %s Invalid user name "%s" or password for remote logbook = Nom d'utilisateur "%s" ou mot de passe invalide pour l'accs au registre distant Hide attachments = Cacher les pices jointes Show only new entries = Ne montrer que les nouvelles entres Show all entries = Montrer toutes les entres New entries since = Nouvelles entres depuis An old ELOG entry has been updated = Une entre ELOG existante a t mise jour A new ELOG entry has been submitted = Une nouvelle entre ELOG a t cre Invalid user name or password = Nom d'utilisateur ou mot de passe invalide Upload image = Tlcharger une image Please enter filename or URL = Indiquez un nom de fichier ou une URL Maximum allowed file size is = La taille maximale autorise pour les fichiers est de Enter filename or URL = Indiquez un nom de fichier ou une URL Image uploaded successfully = Image tlcharge avec succs Image "%s" uploaded successfully = Image "%s" tlcharge avec succs HelpELCode = AideELCode Cannot retrieve file from URL "%s" = Impossible d'importer le fichier depuis l'URL "%s" Enter name of hyperlink = Nom du lien Enter URL of hyperlink = URL du lien Error in regular expression "%s" = Erreur dans l'expression rgulire "%s" File not found at URL "%s" = Fichier non trouv l'URL "%s" Entry size too large for email notification = La taille de l'entre est trop grande pour une notification par ml. Please specify a valid email address = Merci de spcifier une adresse ml valide Cannot send email notification to "%s" = Impossible d'adresser la notification par ml l'adresse "%s" Error: Command "%s" is not allowed for user "%s" = Erreur : la commande "%s" n'est pas autorise pour l'utilisateur "%s" Shell execution not enabled via -x flag = L'excution par le shell n'a pas t active avec l'option -x Enter heading level (1, 2 or 3) = Indiquez le niveau d'en-tte (1, 2 ou 3) User name may not contain blanks = Le nom d'utilisateur ne doit pas contenir d'espace Empty password not allowed = Le mot de passe ne doit pas tre vide Password may not contain blanks = Le mot de passe ne doit pas contenir d'espace Anonymous = Anonyme Activation notice has been sent to %s = L'avis d'activation a t envoy %s Your request has been forwarded to the administrator = Votre demande a t transmise l'administrateur You will be notified by email upon activation of your new account = Vous serez averti(e) par courriel de l'activation de votre nouveau compte Select period = Slectionner la priode Last week = Semaine coule Last month = Mois coul Last 3 Months = 3 derniers mois Last 6 Months = 6 derniers mois Last Year = Anne coule Enter %s = Entrer %s Select %s = Slectionner %s Bold text CTRL+B = Texte centr CTRL+B Italics text CTRL+I = Texte italique CTRL+I Underlined text CTRL+U = texte soulign CTRL+U Centered text = Texte centr Insert hyperlink = Insrer lien Insert email = Insrer adresse mail Insert image CTRL+M = Insrer image CTRL+M Insert quote = Insrer citation Insert list CTRL+L = Insrer liste CTRL+L Insert table = Insrer table Insert heading CTRL+H = Insrer entte CTRL+H Insert code CTRL+O = Insrer code CTRL+O Show the smiley bar = Montrer la barre des smileys Insert current time/date = Insrer l'heure/la date courante Entry is locked on local server and therefore skipped = L'entre est verrouille sur le serveur local et sera ignore Keep me logged in on this computer = Me laisser connect(e) sur cet ordinateur for the next %d minutes = pour les %d prochaines minutes for the next hour = pour la prochaine heure for the next %d hours = pour les %d prochaines heures for the next %d days = pour les %d prochains jours keep original values = garder les valeurs d'origine Entry is locked = L'entre est verrouille Edit anyhow = Modifier tout de mme Insert horizontal line = Insrer ligne horizontale Insert anchor point = Insrer un point d'ancrage keep original text = garder le texte d'irigine unspecified = non spcifi To subscribe to any logbook, click on 'Config' in that logbook = Pour s'abonner un registre, cliquer sur 'Configuration' dans ce registre ELOG XML import = Import XML ELOG Derive attributes from XML file = dduire les attributs du fichier XML XML filename = Nom du fichier XML Invalid date format = Format de date invalide XML file does not contain %s element = Le fichier XML ne contient pas l'lment %s XML import preview of %s = Prvisualisation de l'import XML de %s XML Import = Import XML A old entry has been updated on %s = Un ancienne entre a t mise jour sur %s First entry, Ctrl-Home = Premire entre, Ctrl-Home Previous entry, Ctrl-PgUp = Entre prcdente, Ctrl-PgUp Next entry, Ctrl-PgDn = Entre suivante, Ctrl-PgDn Last entry, Ctrl-End = Dernire entre, Ctrl-End ELOG import = Import ELOG Please choose format to import: = Veuillez choisir le format d'importation : Enter text = Saisir le texte or until I log out = ou jusqu' ce que je me dconnecte Make smaller = Rduire Original size = Taille d'origine Make larger = Agrandir Rotate left = Rotation gauche Rotate right = Rotation droite Delete attachment = Supprimer la pice jointe Cannot create thumbnail, please check ImageMagick installation = Impossible de crer l'aperu, merci de vrifier l'installation de ImageMagick Attachments = Pices jointes Error: Content length (%d) larger than maximum content length (%d) = Erreur : la taille du contenu (%d) est suprieure au maximum autoris (%d) ELOG error = erreur ELOG Only user %s can edit this entry = Seul l'utilisateur %s peut modifier cette entre Export to = Exporter vers Do not ignore first line = Ne pas sauter la premire ligne Last %d hours = %d dernires heures Invalid URL = URL invalide Attribute "%s" is not allowed in config file = L'attribut "%s" n'est pas autoris dans le fichier de configuration Only user %s can delete this entry = Seul l'utilisateur %s peut supprimer cette entre File system full, ELOG cannot continue to work = Le systme de fichiers est plein, ELOG ne peut plus fonctionner 3 Days = 3 jours Last 3 Days = Les 3 derniers jours Really submit this entry? = Voulez-vous vraiment soumettre cette entre ? Account activation for ELOG logbook "%s" = Activation de compte pour le registre ELOG "%s" Account activation for ELOG on host "%s" = Activation de compte ELOG sur le serveur "%s" Please click the URL below to activate following ELOG account = Veuillez cliquer sur l'URL ci-dessous pour activer le compte ELOG suivant Activation URL = URL d'activation An email has been sent to <%s> = Un message a t envoy <%s> Use that email to activate your account = Utilisez ce message pour activer votre compte Registration request for ELOG logbook "%s" = Demande d'inscription pour le registre ELOG "%s" Registration request for ELOG on host "%s" = Demande d'inscription ELOG sur le serveur "%s" Are you sure you want to deactivate your own account? = Etes-vous sr(e) de vouloir dsactiver votre propre compte ? Active = Actif Invalid activation code = Code d'activation invalide Error activating user = Erreur lors de l'activation du compte utilisateur A password recovery email for user "%s" has been sent to %s = Un message de rcupration de mot de passe pour le compte "%s" a t envoy %s User "%s" has no access to this logbook = Le compte "%s" n'a pas accs ce registre Your account has been activated = Votre compte a t activ Please subscribe to logbooks if you want to receive automatic email notifications = Vous devez vous abonner des registres si vous voulez recevoir des messages de notification automatiques This is an automatically generated account recovery email for host %s = Ceci est un message automatique de rcupration de compte pour le serveur %s Please click on following link to recover your account = Veuillez cliquer sur le lien suivant pour rcuprer votre compte Email address "%s" not registered = L'adresse mail "%s" n'est pas enregistre User name "%s" not registered = Le nom d'utilisateur "%s" n'est pas enregistr Error accessing password file = Problme d'accs au fichier des mots de passe CSV (";" separated) + Text = CSV (sparateur ";") + Texte Entry can only be deleted %1.2lg hours after creation = Une entre ne peut tre supprime que %1.2lg heures aprs sa cration Drop attachments here... = Glisser les attachements ici... Insert Timestamp = Insrer horodatage Pending draft available = Brouillon en attente disponible %d pending drafts available = %d brouillons en attente disponibles Create new entry = Crer une nouvelle entre If you leave this page you will lose your unsaved changes = Si vous quittez cette page vos modifications non enregistres seront perdues Edited = Modifi All time = Tous temps Draft = Brouillon days = derniers jours Draft saved at = Brouillon enregistr le You might however then overwrite each other's modifications = Vous pourriez toutefois craser mutuellement vos modifications Draft entry created on %s by %s = Brouillon cr le %s par %s Really delete this entry? = Supprimer cette entre, vraiment ? This is a draft message, edit and submit it to make it permanent = Ceci est un brouillon, modifiez-le et validez-le pour le rendre dfinitif Restrict search to last = Restreindre la recherche au dernier Do you want to restore your original text? = Voulez-vous restaurer votre texte original ? This will overwrite your current modifications. = Cela va craser vos modifications en cours. # #---- please translate following items and then remove this comment ----# # elog-3.1.3-1/resources/eloglang.czech0000644000175000017500000005256313076344200016452 0ustar rogerroger# # Czech translation by Ján Tomášik # A more recent version may be available at : # http://savannah.psi.ch/viewcvs/trunk/resources/eloglang.czech?root=elog # New = Nový Edit = Úprava Delete = Smazat Reply = Odepsat Find = Hledat Last day = Poslední den Last 10 = Posledních 10 Config = Konfigurace Change password = Změna hesla Logout = Odhlášení Help = Pomoc Back = Zpět Submit = Submit First = První Last = Poslední Previous = Předchozí Next = Další Copy to = Kopírovat do Move to = Přesunout do Save = Uložit Cancel = Storno Wrong password = Nesprávné heslo Please login = Přihlašte se Username = Přihlašovací jméno Password = Heslo Old password = Staré heslo New password = Nové heslo ELOG change password = ELOG změna hesla Several logbooks are defined on this host = Na tomto serveru je definováno několik logbooků Please select the one to connect to = Zvolte jeden k připojení Change password for user = Změna hesla uživatele Logged in as = Přihlášen jako Entry date = Datum příspěvku with = s This is the last entry = Toto je poslední příspěvek This is the first entry = Toto je první příspěvek Email sent to = Email odeslán na Email notification suppressed = Potlačeno upozornění Emailem please select = prosím zvolte Suppress Email notification = Potlač upozornění Emailem Resubmit as new entry = Pošli znovu jako novou položku Attachment = Příloha Reply to this = Odpověď na In reply to = V odpovědi na Fields marked with = Pole označená are required = jsou vyžadována Please check that it exists and elogd has write access = Ujistěte se o existenci elogd a jeho zápisových právech Error: Attribute %s not supplied = Chyba: Atribut %s není dodán Please go back and enter the %s field = Jděte zpět a zadejte %s pole Please use your browser's back button to go back = Použijte tlačítka Zpět vašeho prohlížeče ELOG find = ELOG najít Find = Najít Search = Hledat Reset Form = Nulovat formulář Summary only = Pouze shrnutí Show attachments = Ukaž přílohy Printable output = Tiskový výstup Sort in reverse order = Seřaď v obráceném pořadí Start date = Počáteční datum End date = Koncové datum Year = Rok Text = Text Search all logbooks = Prohledej všechny logbooky Last %d days = Posledních %d dnů Last %d entries = Posledních %d položek No entries found = Žádné položky nebyly nalezeny A new entry has been submitted on %s = Byla odeslána nová položka na %s Logbook = Logbook Yes = Ano No = Ne Error deleting message: status = Chyba mazání zprávy: Status Error: Command "%s" not allowed = Chyba: Příkaz "%s" není dovolen Cannot open file %s = Soubor %s nelze otevřít Cannot write to %s = Do souboru %s nelze zapisovet No SMTP host defined in [global] section of configuration file = Není definován žádný SMTP Host v sekci [global] konfiguračního souboru Display threads = Zobraz vlákno Logbook is empty = Logbook je prázdný Download = Stáhnout and all its replies = a všechny odpovědi Message ID = ID zprávy Not logged in = Není přihlášen Login = Přihlášení Page %d of %d = Strana %d z %d all entries = všechny položky Login name = Přihlašovací jméno Full name = Celé jméno ELOG user config = ELOG konfigurace uživatele ELOG new user = Nový ELOG-uživatel Admin = Správa Select user = Vyber uživatele Remove user = Zruš uživatele New user = Nový uživatel Retype new password = Zadej nové heslo New passwords do not match, please retype = Nové heslo nesouhlasí, zadej znovu Retype password = Zadej znovu heslo exists already = už existuje Register as new user = Registruj se jako nový uživatel A new ELOG user has been registered on %s = Nový ELOG uživatel se registroval na %s Email = Email User "%s" registered on logbook "%s" = Uživatel "%s" logbooku "%s" registrován User [%s] has been deleted = Uživatel [%s ]byl smazán Hit following URL to activate that account = Zadejte následující URL k aktivaci tohoto účtu ELOG registration = ELOG Registrace Your ELOG account has been activated on host = Váš ELOG účet byl aktivován na hostu You can access it at = Máte přístup k Are you sure to delete these messages? = Opravdu chcete smazat tuto zprávu? Select = Označte All entries = Všechny položky Day = Den Week = Týden Month = Měsíc Show last = Ukaž poslední Goto page = Jdi na stránku All = Vše Display = Zobraz entries per page = položek na stránku Toggle all = Odeber vše Selected entries = Označené položky Collapse = Sbalit Expand = Rozbalit Full = Plně Summary = Souhrn Threaded = Zřetězeně Filters = Filtry Mode = Mód Options = Volba Error: start date after end date = Chyba: počáteční datum a konečné datum and all their replies = a všechny jejich odpovědi Upload = Odeslat Requested = Požadovaný A new ELOG user wants to register on "%s" = Nový uživatel se chce registrovat na "%s" Search text also in attributes = Hledej text také v atributech Date = Date ID = ID Forgot password? = Zapoměl si heslo? ELOG password recovery = ELOG obnova hesla No Email address registered with user name "%s" = Žádná Emailová adresa není registrována s uživatelským jménem "%s" Error sending Email via "%s" = Chybné zasílání Emailu přes "%s" Forgot = Zapomeň User "%s" registered on host "%s" = Uživatel "%s" registrován na počítači "%s" Enter your user name or email address = Zadejte vaše uživatelské jméno nebo Emailovou adresu Password recovery for ELOG %s = Obnova hesla pro ELOG %s Host = Počítač Your ELOG account has been activated = Váš ELOG účet byl aktivován Maximum number of attachments reached = Maximální počet příloh byl překročen on = na Entry is currently edited by = Položka je právě měněna recipients = odberatelu Suppress shell execution = Potlač provádění shell programů Update = Aktualizace by = za Change %s = Změň %s Add new option here = Přidej sem novou volbu Expand all = Rozbal vše Add %s = Přidej %s Synchronization = Synchronizace Error: Value %s not allowed for boolean attributes = Chyba: Hodnota %s není dovolena pro boolean atribut Error: Attribute option %s not existing = Chyba: Atribut Volba %s neexistuje Last submission = Poslední podání Synchronize = Synchronizuj No mirror server defined in configuration file = V konfiguračním souboru nejsou definovány žádné zrcadlené servery user = uživatel Maximum number of replies (%d) exceeded = Maximální počet odpovědí (%d) překročen Entry can only be edited %1.2lg hours after creation = Položky mohou být měněny pouze %1.2lg hodin po vytvoření Display full entries = Položky zobraz plně No entry selected for deletion = Pro mazání nejsou vybrány žádné položky Cannot read entry from local logbook = Nelze číst žádné položky z lokálního logbooku Cannot connect to host %s, port %d = Nelze se připojit k uzlu %s, port %d Cannot receive "%s" = Nelze přijmout "%s" Received wrong entry id "%d" = Přijata chybná položka ID "%d" Entry #%d is locked on remote server = Položka #%d je uzamčena na vzdáleném serveru Cannot save remote entry locally = Nelze uložit vzdálenou položku lokálně local = místní remote = vzdálený Please delete %s or %s entry to resolve conflict = Smažte položku %s nebo %s pro vyřešení vzdáleného konfliktu Error sending local entry = Chyba zasílání místní položky Local entry submitted = Místní položka odeslána Error deleting remote entry = Chyba mazání vzdálené položky New entry cannot be written to directory "%s" = Nelze zapsat novou položku do adresáře "%s" Entry %s cannot be read from logbook "%s" = Nelze přečíst položku %s z logbooku "%s" This entry has been deleted = Nelze smazat tuto položku Entries = Položky Cannot connect to remote server "%s" = Nelze se připojit k vzdálenému serveru "%s" Remote server is not an ELOG server = Vzdálený server není ELOG Server Incorrect remote ELOG server version %s = Nesprávná verze %s vzdáleného serveru Error accessing remote logbook = Chyba přístupu k vzdálenému logbooku Invalid HTTP header = Vadná HTTP hlavička No user name supplied to access remote logbook = Ve vzdáleném logbooku nejsou zadáni žádní uživatelé Synchronizing logbook %s with server "%s" = Synchronizace logbooku %s se serverem "%s" Configuration has been changed locally and remotely = Konfigurace změněna místně a vzdáleně Please merge manually to resolve conflict = Připojte ručně k vyřešení konfliktu Logbook "%s" does not exist on remote server = Logbook "%s" neexistuje na vzdáleném serveru Entry has been changed locally and remotely = Položka byla změněna místně nebo vzdáleně Entry deleted locally = Položka byla místně smazána Changed local entry ID to %d = Změněna místní položka ID na %d Entry deleted remotely = Položka byla vzdáleně smazána All entries identical = Všechny položky jsou stejné Invalid mirror_id or entry_date = Neplatný mirror_id nebo entry_date Synchronize all logbooks = Synchronizuj všechny logbooky Calendar = Kalendář Remote entry received = Přijata vzdálená položka Pick a date = Vložte datum Please select at least one '%s' = Vyberte nejméně jednu '%s' Please select a '%s' = Vyberte '%s' Please enter month for attribute '%s' = Zvolte měsíc pro atribut '%s' Please enter day for attribute '%s' = Zvolte den pro atribut '%s' Please enter year for attribute '%s' = Zvolte rok pro atribut '%s' Please enter attribute '%s' = Zvolte atribut '%s' Entry time = Čas vložení Start = Začátek End = Konec From = Z After = Po to = do Before = Před Previous Year = Minulý rok Next Year = Příští rok Please enter numeric value for '%s' = Vložte číselnou hodnotu pro '%s' Error: Attribute %s must be numeric = Chyba: Atribut %s musí být číslo 3 Months = 3 Měsíce 6 Months = 6 Měsíců Error sending local message = Chyba zasílání místní zprávy Error receiving message = Chyba příjmu zprávy Are you sure to delete this entry? = Určitě smazat tuto položku? ELOG CSV import = ELOG CSV Import Import = Import CSV filename = CSV jméno souboru Field separator = Oddělovač polí Edit entry = Měnit položku Delete entry = Smazat položku CSV Import = CSV Import Derive attributes from CSV file = Odvoď atributy z CSV souboru Comma = Ćárka Semicolon = Středník Preview import = Import náhledu CSV import preview of %s = CSV Import náhledu z %s Too many attributes in CSV file = Příliš mnoho atributů v CSV souboru %d entries successfully imported = %d položek úspěšně importováno No 'Attributes' option present in %s = Volba 'Attributes' není v %s CSV ("," separated) = CSV (s "," oddělovačem) CSV (";" separated) = CSV (s ";" oddělovačem) Auto detect = Automatické zjištění text = text Column header '%s' must be present in CSV file = V CSV souboru musí být hlavička sloupce '%s' Fill text body = Vyplňte tělo textu Please re-enter filename = Zadejte znovu jméno souboru Last x = Poslední x Activate = Aktivuj Entry has been modified = Položka byla modifikována No attachment file specified = Nejsou specifikovány žádné přílohy Delete this logbook = Smaž tento logbook Rename this logbook = Přejmenuj tento logbook Create new logbook = Vytvoř nový logbook Syntax error in config file = Syntaktická chyba v konfiguračním souboru Email notification = Potvrzení Emailu Are you sure to delete logbook "%s"? = Určitě smazat logbook "%s"? Logbook "%s" exists already, please choose different name = Logbook "%s" již existuje, zvolte jiné jméno Rename logbook = Přejmenuj logbook Enter new logbook name = Vložte nové jméno logbooku Logbook name = Jméno logbooku Use existing logbook as template = Použij existující logbook jako šablonu none = žádný URL is redirected to: = URL je přesměrováno na: Remote config should be received = Vzdálená konfigurace může být přijata Local entry should be submitted = Lokální položka může být odeslána Remote entry should be received = Vzdálená položka může být přijata Local entry = Lokální položka %s should be deleted = %s může být smazána Entry should be deleted locally = Položka může být místně smazána Local entry ID should be changed to %d = Místní položka ID může být změněna na %d Remote entry = Vzdálená položka Entry should be deleted remotely = Položka může být smazána vzdáleně Click here to delete %d entries = Klikněte sem pro smazání %d položek Click here to delete this entry = Klikněte sem pro smazání této položky Maximum number of attribute options exceeded = Maximální počet atributů voleb byl překročen Please increase MAX_N_LIST in elogd.c and recompile = Please increase MAX_N_LIST in elogd.c and recompile You can "steal" the lock by editing this entry = Můžete "ukrást" zámek změněním této položky Several logbooks groups are defined on this host = Na tomto serveru je definováno několik logbooků Please select one to list the logbooks in that group = Vyberte jeden z logbooků v této skupině No logbook defined on this server = Na tomto serveru nejsou definovány žádné logbooky Goto ELOG home page = Jdi na ELOG Home Page Please enter "%s" = Vložte "%s" Change config file = Změna v konfiguračním souboru Please check that it exists and elogd has write access and disk is not full = Zkontrolujte zda elogd existuje, má zápisová práva a disk není plný Show = Ukaž Hide = Skryj Hide all = Skryj vše Show all = Ukaž vše This logbook requires authentication = Tento logbook vyžaduje autentizaci Attachment file "%s" empty or not found = Příloha "%s" je prázdná, nebo nebyla nalezena Case sensitive = Rozlišovat malá a VELKÁ písmena List = Seznam Date must be between 1970 and 2037 = Das Datum mu zwischen 1970 und 2037 liegen up = nahoru down = dolů stop = zastavit Entry = Položka name may not contain blanks = jméno nesmí obsahovat mezery regular expressions = regulární výraz Text fields are treated as %s = Textová pole jsou upravena jako %s Subscribe to logbooks = Zapsat se do logbooku enable automatic email notifications = povolit automatické emailové potvrzování Set all = Nastavit vše Set none = Nic nenastavuj Insert current time = Vlož aktuální čas Please enter hour for attribute '%s' = Zadejte hodinu pro atribut '%s' Please enter minute for attribute '%s' = Zadejte minutu pro atribut '%s' Please enter second for attribute '%s' = Zadejte sekundu pro atribut '%s' No admin user has been defined in configuration file = V konfiguračním souboru není zadán žádný admin uživatel Duplicate = Duplikace Cannot lookup server name = Nelze zjistit jméno serveru Cannot connect to server = Nelze se připojit k serveru %s wrote = %s napsal Quote = Citace Please enter numeric value for year of attribute '%s' = Zadejte číselnou hodnotu pro atribut '%s' Preview = Náhled FONT = PÍSMO SIZE = VELIKOST COLOR = BARVA smiling = usměvavý happy = šťastný winking = blikající big grin = široký úsměv crying = plačící cool = klidný frowning = zamračený confused = zmatený mad = šílený pleased = spokojený tongue = jazyk yawn = znuděný Encoding = kodóvaní User "%s" not found in password file = Uživatel "%s" nebyl v souboru hesel nalezen Cannot write to file %s = Nelze zapisovat do souboru %s Invalid user name "%s" or password for remote logbook = Neplatné uživatelské jméno "%s", nebo heslo pro vzdálený logbook Hide attachments = Skryj přílohy Show only new entries = Ukaž pouze nové položky Show all entries = Ukaž všechny položky New entries since = Nové položky od An old ELOG entry has been updated = Stará ELOG položka byla obnovena A new ELOG entry has been submitted = Nová ELOG položka byla odeslána Invalid user name or password = Neplatné jméno nebo heslo Upload image = Odeslání obrázku Please enter filename or URL = Zadejte jméno souboru nebo URL Maximum allowed file size is = Maximální povolená velikost souboru je Enter filename or URL = Zadejte jméno souboru nebo URL Image uploaded successfully = Obrázek byl odeslán úspěšně Image "%s" uploaded successfully = Obrázek "%s" byl odeslán úspěšně HelpELCode = PomocELCode Cannot retrieve file from URL "%s" = Nelze dostat soubor URL "%s" Enter name of hyperlink = Zadej jméno hyperlinku Enter URL of hyperlink = Zadej URL hyperlinku astonished = užaslý Error in regular expression "%s" = Chyba v regulárním výrazu "%s" File not found at URL "%s" = Soubor nebyl nalezen na URL "%s" Entry size too large for email notification = Položka je příliš velká pro emailové potvrzování Please specify a valid email address = Zadejte platnou emailovou adresu Cannot send email notification to "%s" = Nelze odeslat emailové potvrzení "%s" Error: Command "%s" is not allowed for user "%s" = Chyba: Příkaz "%s" nemůže uživatel "%s" použít Shell execution not enabled via -x flag = Shell příkaz nebyl povolen přepínačem -x Enter heading level (1, 2 or 3) = Zadejte úroveň záhlaví (1, 2 oder 3) User name may not contain blanks = Uživatelské jméno nesmí obsahovat mezery Empty password not allowed = Prázdné heslo není povoleno Password may not contain blanks = Heslo nesmí obsahovat mezery Anonymous = Anonym Activation notice has been sent to %s = Oznámení o aktivaci bylo zasláno na %s Your request has been forwarded to the administrator = Váš požadavek byl odeslán na administrátora You will be notified by email upon activation of your new account = O aktivaci vašeho nového účtu budete informován emailem # #---- please translate following items and then remove this comment ----# # Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_brazilian.html0000755000175000017500000000661612556103751020373 0ustar rogerroger ELOG Electronic Logbook Help
Ajuda do ELOG Electronic Logbook

The Electronic Logbook (ELog) pode ser usado para armazenar e recuperar mensagens através de uma interface Web. Dependendo da configuração, o sistema ELog pode administrar um ou mais logbooks, que são armazenados em seções separadas no servidor.

Resumo rápido

Por padrão, a última mensagem em um logbook é exibida. Pode-se usar os botões de navegação para exibir a primeira mensagem, a mensagem anterior, a próxima, e a última mensagem.

Se um dos checkboxes próximos a um dos campos de atributo estiver checado, apenas mensagens referentes àquele atributo serão exibidas pelos botões de navegação. Isso pode ser usado como um filtro rápido para exibir apenas páginas de um certo autor ou categoria, por exemplo.

O botão Novo cria uma nova mensagem. Com o botão Editar pode-se editar uma mensagem existente, se isso for permitido pelo arquivo de configuração. O botão Responder cria uma resposta para uma mensagem existente, semelhante à uma resposta de email.

O botão Procurar abre uma página de busca, na qual mensagens de um logbook podem ser exibidas com base em regras de filtragem. Cada campo que não estiver vazio funciona como um filtro adicional, que é acrescentado às outras regras. Se nenhum filtro for selecionado, todas as mensagens de um logbook serão exibidas. Os botões Último dia e Últimas 10 exibem todas as mensagens das últimas 24 horas e as dez últimas mensangens, respectivamente.

Maiores informações

Para maiores informações, especialmente quanto à configuração do ELog, consulte a Página do ELOG .


S. Ritt, 28 August 2001
Traduzido para o português do Brasil por Carlos Augusto Marcicano, abril de 2002.
elog-3.1.3-1/resources/eloglang.spanish0000644000175000017500000006212513076344200017016 0ustar rogerroger# # Spanish translation by Julio Calvo # Upgraded by Jean Sagi # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Nuevo Edit = Editar Delete = Borrar Reply = Responder Find = Encontrar Last day = Ultimo dia Last 10 = Ultimos 10 Config = Configurar Logout = Salir Help = Ayuda Back = Atras Submit = Enviar First = Primero Last = Ultimo Previous = Anterior Next = Siguiente Copy to = Copiar a Move to = Mover a Save = Guardar Cancel = Cancelar Wrong password = Clave incorrecta Please login = Por favor ingrese Username = Usuario Password = Clave ELOG change password = Cambio de clave de ELOG Several logbooks are defined on this host = Hay varios logbooks definidos en este servidor Please select the one to connect to = Por favor elija uno para conectarse Change password for user = Cambiar clave para el usuario Logged in as = Conectado como Entry date = Fecha de entrada with = con This is the last entry = Esta es la ultima entrada This is the first entry = Esta es la primera entrada Email sent to = Email enviado a Email notification suppressed = Se omitio la notificacion de Email please select = por favor elija Suppress Email notification = Omitir la notificacion por Email Resubmit as new entry = Reenviar como una nueva entrada Attachment = Attachment Reply to this = Responder a esto In reply to = En respuesta a Fields marked with = Campos marcados con are required = se requieren Please check that it exists and elogd has write access = Por favor verifique que exista y que elogd tenga acceso de escritura Error: Attribute %s not supplied = Error: No se suministro el atributo %s Please go back and enter the %s field = Por favor vuelva atras e ingrese al campo %s Please use your browser's back button to go back = Por favor vuelva atras con el boton de su browser ELOG find = ELOG encontro Find = Encontrar Search = Buscar Reset Form = Limpiar formulario Summary only = Solo resumen Show attachments = Mostrar attachments Printable output = Salida imprimible Sort in reverse order = Ordenar en orden inverso Start date = Fecha comienzo End date = Fecha termino Year = Anio Text = Texto Search all logbooks = Buscar en todos los logbooks Last %d days = Ultimos %d dias Last %d entries = Ultimas %d Entradas No entries found = No se encontro ninguna entrada A new entry has been submitted on %s = Nueva entrada enviada en %s Logbook = Logbook Yes = Si No = No Error deleting message: status = Error al borrar mensaje: Status Error: Command "%s" not allowed = Error: Comando "%s" no permitido Cannot open file %s = No se puede abrir el archivo %s Cannot write to %s = No se puede escribir en el %s No SMTP host defined in [global] section of configuration file = No se definio el host SMTP en en la seccion [global] del archivo de configuracion Display threads = Mostrar hilos Logbook is empty = El logbook esta vacio Download = Bajar and all its replies = y todas sus respuestas Message ID = ID de Mensaje Not logged in = No ha iniciado sesion New passwords do not match, please retype = Las claves no coinciden, por favor reescriba Old password = Clave anterior Retype new password = Reescriba nueva clave Maximum number of replies (%d) exceeded = Numero maximo de respuestas (%d) excedido Update = Actualizar Add %s = Agregar %s Add new option here = Agregar nueva opcion aqui Suppress shell execution = Suprimir ejecucion de shell Maximum number of attachments reached = Se alcanzo el numero maximo de archivos adjuntos Upload = Subir archivo Mode = Modo Options = Opciones entries per page = entradas por pagina Filters = Filtros Show last = Mostrar ultimo Day = Dia Week = Semana Month = Mes Search text also in attributes = Buscar texto tambien en atributos Admin = Admin Change %s = Cambiar %s Login name = Nombre de login exists already = ya existe Your ELOG account has been activated = Su cuenta de ELOG ha sido activada Your ELOG account has been activated on host = Su cuenta de ELOG ha sido activada en el host You can access it at = Puede accederla en A new ELOG user wants to register on "%s" = Un nuevo usuario de ELOG quiere registrarse en "%s" User "%s" registered on logbook "%s" = Usuario "%s" registrado en el logbook "%s" User "%s" registered on host "%s" = Usuario "%s" registrado en el host "%s" A new ELOG user has been registered on %s = Un nuevo usuario de ELOG ha sido registrado en %s Host = Host Full name = Nombre completo Hit following URL to activate that account = Haga click en el siguiente URL para activar esa cuenta Requested = Pedido ELOG user config = Configuracion de usuario ELOG Select user = Elegir usuario User [%s] has been deleted = El usuario [%s] ha sido borrado Remove user = Eliminar usuario New user = Nuevo usuario No Email address registered with user name "%s" = No se encuentra registrado un usuario con direccion de email "%s" Password recovery for ELOG %s = Recuperacion de clave para ELOG %s ELOG password recovery = Recuperacion de clave ELOG Error sending Email via "%s" = Error al enviar email via "%s" Enter your user name or email address = Ingrese su nombre de usuario o direccion de email Forgot = Olvidar ELOG new user = Nuevo usuario de ELOG Retype password = Reescriba la clave Are you sure to delete these messages? = Esta seguro de borrar estos mensajes? and all their replies = y todas sus respuestas Synchronization = Sincronizacion Entry is currently edited by = La entrada esta siendo editada por Date = Fecha Threaded = Hilo Collapse = Colapsar Expand = Expandir All entries = Todas las entradas Goto page = Ir a pagina Selected entries = Entradas elegidas Toggle all = Cambiar todos Error: start date after end date = Error: fecha de comienzo posterior a fecha de finalizacion all entries = todas las entradas Page %d of %d = Pagina %d de %d recipients = destinatarios Error: Value %s not allowed for boolean attributes = Error: El valor %s no esta permitido para atributos booleanos Error: Attribute option %s not existing = Error: La opcion de atributo %s no existe Forgot password? = Olvido su clave? Register as new user = Registrado como nuevo usuario Entries = Entradas Last submission = Ultimo envio Expand all = Expandir todos ELOG registration = Registracion ELOG Synchronize = Sincronizar No mirror server defined in configuration file = No se encuentra definido el servidor de mirror en el archivo de configuracion New password = Nueva clave user = usuario on = en Entry can only be edited %1.2lg hours after creation = La entrada solo puede editarse %1.2lg horas despues de su creacion Display full entries = Mostrar entradas completas Summary = Resumen Display = Mostrar Email = Email Change password = Cambiar clave No entry selected for deletion = No se eligio entrada para borrar Cannot read entry from local logbook = No se puede leer la entrada del logbook local Cannot connect to host %s, port %d = No se puede conectar al host %s, puerto %d Cannot receive "%s" = No se puede recibir "%s" Received wrong entry id "%d" = Se recibio un id de entrada erroneo "%d" Entry #%d is locked on remote server = La entrada #%d esta lockeada en el servidor remoto Cannot save remote entry locally = No se puede guardar localmente la entrada remota local = local remote = remoto Please delete %s or %s entry to resolve conflict = Por favor borrar la entrada %s o %s para resolver el conflicto Error sending local entry = Error al enviar la entrada local Local entry submitted = Entrada local enviada Error deleting remote entry = Error al borrar la entrada remota ID = ID Full = Completo All = Todos Select = Elegir New entry cannot be written to directory "%s" = La nueva entrada no puede escribirse en el directorio "%s" Entry %s cannot be read from logbook "%s" = La entrada %s no puede leerse del logbook "%s" This entry has been deleted = Esta entrada ha sido eliminada Login = Login by = por Cannot connect to remote server "%s" = No se puede conectar al servidor remoto "%s" Remote server is not an ELOG server = El servidor remoto no es un servidor ELOG Incorrect remote ELOG server version %s = Version del servidor ELOG remoto %s incorrecta Error accessing remote logbook = Error al acceder al logbook remoto Invalid HTTP header = Encabezado HTTP invalido No user name supplied to access remote logbook = No se suministro un nombre de usuario para acceder al logbook remoto Synchronizing logbook %s with server "%s" = Sincronizando logbook %s con el servidor "%s" Configuration has been changed locally and remotely = La configuracion ha sido cambiada local y remotamente Please merge manually to resolve conflict = Por favor combinar manualmente para resolver conflicto Logbook "%s" does not exist on remote server = El logbook "%s" no existe en el server remoto Entry has been changed locally and remotely = La entrada ha sido cambiada local y remotamente Entry deleted locally = Entrada borrada localmente Changed local entry ID to %d = Se cambio el ID de la entrada local a %d Entry deleted remotely = Entrada borrada remotamente All entries identical = Todas las entradas son identicas Invalid mirror_id or entry_date = Mirror_id o fecha_de_entrada invalido Synchronize all logbooks = Sincronizar todos los logbooks Calendar = Calendario Remote entry received = Se recibio entrada remota Pick a date = Elegir una fecha Please select at least one '%s' = Por favor elegir por lo menos un '%s' Please select a '%s' = Por favor elegir un '%s' Please enter month for attribute '%s' = Por favor ingrese mes para el atributo '%s' Please enter day for attribute '%s' = Por favor ingrese dia para el atributo '%s' Please enter year for attribute '%s' = Por favor ingrese anio para el atributo '%s' Please enter attribute '%s' = Por favor ingrese atributo '%s' Entry time = Registro ingresado Start = Comienzo End = Fin After = Despues to = para Before = Antes Previous Year = Anio anterior Next Year = Anio proximo Please enter numeric value for '%s' = Por favor ingrese valor numerico para '%s' Error: Attribute %s must be numeric = Error: El atributo %s debe ser numerico 3 Months = 3 meses 6 Months = 6 meses Are you sure to delete this entry? = Esta seguro de borrar estas entradas? ELOG CSV import = Importar ELOG CSV Import = Importar Field separator = Separador de campos Comma = Coma Semicolon = Punto y coma Derive attributes from CSV file = Derivar atributos del archivo CSV Preview import = Vista previa de importacion CSV filename = Archivo CVS CSV import preview of %s = Vista previa de importacion CSV %s CSV Import = Importar CSV Too many attributes in CSV file = Demasiados atributos en archivo CSV %d entries successfully imported = %d entradas importadas con exito Error sending local message = Error al enviar mensaje local Error receiving message = Error al recibir mensaje Edit entry = Editar entrada Delete entry = Borrar entrada No 'Attributes' option present in %s = No se encuentra presente la opcion 'Attributes' en %s CSV ("," separated) = Control de Version (separado por ",") CSV (";" separated) = Control de Version (separado por ";") Auto detect = Autodetectar text = texto Column header '%s' must be present in CSV file = Columna de encabezado '%s' debe estar presente en el archivo de Control de Versión Fill text body = Texto para rellenar el cuerpo del mensaje Please re-enter filename = Por favor redigite el nombre del archivo Last x = Ultimo x Activate = Activar Entry has been modified = Entrada ha sido modificada No attachment file specified = No se especificó archivo adjunto Delete this logbook = Borrar este libro de anotaciones Rename this logbook = Renombrar este libro de anotaciones Create new logbook = Renombrar un nuevo libro de anotaciones Syntax error in config file = Error de sintaxis en el archivo de configuración Email notification = Notificación por correo Are you sure to delete logbook "%s"? = Está seguro que desea borrar el libro de anotaciones "%s"? Logbook "%s" exists already, please choose different name = Libro de anotaciones "%s" ya existe, por favor escoja un nombre diferente Rename logbook = Renombrar libro de anotaciones Enter new logbook name = Ingrese un nuevo nombre para el libro de anotaciones Logbook name = Nombre del libro de anotaciones Use existing logbook as template = Usar el libro de anotaciones existente como plantilla none = ninguno URL is redirected to: = URL se redirige a: Remote config should be received = Configuración remota debería ser recibida Local entry should be submitted = Entrada local debería ser publicada Remote entry should be received = Entrada remota debería ser publicada Local entry = Entrada local %s should be deleted = %s debería ser borrada Entry should be deleted locally = Entrada debería ser borrada localmente Local entry ID should be changed to %d = Identificador de entrada local debería ser cambiada a %d Remote entry = Entrada remota Entry should be deleted remotely = Entrada debería ser borrada remotamente Click here to delete this entry = Presione Click aquí para borrar esta entrada Maximum number of attribute options exceeded = Se exedió el máximo número de opciones de atributo Please increase MAX_N_LIST in elogd.c and recompile = Por favor incremente MAX_N_LIST en el archivo elogd.c y luego recompilelo You can "steal" the lock by editing this entry = Usted puede "robar" el candado editando esta entrada Several logbooks groups are defined on this host = Varios grupos de libros de anotaciones están definidos en este servidor Please select one to list the logbooks in that group = Por favor seleccione alguno, para listar los libros de anotaciones en ese grupo No logbook defined on this server = No se ha definido ningún libro de anotaciones en este servidor Goto ELOG home page = Ir a la página principal de ELOG Please enter "%s" = Por favor ingrese "%s" Change config file = Cambiar archivo de configuración Click here to delete %d entries = Presione Click aquí para borrar %d entradas Please check that it exists and elogd has write access and disk is not full = Por favor verifique que exista y que el proceso elogd tenga permiso de escritura y que el disco no esté lleno Show = Mostrar Hide = Esconder Hide all = Esconder todo Show all = Mostrar todo This logbook requires authentication = Este libro de anotaciones requiere autenticación Attachment file "%s" empty or not found = Archivo adjunto "%s" está vacio o no se encontró Case sensitive = Sensitivo a mayúscula y minúsculas From = Desde List = Lista Date must be between 1970 and 2037 = La fecha debe estar entre 1970 y 2037 up = Arriba down = Abajo stop = Alto Entry = Entrada name may not contain blanks = El nombre no debe contener espacios en blanco regular expressions = Expresiones regulares Text fields are treated as %s = Los campos de texto son tratados como %s Subscribe to logbooks = Suscríbase a la bitácora enable automatic email notifications = Habilitar notificaiones por e-mail automáticamente Set all = Habilitar todos Set none = Deshabiltar todos Please enter hour for attribute '%s' = Por favor especifique la hora para el atributo '%s' Please enter minute for attribute '%s' = Por favor especifique los minutos para el atributo '%s' Please enter second for attribute '%s' = Por favor especifique los segundos para el atributo '%s' No admin user has been defined in configuration file = No se ha definido usuario administrador en el archivo de configuración Duplicate = Duplicado Cannot lookup server name = No puedo encontrar servidor de nombres Cannot connect to server = No conectar al servidor %s wrote = %s escribio Quote = Cita Insert current time = Ingrese hora actual Please enter numeric value for year of attribute '%s' = Por favor ingrese valor numerico para el año del atributo '%s' Enter name of hyperlink = Ingrese nombre del enlace Enter URL of hyperlink = Ingrese URL del enlace Preview = Vista previa FONT = FONT SIZE = TAMAÑO COLOR = COLOR smiling = sonriente happy = feliz winking = guiñando big grin = carcajada crying = llorando cool = calma frowning = enojado confused = confundido mad = loco pleased = agradecido tongue = lengua yawn = bostezo Encoding = Codificando User "%s" not found in password file = El usuario "%s" no se encuentra en el archivo de claves Cannot write to file %s = No se puede escribir al archivo %s Invalid user name "%s" or password for remote logbook = Usuario "%s" o clave incorrecta para el logbook remoto Delete entry = Borrar entrada Hide attachments = Esconder adjuntos Show only new entries = Solo mostrar entradas nuevas Show all entries = Mostrar todas las entradas New entries since = Nuevas entradas desde An old ELOG entry has been updated = Una entrada de ELOG ha sido actualizada A new ELOG entry has been submitted = Una nueva entrada de ELOG se ha enviado Invalid user name or password = Nombre de usuario o clave incorrecto Upload image = Subir imagen Please enter filename or URL = Por favor ingrese nombre de archivo o URL Maximum allowed file size is = Tamaño maximo de archivo permitido es Enter filename or URL = Ingrese nombre de archivo o URL Image uploaded successfully = Imagen recibida exitosamente Image "%s" uploaded successfully = Imagen "%s" recibida exitosamente HelpELCode = HelpELCode Cannot retrieve file from URL "%s" = No puedo obtener el archivo desde la URL "%s" astonished = Asombrado Error in regular expression "%s" = Error en la expresion regular "%s" File not found at URL "%s" = Archivo no encontrado en la URL "%s" Entry size too large for email notification = Entrada demasiado grande Please specify a valid email address = Por favor especifique una direccion de email valida Cannot send email notification to "%s" = No se puede enviar una notificacion por email a "%s" Error: Command "%s" is not allowed for user "%s" = Error: El comando "%s" no esta permitido para el usuario "%s" Shell execution not enabled via -x flag = La ejecución del shell no está habilitada via flag -x Enter heading level (1, 2 or 3) = Ingrese nivel de encabezado (1, 2 o 3) User name may not contain blanks = El nombre de usuario no puede Empty password not allowed = No se permite clave vacía Password may not contain blanks = La clave no puede contener espacios en blancos Anonymous = Anonimo Activation notice has been sent to %s = Notificacion de activacion enviada a %s Your request has been forwarded to the administrator = Su solicitud fue enviada al administrador You will be notified by email upon activation of your new account = Sera notificado por email cuando su cuenta sea activada Select period = Elija periodo Last week = Ultima semana Last month = Ultimo mes Last 3 Months = Ultimos 3 meses Last 6 Months = Ultimos 6 meses Last Year = Ultimo año Enter %s = Ingrese %s Select %s = Elija %s Bold text CTRL+B = Negrita CTRL+B Italics text CTRL+I = Italicas CTRL+I Underlined text CTRL+U = Subrayado CTRL+U Centered text = Texto centrado Insert hyperlink = Insertar hipervinculo Insert email = Insertar email Insert image CTRL+M = Insertar imagen CTRL+M Insert quote = Insertar cita Insert list CTRL+L = Insertar lista CTRL+L Insert table = Insertar tabla Insert heading CTRL+H = Insertar encabezado CTRL+H Insert code CTRL+O = Insertar codigo CTRL+O Show the smiley bar = Mostrar la barra de emoticons Insert current time/date = Insertar fecha/hora actual Entry is locked on local server and therefore skipped = La entrada esta bloqueada en el servidor local y por lo tanto se saltea Keep me logged in on this computer = Mantenerme logueado en esta computadora for the next %d minutes = durante los proximos %d minutos for the next hour = durante la proxima hora for the next %d hours = durante las proximas %d horas for the next %d days = durante los proximos %d dias keep original values = mantener los valores originales Entry is locked = La entrada esta bloqueada Edit anyhow = Editar de cualquier manera Insert horizontal line = Insertar linea horizontal Insert anchor point = Insertar punto de anclaje keep original text = mantener texto original unspecified = sin especificar To subscribe to any logbook, click on 'Config' in that logbook = Para suscribirse a cualquier logbook, hacer click en 'Config' en ese logbook ELOG XML import = Importar ELOG XML Derive attributes from XML file = Derivar atributos del archivo XML XML filename = archivo XML Invalid date format = Formato de fecha invalido XML file does not contain %s element = Archivo XML no contiene elemento %s XML import preview of %s = Vista previa de la importacion XML de %s XML Import = Importar XML A old entry has been updated on %s = Una entrada anterior ha sido actualizada en %s First entry, Ctrl-Home = Primera entrada, Ctrl-Home Previous entry, Ctrl-PgUp = Entrada anterior, Ctrl-PgUp Next entry, Ctrl-PgDn = Entrada siguiente, Ctrl-PgDn Last entry, Ctrl-End = Ultima entrada, Ctrl-End ELOG import = ELOG importar Please choose format to import: = Por favor elegir formato a exportar Enter text = Ingresar texto or until I log out = o hasta que salga Make smaller = Mas pequeño Original size = Tamaño original Make larger = Mas grande Rotate left = Girar a la izquierda Rotate right = Girar a la derecha Delete attachment = Borrar adjunto Cannot create thumbnail, please check ImageMagick installation = No se puede crear thumbnail, por favor verifique la instalacion de ImageMagick Attachments = Adjuntos Error: Content length (%d) larger than maximum content length (%d) = Error: La longitud del contenido (%d) es mayor que la permitida (%d) ELOG error = error de ELOG Only user %s can edit this entry = Sólo el usuario %s puede editar esta entrada Export to = Exportar a Do not ignore first line = No ignorar la primera línea Last %d hours = Ultimas %d horas Invalid URL = URL inválido Attribute "%s" is not allowed in config file = El atributo "%s" no está permitido en el archivo de configuración Only user %s can delete this entry = Sólo el usuario %s puede borrar esta entrada File system full, ELOG cannot continue to work = Sistema de archivos lleno, ELOG no puede continuar trabajando 3 Days = 3 Días Last 3 Days = Ultimos 3 Días Really submit this entry? = Realmente desea publicar esta entrada? Account activation for ELOG logbook "%s" = Activación de cuenta para libro "%s" de Account activation for ELOG on host "%s" = Activación de cuenta para ELOG en el host "%s" Please click the URL below to activate following ELOG account = Por favor haga click en el URL abajo para activar la siguiente cuenta de ELOG Activation URL = URL de activación An email has been sent to <%s> = Se ha enviado un email a <%s> Use that email to activate your account = Use ese email para activar su cuenta Registration request for ELOG logbook "%s" = Pedido de registración para el libro "%s" de ELOG Registration request for ELOG on host "%s" = Pedido de registración para ELOG en el host "%s" Are you sure you want to deactivate your own account? = Está seguro de querer desactivar su propia cuenta? Active = Activa Invalid activation code = Código de activación no válido Error activating user = Error al activar usuario A password recovery email for user "%s" has been sent to %s = Un email para recuperar la clave del usuario "%s" ha sido enviado a %s User "%s" has no access to this logbook = El usuario "%s" no tiene acceso a este libro Your account has been activated = Su cuenta ha sido activada Please subscribe to logbooks if you want to receive automatic email notifications = Por favor suscríbase a los libros si desea recibir notificaciones automáticas por email This is an automatically generated account recovery email for host %s = Este es un email de recuperación de cuenta generado automaticamente para el host %s Please click on following link to recover your account = Por favor haga click en el siguiente link para recuperar su cuenta Email address "%s" not registered = La dirección de correo "%s" no está registrada User name "%s" not registered = El usuario "%s" no está registrado Error accessing password file = Error al acceder al archivo de password CSV (";" separated) + Text = CSV (separado por ";") + Texto Entry can only be deleted %1.2lg hours after creation = La entrada śolo de puede borrar %1.2lg horas después de su creación Drop attachments here... = Dejar los adjuntos aquí ... Insert Timestamp = Inserte marca de tiempo Pending draft available = Borrador pendiente disponible %d pending drafts available = %d borradores pendientes disponibles Create new entry = Crear nueva entrada If you leave this page you will lose your unsaved changes = Si sale de esta página perderá todos los cambios no guardados Edited = Editado All time = Todo el tiempo Draft = Borrador Restrict search to last = Restringir búsqueda al final days = días Draft saved at = Borrador guardado en You might however then overwrite each other's modifications = Sin embargo puede sobreescribir las modificaciones de los demás Draft entry created on %s by %s = Entrada borrador creada en %s por %s Really delete this entry? = Desea borrar realmente esta entrada? This is a draft message, edit and submit it to make it permanent = Este es un mensaje borrador, editelo y envíelo para hacerlo permanente # #---- please translate following items and then remove this comment ----# # Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.indonesia0000644000175000017500000005663213076344200017330 0ustar rogerroger# # Indonesia translation by Banata and SABO Team (sabo.pusair-pu.go.id) # Revision 1 # A more recent version may be available at: # https://bitbucket.org/ritt/elog # New = Baru Edit = Ubah Delete = Hapus Reply = Balas Find = Cari Last day = Hari terakhir Last 10 = 10 terakhir Config = Konfigurasi Change password = Ubah sandi Logout = Keluar Help = Bantuan Back = Kembali Submit = Kirim First = Pertama Last = Terakhir Previous = Sebelumnya Next = Berikutnya Copy to = Salin ke Move to = Pindah ke Save = Simpan Cancel = Batal Wrong password = Sandi salah Please login = Silahkan masuk Username = Nama pengguna Password = Sandi Old password = Sandi lama New password = Sandi baru ELOG change password = Ganti kata sandi ELOG Several logbooks are defined on this host = Beberapa logbook didefinisikan di host ini Please select the one to connect to = Silahkan pilih salah satu untuk dihubungkan Change password for user = Merubah sandi untuk pengguna Logged in as = Masuk sebagai Entry date = Tanggal catatan with = dengan This is the last entry = Ini adalah catatan terakhir This is the first entry = Ini adalah catatan pertama Email sent to = Email dikirim ke Email notification suppressed = Pemberitahuan email dimatikan please select = silahkan dipilih Suppress Email notification = Matikan pemberitahuan email Resubmit as new entry = Kirim ulang sebagai catatan baru Attachment = Lampiran Reply to this = Membalas ini In reply to = Balasan ke Fields marked with = Kolom ditandai dengan are required = diperlukan Please check that it exists and elogd has write access = Silahkan periksa jika ada dan elogd mempunyai akses tulis Error: Attribute %s not supplied = Kesalahan: Atribut %s tidak diberikan Please go back and enter the %s field = Silahkan kembali dan isi di bagian %s Please use your browser's back button to go back = Silahkan gunakan tombol kembali browser anda untuk kembali ELOG find = Temukan ELOG Find = Temukan Search = Cari Reset Form = Ulangi Isian Summary only = Hanya ringkasan Show attachments = Tampilkan ringkasan Printable output = Keluaran cetak Sort in reverse order = Urutkan terbalik Start date = Tanggal mulai End date = Tanggal akhir Year = Tahun Text = Teks Search all logbooks = Cari semua logbook Last %d days = %d hari terakhir Last %d entries = %d catatan terakhir No entries found = catatan tidak ditemukan A new entry has been submitted on %s = Catatan baru telah dikirimkan di %s Logbook = Logbook Yes = Ya No = Tidak Error deleting message: status = Kesalahan menghapus pesan: status Error: Command "%s" not allowed = Kesalahan: Perintah "%s" tidak diizinkan Cannot open file %s = Tidak bisa membuka file %s Cannot write to %s = Tidak bisa menulis ke %s No SMTP host defined in [global] section of configuration file = Tidak ada host SMTP yang didefinisikan di bagian [global] di file konfigurasi Display threads = Tampilkan berkas Logbook is empty = Logbook kosong Download = Unduh and all its replies = dan semua balasannya Message ID = ID Pesan Not logged in = Tidak masuk Login = Masuk Page %d of %d = Halaman %d dari %d all entries = semua isian Login name = Nama login Full name = Nama lengkap ELOG user config = Konfigurasi pengguna ELOG ELOG new user = Pengguna baru ELOG Admin = Admin Select user = Pilih pengguna Remove user = Hapus pengguna New user = Pengguna baru Retype new password = Ketik ulang sandi baru New passwords do not match, please retype = Kata sandi tidak cocok, ketik lagi Retype password = Ketik ulang sandi exists already = sudah ada Register as new user = Daftar sebagai pengguna baru A new ELOG user has been registered on %s = Pengguna ELOG baru terdaftar pada %s Email = Email User "%s" registered on logbook "%s" = Pengguna "%s" terdaftar pada logbook "%s" User [%s] has been deleted = Pengguna [%s] telah dihapus Hit following URL to activate that account = Ikuti URL ini untuk mengaktifkan akun tersebut ELOG registration = Pendaftaran ELOG Your ELOG account has been activated on host = Akun ELOG anda sudah diaktifkan pada host You can access it at = Anda dapat mengaksesnya di Are you sure to delete these messages? = Apakah anda yakin ingin menghapus pesan ini? Select = Pilih All entries = Semua isian Day = Hari Week = Minggu Month = Bulan Show last = Tampilkan terakhir Goto page = Pilih halaman All = Semua Display = Tampilan entries per page = entri tiap halaman Toggle all = Beralih semua Selected entries = Isian yang dipilih Collapse = Sembunyikan Expand = Bentangkan Full = Penuh Summary = Ringkasan Threaded = Rangkaian Filters = Filter Mode = Mode Options = Pilihan Error: start date after end date = Kesalahan: tanggal mulai setelah tanggal akhir and all their replies = dan semua balasannya Upload = Unggah Requested = Diminta A new ELOG user wants to register on "%s" = Pengguna ELOG baru ingin mendaftar pada "%s" Search text also in attributes = Cari teks dalam atribut Date = Tanggal ID = ID Forgot password? = Lupa sandi? ELOG password recovery = Pemulihan kata sandi ELOG No Email address registered with user name "%s" = Tidak ada alamat email yang terdaftar dengan nama pengguna "%s" Error sending Email via "%s" = Kesalahan mengirim email melalui "%s" Forgot = Lupa User "%s" registered on host "%s" = Pengguna "%s" terdaftar pada host "%s" Enter your user name or email address = Masukkan nama pengguna atau alamat email Password recovery for ELOG %s = Pemulihan sandi untuk ELOG %s Host = Host Your ELOG account has been activated = Akun ELOG anda telah diaktifkan Maximum number of attachments reached = Jumlah maksimum lampiran telah tercapai on = pada Entry is currently edited by = Entri saat ini diedit oleh recipients = penerima Suppress shell execution = Matikan eksekusi shell Update = Perbarui by = oleh Change %s = Ubah %s Add new option here = Tambahkan opsi baru di sini Expand all = Buka semua Add %s = Tambahkan %s Synchronization = Sinkronisasi Error: Value %s not allowed for boolean attributes = Kesalahan: Fehler: nilai %s tidak diizinkan untuk atribut boolean Error: Attribute option %s not existing = Kesalahan: Opsi atribut %s tidak ada Last submission = Pengajuan terakhir Synchronize = Sinkronkan No mirror server defined in configuration file = Tidak ada server mirror terdefinisi di file konfigurasi user = pengguna Maximum number of replies (%d) exceeded = Telah melebihi jumlah maksimum balasan (%d) Entry can only be edited %1.2lg hours after creation = Entri hanya bisa diedit %1.2lg jam setelah dibuat Display full entries = Tampilkan entri penuh No entry selected for deletion = Tidak ada entri yang dipilih untuk dihapus Cannot read entry from local logbook = Tidak dapat membaca entri dari logbook lokal Cannot connect to host %s, port %d = Tidak dapat terhubung dengan host %s, port %d Cannot receive "%s" = Tidak dapat menerima "%s" Received wrong entry id "%d" = Menerima id entri salah "%d" Entry #%d is locked on remote server = Entri #%d dikunci pada server jauh Cannot save remote entry locally = Tidak dapat menyimpan entri jauh ke lokal local = lokal remote = jauh Please delete %s or %s entry to resolve conflict = Harap menghapus %s atau %s untuk menyelesaikan konflik Error sending local entry = Kesalahan mengirim entri lokal Local entry submitted = Entri lokal dimasukkan Error deleting remote entry = Kesalahan menghapus entri jauh New entry cannot be written to directory "%s" = Entri baru tidak bisa ditulis ke direktori "%s" Entry %s cannot be read from logbook "%s" = Entri %s tidak dapat dibaca di logbook "%s" This entry has been deleted = Entri ini telah dihapus Entries = Entri Cannot connect to remote server "%s" = Tidak bisa terhubung dengan server jauh "%s" Remote server is not an ELOG server = Server jauh bukan server ELOG Incorrect remote ELOG server version %s = Salah versi server ELOG %s Error accessing remote logbook = Kesalahan mengakses logbook jauh Invalid HTTP header = Header HTTP salah No user name supplied to access remote logbook = Tidak ada nama pengguna yang diberikan untuk mengakses logbook jauh Synchronizing logbook %s with server "%s" = Sinkronisasi logbook %s dengan server "%s" Configuration has been changed locally and remotely = Konfigurasi telah dirubah lokal dan jauh Please merge manually to resolve conflict = Slahkan digabungkan manual untuk menyelesaikan konflik Logbook "%s" does not exist on remote server = Logbook "%s" tidak ada pada server remote Entry has been changed locally and remotely = Entri telah diganti lokal dan jauh Entry deleted locally = Entri lokal dihapus Changed local entry ID to %d = ID lokal entri dirubah ke %d Entry deleted remotely = Entri dihapus dari jauh All entries identical = Semua entri sama Invalid mirror_id or entry_date = mirror_id atau entry_date tidak benar Synchronize all logbooks = Sinkronisasi semua logbook Calendar = Kalender Remote entry received = Entri dari jauh diterima Pick a date = Pilih tanggal Please select at least one '%s' = Silahkan pilih paling tidak satu '%s' Please select a '%s' = Silahkan pilih '%s' Please enter month for attribute '%s' = Silahkan masukkan bulan untuk atribut '%s' Please enter day for attribute '%s' = Silahkan masukkan hari untuk atribut '%s' Please enter year for attribute '%s' = Silahkan masukkan tahun untuk atribut '%s' Please enter attribute '%s' = Silahkan masukkan atribut '%s' Entry time = Waktu entri Start = Mulai End = Selesai From = Dari After = Setelah to = untuk Before = Sebelum Previous Year = Tahun sebelumnya Next Year = Tahun berikutnya Please enter numeric value for '%s' = Silahkan isi nilai numerik untuk '%s' Error: Attribute %s must be numeric = Kesalahan: Atribut %s harus numerik 3 Months = 3 Bulan 6 Months = 6 Bulan Error sending local message = Kesalahan mengirim pesan lokal Error receiving message = Kesalahan menerima pesan Are you sure to delete this entry? = Apakah anda yakin menghapus entri ini? ELOG CSV import = Impor CSV ELOG Import = Impor CSV filename = Nama file CSV Field separator = Pemisah kolom Edit entry = Edit entri Delete entry = Hapus entri CSV Import = Impor CSV Derive attributes from CSV file = Menurunkan atribut dari CSV Comma = Koma Semicolon = Titik koma Preview import = Pratinjau impor CSV import preview of %s = Pratinjau impor CSV dari %s Too many attributes in CSV file = Terlalu banyak atribut di file CSV %d entries successfully imported = %d Entri sukses diimpor No 'Attributes' option present in %s = Tidak ada opsi 'Atribut'di %s CSV ("," separated) = CSV (dipisahkan ",") CSV (";" separated) = CSV (dipisahkan ";") Auto detect = Deteksi otomatis text = Teks Column header '%s' must be present in CSV file = Tajuk kolom '%s' harus ada di dalam file CSV Fill text body = Isi tubuh teks Please re-enter filename = Silahkan masukkan kembali nama file Last x = x terakhir Activate = Aktifkan Entry has been modified = Entri telah dimodifikasi No attachment file specified = Tidak ada lampiran yang ditentukan Delete this logbook = Hapus logbook ini Rename this logbook = Ubah nama logbook ini Create new logbook = Buat logbook baru Syntax error in config file = Kesalahan sintaks di file configurasi Email notification = Pemberitahuan email Are you sure to delete logbook "%s"? = Apakah anda yakin menghapus logbook "%s"? Logbook "%s" exists already, please choose different name = Logbook "%s" sudah ada, silahkan pilih nama lain Rename logbook = Ubah nama logbook Enter new logbook name = Masukkan nama logbook baru Logbook name = Nama logbook Use existing logbook as template = Gunakan logbook yang ada sebagai contoh none = tidak ada URL is redirected to: = URL diarahkan ke: Remote config should be received = Konfigurasi dari jauh seharusnya sudah diterima Local entry should be submitted = Entri lokal harus dimasukkan Remote entry should be received = Entri jauh seharusnya diterima Local entry = Entri lokal %s should be deleted = %s harus dihapus Entry should be deleted locally = Entri harus dihapus secara lokal Local entry ID should be changed to %d = ID entri lokal harus dirubah menjadi %d Remote entry = Entri jauh Entry should be deleted remotely = Entri harus dihapus dari jarak jauh Click here to delete %d entries = Klik disini untuk menghapus entri %d Click here to delete this entry = Klik disini untuk menghapus entri ini Maximum number of attribute options exceeded = Jumlah maksimum pilihan atribut telah terlewati Please increase MAX_N_LIST in elogd.c and recompile = Silahkan meningkatkan MAX_N_LIST di elogd.c dan kompilasi ulang You can "steal" the lock by editing this entry = Anda bisa "mencuri" kunci dengan mengedit entri ini Several logbooks groups are defined on this host = Beberapa grup logbook didefinisikan pada mesin ini Please select one to list the logbooks in that group = Silahkan pilih salah satu logbook dari daftar dalam kelompok No logbook defined on this server = Tidak ada logbook yang didefinisikan dalam server ini Goto ELOG home page = Menuju ke halaman utama ELOG Please enter "%s" = Silahkan masukkan "%s" Change config file = Ubah file konfigurasi Please check that it exists and elogd has write access and disk is not full = Silahkan dicek jika ada, mempunuyai hak akses dan disk tidak penuh Show = Tampilkan Hide = Sembunyikan Hide all = Sembunyikan semua Show all = Tampilkan semua This logbook requires authentication = Logbook ini memerlukan otentifikasi Attachment file "%s" empty or not found = File lampiran "%s" kosong atau tidak ditemukan Case sensitive = Dibedakan List = Daftar Date must be between 1970 and 2037 = Tanggal harus antara 1970 dan 2037 up = atas down = bawah stop = berhenti Entry = Entri name may not contain blanks = nama tidak boleh kosong regular expressions = Ekspresi umum Text fields are treated as %s = Area teks diperlakukan sebagai %s Subscribe to logbooks = Berlangganan ke logbook enable automatic email notifications = mengaktifkan pemberitahuan email otomatis Set all = Atur semua Set none = Tidak diatur Insert current time = Masukkan waktu sekarang Please enter hour for attribute '%s' = Silahkan masukkan jam untuk atribut '%s' Please enter minute for attribute '%s' = Silahkan masukkan menit untuk atribut '%s' Please enter second for attribute '%s' = Silahkan masukkan detik untuk atribut '%s' No admin user has been defined in configuration file = Tidak ada admin yang didefinisikan pada file konfigurasi Duplicate = Gandakan Cannot lookup server name = Tidak dapat mencari nama server Cannot connect to server = Tidak dapat terhubung ke server %s wrote = tertulis %s Quote = Kutipan Please enter numeric value for year of attribute '%s' = Masukkan bilangan untuk tahun pada atribut '%s' Preview = Pratinjau FONT = FONT SIZE = UKURAN COLOR = WARNA smiling = senyuman happy = senang winking = kedipan big grin = senyum lebar crying = menangis cool = keren frowning = mengernyit confused = bingung mad = marah pleased = puas tongue = lidah yawn = menguap Encoding = Pengkodean User "%s" not found in password file = Pengguna "%s" tidak ditemukan di dalam file sandi Cannot write to file %s = Tidak bisa menulis ke file %s Invalid user name "%s" or password for remote logbook = Pengguna "%s" tidak valid atau kata sandi salah dari logbook Hide attachments = Sembunyikan lampiran Show only new entries = Tampilkan hanya catatan baru Show all entries = Tampilkan semua catatan New entries since = Catatan baru sejak An old ELOG entry has been updated = Catatan ELOG lama telah terbarui A new ELOG entry has been submitted = Catatan ELOG baru telah dimasukkan Invalid user name or password = Nama pengguna atau sandi tidak valid Upload image = Unggah gambar Please enter filename or URL = Silahkan isikan nama file atau URL Maximum allowed file size is = Ukuran file maksimal yang diizinkan adalah Enter filename or URL = Masukkan nama file atau URL Image uploaded successfully = Gambar berhasil diunggah Image "%s" uploaded successfully = Gambar "%s" berhasil diunggah HelpELCode = HelpElCode Cannot retrieve file from URL "%s" = Tidak dapat mengambil file dari URL "%s" Enter name of hyperlink = Masukkan nama hiperlink Enter URL of hyperlink = Masukkan URL hiperlink astonished = heran Error in regular expression "%s" = Kesalahan dalam ekspresi reguler "%s" File not found at URL "%s" = File tidak ditemukan di URL "%s" Entry size too large for email notification = Ukuran terlalu besar untuk pemberitahuan email Please specify a valid email address = Silahkan tentukan alamat email yang valid Cannot send email notification to "%s" = Tidak dapat mengirimkan notifikasi email ke "%s" Error: Command "%s" is not allowed for user "%s" = Kesalahan: Perintah "%s" tidak dijinkan untuk pengguna "%s" Shell execution not enabled via -x flag = Eksekusi shell tidak diaktifkan menggunakan penanda -x Enter heading level (1, 2 or 3) = Masukkan tingkat judul (1, 2 atau 3) User name may not contain blanks = Nama pengguna tidak boleh ada kekosongan Empty password not allowed = Kata sandi kosong tidak diperbolehkan Password may not contain blanks = Kata sandi tidak boleh mengandung kekosongan Anonymous = Anonim Activation notice has been sent to %s = Pemberitahuan aktivasi telah dikirimkan ke %s Your request has been forwarded to the administrator = Permintaan anda telah diteruskan ke administrator You will be notified by email upon activation of your new account = Anda akan diberitahu melalui email pada saat aktivasi akun baru anda Select period = Pilih periode Last week = Pekan terakhir Last month = Bulan terakhir Last 3 Months = 3 Bulan terakhir Last 6 Months = 6 Bulan terakhir Last Year = Tahun lalu Enter %s = Masukkan %s Select %s = Pilih %s Bold text CTRL+B = Tebalkan teks CTRL+B Italics text CTRL+I = Miringkan teks CTRL+I Underlined text CTRL+U = Garisbawah teks CTRL+U Centered text = Tengahkan teks Insert hyperlink = Sisipkan hiperlink Insert email = Sisipkan email Insert image CTRL+M = Sisipkan gambar CTRL+M Insert quote = Sisipkan kutipan Insert list CTRL+L = Sisipkan daftar CTRL+L Insert table = Sisipkan tabel Insert heading CTRL+H = Sisipkan judul CTRL+H Insert code CTRL+O = Sisipkan kode CTRL+O Show the smiley bar = Tampilkan tempat smiley Insert current time/date = Sisipkan waktu/tanggal sekarang Entry is locked on local server and therefore skipped = Masukkan telah dikunci pada server lokal dan oleh karena itu dilewati Keep me logged in on this computer = Biarkan saya tetap masuk di komputer ini for the next %d minutes = untuk %d menit selanjutnya for the next hour = untuk jam selanjutnya for the next %d hours = untuk %d jam berikutnya for the next %d days = untuk %d hari berikutnya keep original values = Simpan nilai asli Entry is locked = Catatan terkunci Edit anyhow = Bagaimanapun ubah Insert horizontal line = Sisipkan garis mendatar Insert anchor point = Sisipkan titik dasar keep original text = simpan teks asli unspecified = tidak ditentukan To subscribe to any logbook, click on 'Config' in that logbook = Untuk berlangganan logbook apapun, klik pada 'Konfig' di dalam logbook tersebut ELOG XML import = impor XML ELOG Derive attributes from XML file = Turunkan atribut dari file XML XML filename = Nama file XML Invalid date format = Format tanggal tidak valid XML file does not contain %s element = File XML tidak mengandung elemen %s XML import preview of %s = Pratinjau impor XML dari %s XML Import = Impor XML A old entry has been updated on %s = Sebuah entri lama telah diperbarui pada %s First entry, Ctrl-Home = Entri pertama, Ctrl-Home Previous entry, Ctrl-PgUp = Entri sebelumnya, Ctrl-PgUp Next entry, Ctrl-PgDn = Entri selanjutnya, Ctrl-PgDn Last entry, Ctrl-End = Entri terakhir, Crl-End ELOG import = impor ELOG Please choose format to import: = Silahkan pilih format untuk impor: Enter text = Masukkan teks or until I log out = atau sampai saya keluar Make smaller = Diperkecil Original size = Ukuran asli Make larger = Diperbesar Rotate left = Putar kiri Rotate right = Putar kanan Delete attachment = Hapus lampiran Cannot create thumbnail, please check ImageMagick installation = Tidak bisa membuat thumbnail, silahkan cek instalasi ImageMagick Attachments = Lampiran Error: Content length (%d) larger than maximum content length (%d) = Kesalahan: Panjang konten (%d) melebihi panjang konten maksimum (%d) ELOG error = Kesalahan ELOG Only user %s can edit this entry = Hanya pengguna %s dapat mengubah entri ini Export to = Ekspor ke Do not ignore first line = Jangan abaikan baris pertama Last %d hours = %d jam terakhir Invalid URL = URL tidak valid Attribute "%s" is not allowed in config file = Atribut "%s" tidak diijinkan di file config Only user %s can delete this entry = Hanya pengguna %s dapat menghapus entri ini File system full, ELOG cannot continue to work = Sistem file penuh, ELOG tidak dapat bekerja 3 Days = 3 hari Last 3 Days = 3 Hari terakhir Really submit this entry? = Apakah benar-benar ingin memasukkan entri ini? Account activation for ELOG logbook "%s" = Aktivasi akun untuk ELOG di logbook "%s" Account activation for ELOG on host "%s" = Aktivasi akun untuk ELOG di host "%s" Please click the URL below to activate following ELOG account = Silahkan klik URL di bawah ini untuk aktivasi akun ELOG berikut Activation URL = URL aktivasi An email has been sent to <%s> = Email telah dikirimkan ke <%s> Use that email to activate your account = Gunakan email tersebut untuk mengaktifkan akun anda Registration request for ELOG logbook "%s" = Permintaan pendaftaran untuk ELOG di logbook "%s" Registration request for ELOG on host "%s" = Permintaan pendaftaran untuk ELOG di host "%s" Are you sure you want to deactivate your own account? = Apakah anda yakin ingin menonaktifkan akun anda sendiri? Active = Aktif Invalid activation code = Kode aktivasi tidak sah Error activating user = Kesalahan mengaktifkan pengguna A password recovery email for user "%s" has been sent to %s = Email pemulihan kata sandi untuk pengguna "%s" telah dikirim ke %s User "%s" has no access to this logbook = Pengguna "%s" tidak mempunyai akses ke logbook ini Your account has been activated = Akun anda telah diaktifkan Please subscribe to logbooks if you want to receive automatic email notifications = Silahkan berlangganan logbook jika anda ingin menerima notifikasi email otomatis This is an automatically generated account recovery email for host %s = Ini adalah email pemulihan kata sandi otomatis untuk host %s Please click on following link to recover your account = Silahkan klik tautan berikut untuk memulihkan akun anda Email address "%s" not registered = Alamat email "%s" belum didaftarkan User name "%s" not registered = Nama pengguna "%s" belum didaftarkan Error accessing password file = Kesalahan mengakses file kata sandi CSV (";" separated) + Text = CSV (dipisahkan ";") + Teks Entry can only be deleted %1.2lg hours after creation = Entri dapat dihapus %1.2lg jam setelah dibuat Drop attachments here... = Letakkan lampiran disini... Insert Timestamp = Sisipkan cap waktu Pending draft available = Rancangan tertunda ada %d pending drafts available = %d rancangan tertunda ada Create new entry = Membuat entri baru If you leave this page you will lose your unsaved changes = Jika anda meninggalkan halaman ini, anda akan kehilangan perubahan yang belum disimpan Edited = Teredit All time = Semua waktu Draft = Rancangan days = Hari Draft saved at = Rancangan disimpan pada You might however then overwrite each other's modifications = Anda mungkin saling menulis ulang modifikasi orang lain # #---- please translate following items and then remove this comment ----# # Draft entry created on %s by %s = Really delete this entry? = Restrict search to last = This is a draft message, edit and submit it to make it permanent = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/elcode_zh_CN-UTF8.html0000644000175000017500000002250012556103750017563 0ustar rogerroger ELCode

ELCode


ELCode һıǣڸʽһ ELOG ¼ HTML ﷨ƣ 򵥡ǰڷ [ ] ֮䣬 < >һЩ ڸııʽ塢СɫһЩǶ URL ͼƬ ELCode Ǻ BBCode ƣʱҲΪ vB code

ıʽ


ELCode ڻıʽ

[b]Hello[/b] ɴı Hello
[u]Hello[/u] ɴ»ߵı Hello
[i]Hello[/i] бı Hello
[color=red]Hello[/color]
[color=#FF0000]Hello[/color]
ı Hello ָ֪ɫƣ white, red, green, lightgreen һʮƵ RGB ֵͷλ (00-FF) ʾɫ֣ڶֱʾɫֱʾɫ
[size=5]Hello[/size] ı Hello ıĴСرʾΧ 1 29 һЩӣ size=1, size=2, size=3, size=4, size=5, size=6.
[font=comic]Hello[/font] ı塣һЩʾ comic, courier, arial, tahoma, times, verdana
עЩʾûʲô仯ӢĶԡ
[center]Hello[/center]
ʹıĶ롣

ǿǶף

[size=5][color=red][b]Hello[/b][/color][/size]

Hello ע⣬ǵǶױ밴ȷ˳Уд

[b][color=red]Ǵģ[/b][/color]

ǴģΪ [b] ȿʼģҲӦ [/color] ʶ֮󣬱

[b][color=red]ȷ[/color][/b]

رַܱ


ʱ㲢 ELCode DZ̳ͣһЩǵʾʱ ΪЩǣҪڱǰϷб "\" \[b] [b]


Цü̵ĴһЩͼ󣬱 :) ʾˡ elog ﱻԶתСͼ󣬱



:) :( ;)
:)) :D ?)
;( :] :O
8)8o X(
:P


÷ʽοĺͲοģ

  • [quote]ԭʼı[/quote] ʽһЩıһ οã磺

    Quote:
    ԭʼı

  • [quote="Mr. Bean"]ԭʼı[/quote] ʽһ Щıɴοã磺

    Mr. Bean wrote:
    ԭʼı

    עʹòοñҪñʶο "Mr. Bean"

̶ı


Ϊʹ Courier ̶ȵı

[code]Hello[/code]

ܻڼбߺƱԷĶʱã

Item                Price     Availability
====                =====     ============

Apples               0.50     1000
Grapefruits          2.50     100
Sun-dried Tomatos   12.50     20

versus

Item Price Availability
==== ===== ============

Apples 0.50 1000
Grapefruits 2.50 100
Sun-dried Tomatos 12.50 20

Lists


һûбʹñ [list][/list]

[list]
[*]Ocean
[*]Forest
[*]Desert
[/list]

  • Ocean
  • Forest
  • Desert

бУָÿĿǰʹʲôʶΪɴֵб ʹ [list=1][/list]:

[list=1]
[*]Ocean
[*]Forest
[*]Desert
[/list]

  1. Ocean
  2. Forest
  3. Desert

Ϊɰĸбʹ [list=a][/list]

[list=a]
[*]Ocean
[*]Forest
[*]Desert
[/list]

to produce

  1. Ocean
  2. Forest
  3. Desert

õѡ [list=A] ʹôдĸ[list=I] ʹ ĸ֡


ӻͳһʽԴַ (URL) öַʽɣ

  • κ﷨ȷ URL ԶתӣҪϿͷ http:// midas.psi.ch/elog midas.psi.ch/elog http://www.cern.ch http://www.cern.ch.

  • Ϊʽָһ URL [url]www.cern.ch[/url]

  • ҪһЩͬ URL ıָ [url=http://www.cern.ch]Text[/url]

    [url=http://www.cern.ch]CERN[/url]

    ɣ

    CERN

  • ʼĵַơǿ [email]john.doe@domain.org[/email] д߼򵥵 john.dow@domain.org ת john.doe@domain.org

Ƕͼ


ַʽıǶͼ

  • ͼñ [img]URL[/img] Ƕ뵽ıУ˴ URL ָһϿõͼע⣬Щͨ Web ɷʵͼ ԡûһ Web 㱾Ӳϵͼ󲻿Դⲿʡ Ƕ elog ͼ꣺

    [img]http://midas.psi.ch/elogs/elog.png[/img]

  • һͼΪ elog ¼ĸķʽ

    [img]elog:/1[/img]

    ˴ elog:/1 ʾһelog:/2ʾڶԴơ ַʽοĸͼǾͲڼ¼Ľβˡ

ELOG ҳ
elog-3.1.3-1/resources/eloghelp_polish.html0000644000175000017500000000421212757305354017707 0ustar rogerroger Elektroniczna pomoc programu Electronic Logbook (ELOG)
Elektroniczna pomoc programu Electronic Logbook (ELOG)

Electronic Logbook (ELog) może być używany do składowania i pobierania wiadomości przez interfejs webowy. W zależności od konfiguracji, ELog może przechowywać jeden lub więcej dziennik (zwany logbooks). Każdy z nich jest przechowywany w osobnej sekcji na serwerze.

Szybki przegląd

Domyślnie, strona zawierająca ostatnie wpisy z lobgooka jest wyświetlana. Kliknięcie na wpis powoduje wyświetlenie jego zawartości. One can use the browser buttons to display the first, previous, next and last message.

Przycisk Nowy tworzy nowy wpis. Przyciskiem Edycja można edytować istniejący wpis, o ile jest to dozwolone w pliku konfiguracyjnym. Przycisk Odpowiedz tworzy odpowiedź do istniejącego już wpisu, podobnie jak ma to miejsce w przypadku wiadomości email.

Przycisk Szukaj otwiera stronę wyszukiwania, gdzie wpisy z logbooka mogą być filtrowane. Każde niepuste pole działa jak dodatkowy filtr, który jest łączony z innymi regułami. Jeżeli filtr nie zosatnie wybrany, wszystkie wpisy z logbooka zostaną wyświetlone.

Więcej informacji

Po więcej informacji, zwłaszcza dotyczących konfiguracji, udaj się na domową stronę projektu ELOG.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.brazilian0000755000175000017500000005265613076344200017337 0ustar rogerroger# # Brazilian Portuguese translation by Carlos Augusto Marcicano # Updated by Elaine Franchini dos Anjos # A more recent version may be available at : # http://savannah.psi.ch/viewcvs/trunk/eloglang.brazilian?root=elog& # New = Novo Edit = Editar Delete = Apagar Reply = Responder Find = Procurar Last day = ltimo dia Last 10 = ltimos 10 Config = Configurar Logout = Sair Help = Ajuda Back = Voltar Submit = Enviar First = Primeiro Last = ltimo Previous = Anterior Next = Prximo Copy to = Copiar para Move to = Mover para Save = Salvar Cancel = Cancelar Wrong password = Senha incorreta Please login = Por favor, identifique-se Username = Usurio Password = Senha ELOG change password = Mudar a senha do ELOG Several logbooks are defined on this host = H diversos logbooks definidos neste servidor Please select the one to connect to = Por favor, escolha a qual deles voc deseja conectar-se Change password for user = Mudar a senha do usurio Logged in as = Conectado como Entry date = Data da mensagem with = com This is the last entry = Esta a ltima mensagem This is the first entry = Esta primeira mensagem Email sent to = E-mail enviado para Email notification suppressed = Foi omitida a notificao por E-mail please select = por favor, escolha Suppress Email notification = Omitir a notificao por E-mail Resubmit as new entry = Reenviar como uma nova mensagem Attachment = Anexo Reply to this = Responder In reply to = Em resposta a Fields marked with = Campos marcados com are required = so obrigatrios Please check that it exists and elogd has write access = Por favor, verifique se ele existe e se elogd tem acesso para escrita Error: Attribute %s not supplied = Erro: No foi preenchido o atributo %s Please go back and enter the %s field = Retorne e informe o campo %s Please use your browser's back button to go back = Por favor, use o boto Voltar em seu navegador para retornar ELOG find = ELOG procurar Find = Procurar Search = Iniciar Procura Reset Form = Limpar formulrio Summary only = Apenas resumo Show attachments = Mostrar anexo Printable output = Sada para impresso Sort in reverse order = Ordenar em ordem reversa Start date = Data inicial End date = Data final Year = Ano Text = Texto Search all logbooks = Buscar em todos os logbooks Last %d days = ltimos %d dias Last %d entries = ltimas %d mensagems No entries found = No foi encontrada nenhuma mensagem A new entry has been submitted on %s = Uma nova mensagem foi enviada em %s Logbook = Logbook Yes = Sim No = No Error deleting message: status = Erro ao apagar a mensagem: status Error: Command "%s" not allowed = Erro: Comando "%s" no permitido Cannot open file %s = No foi possvel abrir o arquivo %s Cannot write to %s = No foi possvel gravar em %s No SMTP host defined in [global] section of configuration file = No foi definido um servidor SMTP na seo [global] do arquivo de configurao Display threads = Exibir threads Logbook is empty = O logbook est vazio Download = Download and all its replies = e todas suas respostas Message ID = Message ID Not logged in = No est logado New passwords do not match, please retype = Nova senha no confere, por favor digite novamente Old password = Senha anterior Retype new password = Digite novamente a nova senha Maximum number of replies (%d) exceeded = Nmero mximo de repostas (%d) excedido Update = Atualizar Add %s = Adicionar %s Add new option here = Adicionar nova opo aqui Suppress shell execution = Omitir execuo de shell Maximum number of attachments reached = Nmero mximo de anexos foi atingido Upload = Upload Mode = Estilo Options = Opes entries per page = entradas por pgina Filters = Filtros Show last = Exibe ltimos Day = Dia Week = Semana Month = Ms Search text also in attributes = Procura o texto tambm nos atributos Admin = Admin Change %s = Modificar %s Login name = Nome de Login exists already = j existe Your ELOG account has been activated = Sua conta do ELOG foi ativada Your ELOG account has been activated on host = Sua conta do ELOG foi ativada no host You can access it at = Voce pode acessa-lo em A new ELOG user wants to register on "%s" = Um novo usurio do ELOG deseja registrar-se em "%s" User "%s" registered on logbook "%s" = Usurio "%s" registrado no logbook "%s" User "%s" registered on host "%s" = Usurio "%s" registrado no host "%s" A new ELOG user has been registered on %s = Um novo usurio do ELOG foi registrado em %s Host = Host Full name = Nome Completo Hit following URL to activate that account = Acesse a URL para ativar a conta Requested = Requerido ELOG user config = Configurao do usurio do ELOG Select user = Selecione o usurio User [%s] has been deleted = Usurio [%s] foi removido Remove user = Remover usurio New user = Novo usurio No Email address registered with user name "%s" = No h endereo de email registrado com esse nome de usurio "%s" Password recovery for ELOG %s = Recuperao de senha para ELOG %s ELOG password recovery = Recuperao de senha do ELOG Error sending Email via "%s" = Erro no envio de email via "%s" Enter your user name or email address = Entre com o nome do usurio ou com endereo de email Forgot = Esquecido ELOG new user = Novo usurio do ELOG Retype password = Digite novamente a senha Are you sure to delete these messages? = Voce tem certeza de que deseja apagar essa mensagem ? and all their replies = e todas suas repostas Synchronization = Sincronizao Entry is currently edited by = Essa entrada est sendo editada por Date = Data Threaded = Threaded Collapse = Collapse Expand = Expandir All entries = Todas entradas Goto page = Vai para pagina Selected entries = Entradas selecionadas Toggle all = Selecionar todos Error: start date after end date = Erro: data inicial maior que data final all entries = todas entradas Page %d of %d = Pgina %d de %d recipients = receptores Error: Value %s not allowed for boolean attributes = Error: Valor %s<;b> no permitido para atributos booleanos Error: Attribute option %s not existing = Error: Opo de atributo %s no existe Forgot password? = Esqueceu a senha? Register as new user = Registrar como novo usurio Entries = Entradas Last submission = ltimo envio Expand all = Expandir todas ELOG registration = Registro no ELOG Synchronize = Sincronizar No mirror server defined in configuration file = No h servidor mirror definido no arquivo de configurao New password = Nova senha user = usurio on = em Entry can only be edited %1.2lg hours after creation = Entrada pode ser editada somente %1.2lg horas aps a criao Display full entries = Exibir entradas completas Summary = Resumo Display = Exibir Email = Email Change password = Modificar a senha No entry selected for deletion = No h entrada selecionada para remover Cannot read entry from local logbook = No possvel ler a entrada do logbook local Cannot connect to host %s, port %d = No possvel conectar no host %s, port %d Cannot receive "%s" = No possvel receber "%s" Received wrong entry id "%d" = Recebido ID errado da entrada "%d" Entry #%d is locked on remote server = Entrada #%d est bloqueada no servidor remoto Cannot save remote entry locally = No possvel salvar entrada remota localmente local = local remote = remoto Please delete %s or %s entry to resolve conflict = Por favor remova %s ou %s entrada para resolver o conflito Error sending local entry = Erro ao enviar a entrada local Local entry submitted = Entrada local enviada Error deleting remote entry = Erro ao remover a entrada remota ID = ID Full = Completo All = Todos Select = Selecionar New entry cannot be written to directory "%s" = Nova entrada no pode ser gravada no diretrio "%s" Entry %s cannot be read from logbook "%s" = A entrada %s no pode ser lida do logbook "%s" This entry has been deleted = Essa entrada foi removida Login = Login by = por Cannot connect to remote server "%s" = No possvel conectar no server remoto "%s" Remote server is not an ELOG server = Servidor remoto no um servidor de ELOG Incorrect remote ELOG server version %s = Verso incorreta do servidor remoto de ELOG Error accessing remote logbook = Erro ao acessar o logbook remoto Invalid HTTP header = Cabealho de HTTP invlido No user name supplied to access remote logbook = Nome de usurio no fornecido para acessar o logbook remoto Synchronizing logbook %s with server "%s" = Sincronizando logbook %s com o servidor "%s" Configuration has been changed locally and remotely = A configurao foi alterada localmente e remotamente Please merge manually to resolve conflict = Por favor, unir manualmente para resolver o conflito Logbook "%s" does not exist on remote server = Logbook "%s" no existe no servidor remoto Entry has been changed locally and remotely = A entrada foi modificada localmente e remotamente Entry deleted locally = Entrada removida localmente Changed local entry ID to %d = Alterado o ID da entrada local para %d Entry deleted remotely = Entrada removida remotamente All entries identical = Todas as entradas so idnticas Invalid mirror_id or entry_date = Invlidos mirror_id ou entry_date Synchronize all logbooks = Sincroniza todos os logbooks Calendar = Calendrio Remote entry received = Recebida entrada remota Pick a date = Escolha uma data Please select at least one '%s' = Por favor, selecione ao menos um '%s' Please select a '%s' = Por favor, selecione um '%s' Please enter month for attribute '%s' = Por favor, entre com o ms para o atributo '%s' Please enter day for attribute '%s' = Por favor, entre com o dia para o atributo '%s' Please enter year for attribute '%s' = Por favor, entre com o ano para o atributo '%s' Please enter attribute '%s' = Por favor, entre com o atributo '%s' Entry time = Data da entrada Start = Incio End = Fim From = De After = Depois to = para Before = Antes Previous Year = Ano anterior Next Year = Prximo Ano Please enter numeric value for '%s' = Por favor, entre com um valor numrico para '%s' Error: Attribute %s must be numeric = Erro : Atributo %s deve ser numrico 3 Months = 3 Meses 6 Months = 6 Meses Error sending local message = Erro ao enviar mensagem local Error receiving message = Erro ao receber mensagem Are you sure to delete this entry? = Voce est certo de que deseja apagar essa entrada? Import = Importar CSV filename = Nome do arquivo CSV Field separator = Delimitador de campo Edit entry = Editar entrada Delete entry = Apagar entrada CSV Import = Importar CSV ELOG CSV import = Importe de CSV do ELOG Derive attributes from CSV file = Derivar atributos do arquivo de CSV Comma = Vrgula Semicolon = Ponto e vrgula Preview import = Preview import Too many attributes in CSV file = Muitos atributos no arquivo de CSV %d entries successfully imported = %d entradas importadas com sucesso No 'Attributes' option present in %s = No h opo de atributos em %s CSV ("," separated) = CSV (separado por ",") CSV (";" separated) = CSV (separado por ";") Auto detect = Detectar automticamente CSV import preview of %s = Preview do importe de CSV de %s text = texto Column header '%s' must be present in CSV file = Cabealho da coluna '%s' deve estar presente no arquivo de CSV Fill text body = Preencher o corpo do texto Please re-enter filename = Por favor, digite novamente o nome do arquivo Last x = ltimo x Activate = Ativar Entry has been modified = A entrada foi modificada No attachment file specified = No foi especificado um arquivo anexo Delete this logbook = Apagar esse logbook Rename this logbook = Renomear esse logbook Create new logbook = Criar novo logbook Syntax error in config file = Erro de sintaxe no arquivo de configurao Email notification = Notificao via Email Are you sure to delete logbook "%s"? = Voc est certo de que deseja apagar o logbook "%s"? Logbook "%s" exists already, please choose different name = O logbook "%s" j existe, por favor escolha um nome diferente Rename logbook = Renomear logbook Enter new logbook name = Digite o nome do novo logbook Logbook name = Nome do logbook Use existing logbook as template = Utilizar um modelo de logbook existente none = nenhum URL is redirected to: = URL est redirecionada para: Remote config should be received = Configurao remota deveria ser recebida Local entry should be submitted = Entrada local deveria ser enviada Remote entry should be received = Entrada remota deveria ser recebida Local entry = Entrada local %s should be deleted = %s deveria ser removida Entry should be deleted locally = Entrada deveria ser removida localmente Local entry ID should be changed to %d = ID da entrada local deveria ser modificado para %d Remote entry = Entrada remota Entry should be deleted remotely = Entrada deveria ser removida remotamente Click here to delete this entry = Clique aqui pra apagar essa entrada Maximum number of attribute options exceeded = Excedido nmero mximo de opes de atributos Please increase MAX_N_LIST in elogd.c and recompile = Por favor, aumente o MAX_N_LIST no elogd.c e recompile You can "steal" the lock by editing this entry = Voc pode "roubar" esse bloqueio editando essa entrada Several logbooks groups are defined on this host = Esto definidos muitos grupos de logbooks neste host Please select one to list the logbooks in that group = Por favor, selecione um para listar os logbooks neste grupo No logbook defined on this server = No h logbook definido neste servidor Goto ELOG home page = Vai para a home page do ELOG Please enter "%s" = Por favor, digite "%s" Change config file = Modificar arquivo de configurao Click here to delete %d entries = Clique aqui para apagar %d entradas Please check that it exists and elogd has write access and disk is not full = Por favor, verifique se isto existe e elogd tem acesso de escrita e o disco no est cheio Show = Exibir Hide = Ocultar Hide all = Ocultar todos Show all = Exibir todos This logbook requires authentication = Esse logbook requer autenticao Attachment file "%s" empty or not found = Arquivo anexado "%s" vazio ou no encontrado Case sensitive = Diferenciar masculas de mnusculas List = Lista Date must be between 1970 and 2037 = A data deve estar entre 1970 e 2037 up = cima down = baixo stop = parar Entry = Entrada name may not contain blanks = nome no deve conter espaos em branco regular expressions = expresses regulares Text fields are treated as %s = Campos de texto so tratados como %s Subscribe to logbooks = Inscrever-se nos logbooks enable automatic email notifications = permite notificaes automticas por email Set all = Selecionar todos Set none = No selecionar nenhum Please enter hour for attribute '%s' = Por favor, entre com a hora para o atributo '%s' Please enter minute for attribute '%s' = Por favor, entre com o minuto para o atributo '%s' Please enter second for attribute '%s' = Por favor, entre com o segundo par ao atributo '%s' No admin user has been defined in configuration file = No foi definido um usurio "admin" no arquivo de configurao Duplicate = Duplicar Cannot lookup server name = No possvel buscar o nome do servidor Cannot connect to server = No possvel conectar no servidor %s wrote = %s escreveu Quote = Citao Insert current time = Inserir a data corrente Please enter numeric value for year of attribute '%s' = Por favor, entre com um valor numrico para o ano do atributo '%s' Preview = Preview FONT = FONTE SIZE = TAMANHO COLOR = COR smiling = sorrindo happy = alegre winking = piscando big grin = grande riso crying = chorando cool = legal/maneiro frowning = carrancudo confused = confuso mad = maluco pleased = grato tongue = mostrando a lingua yawn = com sono Encoding = Codificao User "%s" not found in password file = Usurio "%s" no foi encontrado no arquivo de senhas Cannot write to file %s = No possvel escrever no arquivo %s Invalid user name "%s" or password for remote logbook = Nome ou senha invlidos do usurio "%s" para o logbook remoto Hide attachments = Esconder os anexos Show only new entries = Exibir apenas as novas entradas Show all entries = Exibir todas as entradas New entries since = Novas entradas desde An old ELOG entry has been updated = Uma antiga entrada do ELOG foi atualizada A new ELOG entry has been submitted = Uma nova entrada do ELOG foi enviada Invalid user name or password = Nome ou senha invlidos Upload image = Enviar imagem Please enter filename or URL = Por favor, entre com o nome do arquivo ou com a URL Maximum allowed file size is = Tamanho mximo permitido do arquivo Enter filename or URL = Entre com o nome do arquivo ou a URL Image uploaded successfully = Imagem carregada com sucesso Image "%s" uploaded successfully = Imagem "%s" carregada com sucesso HelpELCode = HelpELCode Cannot retrieve file from URL "%s" = No possvel restaurar o arquivo da URL "%s" Enter name of hyperlink = Entre com o nome do hyperlink Enter URL of hyperlink = Entre com a URL do hyperlink astonished = surpreso Error in regular expression "%s" = Erro na expresso regular "%s" File not found at URL "%s" = Arquivo no encontrado na URL "%s" Copy to = Copiar para Move to = Mover para Select = Selecionar Entry size too large for email notification = O tamanho da entrada muito grande para notificao via email Please specify a valid email address = Por favor, especifique um endereo vlido de email Cannot send email notification to "%s" = No possvel enviar a notificao para "%s" Error: Command "%s" is not allowed for user "%s" = Erro: O Comando "%s" no permitido para o usurio "%s" Shell execution not enabled via -x flag = Execuo de shell no permitida via flag -x Enter heading level (1, 2 or 3) = Entre com o nvel de ttulo (1, 2 ou 3) User name may not contain blanks = O nome do usurio no pode conter brancos Empty password not allowed = No permitida senha vazia Password may not contain blanks = A senha no pode conter brancos Anonymous = Annimo # #---- please translate following items and then remove this comment ----# # Activation notice has been sent to %s = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.german0000644000175000017500000006135213076344200016623 0ustar rogerroger# # German translation by stefan.ritt@psi.ch # A more recent version may be available at: # https://bitbucket.org/ritt/elog # # Use following command to generate the UTF-8 file: # $ iconv -f iso-8859-1 -t UTF-8 eloglang.german >eloglang.german_UTF8 # New = Neu Edit = ndern Delete = Lschen Reply = Antworten Find = Finden Last day = Letzter Tag Last 10 = Letzte 10 Config = Konfigurieren Change password = Kennwort ndern Logout = Abmelden Help = Hilfe Back = Zurck Submit = Absenden First = Erster Last = Letzter Previous = Vorheriger Next = Nchster Copy to = Kopieren nach Move to = Verschieben nach Save = Speichern Cancel = Abbrechen Wrong password = Falsches Kennwort Please login = Bitte Anmelden Username = Benutzername Password = Kennwort Old password = Altes Kennwort New password = Neues Kennwort ELOG change password = ELOG Kennwort ndern Several logbooks are defined on this host = Mehrere Logbcher sind auf diesem Computer eingerichtet Please select the one to connect to = Bitte whlen Sie eines aus Change password for user = Kennwort ndern fr Benutzer Logged in as = Angemeldet als Entry date = Eingabedatum with = mit This is the last entry = Dies ist der letzte Eintrag This is the first entry = Dies ist der erste Eintrag Email sent to = Email gesendet an Email notification suppressed = Email-Benachrichtigung unterdrckt please select = bitte auswhlen Suppress Email notification = Email-Benachrichtigung unterdrcken Resubmit as new entry = Als neuen Eintrag absenden Attachment = Anhang Reply to this = Antwort hierzu In reply to = Als Antwort auf Fields marked with = Felder markiert mit are required = sind Pflichteintrge Please check that it exists and elogd has write access = Bitte berprfen, ob es existiert und ob elogd Schreibzugriff hat Error: Attribute %s not supplied = Fehler: Attribut %s nicht ausgefllt Please go back and enter the %s field = Bitte zurckgehen und das Feld %s ausfllen Please use your browser's back button to go back = Bitte die Zurck-Taste Ihres Browsers bettigen ELOG find = ELOG suchen Find = Finden Search = Suchen Reset Form = Eingabe zurcksetzen Summary only = Nur Zusammenfassung Show attachments = Anhnge anzeigen Printable output = Druckfhige Anzeige Sort in reverse order = Sortieren in umgekehrter Reihenfolge Start date = Anfangsdatum End date = Enddatum Year = Jahr Text = Text Search all logbooks = Alle Logbcher durchsuchen Last %d days = Letzte %d Tage Last %d entries = Letzte %d Eintrge No entries found = Keine Eintrge gefunden A new entry has been submitted on %s = Ein neuer Eintrag wurde von %s abgeschickt Logbook = Logbuch Yes = Ja No = Nein Error deleting message: status = Fehler beim Lschen: Status Error: Command "%s" not allowed = Fehler: Befehl "%s" nicht erlaubt Cannot open file %s = Datei %s kann nicht geffnet werden Cannot write to %s = Datei %s kann nicht geschrieben werden No SMTP host defined in [global] section of configuration file = Kein SMTP Host im [global] Bereich der Konfigurationsdatei definiert Display threads = Antwortkette darstellen Logbook is empty = Logbuch ist leer Download = Herunterladen and all its replies = und alle Antworten Message ID = Eintrag-ID Not logged in = Nicht eingeloggt Login = Anmelden Page %d of %d = Seite %d von %d all entries = alle Eintrge Login name = Name zum Anmelden Full name = Benutzername ELOG user config = ELOG Benutzerkonfiguration ELOG new user = Neuer ELOG-Benutzer Admin = Verwaltung Select user = Benutzer auswhlen Remove user = Benutzer entfernen New user = Neuen Benutzer anlegen Retype new password = Neues Kennwort nochmals eingeben New passwords do not match, please retype = Kennwrter verschieden, bitte nochmals eingeben Retype password = Kennwort erneut eingeben exists already = existiert bereits Register as new user = Als neuer Benutzer anmelden A new ELOG user has been registered on %s = Ein neuer Benutzer hat sich auf %s angemeldet Email = Email User "%s" registered on logbook "%s" = Benutzer "%s" im Logbuch "%s" registriert User [%s] has been deleted = Benutzer [%s] wurde gelscht Hit following URL to activate that account = Auf folgende URL klicken um Benutzerkonto zu aktivieren ELOG registration = ELOG Registrierung Your ELOG account has been activated on host = Ihr ELOG Konto wurde aktiviert auf You can access it at = Der Zugriff erfolgt ber Are you sure to delete these messages? = Diese Eintrge wirklich lschen? Select = Auswhlen All entries = Alle Eintrge Day = Tag Week = Woche Month = Monat Show last = Zeige letzte(n) Goto page = Gehe zu Seite All = Alle Display = Zeige entries per page = Eintrge pro Seite Toggle all = Alle umschalten Selected entries = Ausgewhlte Eintrge Collapse = eingeklappt Expand = erweitert Full = ausfhrlich Summary = zusammengefasst Threaded = mit Antwortketten Filters = Filter Mode = Modus Options = Optionen Error: start date after end date = Fehler: Anfangsdatum nach Enddatum and all their replies = und alle Antworten Upload = Hochladen Requested = Erfordert A new ELOG user wants to register on "%s" = Ein neuer Benutzer mchte sich auf "%s" registrieren Search text also in attributes = Text auch in Attributen suchen Date = Datum ID = ID Forgot password? = Kennwort vergessen? ELOG password recovery = ELOG Kennwort-Wiederherstellung No Email address registered with user name "%s" = Fr Benutzername "%s" wurde keine Email-Adresse registriert Error sending Email via "%s" = Fehler beim Versenden der Email ber "%s" Forgot = vergessen User "%s" registered on host "%s" = Benutzer "%s" auf Computer "%s" registriert Enter your user name or email address = Bitte Benutzernamen oder Email-Adresse eingeben Password recovery for ELOG %s = Kennwort-Wiederherstellung fr ELOG %s Host = Computer Your ELOG account has been activated = Ihr ELOG Konto wurde aktiviert Maximum number of attachments reached = Maximale Anzahl von Anhngen erreicht on = auf Entry is currently edited by = Eintrag wird momentan bearbeitet von recipients = Empfnger Suppress shell execution = Kommando-Ausfhrung unterdrcken Update = Aktualisieren by = von Change %s = %s ndern Add new option here = Neue Option hier eingeben Expand all = Alles erweitern Add %s = %s hinzufgen Synchronization = Synchronisation Error: Value %s not allowed for boolean attributes = Fehler: Wert %s ist nicht erlaubt fr bool'sche Attribute Error: Attribute option %s not existing = Fehler: Attribut Option %s existiert nicht Last submission = Letzter Eintrag Synchronize = Synchronisieren No mirror server defined in configuration file = Kein Spiegel-Server in Konfigurationsdatei definiert user = Benutzer Maximum number of replies (%d) exceeded = Maximale Anzahl von Antworten (%d) berschritten Entry can only be edited %1.2lg hours after creation = Eintrag kann nur %1.2lg Stunden nach Erstellung bearbeitet werden Display full entries = Gesamte Eintrge darstellen No entry selected for deletion = Kein Eintrag zum Lschen ausgewhlt Cannot read entry from local logbook = Kann Eintrag nicht aus lokalem Logbuch lesen Cannot connect to host %s, port %d = Kann zum Computer %s, Port %d nicht verbinden Cannot receive "%s" = Kann "%s" nicht empfangen Received wrong entry id "%d" = Falscher Eintrag mit ID "%d" empfangen Entry #%d is locked on remote server = Eintrag #%d ist auf anderem Computer in Bearbeitung Cannot save remote entry locally = Kann fremden Eintrag nicht lokal speichern local = lokalen remote = fremden Please delete %s or %s entry to resolve conflict = Bitte %s oder %s Eintrag lschen um Konflikt zu lsen Error sending local entry = Fehler beim Senden von lokalem Eintrag Local entry submitted = Lokaler Eintrag gesendet Error deleting remote entry = Fehler beim Lschen von fremdem Eintrag New entry cannot be written to directory "%s" = Neuer Eintrag kann nicht in Verzeichnis "%s" geschrieben werden Entry %s cannot be read from logbook "%s" = Eintrag %s kann nicht aus Logbuch "%s" gelesen werden This entry has been deleted = Dieser Eintrag wurde gelscht Entries = Eintrge Cannot connect to remote server "%s" = Verbindung mit entferntem Server "%s" kann nicht hergestellt werden Remote server is not an ELOG server = Entfernter Server ist kein ELOG Server Incorrect remote ELOG server version %s = Falsche Version %s des entfernten Servers Error accessing remote logbook = Fehler beim Zugriff auf entferntes Logbuch Invalid HTTP header = Falscher HTTP Kopf No user name supplied to access remote logbook = Kein Benutzername definiert zum Zugriff auf entferntes Logbuch Synchronizing logbook %s with server "%s" = Synchronisiere Logbuch %s mit Server "%s" Configuration has been changed locally and remotely = Konfiguration wurde lokal und entfernt gendert Please merge manually to resolve conflict = Bitte manuell zusammenfgen um Konflikt aufzulsen Logbook "%s" does not exist on remote server = Logbuch "%s" existiert nicht auf entferntem Server Entry has been changed locally and remotely = Eintrag ist lokal und entfernt gendert worden Entry deleted locally = Eintrag lokal entfernt Changed local entry ID to %d = Lokale ID des Eintrages auf %d gendert Entry deleted remotely = Entfernter Eintrag gelscht All entries identical = Alle Eintrge identisch Invalid mirror_id or entry_date = Falsche mirror_id oder entry_date Synchronize all logbooks = Alle Logbcher synchronisieren Calendar = Kalender Remote entry received = Entfernter Eintrag empfangen Pick a date = Datum auswhlen Please select at least one '%s' = Bitte mindestens ein '%s' auswhlen Please select a '%s' = Bitte ein '%s' auswhlen Please enter month for attribute '%s' = Bitte Monat fr Attribut '%s' eingeben Please enter day for attribute '%s' = Bitte Tag fr Attribut '%s' eingeben Please enter year for attribute '%s' = Bitte Jahr fr Attribut '%s' eingeben Please enter attribute '%s' = Bitte Attribut '%s' eingeben Entry time = Eingabezeit Start = Start End = Ende From = Von After = Nach to = bis Before = vor Previous Year = Voriges Jahr Next Year = Nchstes Jahr Please enter numeric value for '%s' = Bitte numerischen Wert fr '%s' eingeben Error: Attribute %s must be numeric = Fehler: Attribut %s mu numerisch sein 3 Months = 3 Monate 6 Months = 6 Monate Error sending local message = Fehler beim Versenden von lokalem Eintrag Error receiving message = Fehler beim Empfangen von Eintrag Are you sure to delete this entry? = Diesen Eintrag wirklich lschen? ELOG CSV import = ELOG CSV Import Import = Import CSV filename = CSV Dateiname Field separator = Trennzeichen Edit entry = Eintrag ndern Delete entry = Eintrag lschen CSV Import = CSV Import Derive attributes from CSV file = Attribute aus CSV Datei bernehmen Comma = Komma Semicolon = Semikolon Preview import = Import berprfen CSV import preview of %s = CSV Import berprfung von %s Too many attributes in CSV file = Zu viele Attribute in CSV Datei %d entries successfully imported = %d Eintrge erfolgreich importiert No 'Attributes' option present in %s = Keine 'Attributes'-Options in %s vorhanden CSV ("," separated) = CSV (mit "," getrennt) CSV (";" separated) = CSV (mit ";" getrennt) Auto detect = Automatisch erkennen text = Text Column header '%s' must be present in CSV file = Spaltenberschrift '%s' mu in der CSV Datei vorhanden sein Fill text body = Haupttext importieren Please re-enter filename = Bitte Dateinamen erneut eingeben Last x = Letzte x Activate = Aktivieren Entry has been modified = Eintrag wurde verndert No attachment file specified = Es wurde kein Anhang angegeben Delete this logbook = Dieses Logbuch lschen Rename this logbook = Dieses Logbuch umbenennen Create new logbook = Neues Logbuch anlegen Syntax error in config file = Syntax-Fehler in Konfigurationsdatei Email notification = Email Benachrichtigung Are you sure to delete logbook "%s"? = Logbuch "%s" wirklich lschen? Logbook "%s" exists already, please choose different name = Logbuch "%s" existiert bereits, bitte anderen Namen whlen Rename logbook = Logbuch umbenennen Enter new logbook name = Bitte neuen Logbuchnamen eingeben Logbook name = Logbuchname Use existing logbook as template = Vorhandenes Logbuch als Vorlage benutzen none = keines URL is redirected to: = URL wird umgeleitet zu: Remote config should be received = Fremde Konfiguration sollte empfangen werden Local entry should be submitted = Lokaler Eintrag sollte gesendet werden Remote entry should be received = Fremder Eintrag sollte empfangen werden Local entry = Lokaler Eintrag %s should be deleted = %s sollte gelscht werden Entry should be deleted locally = Eintrag sollte lokal gelscht werden Local entry ID should be changed to %d = ID von lokalem Eintrag sollte zu %d gendert werden Remote entry = Fremder Eintrag Entry should be deleted remotely = Fremder Eintrag sollte gelscht werden Click here to delete %d entries = Hier klicken um %d Eintrge zu lschen Click here to delete this entry = Hier klicken um diesen Eintrag zu lschen Maximum number of attribute options exceeded = Maximal Anzahl von Attributen wurde berschritten Please increase MAX_N_LIST in elogd.c and recompile = Bitte MAX_N_LIST in elogd.c erhhen und neu kompilieren You can "steal" the lock by editing this entry = Diese Sperre kann "gestohlen" werden durch bearbeiten dieses Eintrags Several logbooks groups are defined on this host = Mehrere Logbuch-Gruppen sind auf diesem Computer eingerichtet Please select one to list the logbooks in that group = Bitte eine auswhlen, um die Logbcher in dieser Gruppe anzuzeigen No logbook defined on this server = Auf diesem Computer ist kein Logbuch eingerichtet Goto ELOG home page = Zur ELOG Home Page Please enter "%s" = Bitte "%s" eingeben Change config file = Konfigurationsdatei ndern Please check that it exists and elogd has write access and disk is not full = Bitte berprfen ob es existiert, ob elogd Schreibzugriff hat und ob Festplatte nicht voll ist Show = Anzeigen Hide = Verstecken Hide all = Alle verstecken Show all = Alle anzeigen This logbook requires authentication = Dieses Logbuch erfordert Authentifizierung Attachment file "%s" empty or not found = Anhang Datei "%s" ist leer oder nicht vorhanden Case sensitive = Groschreibung beachten List = Liste Date must be between 1970 and 2037 = Das Datum mu zwischen 1970 und 2037 liegen up = Hoch down = Runter stop = Stop Entry = Eintrag name may not contain blanks = Name darf keine Leerzeichen enthalten regular expressions = Regulre Ausdrcke Text fields are treated as %s = Textfelder werden als %s behandelt Subscribe to logbooks = Logbcher abonnieren enable automatic email notifications = Automatische Email Benachrichtigung ermglichen Set all = Alle ermglichen Set none = Keinen ermglichen Insert current time = Aktuelle Zeit einfgen Please enter hour for attribute '%s' = Bitte Stunde eingeben fr Attribut '%s' Please enter minute for attribute '%s' = Bitte Minute eingeben fr Attribut '%s' Please enter second for attribute '%s' = Bitte Sekunde eingeben fr Attribut '%s' No admin user has been defined in configuration file = In der Konfigurationsdatei ist kein Benutzer mit Administrationsrechten eingetragen Duplicate = Duplizieren Cannot lookup server name = Kann Server Name nicht finden Cannot connect to server = Kann mit Server nicht verbinden %s wrote = %s schrieb Quote = Zitat Please enter numeric value for year of attribute '%s' = Bitte numerischen Wert fr Jahr eingeben von Attribut '%s' Preview = Vorschau FONT = SCHRIFTART SIZE = GRSSE COLOR = FARBE smiling = lachend happy = frhlich winking = winken big grin = groes grinsen crying = weinen cool = cool frowning = stirnrunzeln confused = verwirrt mad = verrckt pleased = gefallen tongue = Zunge yawn = ghnen Encoding = Kodierung User "%s" not found in password file = Benutzer "%s" kann nicht in der Passwort Datei gefunden werden Cannot write to file %s = Datei %s kann nicht geschrieben werden Invalid user name "%s" or password for remote logbook = Ungltiger Benutzername "%s" oder Passwort fr entferntes Logbuch Hide attachments = Anhnge verstecken Show only new entries = Nur neue Eintrge zeigen Show all entries = Alle Eintrge zeigen New entries since = Neue Eintrge seit An old ELOG entry has been updated = Ein alter ELOG Eintrag wurde erneuert A new ELOG entry has been submitted = Ein neuer ELOG Eintrag wurde eingegeben Invalid user name or password = Ungltiger Benutzername oder Passwort Upload image = Bild hochladen Please enter filename or URL = Bitte Dateiname oder URL eingeben Maximum allowed file size is = Maximal erlaubte Dateigre ist Enter filename or URL = Dateiname oder URL eingeben Image uploaded successfully = Bild erfolgreich hochgeladen Image "%s" uploaded successfully = Datei "%s" erfolgreich hochgeladen HelpELCode = HilfeELCode Cannot retrieve file from URL "%s" = Kann Datei von URL "%s" nicht laden Enter name of hyperlink = Name von Hyperlink eingeben Enter URL of hyperlink = URL von Hyperlink eingeben astonished = erstaunt Error in regular expression "%s" = Fehler in regulrem Ausdruck "%s" File not found at URL "%s" = Datei bei URL "%s" nicht gefunden Entry size too large for email notification = Eintrag zu gro fr Email-Benachrichtigung Please specify a valid email address = Bitte eine gltige Email-Adresse eingeben Cannot send email notification to "%s" = Kann Email-Benachrichtigung nicht an "%s" senden Error: Command "%s" is not allowed for user "%s" = Fehler: Kommando "%s" ist nicht erlaubt fr Benutzer "%s" Shell execution not enabled via -x flag = Kommando-Ausfhrung ist nicht mit -x Kommandozeilenoption eingeschalten Enter heading level (1, 2 or 3) = Bitte berschrift-Stufe eingeben (1, 2 oder 3) User name may not contain blanks = Benutzername darf keine Leerzeichen enthalten Empty password not allowed = Leeres Passwort ist nicht erlaubt Password may not contain blanks = Passwort darf keine Leerzeichen enthalten Anonymous = Anonym Activation notice has been sent to %s = Aktivierungsbenachrichtigung wurde an %s geschickt Your request has been forwarded to the administrator = Der Antrag wurde an den Administrator geschickt You will be notified by email upon activation of your new account = Sie werden benachrichtigt, wenn Ihr Benutzerkonto aktiviert wurde Select period = Zeitraum auswhlen Last week = Letzte Woche Last month = Letzter Monat Last 3 Months = Letzten 3 Monate Last 6 Months = Letzten 6 Monate Last Year = Letztes Jahr Enter %s = %s eingeben Select %s = %s auswhlen Bold text CTRL+B = Fettschrift CTRL+B Italics text CTRL+I = Kursiv CTRL+I Underlined text CTRL+U = Unterstrichen CTRL+U Centered text = Zentrierter Text Insert hyperlink = Hyperlink einfgen Insert email = Email einfgen Insert image CTRL+M = Bild einfgen CTRL+M Insert quote = Zitat einfgen Insert list CTRL+L = Liste einfgen CTRL+L Insert table = Tabelle einfgen Insert heading CTRL+H = berschrift einfgen CTRL+H Insert code CTRL+O = Code einfgen CTRL+O Show the smiley bar = Smileys Balken zeigen Insert current time/date = Aktuelle Zeit/Datum einfgen Entry is locked on local server and therefore skipped = Eintrag ist auf lokalem Server gesperrt und wird deshalb bersprungen Keep me logged in on this computer = Auf diesem Computer eingeloggt bleiben for the next %d minutes = fr die nchsten %d Minuten for the next hour = fr die nchste Stunde for the next %d hours = fr die nchsten %d Stunden for the next %d days = fr die nchsten %d Tage keep original values = ursprngliche Werte behalten Entry is locked = Eintrag ist gesperrt Edit anyhow = Trotzdem ndern Insert horizontal line = Horizontale Linie einfgen Insert anchor point = Ankerpunkt einfgen keep original text = ursprnglichen Text behalten unspecified = unspezifiziert To subscribe to any logbook, click on 'Config' in that logbook = Um ein Logbuch zu abonnieren, auf 'Konfigurieren' in diesem Logbuch klicken ELOG XML import = ELOG XML Import Derive attributes from XML file = Attribute aus XML Datei importieren XML filename = XML Dateiname Invalid date format = Ungltiges Datumsformat XML file does not contain %s element = XML Date beinhaltet kein %s Element XML import preview of %s = XML Import Vorschau von %s XML Import = XML Import A old entry has been updated on %s = Ein alter Eintrag wurde auf %s erneuert First entry, Ctrl-Home = Erster Eintrag, Strg-Pos1 Previous entry, Ctrl-PgUp = Vorheriger Eintrag, Strg-BildHoch Next entry, Ctrl-PgDn = Nchster Eintrag, Strg-BildRunter Last entry, Ctrl-End = Letzter Eintrag, Strg-Ende ELOG import = ELOG Import Please choose format to import: = Bitte Importformat auswhlen: Enter text = Text eingeben or until I log out = oder bis zum Ausloggen Make smaller = Verkleinern Original size = Originalgre Make larger = Vergrern Rotate left = Links rotieren Rotate right = Rechts rotieren Delete attachment = Anhang lschen Cannot create thumbnail, please check ImageMagick installation = Kann Vorschau nicht erstellen, bitte ImageMagick Installation berprfen Attachments = Anhnge Error: Content length (%d) larger than maximum content length (%d) = Fehler: Datengre (%d) ist grer als maximal erlaubte Datengre (%d) ELOG error = ELOG Fehler Only user %s can edit this entry = Nur Benutzer %s kann diesen Eintrag ndern Export to = Exportieren nach Do not ignore first line = Erste Zeile nicht ignorieren Last %d hours = Letzte %d Stunden Invalid URL = Ungltige URL Attribute "%s" is not allowed in config file = Das Attribut "%s" ist in der Konfigurationsdatei nicht erlaubt Only user %s can delete this entry = Nur der Benutzer %s kann diesen Eintrag lschen File system full, ELOG cannot continue to work = Dateisystem voll, ELOG kann nicht weiterarbeiten 3 Days = 3 Tage Last 3 Days = Letzte 3 Tage Really submit this entry? = Diesen Eintrag wirklich abschicken? Account activation for ELOG logbook "%s" = Zugangsaktivierung fr das ELOG Logbuch "%s" Account activation for ELOG on host "%s" = Zugangsaktivierung fr ELOG auf dem Computer "%s" Please click the URL below to activate following ELOG account = Bitte auf den Link klicken, um folgenden ELOG Zugang zu aktivieren Activation URL = Aktivierungs-URL An email has been sent to <%s> = Eine Email wurde an <%s> gesendet Use that email to activate your account = Bitte diese Email benutzen, um den Zugang zu aktivieren Registration request for ELOG logbook "%s" = Registrierungsanfrage fr das ELOG Logbuch "%s" Registration request for ELOG on host "%s" = Registrierungsanfrage fr ELOG auf dem Computer "%s" Are you sure you want to deactivate your own account? = Sind sie sicher, dass sie ihren Zugang stilllegen mchten? Active = Aktiv Invalid activation code = Ungltiger Aktivierungscode Error activating user = Fehler bei der Aktivierung des Benutzers A password recovery email for user "%s" has been sent to %s = Eine Email zur Passwort Wiederherstellung fr Nutzer "%s" wurde gesendet an %s User "%s" has no access to this logbook = Benutzer "%s" hat keinen Zugang zu diesem Logbuch Your account has been activated = Ihr Zugang wurde aktiviert Please subscribe to logbooks if you want to receive automatic email notifications = Bitte abbonieren Sie Logbcher, um automatisch Email-Benachrichtigungen zu bekommen This is an automatically generated account recovery email for host %s = Dies ist eine automatisch generierte Email fr die Rcksetzung des Zugangs auf dem Computer %s Please click on following link to recover your account = Bitte klicken Sie auf folgenden Link, um Ihren Zugang zurckzusetzen Email address "%s" not registered = Email Adresse "%s" ist nicht registriert User name "%s" not registered = Benutzername "%s" ist nicht registriert Error accessing password file = Fehler beim Zugriff auf Passwort Datei CSV (";" separated) + Text = CSV (mit ";" getrennt) + Text Entry can only be deleted %1.2lg hours after creation = Eintrag kann nur %1.2lg Stunden nach dem Anlegen gelscht werden Drop attachments here... = Anhnge bitte hier ablegen... Insert Timestamp = Aktuelle Zeit einfgen Pending draft available = Unvollstndige Entwrfe sind verfgbar %d pending drafts available = %d unvollstndige Entwrfe sind verfgbar Create new entry = Neuen Eintrag anlegen If you leave this page you will lose your unsaved changes = Wenn Sie diese Seite verlassen werden die nicht gesicherten nderungen verworfen Edited = Gendert All time = Immer Draft = Entwurf days = Tage Draft saved at = Entwurf gespeichert um You might however then overwrite each other's modifications = Sie wrde dann jedoch die nderungen von jemand anderem berschreiben Draft entry created on %s by %s = Entwurf erstellt am %s von %s Really delete this entry? = Diesen Eintrag wirklich lschen? This is a draft message, edit and submit it to make it permanent = Dies ist ein Entwurf, bitte ndern und abschicken um ihn permanent zu machen Restrict search to last = Suche beschrnken auf letzte Do you want to restore your original text? = Wollen Sie den ursprnglichen Text wiederherstellen? This will overwrite your current modifications. = Hiermit werden Ihre aktuellen nderunen verworfen. Restore = Widerherstellen elog-3.1.3-1/resources/eloghelp_dutch.html0000755000175000017500000000444112556103751017521 0ustar rogerroger ELOG Electronisch Logboek Help
ELOG Electronisch Logboek Help

Het Electronisch Logboek (ELog) kan worden gebruikt om berichten op te slaan en op te halen via een Web interface. Afhankelijk van de configuratie, kan het ELog systeem een of meer logboeken verzorgen die in aparte secties op de server staan.

Kort overzicht

Standaard wordt het laatste bericht getoond. Met de knoppen kan je naar de eerste, vorige, volgende en het laatste bericht bladeren.

Als een van de aanvinkvakjes naast een van de attribuutvelden is aangevinkt, zullen alleen berichten van dat attribuut getoond worden als de blader knoppen worden gebruikt. Ze kunnen worden gebruikt om bijvoorbeeld snel te filteren op auteur of categorie.

De Nieuw knop maakt een nieuw bericht. Met de Wijzig knop kan een bericht gewijzigd worden, als dit is toegestaan in het configuratie bestand. De Antwoord knop maakt een antwoord aan voor een bericht, net als met bijvoorbeeld e-mail.

De Zoek knop opent de zoekpagina, waar berichten van een logboek kunnen worden getoond op basis van filters. Elk niet-lege veld functioneert als een aanvullende filter, welke worden toegevoegd aan de andere regels. Als er geen filter is geselecteerd, zullen alle boodschappen worden getoond. De Laatste dag en de Laatste 10 knoppen laten de berichten van de laatste 24 uur of de laatste 10 berichten zien.

Meer informatie

Raadpleeg voor meer informatie, in het bijzonder voor de configuratie van Elog, de ELOG thuispagina.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.turkish0000755000175000017500000005021113076344200017036 0ustar rogerroger# Turkish translation by Devrim Esentrk # # To use Turkish translation, add the following lines to [global]: # charset = utf-8 # language = turkish # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Yeni Edit = Dzenle Delete = Sil Reply = Yantla Find = Bul Last day = Dn Last 10 = Son 10 Config = Ayarla Change password = Parola deitir Logout = Oturumdan k Help = Yardm Back = Geri Submit = Onayla First = lk Last = Son Previous = nceki Next = Sonraki Copy to = Kopyala Move to = Ta Save = Kaydet Cancel = k Wrong password = Yanl parola Please login = Ltfen oturum an Username = Kullanc ad Password = Parola Old password = Eski parola New password = Yeni parola ELOG change password = ELOG parolasn deitir Several logbooks are defined on this host = Bu makinede bir ok kayt defteri tanmlanm Please select the one to connect to = Ltfen balanmak iin bunlardan birisini sein Change password for user = Kullanc iin parolay deitir Logged in as = Oturumdaki kullanc Entry date = Kayt tarihi with = ile This is the last entry = Bu son girdidir This is the first entry = Bu ilk girdidir Email sent to = E-posta gnder Email notification suppressed = E-posta bildirimi kesildi please select = ltfen sein Suppress Email notification = E-posta bildirimini kes Resubmit as new entry = Yeni kayt olarak tekrar gnder Attachment = Ek Reply to this = Bunu yantla In reply to = Cevapla Fields marked with = aretlenmi alanlar are required = zorunlu Please check that it exists and elogd has write access = Ltfen var olduunu ve yazma eriimine sahip olduunu kontrol ediniz Error: Attribute %s not supplied = Hata: %s zellikleri mevcut deil Please go back and enter the %s field = Ltfen geri gidin ve %s alanna girin Please use your browser's back button to go back = Ltfen taraycnzn geri tuunu, geri gitmek iin kullann ELOG find = ELOG bul Find = Bul Search = Ara Reset Form = Formu sfrla Summary only = Yalnzca zet Show attachments = Ekleri gster Printable output = Yazlabilir kt Sort in reverse order = Ters srala Start date = Balang tarihi End date = Biti tarihi Year = Yl Text = Metin Search all logbooks = Tm kayt defterlerini ara Last %d days = Son %d gn Last %d entries = Son %d girdi No entries found = Kayt bulunamad A new entry has been submitted on %s = Yeni kayt %s tarihinde onaylanm Logbook = Kayt defteri Yes = Evet No = Hayr Error deleting message: status = Mesaj silerken hata: durum Error: Command "%s" not allowed = Hata: "%s" komutu kabul edilmedi Cannot open file %s = %s dosyas alamyor Cannot write to %s = %s'a yazlamyor No SMTP host defined in [global] section of configuration file = Ayar dosyasnn [global] blmnde SMTP host tanm yok Display threads = lmekleri gster Logbook is empty = Kayt defteri bo Download = ndir and all its replies = ve tm onun cevaplar Message ID = Mesaj ID Not logged in = Oturumda deil Login = Oturum a Page %d of %d = Sayfa %d / %d all entries = tm girdiler Login name = Kullanc ad Full name = Ad ve Soyad ELOG user config = ELOG kullanc ayar ELOG new user = ELOG yeni kullanc Admin = Ynetici Select user = Kullanc se Remove user = Kullanc kaldr New user = Yeni kullanc Retype new password = Yeni parolanz tekrar girin New passwords do not match, please retype = Yeni parolalar elemedi, ltfen tekrar girin Retype password = Parolay tekrar girin exists already = zaten mevcut Register as new user = Yeni kullanc kayd A new ELOG user has been registered on %s = %s'na yeni ELOG kullancs kaydedildi. Email = E-posta User "%s" registered on logbook "%s" = "%s" kullancs "%s" kayt defterinde kaytl User [%s] has been deleted = [%s] kullancs silindi Hit following URL to activate that account = Hesab aktive etmek iin takip eden URL'yi tklaynz ELOG registration = ELOG kayt Your ELOG account has been activated on host = ELOG hesabnz aktive edildi You can access it at = Eriebilirsiniz Are you sure to delete these messages? = Bu mesajlar silmeye emin misiniz? Select = Se All entries = Tm girdiler Day = Gn Week = Hafta Month = Ay Show last = Sonuncuyu gster Goto page = Sayfaya git All = Tm Display = Grntle entries per page = her bir sayfadaki girdiler Toggle all = Hepsini iaretle/temizle Selected entries = Seilmi girdiler Collapse = Topla Expand = A Full = Tm Summary = zet Threaded = lmekler Filters = Filtreler Mode = Mod Options = Seenekler Error: start date after end date = Hata: nce balang tarihi sonra biti tarihi and all their replies = ve onlarn cevaplar Upload = Ykle Requested = stenmi A new ELOG user wants to register on "%s" = Yeni ELOG kullancs "%s"'na kayt olmak istiyor Search text also in attributes = Metni zellikler iinde de ara Date = Tarih ID = ID Forgot password? = Parolay unuttunuz mu? ELOG password recovery = ELOG parola yenileme No Email address registered with user name "%s" = "%s" kullanc ismine herhangi bir e-posta adresi kaytl deil Error sending Email via "%s" = "%s" ile e-posta gnderirken hata Forgot = Unutuldu User "%s" registered on host "%s" = "%s" kullancs "%s" makinesinde kaytl Enter your user name or email address = Kullanc ad veya e-posta adresi girin Password recovery for ELOG %s = ELOG %s iin parola yenileme Host = Makine (Host) Your ELOG account has been activated = ELOG hesabnz etkinletirildi Maximum number of attachments reached = Ekler iin azami sayya ulald on = stnde Entry is currently edited by = Girdi u an dzenlendi recipients = alclar Suppress shell execution = Kabuk uygulamas durduruldu Update = Gncelle by = tarafndan Change %s = %s deitir Add new option here = Buraya yeni seenek ekle Expand all = Tmn a Add %s = %s ekle Synchronization = Eleme Error: Value %s not allowed for boolean attributes = Hata: Boole zellikleri iin %s deerine izin verilmedi Error: Attribute option %s not existing = Hata: %s zellik seenei yok Last submission = Son gnderme Synchronize = Eletir No mirror server defined in configuration file = Yaplandrma dosyasnda yans sunucusu tanml deil user = kullanc Maximum number of replies (%d) exceeded = Azami kullanc cevab (%d) ald Entry can only be edited %1.2lg hours after creation = Girdi yaratldktan %1.2lg saat sonra dzenlenebilir Display full entries = Tm girdileri gster No entry selected for deletion = Silme iin girdi seilmedi Cannot read entry from local logbook = Yerel kayt defterinden girdi okunamyor Cannot connect to host %s, port %d = %s makinesi, %d portuna balanlamyor Cannot receive "%s" = "%s" alnamyor Received wrong entry id "%d" = Yanl girdi "%d" id'si alnd Entry #%d is locked on remote server = #%d girdisi uzak sunucuda tutulmu Cannot save remote entry locally = Uzaktaki girdi yerele kaydedilemiyor local = yerel remote = uzak Please delete %s or %s entry to resolve conflict = Ltfen akmay zebilmek iin %s veya %s girdisini silin Error sending local entry = Yerel girdiyi gnderirken hata Local entry submitted = Yerel girdi onayland Error deleting remote entry = Uzaktaki girdiyi silerken hata New entry cannot be written to directory "%s" = Yeni girdi, "%s" klasrne yazlamaz Entry %s cannot be read from logbook "%s" = Girdi, "%s" kayt defterinden okunamaz This entry has been deleted = Bu girdi silindi Entries = Girdi Cannot connect to remote server "%s" = "%s" uzak sunucusuna balanlamad Remote server is not an ELOG server = Uzak sunucu ELOG sunucusu deil Incorrect remote ELOG server version %s = Hatal uzak ELOG sunucu srm %s Error accessing remote logbook = Uzak kayt defterine eriirken hata Invalid HTTP header = Geersiz HTTP bal No user name supplied to access remote logbook = Uzak kayt defterine eriim iin kullanc ad mevcut deil Synchronizing logbook %s with server "%s" = %s kayt defteri "%s" sunucusu ile eleiyor Configuration has been changed locally and remotely = Yerel ve uzaktaki yaplandrmalar deitirildi Please merge manually to resolve conflict = Ltfen akmalar zmleyebilmek iin el ile birletirin Logbook "%s" does not exist on remote server = "%s" kayt defteri uzak sunucu zerinde mevcut deil Entry has been changed locally and remotely = Yerel ve uzaktaki girdiler deitirildi Entry deleted locally = Girdi yerelde silindi Changed local entry ID to %d = Yerel girdi ID'si %d ile deitirildi Entry deleted remotely = Girdi uzakta silindi All entries identical = Tm girdiler e Invalid mirror_id or entry_date = Geersiz yans numaras veya girdi tarihi Synchronize all logbooks = Tm kayt defterlerini eletir Calendar = Takvim Remote entry received = Uzak girdi alnd Pick a date = Tarih se Please select at least one '%s' = Ltfen en az bir '%s' sein Please select a '%s' = Ltfen '%s' sein Please enter month for attribute '%s' = Ltfen '%s' zellii iin ay girin Please enter day for attribute '%s' = Ltfen '%s' zellii iin gn girin Please enter year for attribute '%s' = Ltfen '%s' zellii iin yl girin Please enter attribute '%s' = Ltfen '%s' zellii girin Entry time = Girdi zaman Start = Bala End = Son From = dan After = Sonra to = ya Before = nce Previous Year = nceki Yl Next Year = Gelecek Yl Please enter numeric value for '%s' = Ltfen '%s' iin saysal deer girin Error: Attribute %s must be numeric = Hata: %s zellii saysal olmal 3 Months = 3 Ay 6 Months = 6 Ay Error sending local message = Yerel mesaj gnderirken hata Error receiving message = Mesaj alrken hata Are you sure to delete this entry? = Bu girdiyi silmeye emin misin? ELOG CSV import = ELOG CSV ieri aktar Import = eri aktar CSV filename = CSV dosya ad Field separator = Alan ayrc Edit entry = Girdiyi dzenle Delete entry = Girdiyi sil CSV Import = CSV ieri aktar Derive attributes from CSV file = CSV dosyasndan zellii al Comma = Virgl Semicolon = Noktal virgl Preview import = eri aktarma nizleme CSV import preview of %s = %s'nin CSV aktarmnn nizlemesi Too many attributes in CSV file = CSV dosyas ierisinde ok fazla zellik %d entries successfully imported = %d girdileri baaryla ieri aktarld No 'Attributes' option present in %s = %s iinde 'Attributes' seenei mevcut deil CSV ("," separated) = CSV ("," ile ayrlm) CSV (";" separated) = CSV (";" ile ayrlm) Auto detect = Otomatik algla text = metin Column header '%s' must be present in CSV file = CSV dosyas iinde, '%s' stun bal mevcut olmal Fill text body = Metin ieriini doldur Please re-enter filename = Ltfen dosya adn tekrar girin Last x = Son x Activate = Etkinletir Entry has been modified = Girdi deitirildi No attachment file specified = Ek dosya belirlenmemi Delete this logbook = Bu kayt defterini sil Rename this logbook = Bu kayt defterinin adn deitir Create new logbook = Yeni kayt defteri yarat Syntax error in config file = Ayar dosyas iinde szdizimi hatas Email notification = E-posta bildirimi Are you sure to delete logbook "%s"? = "%s" kayt defterini silmeye emin misiniz? Logbook "%s" exists already, please choose different name = "%s" kayt defteri zaten mevcut, ltfen farkl isim sein Rename logbook = Kayt defterini yeniden adlandr Enter new logbook name = Yeni kayt defteri ad gir Logbook name = Kayt defteri ad Use existing logbook as template = Mevcut kayt defterini ablon olarak kullan none = hibiri URL is redirected to: = URL ynlendirildi Remote config should be received = Uzak yaplandrma alnmal Local entry should be submitted = Yerel girdi onaylanmal Remote entry should be received = Uzak girdi alnmal Local entry = Yerel girdi %s should be deleted = %s silinmeli Entry should be deleted locally = Girdi yerelde silinmeli Local entry ID should be changed to %d = Yerel girdi ID %d'ye deitirilmeli Remote entry = Uzak girdi Entry should be deleted remotely = Girdi uzakta silinmeli Click here to delete %d entries = %d girdileri silmek iin tklayn Click here to delete this entry = Bu girdiyi silmek iin tklayn Maximum number of attribute options exceeded = Azami zellik seenekleri saysna ulald Please increase MAX_N_LIST in elogd.c and recompile = Ltfen elogd.c iindeki MAX_N_LIST'i artrn ve tekrar derleyin You can "steal" the lock by editing this entry = Bu girdiyi dzenleyerek tutuluyu "steal" yapabilirsiniz Several logbooks groups are defined on this host = Bu makinede bir ok kayt defteri gruplar tanml Please select one to list the logbooks in that group = Ltfen grup iindeki kayt defterlerinden birini sein No logbook defined on this server = Bu sunucu zerinde kayt defteri tanml deil Goto ELOG home page = ELOG anasayfasna git Please enter "%s" = Ltfen "%s" girin Change config file = Yaplandrma dosyasn deitir Please check that it exists and elogd has write access and disk is not full = Ltfen mevcut olduunu ve elogd'nin yazma eriimine sahip olduunu ve diskin dolu olmadn kontrol ediniz Show = Gster Hide = Sakla Hide all = Tmn sakla Show all = Tmn gster This logbook requires authentication = Bu kayt defteri kimlik denetimi gerektiriyor Attachment file "%s" empty or not found = "%s" ek dosyas bo veya bulunamad Case sensitive = Byk, kk harf duyarl List = Liste Date must be between 1970 and 2037 = Tarih 1970 - 2037 arasnda olmal up = yukar down = aa stop = dur Entry = Girdi Shell execution not enabled via -x flag = Kabuk altrma -x seenei ile etkinletirilmemi Cannot lookup server name = Sunucu ad bulunamyor Cannot connect to server = Sunucuya balanlamyor Entry size too large for email notification = Kayt boyutu e-posta bildirimi iin ok byk %s wrote = %s kaydedildi Quote = Alnt Insert current time = u anki zaman ekle Please enter hour for attribute '%s' = Ltfen '%s' zellii iin saat girin Please enter minute for attribute '%s' = Ltfen '%s' zellii iin dakika girin Please enter second for attribute '%s' = Ltfen '%s' zellii iin saniye girin Please enter numeric value for year of attribute '%s' = Ltfen '%s' zellii iin yl girin Enter name of hyperlink = Ba ismini girin Enter URL of hyperlink = Ba adresini girin Enter heading level (1, 2 or 3) = Balk seviyesini girin (1, 2 veya 3) Preview = nizleme FONT = YAZITP SIZE = BOYUT COLOR = RENK smiling = glen yz happy = mutlu winking = gz krpan big grin = srtan crying = alayan cool = kafadar frowning = ka atan confused = kafas karm astonished = akn mad = delirmi pleased = memnun tongue = dil karan yawn = esneyen Encoding = Kodlama regular expressions = dzenli ifadeler Text fields are treated as %s = Metin alanlar %s olarak alnr Please specify a valid email address = Ltfen geerli bir e-posta adresi girin Cannot send email notification to "%s" = "%s" iin e-posta bildirimi gnderilemedi No admin user has been defined in configuration file = Yaplandrma dosyasnda ynetici kullancs tanmlanmam User "%s" not found in password file = Parola dosyasnda "%s" kullancs bulunamad Cannot write to file %s = %s dosyasna yazlamyor Subscribe to logbooks = Kayt defterlerine abone ol enable automatic email notifications = otomatik e-posta bildirimini etkinletir Set all = Hepsi olarak ayarla Set none = Hibirisi olarak ayarla Invalid user name "%s" or password for remote logbook = Uzaktaki kayt defteri iin geersiz kullanc ad "%s" veya geersiz parola Hide attachments = Ekleri gizle Show only new entries = Sadece yeni girdileri gster Show all entries = Tm girdileri gster Error in regular expression "%s" = Dzenli ifadede hata New entries since = Yeni girdiler, balang tarihi An old ELOG entry has been updated = Eski bir ELOG girdisi gncellendi A new ELOG entry has been submitted = Yeni bir ELOG girdisi gnderildi Invalid user name or password = Geersiz kullanc ad veya parolas Upload image = Resim ykle Please enter filename or URL = Ltfen dosya ad veya A adresi girin Maximum allowed file size is = zin verilen en byk dosya boyutu Enter filename or URL = Dosya ad veya A adresi girin Image uploaded successfully = Resim baaryla yklendi Image "%s" uploaded successfully = "%s" resmi baaryla yklendi Duplicate = ift Error: Command "%s" is not allowed for user "%s" = Hata: "%s" komutu iin "%s" kullancs izinli deil HelpELCode = ElCodeYardm Cannot retrieve file from URL "%s" = Dosya "%s" a adresinden alnamyor File not found at URL "%s" = Dosya "%s" a adresinde bulunamyor name may not contain blanks = isim boluk iermemeli # #---- please translate following items and then remove this comment ----# # User name may not contain blanks = Empty password not allowed = Password may not contain blanks = Anonymous = Anonim Activation notice has been sent to %s = Your request has been forwarded to the administrator = You will be notified by email upon activation of your new account = Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_french.html0000755000175000017500000000625412556103751017663 0ustar rogerroger Aide sur la main-courante électronique ELOG
Aide sur la main-courante électronique ELOG

La "main-courante électronique" (ELog) peut être utilisée pour créer des messages et y accéder à travers une interface Web. Suivant le paramétrage, un serveur ELog peut héberger un ou plusieurs "registres" (logbooks) qui sont enregistrés séparément.

Aperçu rapide

Par défaut, la dernière entrée en date est affichée. On peut utiliser les boutons de navigation pour afficher l'entrée précédente ou suivante, ou aller directement à la première ou à la dernière.

Si l'une des cases à côté des intitulés de champs est cochée, seuls les messages ayant la même valeur pour cet attribut seront affichés par les boutons de navigation : ceci peut être utilisé comme un filtre rapide, pour parcourir toutes les entrées d'un même auteur ou d'une catégorie donnée.

La commande Créer permet d'ajouter une nouvelle entrée. La commande Modifier, si le paramétrage l'autorise, permet l'édition d'une entrée existante. La commande Répondre crée une entrée en référence à une entrée existante, comme lorsqu'on répond à un mél.

La commande Chercher affiche un formulaire de recherche, qui permet de filtrer les entrées suivant divers critères. Chaque critère non-vide ajoute un filtre, et les filtres sont cumulés ("ET" logique). Si aucun critère n'est renseigné, toutes les entrées du journal sont affichées.

Les commandes Dernier jour et 10 Dernières entrées agissent comme des recherches simplifiées, affichant respectivement les entrées des dernières 24 heures, et les 10 dernières entrées créées.

Plus d'informations

Pour plus de précisions, notamment sur le paramétrage d'un serveur ELog, référez-vous à la page d'accueil ELOG (en anglais).


S. Ritt, 28 August 2001
Traduit en français par Fred Pacquier, décembre 2001
elog-3.1.3-1/resources/elogd-zh_CN-GB2312.cfg0000644000175000017500000000051212556103750017204 0ustar rogerroger[global] port = 8080 Language = zh_CN-GB2312 charset = gb2312 [Ӽ¼ʾ] Theme = default Comment = ʵ¼ʾ Attributes = , Options = , ʵ, ֵల, ݲɼ, ݷ, , Required Attributes = , Page Title = ELOG - $subject Reverse sort = 1 Quick filter = Date, Type elog-3.1.3-1/resources/elcode_indonesia.html0000644000175000017500000003337112560137216020016 0ustar rogerroger Bantuan ELCode

Bantuan ELCode


ELCode adalah satu set khusus tag untuk memformat entri elog. Hal ini mirip dengan HMTL, tapi lebih sederhana. tag diapit oleh kurung [dan], bukan < dan >. Beberapa tag mengubah format teks seperti tebal, ukuran dan warna, sementara tag lain memungkinkan menyisipkan URL dan gambar. Tag ELCode adalah mirip dengan tag BBCode , kadang-kadang juga disebut sebagai kode vB.

Format teks


Tag berikut di ELCode memungkinkan format dasar teks:

TagShortcut keyboardArti
[b]Halo[/b] CTRL+B akan menjadi teks tebal seperti Halo
[u]Halo[/u] CTRL+U akan menjadi teks digaris bawahi seperti Halo
[i]Halo[/i] CTRL+I akan menjadi teks miring seperti Halo
[color=red]Halo[/color] atau
[color=#FF0000]Halo[/color]
keduanya menjadi Halo. kita bisa menyatakan nama warna seperti putih, merah, hijau, hijau muda, atau nilai heksadesimal RGB, di mana dua digit yang pertama (00 - FF) adalah untuk komponen merah, yang kedua untuk hijau dan yang ketiga untuk biru.
[size=5]Halo[/size] akan menjadi teks Halo. Ukuran teks dalam pixel berkisar dari 1 sampai 29. Berikut adalah beberapa contoh: ukuran=1, ukuran=2, ukuran=3, ukuran=4, ukuran=5, ukuran=6.
[font=comic]Halo[/font] akan mengubah font teks. Berikut adalah beberapa contoh: comic, courier, arial, tahoma, times, verdana
[center]Halo[/center]
akan menempatkan teks di tengah
[h1]Halo[/h1]
[h2]Halo[/h2]
[h3]Halo[/h3]
CTRL+H akan menjadi judul tingkat 1, tingkat 2 or tingkat 3
[line] akan menjadi garis horizontal seperti

Tags dapat dikombinasikan, seperti

[size=5][color=red][b]Halo[/b][/color][/size]

yang akan menghasilkan Halo. Perhatikan bahwa tag harus dikombinasikan dengan benar, hal-hal seperti

[b][color=red]Ini salah![/b][/color]

tidak benar, karena [b] tag dibuka pertama dan karena itu harus ditutup paling akhir, setelah tutupan [/color] tag, seperti di

[b][color=red]Ini benar[/color][/b]

Karakter lepas


Kadang-kadang seseorang tidak ingin tag ELCode dijalankan, seperti di forum diskusi di mana seseorang ingin menjelaskan beberapa tag untuk contoh. Untuk menghindari tag dijalankan, tempatkan garis miring "\" di depan tag, seperti \[b], bukan [b].

Jangkar


Jangkar dapat dimasukkan ke dalam entri dengan [anchor] nama jangkar [/anchor] . Hal ini setara dengan jangkar HTML seperti <a name= " nama jangkar"></a>. Jangkar tersebut dapat dirujuk dalam entri dengan elog:<id>#nama jangkar di mana <id> adalah nomor ID dari entri (nomor dari kolom kiri di layar daftar).

Smiley


Smiley, atau Emoticon adalah gambar kecil yang dapat digunakan untuk menunjukkan perasaan menggunakan kode pendek, misalnya :) berarti senang. Dikonversi oleh elog secara otomatis ke gambar grafik kecil, seperti



:) :( ;)
:)) :D ?-)
;( :] :O
8-)8o X(
:P

Mengutip


Ada dua cara mengutip, dengan dan tanpa referensi:

  • Menyisipkan beberapa tulisan dengan [quote] Teks Asli [/quote] menghasilkan kutipan tanpa referensi seperti:

    mengutip:
    Teks Asli

  • Menyisipkan beberapa tulisan dengan [quote = "Mr. Bean"] Teks Asli [/ quote] menghasilkan kutipan dengan referensi seperti:

    Mr. Bean wrote:
    Teks Asli

    Perhatikan bahwa wajib untuk menyertakan referensi "Mr Bean" dalam tanda kutip.

Tulisan dengan lebar tetap [CTRL+O]


Untuk menampilkan teks dengan lebar tetap menggunakan font jenis Courier, apit dengan

[code]Halo[/code]

Ini mungkin berguna dalam daftar kode komputer atau tabel untuk meningkatkan pembacaan, seperti

Barang              Harga     Ketersediaan
====                =====     ============

Apel                 0.50       1000
Anggur               2.50       100
Tomat Kering	    12.50       20

daripada

Barang Harga Ketersediaan
==== ===== ============

Apel 0.50 1000
Anggur 2.50 100
Tomat Kering 12.50 20

Perhatikan bahwa semua tag ELCode lainnya dinonaktifkan di dalam tag [code] . Ini mungkin bisa membantu untuk menampilkan beberapa kode program atau yang serupa yang berisi karakter yang akan salah diartikan sebagai tag atau smiley. Jika perilaku ini tidak inginkan, seperti jika beberapa kode program harus dijelaskan dengan huruf tebal dll tag [code1] ... [/code1] dapat digunakan yang memungkinkan tag ELCode lainnya dalamnya.

Daftar [CTRL+L]


Daftar tidak berurutan dapat dibuat dengan tag [list][/list]seperti

[list]
[*]Laut
[*]Hutan
[*]Gurun
[/list]

untuk menghasilkan

  • Laut
  • Hutan
  • Gurun

Daftar item baru dimulai dengan [*] , yang dapat dimasukkan melalui shortcut keyboard CTRL-N. Dalam daftar berurutan, dapat ditentukan apa yang digunakan untuk setiap item. Untuk membuat daftar bernomor dapat menggunakan [list=1][/list]:

[list=1]
[*]Laut
[*]Hutan
[*]Gurun
[/list]

untuk menghasilkan

  1. Laut
  2. Hutan
  3. Gurun

untuk daftar dengan abjad [list = a] [/list] seperti

[list=a]
[*]Laut
[*]Hutan
[*]Gurun
[/list]

untuk menghasilkan

  1. Laut
  2. Hutan
  3. Gurun

Kemungkinan lain adalah [list=A] untuk huruf kapital dan [list=I] untuk penomoran Romawi.

Tabel


Tabel dapat dibuat dengan tag [table] [/table] seperti

[table border="1"]
Satu|Dua
|-
Tiga|Empat
[/table]

untuk menghasilkan

SatuDua
TigaEmpat

Parameter setelah [table ...] secara langsung digunakan dalam tag HTML <table>. Untuk meningkatkan padding sel, dapat menambahkan misalnya

[table border="1" cellpadding="20"]
Satu|Dua
|-
Tiga|Empat
[/table]

Untuk Menghasilkan
SatuDua
TigaEmpat

Judul tabel tidak didukung, tetapi dapat disimulasikan dengan menyisipkan isi sel dengan [B]...[/B] tag.

Membuat tautan


Hyperlink (Tautan) atau Uniform Resource Locators (URL) dapat dibuat dengan berbagai cara:

  • Setiap syntaks URL yang benar akan otomatis berubah menjadi tautan, bahkan tanpa menambah http:// di depannya. Jadi midas.psi.ch/elog akan diubah menjadi midas.psi.ch/elog dan http://www.cern.ch akan berubah menjadi http://www.cern.ch.

  • Untuk menentukan URL secara eksplisit, apit dengan [url]www.cern.ch[/url]

  • Jika link harus mengandung beberapa teks yang berbeda dari URL sebutkan dengan [url = http://www.cern.ch] Teks [/ url] seperti di

    Menuju ke [url=http://www.cern.ch]CERN[/url]

    yang akan menghasilkan:

    Menuju ke CERN

  • Hal yang sama juga untuk alamat email. Mereka dapat digunakan seperti [email] john.doe@domain.org [/ email] atau hanya sebagai john.dow@domain.org yang akan berubah otomatis menjadi john.doe@domain.org.

Menyisipkan gambar [CTRL+M]


Ada dua cara untuk menyisipkan gambar dalam teks:

  • Gambar dapat disisipkan dalam teks dengan tag [img] URL [/img] , di mana URL harus menunjuk ke sebuah gambar yang tersedia di internet. Catatan bahwa ini hanya bekerja untuk gambar yang dapat diakses melalui browser web. Gambar pada hard disk lokal anda tidak dapat diakses dari luar kecuali jika Anda tidak menjalankan di server web publik. Anda dapat misalnya menanamkan logo elog dengan:

    [img] http://midas.psi.ch/elogs/elog.png [/img]

    yang menghasilkan

  • Anda dapat mengunggah gambar sebagai lampiran dalam entri elog Anda dan menautkannya dengan

    [img]elog:/1[/img]

    di manaelog:/1 menyatakan lampiran pertama, elog:/2 untuk kedua dan seterusnya. Jika Anda mereferensi gambar terlampir dengan cara ini, mereka tidak ditampilkan lagi pada akhir entri.

Shortcut keyboard lainnya

  • CTRL+P: Tinjau
  • CTRL+ENTER: Kirim
Perlu dicatat bahwa cara pintas keyboard tidak bekerja dengan baik di semua browser. Mereka telah berhasil diuji di Firefox.
Halaman Elog
elog-3.1.3-1/resources/elcode_english.html0000755000175000017500000003205412556103750017477 0ustar rogerroger ELCode Help

ELCode Help


ELCode is a special set of tags to format an ELOG entry. It is similar to HMTL, but simpler. Tags are enclosed in braces [ and ] rather than < and >. Some tags change the formatting of the text like boldface, size and color, while other tags allow the embedding of URLs and images. The ELCode tags are similar to the BBCode tags, sometimes also referred as vB code.

Text formatting


Following tags in ELCode allow the basic formatting of the text:

TagKeyboard shortcutMeaning
[b]Hello[/b] CTRL+B will become bold text like Hello
[u]Hello[/u] CTRL+U will become underlined text like Hello
[i]Hello[/i] CTRL+I will become italics text like Hello
[color=red]Hello[/color] or
[color=#FF0000]Hello[/color]
will both become Hello. One can either speciy a well know color name like white, red, green, lightgreen, or a hexadecimal RGB value, where the first two digits (00 - FF) are for the red component, the second for the green and the third for the blue part.
[size=5]Hello[/size] will become text like Hello. The size of the text in pixels ranges from 1 to 29. Here are some examples: size=1, size=2, size=3, size=4, size=5, size=6.
[font=comic]Hello[/font] will change the text font. Here are some examples: comic, courier, arial, tahoma, times, verdana
[center]Hello[/center]
will put the text in the center
[h1]Hello[/h1]
[h2]Hello[/h2]
[h3]Hello[/h3]
CTRL+H will become a heading of level 1, level 2 or level 3
[line] will become a horizontal line such as

Tags can be nested, such as

[size=5][color=red][b]Hello[/b][/color][/size]

which would produce Hello. Note that tags have to be nested correctly, things like

[b][color=red]This is wrong![/b][/color]

are incorrect, since the [b] tag is opened first and therefore must be closed last, after the closing [/color] tag, such as in

[b][color=red]This is correct[/color][/b]

Escape character


Sometimes one does not want ELCode tags to be interpreted, like in a discussion forums where one wants to explain some tags for example. In order to avoid interpretation of tags, one puts a backslash "\" in front of the tag, like \[b] instead of [b].

Anchor


An anchor can be put into an entry with [anchor]anchor_name[/anchor]. This is equivalent to a HTML anchor like <a name="anchor_name"></a>. Such anchors can be referenced inside an entry with elog:<id>#anchor_name where <id> is the ID number of the entry (the number from the left column in the list display).

Smileys


Smileys, or Emoticons, are small graphical images which can be used to express some feeling using a short code, e.g. :) means happy. They are converted by elog automatically into small graphical images, such as



:) :( ;)
:)) :D ?-)
;( :] :O
8-)8o X(
:P

Quoting


There are two ways of quoting, with a reference and without:

  • Enclosing some text with [quote]Original Text[/quote] produces a quote without reference such as:

    Quote:
    Original Text

  • Enclosing some text with [quote="Mr. Bean"]Original Text[/quote] produces a quote with reference such as:

    Mr. Bean wrote:
    Original Text

    Note that it is mandatory to enclose the reference "Mr. Bean" in quotation marks.

Fixed width text [CTRL+O]


To output text with fixed width using a Courier-type font, enclose it in

[code]Hello[/code]

This might be useful in computer code listings or tables to increase the readability, like

Item                Price     Availability
====                =====     ============

Apples               0.50     1000
Grapefruits          2.50     100
Sun-dried Tomatos   12.50     20

versus

Item Price Availability
==== ===== ============

Apples 0.50 1000
Grapefruits 2.50 100
Sun-dried Tomatos 12.50 20

Note that all other ELCode tags are disabled inside the [code] tags. This might be helpful to display some program code or similar which contains characters which would wrongly be interpreted as tags or smileys. If this behaviour is not wanted, like if some program code should be annotated with bold face etc. the tags [code1]...[/code1] can be used which allows other ELCode tags inside.

Lists [CTRL+L]


An unordered list can be created with the tags [list][/list] like

[list]
[*]Ocean
[*]Forest
[*]Desert
[/list]

to produce

  • Ocean
  • Forest
  • Desert

A new list item starts with [*], which can be entered via the keyboard shortcut CTRL-N. In an ordered list one can specify what is used before each item. To create a numbered list one can use [list=1][/list]:

[list=1]
[*]Ocean
[*]Forest
[*]Desert
[/list]

to produce

  1. Ocean
  2. Forest
  3. Desert

for an alphabetical list [list=a][/list] like

[list=a]
[*]Ocean
[*]Forest
[*]Desert
[/list]

to produce

  1. Ocean
  2. Forest
  3. Desert

Other possibilities are [list=A] for capital letters and [list=I] for Roman numbering.

Tables


Tables can be created with the tags [table][/table] like

[table border="1"]
One|Two
|-
Three|Four
[/table]

to produce

OneTwo
ThreeFour

The parameters after [table ...] are directly used in the HTML <table> tag. To increase the cell padding, on can add for example

[table border="1" cellpadding="20"]
One|Two
|-
Three|Four
[/table]

to produce

OneTwo
ThreeFour

Table headings are not supported, but can be simulated by embedding the cell contents with a [B]...[/B] tag.

Creating links


Hyperlinks or Uniform Resouce Locators (URLs) can be created in various ways:

  • Any syntactically correct URL gets automatically transformd into a link, even without any leading http:// in front of it. So midas.psi.ch/elog gets converted into midas.psi.ch/elog and http://www.cern.ch gets converted to http://www.cern.ch.

  • To specify an URL explicitly, enclose it in [url]www.cern.ch[/url]

  • If the link should contain some text different from the URL, specify it with [url=http://www.cern.ch]Text[/url] like in

    Go to [url=http://www.cern.ch]CERN[/url]

    which will produce:

    Go to CERN

  • The same works for email addresses. They can be used like in [email]john.doe@domain.org[/email] or simply as john.dow@domain.org which will be converted automatically into john.doe@domain.org.

Embedding images [CTRL+M]


There are two ways to embed images in the text:

  • Images can be embedded in the text with the [img]URL[/img] tags, where the URL must point to an image availabe on the internet. Note that this only works for images which are accessible through a web browser. Images on your local hard disk cannot be accessed from outside unless you don't run a public web server. You can for example embed the elog logo with:

    [img]http://midas.psi.ch/elogs/elog.png[/img]

    which produces

  • You can upload an image as an attachment in your elog entry and link to it with

    [img]elog:/1[/img]

    where elog:/1 stands for the first attachment, elog:/2 for the second etc. If you reference your attached images in this way, they are not shown again at the end of the entry.

Other keyboard shortcuts

  • CTRL+P: Preview
  • CTRL+ENTER: Submit
It should be noted that keyboard shortcuts do not work properly in all browsers. They have been successfully tested in Firefox.
ELOG Home Page
elog-3.1.3-1/resources/eloglang.zh_CN-UTF80000644000175000017500000004777413076344200017113 0ustar rogerroger# # Chinese (Simplified) translation by Exaos.Lee@gmail.com # # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = 新建 Edit = 编辑 Delete = 删除 Reply = 回复 Find = 查找 Last day = 昨天 Last 10 = 最近 10 条 Config = 配置 Change password = 修改密码 Logout = 注销 Help = 帮助 Back = 返回 Submit = 提交 First = 最先 Last = 最后 Previous = 前一条 Next = 下一条 Copy to = 拷贝到 Move to = 移动到 Save = 保存 Cancel = 取消 Wrong password = 密码错误 Please login = 请登陆 Username = 用户 Password = 密码 Old password = 旧密码 New password = 亲密码 ELOG change password = 修改 ELOG 密码 Several logbooks are defined on this host = 该主机设置多个记录本 Please select the one to connect to = 请选择要打开的记录本 Change password for user = 修改用户密码 Logged in as = 登陆者: Entry date = 记录日期 with = 和 This is the last entry = 这是最后一条记录 This is the first entry = 这是第一条记录 Email sent to = 发送电子邮件到 Email notification suppressed = 电子邮件通知已被挂起 please select = 请选择 Suppress Email notification = 挂起 Email 通知 Resubmit as new entry = 重新提交为新记录 Attachment = 附件 Reply to this = 回复此内容 In reply to = 此回复是针对 Fields marked with = 内容标识为 are required = 是必须的 Please check that it exists and elogd has write access = 请检查它是否存在并且 elogd 有修改的权限 Error: Attribute %s not supplied = 错误:没有提供 %s 属性 Please go back and enter the %s field = 请返回并填写 %s 内容 Please use your browser's back button to go back = 请使用你浏览器的返回按钮返回 ELOG find = ELOG 查找 Find = 查找 Search = 搜索 Reset Form = 重置表单 Summary only = 仅显示摘要 Show attachments = 显示附件 Printable output = 打印输出 Sort in reverse order = 按逆序排序 Start date = 开始日期 End date = 结束日期 Year = 年 Text = 文本 Search all logbooks = 搜索所有的记录本 Last %d days = 最近 %d 天 Last %d entries = 最近 %d 条记录 No entries found = 未发现记录 A new entry has been submitted on %s = 新的记录已经提交到 %s Logbook = 记录本 Yes = 确定 No = 取消 Error deleting message: status = 删除记录错误:状态 Error: Command "%s" not allowed = 错误:命令 "%s" 被禁止 Cannot open file %s = 无法打开文件 %s Cannot write to %s = 无法写入文件 %s No SMTP host defined in [global] section of configuration file = 配置文件的 [global] 区域中没有定义 SMTP 主机 Display threads = 显示主题 Logbook is empty = 记录本无内容 Download = 下载 and all its replies = 和它所有的回复 Message ID = 消息 ID Not logged in = 没有登陆 Login = 登陆 Page %d of %d = 第 %d 页/共 %d 页 all entries = 所有记录 Login name = 登陆名称 Full name = 全名 ELOG user config = ELOG 用户配置 ELOG new user = ELOG 新用户 Admin = 管理 Select user = 选择用户 Remove user = 删除用户 New user = 创建新用户 Retype new password = 再次输入新密码 New passwords do not match, please retype = 两次输入的新密码不匹配,请重新输入 Retype password = 重新输入密码 exists already = 已经存在 Register as new user = 注册新用户 A new ELOG user has been registered on %s = 在 %s 有新的用户注册 Email = 电子邮件 User "%s" registered on logbook "%s" = 用户 "%s" 已经在记录本 "%s" 上注册 User [%s] has been deleted = 用户 [%s] 已被删除 Hit following URL to activate that account = 点击下面的 URL 来激活相应帐号 ELOG registration = ELOG 注册 Your ELOG account has been activated on host = 你的 ELOG 帐号已经在主机上激活 You can access it at = 你可以访问 Are you sure to delete these messages? = 你真的决定删除这些记录吗? Select = 选择 All entries = 所有记录 Day = 天 Week = 周 Month = 月 Show last = 显示最近记录 Goto page = 跳转到页面 All = 所有 Display = 显示 entries per page = 记录每页 Toggle all = 反向选择全部 Selected entries = 已选记录 Collapse = 折叠 Expand = 展开 Full = 全文 Summary = 摘要 Threaded = 主题 Filters = 过滤条件 Mode = 模式 Options = 选项 Error: start date after end date = 错误:开始日期在结束日期之后 and all their replies = 和它们所有的回复 Upload = 上载 Requested = 请求的 A new ELOG user wants to register on "%s" = 新的 ELOG 用户希望注册到 "%s" Search text also in attributes = 同时搜索属性中的文本 Date = 日期 ID = ID Forgot password? = 忘记密码了吧? ELOG password recovery = ELOG 密码恢复 No Email address registered with user name "%s" = 用户 "%s" 没有提供电子邮件地址 Error sending Email via "%s" = 无法通过 "%s" 发送电子邮件 Forgot = 忘记 User "%s" registered on host "%s" = 用户 "%s" 注册到机器 "%s" Enter your user name or email address = 编辑你的用户名或者电子邮件地址 Password recovery for ELOG %s = ELOG %s 的密码恢复 Host = 主机 Your ELOG account has been activated = 你的 ELOG 帐号已经被激活 Maximum number of attachments reached = 达到最大附件数 on = 在 Entry is currently edited by = 记录正在被编辑,编辑者为 recipients = 回执 Suppress shell execution = 禁止执行脚本 Maximum number of replies (%d) exceeded = 超过最大的回复数 (%d) Update = 更新 Change %s = 修改 %s Add new option here = 新的选项 Add %s = 增加 %s Synchronization = 同步 Error: Value %s not allowed for boolean attributes = 错误:值 %s 不允许为布尔属性 Error: Attribute option %s not existing = 错误:属性选项 %s 不存在 Last submission = 最近记录 Expand all = 展开所有 Synchronize = 同步 No mirror server defined in configuration file = 配置文件中没有定义镜像服务器 user = 用户 Entry can only be edited %1.2lg hours after creation = 记录只能在被创建 %1.2lg 小时之后才能够再编辑 Display full entries = 显示全部记录内容 No entry selected for deletion = 没有选择要删除的记录 Cannot read entry from local logbook = 无法从本地记录本中读取记录 Cannot connect to host %s, port %d = 无法连接到主机 %s 端口 %d Cannot receive "%s" = 无法接收 "%s" Received wrong entry id "%d" = 收到错误的记录 ID 号 "%d" Entry #%d is locked on remote server = 记录 #%d 被远程服务器锁定 Cannot save remote entry locally = 无法将远程记录保存在本地 local = 本地 remote = 远程 Please delete %s or %s entry to resolve conflict = 请选择删除记录 %s 或 %s 以解决冲突问题 Error sending local entry = 发送本地记录错误 Local entry submitted = 本地记录已提交 Error deleting remote entry = 删除远程记录错误 New entry cannot be written to directory "%s" = 新记录无法写入路径 "%s" Entry %s cannot be read from logbook "%s" = 记录 "%s" 无法从记录本 "%s" 中读取 This entry has been deleted = 此记录已被删除 by = 通过 Entries = 记录 Cannot connect to remote server "%s" = 无法连接远程服务器 "%s" Remote server is not an ELOG server = 远程服务器不是 ELOG 服务器 Incorrect remote ELOG server version %s = 不正确的远程服务版本 %s Error accessing remote logbook = 访问远程记录本错误 Invalid HTTP header = 无效的 HTTP 字头 No user name supplied to access remote logbook = 未提供访问远程记录本的用户名 Synchronizing logbook %s with server "%s" = 将记录本 %s 与服务器 "%s" 同步 Configuration has been changed locally and remotely = 本地和远程的配置已经改变 Please merge manually to resolve conflict = 请手动合并以解决冲突 Logbook "%s" does not exist on remote server = 记录本 "%s" 并不存在于远程服务器上 Entry has been changed locally and remotely = 本地和远程的记录已经改变 Entry deleted locally = 已删除本地记录 Changed local entry ID to %d = 改变本地记录 ID 号为 %d Entry deleted remotely = 已删除远程记录 All entries identical = 所有记录匹配 Invalid mirror_id or entry_date = 无效的 mirror_id 或 entry_date Synchronize all logbooks = 同步所有的记录本 Calendar = 日历 Remote entry received = 已接收远程记录 Pick a date = 选择一个日期 Please select at least one '%s' = 请选择至少一个 '%s' Please select a '%s' = 请选择一个 '%s' Please enter month for attribute '%s' = 请为属性 '%s' 输入月份 Please enter day for attribute '%s' = 请为属性 '%s' 输入日期 Please enter year for attribute '%s' = 请为属性 '%s' 输入年份 Please enter attribute '%s' = 请输入属性 '%s' Entry time = 记录时间 Start = 开始 End = 结束 After = 之后 to = 到 Before = 之前 Previous Year = 前一年 Next Year = 后一年 Please enter numeric value for '%s' = 请为 '%s' 键入数值 Error: Attribute %s must be numeric = 错误:属性 %s 必须是数值 3 Months = 3 个月 6 Months = 6 个月 Error sending local message = 发送本地消息错误 Error receiving message = 接收消息错误 Are you sure to delete this entry? = 你决定要删除此记录吗? Import = 导入 CSV filename = CSV 文件名 Field separator = 分隔符 Edit entry = 编辑记录 Delete entry = 删除记录 CSV Import = 导入 CSV ELOG CSV import = 导入 ELOG CSV Derive attributes from CSV file = 从 CSV 文件中获取属性 Comma = 逗号 Semicolon = 分号 Preview import = 输入预览 CSV import preview of %s = %s 的 CSV 输入预览 Too many attributes in CSV file = CSV 文件中有太多的属性 %d entries successfully imported = 成功导入 %d 条记录 No 'Attributes' option present in %s = 在 %s 不存在 'Attributes' 项目 CSV ("," separated) = CSV (以 "," 分隔) CSV (";" separated) = CSV (以 ";" 分隔) Auto detect = 自动探测 From = 来自 text = 文本 Column header '%s' must be present in CSV file = 列字头 '%s' 必须存在于 CSV 文件中 Fill text body = 填写文本内容 Please re-enter filename = 请重新输入文件名 Last x = 最后的 x Activate = 激活 Entry has been modified = 记录已被修改 No attachment file specified = 没有指定附件文件 Delete this logbook = 删除此记录本 Rename this logbook = 重命名此记录本 Create new logbook = 创建新的记录本 Syntax error in config file = 配置文件中语法错误 Email notification = 电子邮件通知 Are you sure to delete logbook "%s"? = 你决定删除记录本 "%s" 吗? Logbook "%s" exists already, please choose different name = 记录本 "%s" 已经存在,请选择其它名称 Rename logbook = 重命名记录本 Enter new logbook name = 输入新的记录本名称 Logbook name = 记录本名称 Use existing logbook as template = 使用现有的记录本作为模板 none = 无 URL is redirected to: = URL 重导向到: Remote config should be received = 应该接收远程配置 Local entry should be submitted = 应该提交本地记录 Remote entry should be received = 应该接收远程记录 Local entry = 本地记录 %s should be deleted = %s 应该被删除 Entry should be deleted locally = 应该删除本地记录 Local entry ID should be changed to %d = 本地记录 ID 应该更改为 %d Remote entry = 远程记录 Entry should be deleted remotely = 应该删除远程记录 Click here to delete this entry = 点击此处删除该记录 Maximum number of attribute options exceeded = 超过最大允许的属性项目个数 Please increase MAX_N_LIST in elogd.c and recompile = 请增加 elogd.c 文件中 MAX_N_LIST 的值,然后重新编译 You can "steal" the lock by editing this entry = 你可以通过编辑此记录“悄悄”移去锁定属性 Several logbooks groups are defined on this host = 此主机已经定义了几个记录本的组 Please select one to list the logbooks in that group = 请选择一个组来显示其中的记录本 No logbook defined on this server = 此服务器没有定义记录本 Goto ELOG home page = 跳转到 ELOG 主页 Please enter "%s" = 请输入 "%s" Change config file = 修改配置文件 Click here to delete %d entries = 请点击此处删除 %d 条记录 Please check that it exists and elogd has write access and disk is not full = 请检查它是否存在及 elogd 是否有对它修改的权限并且磁盘是否已满 Show = 显示 Hide = 隐藏 Hide all = 隐藏所有 Show all = 显示所有 This logbook requires authentication = 此记录本需要认证 Attachment file "%s" empty or not found = 附件文件 "%s" 为空或者没有找到 Case sensitive = 大小写敏感 List = 列表 Date must be between 1970 and 2037 = 日期必须在 1970 到 2037 之间 up = 向上 down = 向下 stop = 停止 Entry = 记录 name may not contain blanks = 名称不应该包括空格 regular expressions = 正则表达式 Text fields are treated as %s = 文本区域作为 %s Subscribe to logbooks = 订阅记录本 enable automatic email notifications = 打开自动电子邮件通知功能 Set all = 设置所有 Set none = 设置无 Please enter hour for attribute '%s' = 请为属性 '%s' 输入小时 Please enter minute for attribute '%s' = 请为属性 '%s' 输入分钟 Please enter second for attribute '%s' = 请为属性 '%s' 输入秒 No admin user has been defined in configuration file = 配置文件中没有定义管理用户名 Duplicate = 重复 Preview = 预览 Show only new entries = 仅显示新的记录 New entries since = 自此时间以来的新记录 Show all entries = 显示所有记录 FONT = 字体 SIZE = 大小 COLOR = 颜色 Encoding = 编码格式 Hide attachments = 隐藏附件 HelpELCode = ELCode 帮助 Cannot lookup server name = 无法找到服务器名称 Cannot connect to server = 无法连接到服务器 %s wrote = %s 写道 Quote = 引用 Insert current time = 插入当前时间 Please enter numeric value for year of attribute '%s' = 请输入属性 '%s' 的年份数值 smiling = 微笑 happy = 高兴 winking = 眨眼 big grin = 露齿大笑 crying = 哭泣 cool = 耍酷 frowning = 蹙眉 confused = 迷惑 mad = 疯了 pleased = 愉悦 tongue = 结舌 yawn = 哈欠 User "%s" not found in password file = 用户 "%s" 不在密码本中 Cannot write to file %s = 无法写入文件 %s Invalid user name "%s" or password for remote logbook = 用户名 "%s" 或密码对远程记录本无效 An old ELOG entry has been updated = 一个旧的 ELOG 记录已被更新 A new ELOG entry has been submitted = 已提交一个新的 ELOG 记录 Invalid user name or password = 无效的用户名或密码 Upload image = 上载图片 Please enter filename or URL = 请输入文件名或 URL Maximum allowed file size is = 最大允许上载文件大小为 Enter filename or URL = 输入文件名或 URL Image uploaded successfully = 图片上载成功 Image "%s" uploaded successfully = 图片 "%s" 成功上载 Cannot retrieve file from URL "%s" = 无法从 "%s" 处载入文件 Enter name of hyperlink = 输入超链接名称 Enter URL of hyperlink = 输入超链接的 URL astonished = 惊愕 Error in regular expression "%s" = 正则表达式 "%s" 中有错 File not found at URL "%s" = 无法从 "%s" 处发现文件 Entry size too large for email notification = 记录大小太大,无法发送电子邮件通知 Please specify a valid email address = 请指定有效的电子邮件地址 Cannot send email notification to "%s" = 无法向 "%s" 发送电子邮件通知 Error: Command "%s" is not allowed for user "%s" = 错误:命令 "%s" 禁止让用户 "%s" 执行 Shell execution not enabled via -x flag = Shell 的可执行权限没有打开 -x 标志 Enter heading level (1, 2 or 3) = 输入标题层次 (1, 2 或 3) User name may not contain blanks = 用户名不可以包含空格 Empty password not allowed = 不允许使用空密码 Password may not contain blanks = 密码不允许包含空格 Anonymous = 匿名 Activation notice has been sent to %s = 激活通知已经送达 %s Your request has been forwarded to the administrator = 您的请求已经发送给管理员 You will be notified by email upon activation of your new account = 您在新帐号激活后将会得到电子邮件通知 # #---- please translate following items and then remove this comment ----# # Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_zh_CN-UTF8.html0000644000175000017500000000354212556103751020135 0ustar rogerroger ELOG -- Ӽ¼İ
ELOG -- Ӽ¼İ

Ӽ¼ (The Electronic Logbook, ELog) ͨ Web ҳϢĴ洢Ͳҡļ Elog ϵͳṩһ ¼ (logbooks) Ƿֱ𱣴ڷĶλϡ

ժҪ

Ĭ£ʾǼ¼¼¼ij¼ʾü¼ȫݡ ͨťֱ鿴һǰһһһ¼

½ ťڴµļ¼ļ޸ģ ťڱ༭һѾڵļ¼ظ ťڶһѴڵļ¼һظͻظʼơ

ťڴ򿪲ѯҳ棬ҳݹʾ¼еļ¼ÿһǿյ붼һĹǡ͡(AND) Ĺϵûѡʾ¼ем¼

Ϣ

ϸݣǹ Elog ÷μ ELOG ҳ.


ߣS. Ritt, 28 August 2001 ķ룺 ϼ 2005109
elog-3.1.3-1/resources/eloglang.zh_CN-GB23120000644000175000017500000004357713076344200017162 0ustar rogerroger# # Chinese (Simplified) translation by Exaos.Lee@gmail.com # # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = ½ Edit = ༭ Delete = ɾ Reply = ظ Find = Last day = Last 10 = 10 Config = Change password = ޸ Logout = ע Help = Back = Submit = ύ First = Last = Previous = ǰһ Next = һ Copy to = Move to = ƶ Save = Cancel = ȡ Wrong password = Please login = ½ Username = û Password = Old password = New password = ELOG change password = ޸ ELOG Several logbooks are defined on this host = ö¼ Please select the one to connect to = ѡҪ򿪵ļ¼ Change password for user = ޸û Logged in as = ½ߣ Entry date = ¼ with = This is the last entry = һ¼ This is the first entry = ǵһ¼ Email sent to = ͵ʼ Email notification suppressed = ʼ֪ͨѱ please select = ѡ Suppress Email notification = Email ֪ͨ Resubmit as new entry = ύΪ¼¼ Attachment = Reply to this = ظ In reply to = ˻ظ Fields marked with = ݱʶΪ are required = DZ Please check that it exists and elogd has write access = Ƿڲ elogd ޸ĵȨ Error: Attribute %s not supplied = ûṩ %s Please go back and enter the %s field = 뷵زд %s Please use your browser's back button to go back = ʹķذť ELOG find = ELOG Find = Search = Reset Form = ñ Summary only = ʾժҪ Show attachments = ʾ Printable output = ӡ Sort in reverse order = Start date = ʼ End date = Year = Text = ı Search all logbooks = еļ¼ Last %d days = %d Last %d entries = %d ¼ No entries found = δּ¼ A new entry has been submitted on %s = µļ¼Ѿύ %s Logbook = ¼ Yes = ȷ No = ȡ Error deleting message: status = ɾ¼״̬ Error: Command "%s" not allowed = "%s" ֹ Cannot open file %s = ޷ļ %s Cannot write to %s = ޷дļ %s No SMTP host defined in [global] section of configuration file = ļ [global] ûж SMTP Display threads = ʾ Logbook is empty = ¼ Download = and all its replies = еĻظ Message ID = Ϣ ID Not logged in = ûе½ Login = ½ Page %d of %d = %d ҳ/ %d ҳ all entries = м¼ Login name = ½ Full name = ȫ ELOG user config = ELOG û ELOG new user = ELOG û Admin = Select user = ѡû Remove user = ɾû New user = û Retype new password = ٴ New passwords do not match, please retype = 벻ƥ䣬 Retype password = exists already = Ѿ Register as new user = עû A new ELOG user has been registered on %s = %s µûע Email = ʼ User "%s" registered on logbook "%s" = û "%s" Ѿڼ¼ "%s" ע User [%s] has been deleted = û [%s] ѱɾ Hit following URL to activate that account = URL Ӧʺ ELOG registration = ELOG ע Your ELOG account has been activated on host = ELOG ʺѾϼ You can access it at = Է Are you sure to delete these messages? = ľɾЩ¼ Select = ѡ All entries = м¼ Day = Week = Month = Show last = ʾ¼ Goto page = תҳ All = Display = ʾ entries per page = ¼ÿҳ Toggle all = ѡȫ Selected entries = ѡ¼ Collapse = ۵ Expand = չ Full = ȫ Summary = ժҪ Threaded = Filters = Mode = ģʽ Options = ѡ Error: start date after end date = 󣺿ʼڽ֮ and all their replies = еĻظ Upload = Requested = A new ELOG user wants to register on "%s" = µ ELOG ûϣעᵽ "%s" Search text also in attributes = ͬʱеı Date = ID = ID Forgot password? = ˰ɣ ELOG password recovery = ELOG ָ No Email address registered with user name "%s" = û "%s" ûṩʼַ Error sending Email via "%s" = ޷ͨ "%s" ͵ʼ Forgot = User "%s" registered on host "%s" = û "%s" עᵽ "%s" Enter your user name or email address = ༭ûߵʼַ Password recovery for ELOG %s = ELOG %s ָ Host = Your ELOG account has been activated = ELOG ʺѾ Maximum number of attachments reached = ﵽ󸽼 on = Entry is currently edited by = ¼ڱ༭༭Ϊ recipients = ִ Suppress shell execution = ִֹнű Maximum number of replies (%d) exceeded = Ļظ (%d) Update = Change %s = ޸ %s Add new option here = µѡ Add %s = %s Synchronization = ͬ Error: Value %s not allowed for boolean attributes = ֵ %s Ϊ Error: Attribute option %s not existing = ѡ %s Last submission = ¼ Expand all = չ Synchronize = ͬ No mirror server defined in configuration file = ļûж徵 user = û Entry can only be edited %1.2lg hours after creation = ¼ֻڱ %1.2lg Сʱܹ֮ٱ༭ Display full entries = ʾȫ¼ No entry selected for deletion = ûѡҪɾļ¼ Cannot read entry from local logbook = ޷ӱؼ¼жȡ¼ Cannot connect to host %s, port %d = ޷ӵ %s ˿ %d Cannot receive "%s" = ޷ "%s" Received wrong entry id "%d" = յļ¼ ID "%d" Entry #%d is locked on remote server = ¼ #%d Զ̷ Cannot save remote entry locally = ޷Զ̼¼ڱ local = remote = Զ Please delete %s or %s entry to resolve conflict = ѡɾ¼ %s %s Խͻ Error sending local entry = ͱؼ¼ Local entry submitted = ؼ¼ύ Error deleting remote entry = ɾԶ̼¼ New entry cannot be written to directory "%s" = ¼¼޷д· "%s" Entry %s cannot be read from logbook "%s" = ¼ "%s" ޷Ӽ¼ "%s" жȡ This entry has been deleted = ˼¼ѱɾ by = ͨ Entries = ¼ Cannot connect to remote server "%s" = ޷Զ̷ "%s" Remote server is not an ELOG server = Զ̷ ELOG Incorrect remote ELOG server version %s = ȷԶ̷汾 %s Error accessing remote logbook = Զ̼¼ Invalid HTTP header = Ч HTTP ͷ No user name supplied to access remote logbook = δṩԶ̼¼û Synchronizing logbook %s with server "%s" = ¼ %s "%s" ͬ Configuration has been changed locally and remotely = غԶ̵Ѿı Please merge manually to resolve conflict = ֶϲԽͻ Logbook "%s" does not exist on remote server = ¼ "%s" Զ̷ Entry has been changed locally and remotely = غԶ̵ļ¼Ѿı Entry deleted locally = ɾؼ¼ Changed local entry ID to %d = ı䱾ؼ¼ ID Ϊ %d Entry deleted remotely = ɾԶ̼¼ All entries identical = м¼ƥ Invalid mirror_id or entry_date = Ч mirror_id entry_date Synchronize all logbooks = ͬеļ¼ Calendar = Remote entry received = ѽԶ̼¼ Pick a date = ѡһ Please select at least one '%s' = ѡһ '%s' Please select a '%s' = ѡһ '%s' Please enter month for attribute '%s' = Ϊ '%s' · Please enter day for attribute '%s' = Ϊ '%s' Please enter year for attribute '%s' = Ϊ '%s' Please enter attribute '%s' = '%s' Entry time = ¼ʱ Start = ʼ End = After = ֮ to = Before = ֮ǰ Previous Year = ǰһ Next Year = һ Please enter numeric value for '%s' = Ϊ '%s' ֵ Error: Attribute %s must be numeric = %s ֵ 3 Months = 3 6 Months = 6 Error sending local message = ͱϢ Error receiving message = Ϣ Are you sure to delete this entry? = Ҫɾ˼¼ Import = CSV filename = CSV ļ Field separator = ָ Edit entry = ༭¼ Delete entry = ɾ¼ CSV Import = CSV ELOG CSV import = ELOG CSV Derive attributes from CSV file = CSV ļлȡ Comma = Semicolon = ֺ Preview import = Ԥ CSV import preview of %s = %s CSV Ԥ Too many attributes in CSV file = CSV ļ̫ %d entries successfully imported = ɹ %d ¼ No 'Attributes' option present in %s = %s 'Attributes' Ŀ CSV ("," separated) = CSV ( "," ָ) CSV (";" separated) = CSV ( ";" ָ) Auto detect = Զ̽ From = text = ı Column header '%s' must be present in CSV file = ͷ '%s' CSV ļ Fill text body = дı Please re-enter filename = ļ Last x = x Activate = Entry has been modified = ¼ѱ޸ No attachment file specified = ûָļ Delete this logbook = ɾ˼¼ Rename this logbook = ˼¼ Create new logbook = µļ¼ Syntax error in config file = ļ﷨ Email notification = ʼ֪ͨ Are you sure to delete logbook "%s"? = ɾ¼ "%s" Logbook "%s" exists already, please choose different name = ¼ "%s" Ѿڣѡ Rename logbook = ¼ Enter new logbook name = µļ¼ Logbook name = ¼ Use existing logbook as template = ʹеļ¼Ϊģ none = URL is redirected to: = URL ص򵽣 Remote config should be received = ӦýԶ Local entry should be submitted = Ӧύؼ¼ Remote entry should be received = ӦýԶ̼¼ Local entry = ؼ¼ %s should be deleted = %s Ӧñɾ Entry should be deleted locally = Ӧɾؼ¼ Local entry ID should be changed to %d = ؼ¼ ID ӦøΪ %d Remote entry = Զ̼¼ Entry should be deleted remotely = ӦɾԶ̼¼ Click here to delete this entry = ˴ɾü¼ Maximum number of attribute options exceeded = Ŀ Please increase MAX_N_LIST in elogd.c and recompile = elogd.c ļ MAX_N_LIST ֵȻ± You can "steal" the lock by editing this entry = ͨ༭˼¼ġȥ Several logbooks groups are defined on this host = Ѿ˼¼ Please select one to list the logbooks in that group = ѡһʾеļ¼ No logbook defined on this server = ˷ûж¼ Goto ELOG home page = ת ELOG ҳ Please enter "%s" = "%s" Change config file = ޸ļ Click here to delete %d entries = ˴ɾ %d ¼ Please check that it exists and elogd has write access and disk is not full = Ƿڼ elogd Ƿж޸ĵȨ޲ҴǷ Show = ʾ Hide = Hide all = Show all = ʾ This logbook requires authentication = ˼¼Ҫ֤ Attachment file "%s" empty or not found = ļ "%s" Ϊջûҵ Case sensitive = Сд List = б Date must be between 1970 and 2037 = ڱ 1970 2037 ֮ up = down = stop = ֹͣ Entry = ¼ name may not contain blanks = ƲӦðո regular expressions = ʽ Text fields are treated as %s = ıΪ %s Subscribe to logbooks = ļ¼ enable automatic email notifications = Զʼ֪ͨ Set all = Set none = Please enter hour for attribute '%s' = Ϊ '%s' Сʱ Please enter minute for attribute '%s' = Ϊ '%s' Please enter second for attribute '%s' = Ϊ '%s' No admin user has been defined in configuration file = ļûжû Duplicate = ظ Preview = Ԥ Show only new entries = ʾµļ¼ New entries since = Դʱ¼¼ Show all entries = ʾм¼ FONT = SIZE = С COLOR = ɫ Encoding = ʽ Hide attachments = ظ HelpELCode = ELCode Cannot lookup server name = ޷ҵ Cannot connect to server = ޷ӵ %s wrote = %s д Quote = Insert current time = 뵱ǰʱ Please enter numeric value for year of attribute '%s' = '%s' ֵ smiling = ΢Ц happy = winking = գ big grin = ¶ݴЦ crying = cool = ˣ frowning = ü confused = Ի mad = pleased = tongue = yawn = Ƿ User "%s" not found in password file = û "%s" 뱾 Cannot write to file %s = ޷дļ %s Invalid user name "%s" or password for remote logbook = û "%s" Զ̼¼Ч An old ELOG entry has been updated = һɵ ELOG ¼ѱ A new ELOG entry has been submitted = ύһµ ELOG ¼ Invalid user name or password = Чû Upload image = ͼƬ Please enter filename or URL = ļ URL Maximum allowed file size is = ļСΪ Enter filename or URL = ļ URL Image uploaded successfully = ͼƬسɹ Image "%s" uploaded successfully = ͼƬ "%s" ɹ Cannot retrieve file from URL "%s" = ޷ "%s" ļ Enter name of hyperlink = 볬 Enter URL of hyperlink = 볬ӵ URL astonished = Error in regular expression "%s" = ʽ "%s" д File not found at URL "%s" = ޷ "%s" ļ Entry size too large for email notification = ¼С̫޷͵ʼ֪ͨ Please specify a valid email address = ָЧĵʼַ Cannot send email notification to "%s" = ޷ "%s" ͵ʼ֪ͨ Error: Command "%s" is not allowed for user "%s" = "%s" ֹû "%s" ִ Shell execution not enabled via -x flag = Shell ĿִȨûд -x ־ Enter heading level (1, 2 or 3) = (1, 2 3) User name may not contain blanks = û԰ո Empty password not allowed = ʹÿ Password may not contain blanks = 벻ո Anonymous = Activation notice has been sent to %s = ֪ͨѾʹ %s Your request has been forwarded to the administrator = Ѿ͸Ա You will be notified by email upon activation of your new account = ʺż󽫻õʼ֪ͨ # #---- please translate following items and then remove this comment ----# # Select period = Last week = Last month = Last 3 Months = Last 6 Months = Last Year = Enter %s = Select %s = Bold text CTRL+B = Italics text CTRL+I = Underlined text CTRL+U = Centered text = Insert hyperlink = Insert email = Insert image CTRL+M = Insert quote = Insert list CTRL+L = Insert table = Insert heading CTRL+H = Insert code CTRL+O = Show the smiley bar = Insert current time/date = Entry is locked on local server and therefore skipped = Keep me logged in on this computer = for the next %d minutes = for the next hour = for the next %d hours = for the next %d days = keep original values = Entry is locked = Edit anyhow = Insert horizontal line = Insert anchor point = keep original text = unspecified = To subscribe to any logbook, click on 'Config' in that logbook = ELOG XML import = Derive attributes from XML file = XML filename = Invalid date format = XML file does not contain %s element = XML import preview of %s = XML Import = A old entry has been updated on %s = First entry, Ctrl-Home = Previous entry, Ctrl-PgUp = Next entry, Ctrl-PgDn = Last entry, Ctrl-End = ELOG import = Please choose format to import: = Enter text = or until I log out = Make smaller = Original size = Make larger = Rotate left = Rotate right = Delete attachment = Cannot create thumbnail, please check ImageMagick installation = Attachments = Error: Content length (%d) larger than maximum content length (%d) = ELOG error = Only user %s can edit this entry = Export to = Do not ignore first line = Last %d hours = Invalid URL = Attribute "%s" is not allowed in config file = Only user %s can delete this entry = File system full, ELOG cannot continue to work = 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.swedish0000644000175000017500000005513313076344200017020 0ustar rogerroger# Swedish translation by Johan Nyberg # 2006-09-29 # Completed and adjusted by Kenneth Andersson # 2008-01-08, 2010-02-06 # A more recent version may be available at : # http://savannah.psi.ch/websvn/listing.php?repname=elog&path=%2Ftrunk%2Fresources%2F # New = Ny Edit = ndra Delete = Radera Reply = Svara Find = Sk Last day = Idag Last 10 = Senaste 10 Config = Konfigurera Change password = ndra lsenord Logout = Logga ut Help = Hjlp Back = Tillbaka Submit = Spara First = Frsta Last = Sista Previous = Fregende Next = Nsta Copy to = Kopiera till Move to = Flytta till Save = Spara Cancel = Avbryt Wrong password = Fel lsenord Please login = Logga in Username = Anvndarnamn Password = Lsenord Old password = Nuvarande lsenord New password = Nytt lsenord ELOG change password = ndra ELOG lsenord Several logbooks are defined on this host = Flera loggbcker r definerade p denna server Please select the one to connect to = Vlj vilken du vill ppna Change password for user = ndra lsenord fr anvndare Logged in as = Du r inloggad som Entry date = Datum fr post with = med This is the last entry = Detta r sista posten This is the first entry = Detta r frsta posten Email sent to = E-post snt till Email notification suppressed = E-post snds inte please select = Vlj Suppress Email notification = Snd inte e-post Attachment = Bilaga Reply to this = Svara p detta In reply to = Som svar p Fields marked with = Flt markerade med are required = r obligatoriska Please check that it exists and elogd has write access = Kontrollera att den existerar och att elogd har skrivrttighet Please use your browser's back button to go back = Anvnd tillbaka knappen i din webblsare fr att g tillbaka ELOG find = ELOG sk Find = Sk Search = Sk Reset Form = Tm formulret Summary only = Endast sammanfattning Show attachments = Visa bilagor Printable output = Visa utskrivbart format Sort in reverse order = Sortera i omvnd ordningsfljd Start date = Startdatum End date = Slutdatum Year = r Text = Text Search all logbooks = Sk i alla loggbcker Last %d days = De senaste %d dagarna Last %d entries = De senaste %d posterna No entries found = Inga poster hittade Logbook = Loggbok Yes = Ja No = Nej Error deleting message: status = Fel vid borttagning av meddelande: Status No SMTP host defined in [global] section of configuration file = Ingen smtp host r definierad i [global] sektionen i konfigurationsfilen Display threads = Visa trdar Logbook is empty = Loggboken r tom Download = Hmta and all its replies = och alla dess svar Message ID = Meddelande-ID Not logged in = Inte inloggad Login = Logga in Page %d of %d = Sidan %d av %d all entries = alla poster Login name = Anvndarnamn Full name = Fullstndigt namn ELOG user config = ELOG anvndar-konfiguration ELOG new user = Ny ELOG anvndare Admin = Administratr Select user = Vlj anvndare Remove user = Ta bort anvndare New user = Ny anvndare Retype new password = Upprepa det nya lsenordet New passwords do not match, please retype = Upprepningen av det nya lsenordet stmmer inte, prva igen Retype password = Upprepa lsenordet exists already = finns redan Register as new user = Registrera som ny anvndare Email = E-post User "%s" registered on logbook "%s" = Anvndare "%s" registrerad i loggbok "%s" Hit following URL to activate that account = Klicka p fljande URL fr att aktivera detta anvndarkonto ELOG registration = ELOG registrering Your ELOG account has been activated on host = Ditt ELOG konto har aktiverats p server You can access it at = Du kan f tillgng till det hr: Are you sure to delete these messages? = r du sker p att du vill ta bort dessa meddelanden? Select = Vlj All entries = Alla poster Day = Dag Week = Vecka Month = Mnad Show last = Visa senaste Goto page = G till sida All = Alla Display = Visa entries per page = poster per sida Toggle all = Visa alla Selected entries = Valda poster Collapse = Kollapsa Expand = Expandera Full = Fullstndig Summary = Kortfattad Threaded = Visa som trdar Filters = Filter Mode = Format Options = Funktion Error: start date after end date = Fel: Begynnelsedatum senare n slutdatum and all their replies = och alla deras svar Upload = Skicka till server Requested = Begrd A new ELOG user wants to register on "%s" = En ny anvndare vill bli registrerad i loggboken "%s" Search text also in attributes = Sk text ocks i attributen Date = Datum ID = ID Forgot password? = Glmt lsenordet? ELOG password recovery = terskapande av ELOG lsenord No Email address registered with user name "%s" = Ingen e-post adress registrerad fr anvndare "%s" Error sending Email via "%s" = Fel vid sndingen av e-post via "%s" Forgot = Glmt User "%s" registered on host "%s" = Anvndare "%s" p server "%s" registrerad Enter your user name or email address = Ange anvndarnamn eller e-post adress Host = Host Your ELOG account has been activated = Ditt ELOG konto r aktiverat Maximum number of attachments reached = Det maximala antalet bilagor har uppntts on = till Entry is currently edited by = Posten editeras fr tillfllet av recipients = mottagare Suppress shell execution = Exekvera inte shell kommandon Update = Uppdatera by = av Add new option here = Lgg till ett nytt alternativ (en ny funktion) hr Expand all = Expandera alla Synchronization = Synkronisering Last submission = Senaste post Synchronize = Synkronisera No mirror server defined in configuration file = Ingen mirror-server r definierad i konfigurationsfilen user = anvndare Entry can only be edited %1.2lg hours after creation = Poster kan ndras tidigast %1.2lg timmar efter att det skapats Display full entries = Visa all information No entry selected for deletion = Ingen post har markerets fr borttagning Cannot read entry from local logbook = Kan inte lsa frn den lokala loggboken Cannot receive "%s" = Kan inte ta emot "%s" Received wrong entry id "%d" = Mottog fel ID "%d" Entry #%d is locked on remote server = Post #%d r lst vid den fjrranslutna servern Cannot save remote entry locally = Kan inte spara den fjrranslutna posten lokalt local = lokal remote = fjrransluten Error sending local entry = Fel vid avsndning av lokal post Local entry submitted = Lokal post skickad Error deleting remote entry = Fel vid borttagning av den fjrranslutna posten New entry cannot be written to directory "%s" = Ny post kan inte sparas i katalogen "%s" This entry has been deleted = Denna post har tagits bort Entries = Poster Cannot connect to remote server "%s" = Kan inte ansluta till den fjrranslutna servern "%s" Remote server is not an ELOG server = Den fjrranslutna servern r inte en ELOG server Error accessing remote logbook = Fel vid tkomst till den fjrranslutna loggboken Invalid HTTP header = Felaktig HTTP header No user name supplied to access remote logbook = Inget anvndarnamn angivet fr access till den fjrranslutna loggboken Configuration has been changed locally and remotely = Konfigurationen r ndrad vid lokal- och fjrranslutningen Please merge manually to resolve conflict = Sammanfoga manuellt fr att lsa problemet Logbook "%s" does not exist on remote server = Loggboken "%s" existerer inte p den fjrranslutna server Entry has been changed locally and remotely = Posten r ndrad vid lokal- och fjrranslutning Entry deleted locally = Posten r borttagen lokalt Changed local entry ID to %d = ndrat den lokala postens ID till %d Entry deleted remotely = Posten borttagen frn fjrranslutning All entries identical = Alla poster identiska Invalid mirror_id or entry_date = Ogiltigt mirror_id eller entry_id Synchronize all logbooks = Synkronisera alla loggbcker Calendar = Kalender Remote entry received = Posten frn fjrranslutning r mottagen Pick a date = Vlj ett datum Entry time = Datum Start = Start End = Slut From = Frn After = Efter to = till Before = Fre Previous Year = Fregende r Next Year = Nsta r 3 Months = 3 mnader 6 Months = 6 mnader Error sending local message = Fel vid avsndelsen av lokalt meddelande Error receiving message = Fel vid mottagning av meddelande Are you sure to delete this entry? = r du sker p att du vill ta bort denna post? ELOG CSV import = ELOG CSV import Import = Importera CSV filename = CSV filnamn Field separator = Fltseparator Edit entry = Redigera flt Delete entry = Ta bort flt CSV Import = CSV Import Derive attributes from CSV file = Skapa attributen frn CSV filen Comma = Komma Semicolon = Semikolon Preview import = Frhandsgranska import Too many attributes in CSV file = Fr mnga attribut i CSV filen %d entries successfully imported = %d poster har importerats CSV ("," separated) = CSV ("," separerad) CSV (";" separated) = CSV (";" separerad) Auto detect = Auto-detektering Resubmit as new entry = Snd som ny post text = text Fill text body = Fyll i meddelandetext Please re-enter filename = Upprepa filnamn New = Ny Last x = Senaste x Activate = Aktivera Entry has been modified = Posten har modifierats No attachment file specified = Ingen bilaga angiven Delete this logbook = Ta bort denna loggbok Rename this logbook = ndra namn p denna loggbok Create new logbook = Skapa ny loggbok Syntax error in config file = Syntaxfel i konfigurationsfilen Email notification = E-Post Are you sure to delete logbook "%s"? = r du sker p att du vill ta bort denna loggbok? Logbook "%s" exists already, please choose different name = En loggbok med namnet "%s" existerar redan, vlj ett annat namn Rename logbook = ndra namn p loggboken Enter new logbook name = Ange nytt namn p loggboken Logbook name = Loggbokens namn Use existing logbook as template = Anvnd existerande loggbok som "template" none = ingen URL is redirected to: = URL omdirigering till: Remote config should be received = Den fjrranslutna konfigurationen kommer att mottas Local entry should be submitted = Den lokal posten kommer att skickas Remote entry should be received = Den fjrranslutna posten kommer att mottas Local entry = Lokal post Entry should be deleted locally = Posten br tas bort lokalt Local entry ID should be changed to %d = Lokal post ID br ndras till Remote entry = Fjrransluten post Entry should be deleted remotely = Posten kommer att tas bort frn fjrranslutningen Click here to delete this entry = Klicka hr fr att ta bort posten Maximum number of attribute options exceeded = Det maximala antalet attribut alternativ har verskridits Please increase MAX_N_LIST in elogd.c and recompile = ka MAX_N_LIST i filen elogd.c och kompilera om You can "steal" the lock by editing this entry = Du kan "stjla" lset genom att editera posten Several logbooks groups are defined on this host = Flera grupper av loggbcker r definerade p den host Please select one to list the logbooks in that group = Vlj en fr att lista loggbckerna i den gruppen No logbook defined on this server = Ingen loggbok definierad p denna server Goto ELOG home page = G till ELOG hemsidan Please enter "%s" = Ange "%s" Change config file = ndra konfigurationsfilen Please check that it exists and elogd has write access and disk is not full = Kontrollera att filen existerar, att elogd har skrivrttigheter till filen och att hrddisken inte r full Show = Visa Hide = Gm Hide all = Gm alla Show all = Visa alla This logbook requires authentication = Denna loggbok krver autentisering Attachment file "%s" empty or not found = Bilagefilen "%s" r tom eller saknas Case sensitive = Versalknslig List = Lista Date must be between 1970 and 2037 = Datum mste ligga mellan 1970 och 2037 up = upp down = ner stop = stopp Entry = Post name may not contain blanks = fr inte innehlla blanktecken regular expressions = "regular expressions" Subscribe to logbooks = Prenumerera p loggbcker enable automatic email notifications = aktivera automatisk sndning av e-post Set all = Stt alla Set none = Stt ingen No admin user has been defined in configuration file = Ingen administratr har angivits i konfigurationsfilen Duplicate = Dubblett Cannot lookup server name = Kan inte sl upp domnnamnsserver Cannot connect to server = Kan inte kontakta server Quote = Citat Insert current time = Ange aktuell tid Preview = Frhandsgranska FONT = FONT SIZE = STORLEK COLOR = FRG smiling = ler happy = glad winking = blinkar big grin = storflinar crying = grter cool = cool frowning = fnyser confused = konfunderad mad = arg pleased = njd tongue = tunga yawn = gspning Encoding = Kodning User "%s" not found in password file = Anvndare "%s" existerar inte Invalid user name "%s" or password for remote logbook = Felaktigt anvndarnamn "%s" eller lsenord fr fjrransluten loggbok Hide attachments = Gm bilagor Show only new entries = Visa endast nya poster Show all entries = Visa alla poster New entries since = Nya poster sedan An old ELOG entry has been updated = En gammal ELOG post har uppdaterats A new ELOG entry has been submitted = En ny ELOG post har skickats in Invalid user name or password = Felaktigt anvndarnamn eller lsenord Upload image = Skicka bild till server Please enter filename or URL = Ange filnamn eller URL Maximum allowed file size is = Maximala filstorleken r Enter filename or URL = Ange filnamn eller URL Image uploaded successfully = Bilden har skickats till server Image "%s" uploaded successfully = Bilden "%s" har skickats till server HelpELCode = HelpELCode Cannot retrieve file from URL "%s" = Kan inte hmta filen frn URL "%s" Enter name of hyperlink = Ange lnkens namn Enter URL of hyperlink = Ange lnkens URL astonished = frvnad Error in regular expression "%s" = Fel i "regular experssion" "%s" File not found at URL "%s" = Filen kan inte hittas vid URL "%s" Entry size too large for email notification = Postens storlek r fr att sndas som e-post Please specify a valid email address = Ange en giltig e-post adress Cannot send email notification to "%s" = Kan inte snda e-post till "%s" Error: Command "%s" is not allowed for user "%s" = Fel: kommando "%s" r inte tilltet fr anvndare "%s" Shell execution not enabled via -x flag = Shell exekvering inte aktiverad genom -x flaggan Enter heading level (1, 2 or 3) = Ange titel niv User name may not contain blanks = Anvndarnamn fr inte innehlla blanktecken Empty password not allowed = Tomt lsenord inte tilltet Password may not contain blanks = Lsenordet fr inte innehlla blanktecken Anonymous = Anonym Change %s = ndra %s %s wrote = %s skrev Maximum number of replies (%d) exceeded = Det maximala antalet svar (%d) har verskridits Please select at least one '%s' = Vlj minst en '%s' Please select a '%s' = Vlj en '%s' Please enter month for attribute '%s' = Ange mnad fr attribut '%s' Please enter day for attribute '%s' = Ange dag fr attribut '%s' Please enter year for attribute '%s' = Ange r fr attribut '%s' Please enter hour for attribute '%s' = Ange timme fr attribut '%s' Please enter minute for attribute '%s' = Ange minut fr attribut '%s' Please enter second for attribute '%s' = Ange sekund fr attribut '%s' Please enter attribute '%s' = Ange attribut '%s' Please enter numeric value for '%s' = Ange numeriskt vrde fr '%s' Please enter numeric value for year of attribute '%s' = Ange numeriskt vrde fr ret gllande attribut '%s' Add %s = Lgg till %s Text fields are treated as %s = Textflt behandlas som %s Cannot open file %s = Kan inte ppna filen %s Cannot write to %s = Kan inte skriva till %s A new ELOG user has been registered on %s = En ny ELOG anvndare har blivit registrerad som %s Cannot write to file %s = Kan inte skriva till filen %s Activation notice has been sent to %s = Aktiveringsmeddelande har skickats till %s User [%s] has been deleted = Anvndare [%s] har tagits bort Password recovery for ELOG %s = terskapande av lsenord fr ELOG %s Column header '%s' must be present in CSV file = Kolumnhuvud '%s' mste finnas i CSV filen CSV import preview of %s = Frhandsgranskning av CSV import gllande %s Incorrect remote ELOG server version %s = Fel fjrransluten ELOG server version %s Cannot connect to host %s, port %d = Fr ingen kontakt med host %s, port %d Synchronizing logbook %s with server "%s" = Synkroniserar loggbok %s med server "%s" Please delete %s or %s entry to resolve conflict = Ta bort posten %s eller %s fr att lsa problemet %s should be deleted = %s br tas bort Click here to delete %d entries = Klicka hr fr att ta bort "%d" poster A new entry has been submitted on %s = En ny post har sndts in p %s No 'Attributes' option present in %s = Inga 'Attribut' alternativ givna fr %s Error: Attribute %s not supplied = Fel: Attribut %s r inte angivet Please go back and enter the %s field = G tillbaka och fyll i fltet %s Error: Attribute %s must be numeric = Fel: Attribut %s mste ha ett numeriskt vrde Error: Value %s not allowed for boolean attributes = Fel: Vrdet %s r inte tilltet fr bool'ska attribut Error: Attribute option %s not existing = Fel: Attribut alternativet %s eksisterar inte Entry %s cannot be read from logbook "%s" = Posten %s kan inte lsas i loggboken "%s" Your request has been forwarded to the administrator = Din registrering har vidarebefordrats till ELOG administratren You will be notified by email upon activation of your new account = Du kommer att f en bekrftelse per email nr ditt nya anvndarkonto r aktiverat Error: Command "%s" not allowed = Kommandot "%s" r inte tilltet Select period = Vlj period Last week = Senaste veckan Last month = Senaste mnaden Last 3 Months = Senaste 3 mnaderna Last 6 Months = Senaste 6 mnaderna Last Year = Senaste ret Enter %s = Enter Select %s = Vlj Bold text CTRL+B = Fet text CTRL+B Italics text CTRL+I = Kursiv text CTRL+I Underlined text CTRL+U = Understruken text CTRL+U Centered text = Centrerad text Insert hyperlink = Infoga hyperlnk Insert email = Infoga e-post Insert image CTRL+M = Infoga bild CTRL+M Insert quote = Infoga citationstecken Insert list CTRL+L = Infoga lista CTRL+L Insert table = Infoga tabell Insert heading CTRL+H = Infoga huvud CTRL+H Insert code CTRL+O = infoga kod CTRL+O Show the smiley bar = Visa smileyrad Insert current time/date = Infoga aktuell tid/dag Entry is locked on local server and therefore skipped = Posten r lst p lokal server och hoppas ver Keep me logged in on this computer = Hll mig inloggad p denna dator for the next %d minutes = under fljande %d minuter for the next hour = under nsta timme for the next %d hours = under fljande %d timmar for the next %d days = under fljande %d dagar keep original values = behll originalvrden Entry is locked = Posten r lst Edit anyhow = ndra nd Insert horizontal line = Infoga horisontell linje Insert anchor point = Infoga bokmrke keep original text = behll originaltexten unspecified = ospecifiserad To subscribe to any logbook, click on 'Config' in that logbook = Fr att prenumerera p en loggbok, klicka p 'Konfigurera' i den loggboken ELOG XML import = ELOG XML-import Derive attributes from XML file = Hmta attribut frn XML-fil XML filename = XML filnamn Invalid date format = Ogiltigt datumformat XML file does not contain %s element = XML-filen innehller inte %s element XML import preview of %s = XML-import frhandsgranskning av %s XML Import = XML-Import A old entry has been updated on %s = En gammal post har uppdaterats p %s First entry, Ctrl-Home = Frsta posten, Ctrl-Home Previous entry, Ctrl-PgUp = Fregende post, Ctrl-PgUp Next entry, Ctrl-PgDn = Nsta post, Ctrl-PgDn Last entry, Ctrl-End = Sista posten, Ctrl-End ELOG import = ELOG import Please choose format to import: = Vlj format fr import: Enter text = Skriv text or until I log out = eller tills jag loggar ut Make smaller = Frminska Original size = Originalstorlek Make larger = Frstora Rotate left = Rotera vnster Rotate right = Rotera hger Delete attachment = Radera bilaga Cannot create thumbnail, please check ImageMagick installation = Kan inte skapa thumbnail, kontrollera installationen av ImageMagick Attachments = Bilagor Error: Content length (%d) larger than maximum content length (%d) = Fel: Innehllet (%d) r strre n max tilltet (%d) ELOG error = ELOG fel Only user %s can edit this entry = Endast anvndare %s kan redigera denna post Export to = Exportera till Do not ignore first line = Ignorera inte frsta raden Last %d hours = Senaste %d timmarna Invalid URL = Ogiltig URL Attribute "%s" is not allowed in config file = Attributet "%s" r inte tilltet i konfigurationsfilen Only user %s can delete this entry = Endast anvndare %s kan radera denna post File system full, ELOG cannot continue to work = Filsystemet fullt, ELOG kan inte fortstta arbeta # #---- please translate following items and then remove this comment ----# # 3 Days = Last 3 Days = Really submit this entry? = Account activation for ELOG logbook "%s" = Account activation for ELOG on host "%s" = Please click the URL below to activate following ELOG account = Activation URL = An email has been sent to <%s> = Use that email to activate your account = Registration request for ELOG logbook "%s" = Registration request for ELOG on host "%s" = Are you sure you want to deactivate your own account? = Active = Invalid activation code = Error activating user = A password recovery email for user "%s" has been sent to %s = User "%s" has no access to this logbook = Your account has been activated = Please subscribe to logbooks if you want to receive automatic email notifications = This is an automatically generated account recovery email for host %s = Please click on following link to recover your account = Email address "%s" not registered = User name "%s" not registered = Error accessing password file = CSV (";" separated) + Text = Entry can only be deleted %1.2lg hours after creation = Drop attachments here... = Insert Timestamp = Pending draft available = %d pending drafts available = Create new entry = If you leave this page you will lose your unsaved changes = Edited = All time = Draft = days = Draft saved at = You might however then overwrite each other's modifications = Draft entry created on %s by %s = Really delete this entry? = This is a draft message, edit and submit it to make it permanent = Restrict search to last = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_german.html0000755000175000017500000000443112556103751017662 0ustar rogerroger ELOG Electronisches Logbuch Hilfe
ELOG Elektronisches Logbuch — Hilfe

Das elektronische Logbuch (ELog) kann dazu benutzt werden, Einträge über eine Web Schnittstelle zu speichern und abzurufen. Abhängig von der Konfiguration, kann das ELog-System ein oder mehrere Logbücher beherbergen, die in verschiedenen Bereichen auf dem Server gespeichert sind.

Schnellüberblick

Normalerweise wird eine Seite mit den letzten Einträgen eines Logbuches dargestellt. Mit dem Klick auf einen Eintrag kann dieser komplett dargestellt werden. Man kann die Pfeiltasten verwenden, um den ersten, vorhergenden, nächsten oder letzten Eintrag darzustellen.

Die Schaltfläche Neu erzeugt einen neuen Eintrag. Mit der Ändern Schaltfläche kann man existierende Einträge ändern, sofern dieses in der Konfigurationsdatei erlaubt ist. Die Antworten Schaltfläche erzeugt eine Antwort auf eine existierenden Eintrag, ähnlich wie bei einem Email-System.

Die Finden Schaltfläche ffnet ein Formular, das die Suche von Einträgen aus einem Logbuch über Filterregeln erlaubt. Jedes ausgefüllte Feld wirkt wie ein zusätzlicher Filter, das mit den anderen Regeln UND-verknüpft wird. Falls kein Filter ausgewählt ist, werden alle Einträge eines Logbuches ausgewählt.

Weitere Informationen

Mehr Informationen, insbesondere über die Konfiguration von ELog, gibt es auf der ELOG home page.


S. Ritt, 28 August 2001
elog-3.1.3-1/resources/eloglang.german_UTF80000644000175000017500000006144613076344200017435 0ustar rogerroger# # German translation by stefan.ritt@psi.ch # A more recent version may be available at: # https://bitbucket.org/ritt/elog # New = Neu Edit = Ändern Delete = Löschen Reply = Antworten Find = Finden Last day = Letzter Tag Last 10 = Letzte 10 Config = Konfigurieren Change password = Kennwort ändern Logout = Abmelden Help = Hilfe Back = Zurück Submit = Absenden First = Erster Last = Letzter Previous = Vorheriger Next = Nächster Copy to = Kopieren nach Move to = Verschieben nach Save = Speichern Cancel = Abbrechen Wrong password = Falsches Kennwort Please login = Bitte Anmelden Username = Benutzername Password = Kennwort Old password = Altes Kennwort New password = Neues Kennwort ELOG change password = ELOG Kennwort ändern Several logbooks are defined on this host = Mehrere Logbücher sind auf diesem Computer eingerichtet Please select the one to connect to = Bitte wählen Sie eines aus Change password for user = Kennwort ändern für Benutzer Logged in as = Angemeldet als Entry date = Eingabedatum with = mit This is the last entry = Dies ist der letzte Eintrag This is the first entry = Dies ist der erste Eintrag Email sent to = Email gesendet an Email notification suppressed = Email-Benachrichtigung unterdrückt please select = bitte auswählen Suppress Email notification = Email-Benachrichtigung unterdrücken Resubmit as new entry = Als neuen Eintrag absenden Attachment = Anhang Reply to this = Antwort hierzu In reply to = Als Antwort auf Fields marked with = Felder markiert mit are required = sind Pflichteinträge Please check that it exists and elogd has write access = Bitte überprüfen, ob es existiert und ob elogd Schreibzugriff hat Error: Attribute %s not supplied = Fehler: Attribut %s nicht ausgefüllt Please go back and enter the %s field = Bitte zurückgehen und das Feld %s ausfüllen Please use your browser's back button to go back = Bitte die Zurück-Taste Ihres Browsers betätigen ELOG find = ELOG suchen Find = Finden Search = Suchen Reset Form = Eingabe zurücksetzen Summary only = Nur Zusammenfassung Show attachments = Anhänge anzeigen Printable output = Druckfähige Anzeige Sort in reverse order = Sortieren in umgekehrter Reihenfolge Start date = Anfangsdatum End date = Enddatum Year = Jahr Text = Text Search all logbooks = Alle Logbücher durchsuchen Last %d days = Letzte %d Tage Last %d entries = Letzte %d Einträge No entries found = Keine Einträge gefunden A new entry has been submitted on %s = Ein neuer Eintrag wurde von %s abgeschickt Logbook = Logbuch Yes = Ja No = Nein Error deleting message: status = Fehler beim Löschen: Status Error: Command "%s" not allowed = Fehler: Befehl "%s" nicht erlaubt Cannot open file %s = Datei %s kann nicht geöffnet werden Cannot write to %s = Datei %s kann nicht geschrieben werden No SMTP host defined in [global] section of configuration file = Kein SMTP Host im [global] Bereich der Konfigurationsdatei definiert Display threads = Antwortkette darstellen Logbook is empty = Logbuch ist leer Download = Herunterladen and all its replies = und alle Antworten Message ID = Eintrag-ID Not logged in = Nicht eingeloggt Login = Anmelden Page %d of %d = Seite %d von %d all entries = alle Einträge Login name = Name zum Anmelden Full name = Benutzername ELOG user config = ELOG Benutzerkonfiguration ELOG new user = Neuer ELOG-Benutzer Admin = Verwaltung Select user = Benutzer auswählen Remove user = Benutzer entfernen New user = Neuen Benutzer anlegen Retype new password = Neues Kennwort nochmals eingeben New passwords do not match, please retype = Kennwörter verschieden, bitte nochmals eingeben Retype password = Kennwort erneut eingeben exists already = existiert bereits Register as new user = Als neuer Benutzer anmelden A new ELOG user has been registered on %s = Ein neuer Benutzer hat sich auf %s angemeldet Email = Email User "%s" registered on logbook "%s" = Benutzer "%s" im Logbuch "%s" registriert User [%s] has been deleted = Benutzer [%s] wurde gelöscht Hit following URL to activate that account = Auf folgende URL klicken um Benutzerkonto zu aktivieren ELOG registration = ELOG Registrierung Your ELOG account has been activated on host = Ihr ELOG Konto wurde aktiviert auf You can access it at = Der Zugriff erfolgt über Are you sure to delete these messages? = Diese Einträge wirklich löschen? Select = Auswählen All entries = Alle Einträge Day = Tag Week = Woche Month = Monat Show last = Zeige letzte(n) Goto page = Gehe zu Seite All = Alle Display = Zeige entries per page = Einträge pro Seite Toggle all = Alle umschalten Selected entries = Ausgewählte Einträge Collapse = eingeklappt Expand = erweitert Full = ausführlich Summary = zusammengefasst Threaded = mit Antwortketten Filters = Filter Mode = Modus Options = Optionen Error: start date after end date = Fehler: Anfangsdatum nach Enddatum and all their replies = und alle Antworten Upload = Hochladen Requested = Erfordert A new ELOG user wants to register on "%s" = Ein neuer Benutzer möchte sich auf "%s" registrieren Search text also in attributes = Text auch in Attributen suchen Date = Datum ID = ID Forgot password? = Kennwort vergessen? ELOG password recovery = ELOG Kennwort-Wiederherstellung No Email address registered with user name "%s" = Für Benutzername "%s" wurde keine Email-Adresse registriert Error sending Email via "%s" = Fehler beim Versenden der Email über "%s" Forgot = vergessen User "%s" registered on host "%s" = Benutzer "%s" auf Computer "%s" registriert Enter your user name or email address = Bitte Benutzernamen oder Email-Adresse eingeben Password recovery for ELOG %s = Kennwort-Wiederherstellung für ELOG %s Host = Computer Your ELOG account has been activated = Ihr ELOG Konto wurde aktiviert Maximum number of attachments reached = Maximale Anzahl von Anhängen erreicht on = auf Entry is currently edited by = Eintrag wird momentan bearbeitet von recipients = Empfänger Suppress shell execution = Kommando-Ausführung unterdrücken Update = Aktualisieren by = von Change %s = %s ändern Add new option here = Neue Option hier eingeben Expand all = Alles erweitern Add %s = %s hinzufügen Synchronization = Synchronisation Error: Value %s not allowed for boolean attributes = Fehler: Wert %s ist nicht erlaubt für bool'sche Attribute Error: Attribute option %s not existing = Fehler: Attribut Option %s existiert nicht Last submission = Letzter Eintrag Synchronize = Synchronisieren No mirror server defined in configuration file = Kein Spiegel-Server in Konfigurationsdatei definiert user = Benutzer Maximum number of replies (%d) exceeded = Maximale Anzahl von Antworten (%d) überschritten Entry can only be edited %1.2lg hours after creation = Eintrag kann nur %1.2lg Stunden nach Erstellung bearbeitet werden Display full entries = Gesamte Einträge darstellen No entry selected for deletion = Kein Eintrag zum Löschen ausgewählt Cannot read entry from local logbook = Kann Eintrag nicht aus lokalem Logbuch lesen Cannot connect to host %s, port %d = Kann zum Computer %s, Port %d nicht verbinden Cannot receive "%s" = Kann "%s" nicht empfangen Received wrong entry id "%d" = Falscher Eintrag mit ID "%d" empfangen Entry #%d is locked on remote server = Eintrag #%d ist auf anderem Computer in Bearbeitung Cannot save remote entry locally = Kann fremden Eintrag nicht lokal speichern local = lokalen remote = fremden Please delete %s or %s entry to resolve conflict = Bitte %s oder %s Eintrag löschen um Konflikt zu lösen Error sending local entry = Fehler beim Senden von lokalem Eintrag Local entry submitted = Lokaler Eintrag gesendet Error deleting remote entry = Fehler beim Löschen von fremdem Eintrag New entry cannot be written to directory "%s" = Neuer Eintrag kann nicht in Verzeichnis "%s" geschrieben werden Entry %s cannot be read from logbook "%s" = Eintrag %s kann nicht aus Logbuch "%s" gelesen werden This entry has been deleted = Dieser Eintrag wurde gelöscht Entries = Einträge Cannot connect to remote server "%s" = Verbindung mit entferntem Server "%s" kann nicht hergestellt werden Remote server is not an ELOG server = Entfernter Server ist kein ELOG Server Incorrect remote ELOG server version %s = Falsche Version %s des entfernten Servers Error accessing remote logbook = Fehler beim Zugriff auf entferntes Logbuch Invalid HTTP header = Falscher HTTP Kopf No user name supplied to access remote logbook = Kein Benutzername definiert zum Zugriff auf entferntes Logbuch Synchronizing logbook %s with server "%s" = Synchronisiere Logbuch %s mit Server "%s" Configuration has been changed locally and remotely = Konfiguration wurde lokal und entfernt geändert Please merge manually to resolve conflict = Bitte manuell zusammenfügen um Konflikt aufzulösen Logbook "%s" does not exist on remote server = Logbuch "%s" existiert nicht auf entferntem Server Entry has been changed locally and remotely = Eintrag ist lokal und entfernt geändert worden Entry deleted locally = Eintrag lokal entfernt Changed local entry ID to %d = Lokale ID des Eintrages auf %d geändert Entry deleted remotely = Entfernter Eintrag gelöscht All entries identical = Alle Einträge identisch Invalid mirror_id or entry_date = Falsche mirror_id oder entry_date Synchronize all logbooks = Alle Logbücher synchronisieren Calendar = Kalender Remote entry received = Entfernter Eintrag empfangen Pick a date = Datum auswählen Please select at least one '%s' = Bitte mindestens ein '%s' auswählen Please select a '%s' = Bitte ein '%s' auswählen Please enter month for attribute '%s' = Bitte Monat für Attribut '%s' eingeben Please enter day for attribute '%s' = Bitte Tag für Attribut '%s' eingeben Please enter year for attribute '%s' = Bitte Jahr für Attribut '%s' eingeben Please enter attribute '%s' = Bitte Attribut '%s' eingeben Entry time = Eingabezeit Start = Start End = Ende From = Von After = Nach to = bis Before = vor Previous Year = Voriges Jahr Next Year = Nächstes Jahr Please enter numeric value for '%s' = Bitte numerischen Wert für '%s' eingeben Error: Attribute %s must be numeric = Fehler: Attribut %s muß numerisch sein 3 Months = 3 Monate 6 Months = 6 Monate Error sending local message = Fehler beim Versenden von lokalem Eintrag Error receiving message = Fehler beim Empfangen von Eintrag Are you sure to delete this entry? = Diesen Eintrag wirklich löschen? ELOG CSV import = ELOG CSV Import Import = Import CSV filename = CSV Dateiname Field separator = Trennzeichen Edit entry = Eintrag ändern Delete entry = Eintrag löschen CSV Import = CSV Import Derive attributes from CSV file = Attribute aus CSV Datei übernehmen Comma = Komma Semicolon = Semikolon Preview import = Import überprüfen CSV import preview of %s = CSV Import Überprüfung von %s Too many attributes in CSV file = Zu viele Attribute in CSV Datei %d entries successfully imported = %d Einträge erfolgreich importiert No 'Attributes' option present in %s = Keine 'Attributes'-Options in %s vorhanden CSV ("," separated) = CSV (mit "," getrennt) CSV (";" separated) = CSV (mit ";" getrennt) Auto detect = Automatisch erkennen text = Text Column header '%s' must be present in CSV file = Spaltenüberschrift '%s' muß in der CSV Datei vorhanden sein Fill text body = Haupttext importieren Please re-enter filename = Bitte Dateinamen erneut eingeben Last x = Letzte x Activate = Aktivieren Entry has been modified = Eintrag wurde verändert No attachment file specified = Es wurde kein Anhang angegeben Delete this logbook = Dieses Logbuch löschen Rename this logbook = Dieses Logbuch umbenennen Create new logbook = Neues Logbuch anlegen Syntax error in config file = Syntax-Fehler in Konfigurationsdatei Email notification = Email Benachrichtigung Are you sure to delete logbook "%s"? = Logbuch "%s" wirklich löschen? Logbook "%s" exists already, please choose different name = Logbuch "%s" existiert bereits, bitte anderen Namen wählen Rename logbook = Logbuch umbenennen Enter new logbook name = Bitte neuen Logbuchnamen eingeben Logbook name = Logbuchname Use existing logbook as template = Vorhandenes Logbuch als Vorlage benutzen none = keines URL is redirected to: = URL wird umgeleitet zu: Remote config should be received = Fremde Konfiguration sollte empfangen werden Local entry should be submitted = Lokaler Eintrag sollte gesendet werden Remote entry should be received = Fremder Eintrag sollte empfangen werden Local entry = Lokaler Eintrag %s should be deleted = %s sollte gelöscht werden Entry should be deleted locally = Eintrag sollte lokal gelöscht werden Local entry ID should be changed to %d = ID von lokalem Eintrag sollte zu %d geändert werden Remote entry = Fremder Eintrag Entry should be deleted remotely = Fremder Eintrag sollte gelöscht werden Click here to delete %d entries = Hier klicken um %d Einträge zu löschen Click here to delete this entry = Hier klicken um diesen Eintrag zu löschen Maximum number of attribute options exceeded = Maximal Anzahl von Attributen wurde überschritten Please increase MAX_N_LIST in elogd.c and recompile = Bitte MAX_N_LIST in elogd.c erhöhen und neu kompilieren You can "steal" the lock by editing this entry = Diese Sperre kann "gestohlen" werden durch bearbeiten dieses Eintrags Several logbooks groups are defined on this host = Mehrere Logbuch-Gruppen sind auf diesem Computer eingerichtet Please select one to list the logbooks in that group = Bitte eine auswählen, um die Logbücher in dieser Gruppe anzuzeigen No logbook defined on this server = Auf diesem Computer ist kein Logbuch eingerichtet Goto ELOG home page = Zur ELOG Home Page Please enter "%s" = Bitte "%s" eingeben Change config file = Konfigurationsdatei ändern Please check that it exists and elogd has write access and disk is not full = Bitte überprüfen ob es existiert, ob elogd Schreibzugriff hat und ob Festplatte nicht voll ist Show = Anzeigen Hide = Verstecken Hide all = Alle verstecken Show all = Alle anzeigen This logbook requires authentication = Dieses Logbuch erfordert Authentifizierung Attachment file "%s" empty or not found = Anhang Datei "%s" ist leer oder nicht vorhanden Case sensitive = Großschreibung beachten List = Liste Date must be between 1970 and 2037 = Das Datum muß zwischen 1970 und 2037 liegen up = Hoch down = Runter stop = Stop Entry = Eintrag name may not contain blanks = Name darf keine Leerzeichen enthalten regular expressions = Reguläre Ausdrücke Text fields are treated as %s = Textfelder werden als %s behandelt Subscribe to logbooks = Logbücher abonnieren enable automatic email notifications = Automatische Email Benachrichtigung ermöglichen Set all = Alle ermöglichen Set none = Keinen ermöglichen Insert current time = Aktuelle Zeit einfügen Please enter hour for attribute '%s' = Bitte Stunde eingeben für Attribut '%s' Please enter minute for attribute '%s' = Bitte Minute eingeben für Attribut '%s' Please enter second for attribute '%s' = Bitte Sekunde eingeben für Attribut '%s' No admin user has been defined in configuration file = In der Konfigurationsdatei ist kein Benutzer mit Administrationsrechten eingetragen Duplicate = Duplizieren Cannot lookup server name = Kann Server Name nicht finden Cannot connect to server = Kann mit Server nicht verbinden %s wrote = %s schrieb Quote = Zitat Please enter numeric value for year of attribute '%s' = Bitte numerischen Wert für Jahr eingeben von Attribut '%s' Preview = Vorschau FONT = SCHRIFTART SIZE = GRÖSSE COLOR = FARBE smiling = lachend happy = fröhlich winking = winken big grin = großes grinsen crying = weinen cool = cool frowning = stirnrunzeln confused = verwirrt mad = verrückt pleased = gefallen tongue = Zunge yawn = gähnen Encoding = Kodierung User "%s" not found in password file = Benutzer "%s" kann nicht in der Passwort Datei gefunden werden Cannot write to file %s = Datei %s kann nicht geschrieben werden Invalid user name "%s" or password for remote logbook = Ungültiger Benutzername "%s" oder Passwort für entferntes Logbuch Hide attachments = Anhänge verstecken Show only new entries = Nur neue Einträge zeigen Show all entries = Alle Einträge zeigen New entries since = Neue Einträge seit An old ELOG entry has been updated = Ein alter ELOG Eintrag wurde erneuert A new ELOG entry has been submitted = Ein neuer ELOG Eintrag wurde eingegeben Invalid user name or password = Ungültiger Benutzername oder Passwort Upload image = Bild hochladen Please enter filename or URL = Bitte Dateiname oder URL eingeben Maximum allowed file size is = Maximal erlaubte Dateigröße ist Enter filename or URL = Dateiname oder URL eingeben Image uploaded successfully = Bild erfolgreich hochgeladen Image "%s" uploaded successfully = Datei "%s" erfolgreich hochgeladen HelpELCode = HilfeELCode Cannot retrieve file from URL "%s" = Kann Datei von URL "%s" nicht laden Enter name of hyperlink = Name von Hyperlink eingeben Enter URL of hyperlink = URL von Hyperlink eingeben astonished = erstaunt Error in regular expression "%s" = Fehler in regulärem Ausdruck "%s" File not found at URL "%s" = Datei bei URL "%s" nicht gefunden Entry size too large for email notification = Eintrag zu groß für Email-Benachrichtigung Please specify a valid email address = Bitte eine gültige Email-Adresse eingeben Cannot send email notification to "%s" = Kann Email-Benachrichtigung nicht an "%s" senden Error: Command "%s" is not allowed for user "%s" = Fehler: Kommando "%s" ist nicht erlaubt für Benutzer "%s" Shell execution not enabled via -x flag = Kommando-Ausführung ist nicht mit -x Kommandozeilenoption eingeschalten Enter heading level (1, 2 or 3) = Bitte Überschrift-Stufe eingeben (1, 2 oder 3) User name may not contain blanks = Benutzername darf keine Leerzeichen enthalten Empty password not allowed = Leeres Passwort ist nicht erlaubt Password may not contain blanks = Passwort darf keine Leerzeichen enthalten Anonymous = Anonym Activation notice has been sent to %s = Aktivierungsbenachrichtigung wurde an %s geschickt Your request has been forwarded to the administrator = Der Antrag wurde an den Administrator geschickt You will be notified by email upon activation of your new account = Sie werden benachrichtigt, wenn Ihr Benutzerkonto aktiviert wurde Select period = Zeitraum auswählen Last week = Letzte Woche Last month = Letzter Monat Last 3 Months = Letzten 3 Monate Last 6 Months = Letzten 6 Monate Last Year = Letztes Jahr Enter %s = %s eingeben Select %s = %s auswählen Bold text CTRL+B = Fettschrift CTRL+B Italics text CTRL+I = Kursiv CTRL+I Underlined text CTRL+U = Unterstrichen CTRL+U Centered text = Zentrierter Text Insert hyperlink = Hyperlink einfügen Insert email = Email einfügen Insert image CTRL+M = Bild einfügen CTRL+M Insert quote = Zitat einfügen Insert list CTRL+L = Liste einfügen CTRL+L Insert table = Tabelle einfügen Insert heading CTRL+H = Überschrift einfügen CTRL+H Insert code CTRL+O = Code einfügen CTRL+O Show the smiley bar = Smileys Balken zeigen Insert current time/date = Aktuelle Zeit/Datum einfügen Entry is locked on local server and therefore skipped = Eintrag ist auf lokalem Server gesperrt und wird deshalb übersprungen Keep me logged in on this computer = Auf diesem Computer eingeloggt bleiben for the next %d minutes = für die nächsten %d Minuten for the next hour = für die nächste Stunde for the next %d hours = für die nächsten %d Stunden for the next %d days = für die nächsten %d Tage keep original values = ursprüngliche Werte behalten Entry is locked = Eintrag ist gesperrt Edit anyhow = Trotzdem ändern Insert horizontal line = Horizontale Linie einfügen Insert anchor point = Ankerpunkt einfügen keep original text = ursprünglichen Text behalten unspecified = unspezifiziert To subscribe to any logbook, click on 'Config' in that logbook = Um ein Logbuch zu abonnieren, auf 'Konfigurieren' in diesem Logbuch klicken ELOG XML import = ELOG XML Import Derive attributes from XML file = Attribute aus XML Datei importieren XML filename = XML Dateiname Invalid date format = Ungültiges Datumsformat XML file does not contain %s element = XML Date beinhaltet kein %s Element XML import preview of %s = XML Import Vorschau von %s XML Import = XML Import A old entry has been updated on %s = Ein alter Eintrag wurde auf %s erneuert First entry, Ctrl-Home = Erster Eintrag, Strg-Pos1 Previous entry, Ctrl-PgUp = Vorheriger Eintrag, Strg-BildHoch Next entry, Ctrl-PgDn = Nächster Eintrag, Strg-BildRunter Last entry, Ctrl-End = Letzter Eintrag, Strg-Ende ELOG import = ELOG Import Please choose format to import: = Bitte Importformat auswählen: Enter text = Text eingeben or until I log out = oder bis zum Ausloggen Make smaller = Verkleinern Original size = Originalgröße Make larger = Vergrößern Rotate left = Links rotieren Rotate right = Rechts rotieren Delete attachment = Anhang löschen Cannot create thumbnail, please check ImageMagick installation = Kann Vorschau nicht erstellen, bitte ImageMagick Installation überprüfen Attachments = Anhänge Error: Content length (%d) larger than maximum content length (%d) = Fehler: Datengröße (%d) ist größer als maximal erlaubte Datengröße (%d) ELOG error = ELOG Fehler Only user %s can edit this entry = Nur Benutzer %s kann diesen Eintrag ändern Export to = Exportieren nach Do not ignore first line = Erste Zeile nicht ignorieren Last %d hours = Letzte %d Stunden Invalid URL = Ungültige URL Attribute "%s" is not allowed in config file = Das Attribut "%s" ist in der Konfigurationsdatei nicht erlaubt Only user %s can delete this entry = Nur der Benutzer %s kann diesen Eintrag löschen File system full, ELOG cannot continue to work = Dateisystem voll, ELOG kann nicht weiterarbeiten 3 Days = 3 Tage Last 3 Days = Letzte 3 Tage Really submit this entry? = Diesen Eintrag wirklich abschicken? Account activation for ELOG logbook "%s" = Zugangsaktivierung für das ELOG Logbuch "%s" Account activation for ELOG on host "%s" = Zugangsaktivierung für ELOG auf dem Computer "%s" Please click the URL below to activate following ELOG account = Bitte auf den Link klicken, um folgenden ELOG Zugang zu aktivieren Activation URL = Aktivierungs-URL An email has been sent to <%s> = Eine Email wurde an <%s> gesendet Use that email to activate your account = Bitte diese Email benutzen, um den Zugang zu aktivieren Registration request for ELOG logbook "%s" = Registrierungsanfrage für das ELOG Logbuch "%s" Registration request for ELOG on host "%s" = Registrierungsanfrage für ELOG auf dem Computer "%s" Are you sure you want to deactivate your own account? = Sind sie sicher, dass sie ihren Zugang stilllegen möchten? Active = Aktiv Invalid activation code = Ungültiger Aktivierungscode Error activating user = Fehler bei der Aktivierung des Benutzers A password recovery email for user "%s" has been sent to %s = Eine Email zur Passwort Wiederherstellung für Nutzer "%s" wurde gesendet an %s User "%s" has no access to this logbook = Benutzer "%s" hat keinen Zugang zu diesem Logbuch Your account has been activated = Ihr Zugang wurde aktiviert Please subscribe to logbooks if you want to receive automatic email notifications = Bitte abbonieren Sie Logbücher, um automatisch Email-Benachrichtigungen zu bekommen This is an automatically generated account recovery email for host %s = Dies ist eine automatisch generierte Email für die Rücksetzung des Zugangs auf dem Computer %s Please click on following link to recover your account = Bitte klicken Sie auf folgenden Link, um Ihren Zugang zurückzusetzen Email address "%s" not registered = Email Adresse "%s" ist nicht registriert User name "%s" not registered = Benutzername "%s" ist nicht registriert Error accessing password file = Fehler beim Zugriff auf Passwort Datei CSV (";" separated) + Text = CSV (mit ";" getrennt) + Text Entry can only be deleted %1.2lg hours after creation = Eintrag kann nur %1.2lg Stunden nach dem Anlegen gelöscht werden Drop attachments here... = Anhänge bitte hier ablegen... Insert Timestamp = Aktuelle Zeit einfügen Pending draft available = Unvollständige Entwürfe sind verfügbar %d pending drafts available = %d unvollständige Entwürfe sind verfügbar Create new entry = Neuen Eintrag anlegen If you leave this page you will lose your unsaved changes = Wenn Sie diese Seite verlassen werden die nicht gesicherten Änderungen verworfen Edited = Geändert All time = Immer Draft = Entwurf days = Tage Draft saved at = Entwurf gespeichert um You might however then overwrite each other's modifications = Sie würde dann jedoch die Änderungen von jemand anderem überschreiben Draft entry created on %s by %s = Entwurf erstellt am %s von %s Really delete this entry? = Diesen Eintrag wirklich löschen? This is a draft message, edit and submit it to make it permanent = Dies ist ein Entwurf, bitte ändern und abschicken um ihn permanent zu machen Restrict search to last = Suche beschränken auf letzte # #---- please translate following items and then remove this comment ----# # Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloglang.polish0000644000175000017500000006151213076344200016646 0ustar rogerroger# Polish translation by Daniel Sajdyk # A more recent version may be available at : http://www.sajdyk.pl/2015/06/polskie-tumaczenie-dla-aplikacji-elog.html # Charset = UTF-8 # New = Nowy Edit = Edycja Delete = Usuń Reply = Odpowiedz Find = Szukaj Last day = Ostatni dzień Last 10 = 10 ostatnich Config = Konfiguracja Change password = Zmiana hasła Logout = Wylogowanie Help = Pomoc Back = Wstecz Submit = Zatwierdź First = Pierwszy Last = Ostatni Previous = Poprzedni Next = Następna Copy to = Kopuj do Move to = Przenieś do Save = Zapisz Cancel = Anuluj Wrong password = Niepoprawne hasło Please login = Zaloguj się Username = Nazwa użytkownika Password = Hasło Old password = Stare hasło New password = Nowe hasło ELOG change password = ELOG zmiana hasła Several logbooks are defined on this host = Kilka logbooków jest zdefiniowanych na tym hoście Please select the one to connect to = Wybierz do którego chcesz się podłączyć Change password for user = Zmień hasło użytkownikowi Logged in as = Zalogowany jako Entry date = Data wpisu with = z This is the last entry = To jest ostatni wpis This is the first entry = To jest pierwszy wpis Email sent to = Email wysłany do Email notification suppressed = Nie udało się wysłać powiadomienia emailem please select = proszę wybrać Suppress Email notification = Wyślij powiadomienie emailem Resubmit as new entry = Powtórz jako nowy wpis Attachment = Załącznik Reply to this = Odpowiedz na In reply to = W odpowiedzi na Fields marked with = Pole oznaczone are required = są wymagane Please check that it exists and elogd has write access = Proszę sprawdź czy istnieje i czy elog ma prawo zapisu Error: Attribute %s not supplied = Błąd: Atrybut %s nie został dodany Please go back and enter the %s field = Proszę cofnij się i dodaj %s pole Please use your browser's back button to go back = Proszę użyj przycisku "wstecz" w przeglądarce, aby cofnąć ELOG find = ELOG szukaj Find = Szukaj Search = Szukaj Reset Form = Zresetuj formularz Summary only = Wyświetl jako podsumowanie Show attachments = Pokaż załączniki Printable output = Wynik szukania gotowy do druku Sort in reverse order = Posortuj w odwrotnej kolejności Start date = Data początkowa End date = Data końcowa Year = Rok Text = Tekst Search all logbooks = Przeszukaj wszystkie logbooki Last %d days = Ostatnich %d dni Last %d entries = Ostatnich %d wpisów No entries found = Nie znaleziono wpisów A new entry has been submitted on %s = Nowy wpis został dodany o %s Logbook = Logbook Yes = Tak No = Nie Error deleting message: status = Błąd usuwania wiadomości: Status Error: Command "%s" not allowed = Błąd: Polecenie "%s" nie jest dozwolone Cannot open file %s = Nie można otworzyc pliku %s Cannot write to %s = Nie można zapisywac do %s No SMTP host defined in [global] section of configuration file = Serwer SMTP nie został skonfigurowany w sekcji [global] pliku konfiguracyjnego Display threads = Wyświetl jako wątki Logbook is empty = Logbook jest pusty Download = Pobierz and all its replies = i wszystkie jego odpowiedzi Message ID = ID wiadomości Not logged in = Nie zalogowany Login = Login Page %d of %d = Strona %d z %d all entries = wszystkie wpisy Login name = Nazwa użytkownika Full name = Pełna nazwa ELOG user config = ELOG konfiguracja użytkownika ELOG new user = Nowy użytkownik Admin = admin Select user = Wybierz użytkownika Remove user = Usuń użytkownika New user = Nowy użytkownik Retype new password = Powtórz nowe hasło New passwords do not match, please retype = Nowe hasła się nie zgadzają. Proszę powtórz Retype password = Powtórz hasło exists already = już istnieje Register as new user = Zarejestruj jako nowego użytkownika A new ELOG user has been registered on %s = Nowy użytkownik został zarejestrowany w %s Email = Email User "%s" registered on logbook "%s" = użytkownik "%s" został dodany w logbooku "%s" User [%s] has been deleted = użytkownik [%s ] został usuniety Hit following URL to activate that account = Nacisnij ten URL aby aktywować konto ELOG registration = ELOG rejestracja Your ELOG account has been activated on host = Twoje konto ELOG zostało aktywowane na hoście You can access it at = Mozesz się do niego dostać na Are you sure to delete these messages? = Czy napewno chcesz usunąć te wiadomości? Select = Wybierz All entries = Wszystkie wpisy Day = Dzień Week = Tydzień Month = Miesiąc Show last = Pokaż ostatni Goto page = Idź do strony All = Wszystkie Display = Wyświetl entries per page = wpisów na stronę Toggle all = Zaznacz wszystkie Selected entries = Wybierz wpisy Collapse = Zwiń Expand = Rozwiń Full = Wszystko Summary = Podsumowanie Threaded = Według wątków Filters = Filtry Mode = Tryb Options = Opcje Error: start date after end date = Błąd: data poczatkowa jest po dacie końcowej and all their replies = i wszystkie jego odpowiedzi Upload = Prześlij Requested = Żądany A new ELOG user wants to register on "%s" = Nowy użytkownik ELOG chce się zarejestrować na "%s" Search text also in attributes = Szukaj tekstu także w atrybutach Date = Data ID = ID Forgot password? = Zapomniałeś hasła? ELOG password recovery = ELOG przywracanie hasła No Email address registered with user name "%s" = Żaden email nie został połaczony z użytkownikiem "%s" Error sending Email via "%s" = Błąd wysyłania wiadomosci email przez "%s" Forgot = Zapomnij / Porzuć User "%s" registered on host "%s" = użytkownik %s" zarejestrowany na hoście "%s" Enter your user name or email address = Podaj swoją nazwę użytkownika lub adres email Password recovery for ELOG %s = Przywracanie hasła dla ELOG %s Host = Host Your ELOG account has been activated = Twoje konto ELOG zostało aktywowane Maximum number of attachments reached = Maksymalna ilość załącznikow została osiągnięta on = na Entry is currently edited by = Wpis jest aktualnie edytowany przez recipients = odbiorcy Suppress shell execution = Wyłączenie wykonywania powłoki Update = Aktualizacje by = przez Change %s = Zmień %s Add new option here = Dodaj nową opcję tutaj Expand all = Rozwiń wszystko Add %s = Dodaj %s Synchronization = Synchronizacja Error: Value %s not allowed for boolean attributes = Błąd: Wartość %s nie jest dozwolona przez atrybut boolean Error: Attribute option %s not existing = Błąd: Atrybut opcji %s nie istnieje Last submission = Ostatni dostęp Synchronize = Synchronizuj No mirror server defined in configuration file = Serwer lustrzany nie został skonfigurowany w pliku konfiguracyjnym user = użytkownik Maximum number of replies (%d) exceeded = Maksymalna ilość odpowiedzi (%d) została wykorzystana Entry can only be edited %1.2lg hours after creation = Wpis może być edytowany tylko przez %1.2lg godzin od jego utworzenia Display full entries = Wyświetl pełne wpisy (tryb "wszystko") No entry selected for deletion = Żadne wpisy nie zostały wybrane do skasowania Cannot read entry from local logbook = Nie można odczytać wpisów z lokalnego logbooka Cannot connect to host %s, port %d = Nie można podłączyc się do hosta %s, na porcie %d Cannot receive "%s" = Nie można odebrać "%s" Received wrong entry id "%d" = Odebrano blędny wpis ID "%d" Entry #%d is locked on remote server = Wpis #%d jest zablokowany na zdalnym serwerze Cannot save remote entry locally = Nie można zapisac lokalnie zdalnych wpisow local = lokalnie remote = zdalnie Please delete %s or %s entry to resolve conflict = Proszę usunąć %s lub %s aby rozwiązać konflikt Error sending local entry = Błąd wysyłania lokalnego wpisu Local entry submitted = Lokalny wpis został zapisany Error deleting remote entry = Błąd kasowania zdalnego wpisu New entry cannot be written to directory "%s" = Nowy wpis nie moze być zapisany do folderu "%s" Entry %s cannot be read from logbook "%s" = Wpis "%s" nie może być odczytany z logbooka "%s" This entry has been deleted = Wpis został usunięty Entries = Wpisów Cannot connect to remote server "%s" = Nie można połączyć się do zdalnego serwera "%s" Remote server is not an ELOG server = Zdalny serwer nie jest serwerem ELOG Incorrect remote ELOG server version %s = Niepoprawna wersja %s zdalnego serwera ELOG Error accessing remote logbook = Błąd dostępu do zdalnego logbooka Invalid HTTP header = Błąd nagłówka HTTP No user name supplied to access remote logbook = Nie podano nazwy użytkownika w celu dostępu do zdalnego logbooka Synchronizing logbook %s with server "%s" = Synchronizacja logbooka %s z serwerem "%s" Configuration has been changed locally and remotely = Konfiguracja została zmieniona lokalnie i zdalnie Please merge manually to resolve conflict = Proszę scalić ręcznie aby rozwiązać konflikt Logbook "%s" does not exist on remote server = Logbook "%s" nie istnieje na zdalnym serwerze Entry has been changed locally and remotely = Wpis został zmieniony lokalnie i zdalnie Entry deleted locally = Wpis usunięty lokalnie Changed local entry ID to %d = Zmieniono ID lokalnego wpisu na %d Entry deleted remotely = Wpis usunięty zdalnie All entries identical = Wszystkie wpisy identyczne Invalid mirror_id or entry_date = Niepoprawny mirror_id lub entry_date Synchronize all logbooks = Synchronizuj wszystkie logbook'i Calendar = Kalendarz Remote entry received = Otrzymano zdalny wpis Pick a date = Podaj date Please select at least one '%s' = Proszę wybrać przynajmniej jeden '%s' Please select a '%s' = Proszę wybrać '%s' Please enter month for attribute '%s' = Proszę uzupełnić miesiąc dla atrybutu '%s' Please enter day for attribute '%s' = Proszę uzupełnić dzień dla atrybutu '%s' Please enter year for attribute '%s' = Proszę uzupełnić rok dla atrybutu'%s' Please enter attribute '%s' = Proszę uzupełnić atrybut '%s' Entry time = Czas wpisu Start = Początek End = Koniec From = Z After = Po to = do Before = Przed Previous Year = Poprzedni rok Next Year = Następny rok Please enter numeric value for '%s' = Proszę podać numeryczną wartość dla '%s' Error: Attribute %s must be numeric = Błąd: Artybut %s musi być numerem 3 Months = 3 Miesiące 6 Months = 6 Miesięcy Error sending local message = Błąd wysyłania lokalnej wiadomości Error receiving message = Błąd odbierania wiadomości Are you sure to delete this entry? = Czy napewno chcesz usunąć ten wpis? ELOG CSV import = ELOG CSV Import Import = Import CSV filename = plik CSV Field separator = Separator pól Edit entry = Edytu wpis Delete entry = Skasuj wpis CSV Import = CSV Import Derive attributes from CSV file = Pobierz atrybuty z pliku CSV Comma = Przecinek Semicolon = Średnik Preview import = Podgląd importu CSV import preview of %s = CSV podgląd importu %s Too many attributes in CSV file = Zbyt wiele atrybutów w pliku CSV %d entries successfully imported = %d wpisów pomyślnie zaimportowano No 'Attributes' option present in %s = Brak opcji 'Attributes' w %s CSV ("," separated) = CSV (pola rozdzielane ",") CSV (";" separated) = CSV (pola rozdzielane ";") Auto detect = Automatyczne wykrywanie text = text Column header '%s' must be present in CSV file = Nagłówek kolumny '%s' musi występować w pliku CSV Fill text body = Wypełnij treścią Please re-enter filename = Proszę ponownie podać nazwę pliku Last x = Ostatnie x Activate = Aktywuj Entry has been modified = Wpis został zmodyfikowany No attachment file specified = Nie wybrano załącznika Delete this logbook = Skasuj tego logbooka Rename this logbook = Zmień nazwę logbooka Create new logbook = Załóż nowego logbooka Syntax error in config file = Błąd składni w pliku konfiguracyjnym Email notification = Potwierdzenie email'em Are you sure to delete logbook "%s"? = Czy jesteś pewien, że chcesz skasować logbook "%s"? Logbook "%s" exists already, please choose different name = Logbook "%s" już istnieje. Proszę wybrać inną nazwę Rename logbook = Zmień nazwę logbooka Enter new logbook name = Podaj nową nazwę logbooka Logbook name = Nazwa logbooka Use existing logbook as template = Użyj istniejącego logbooka jako szablonu none = żaden URL is redirected to: = URL jest przekierowany na: Remote config should be received = Zdalna konfiguracja powinna zostać pobrana Local entry should be submitted = Lokalny wpis powinien zostać wysłany Remote entry should be received = Zdalny wpis powinien zostać odebrany Local entry = Lokalny wpis %s should be deleted = %s powinien być skasowany Entry should be deleted locally = Wpis powinien zostać usunięty lokalnie Local entry ID should be changed to %d = Lokalny wpis ID powinien zostać zmieniony na %d Remote entry = Zdalny wpis Entry should be deleted remotely = Wpis powinien zostać skasowany zdalnie Click here to delete %d entries = Kliknij tutaj aby skasować %d wpisów Click here to delete this entry = Kliknij tutaj aby skasować ten wpis Maximum number of attribute options exceeded = Została przekroczona maksymalna liczba atrybutów opcji Please increase MAX_N_LIST in elogd.c and recompile = Proszę zwiększyć MAX_N_LIST w elogd.c i przekompilować You can "steal" the lock by editing this entry = Możesz "ukraść" blokadę edytując ten wpis Several logbooks groups are defined on this host = Kilka grup logbooków jest zdefiniowanych dla tego hosta Please select one to list the logbooks in that group = Proszę wybrać jedeną grupę, aby wyświetlić zawarte logbooki No logbook defined on this server = Na tym serwerze nie ma zdefiniowanych logbooków Goto ELOG home page = Idź do strony projektu ELOG Please enter "%s" = Proszę podać "%s" Change config file = Edytuj plik konfiguracyjny Please check that it exists and elogd has write access and disk is not full = Proszę sprawdź czy istieje, ELOG ma uprawnienie zapisu i czy dysk nie jest zapełniony Show = Pokaż Hide = Ukryj Hide all = Ukryj wszystkie Show all = Pokaż wszystkie This logbook requires authentication = Ten logbook wymaga autoryzacji Attachment file "%s" empty or not found = Załącznik "%s" jest pusty lub go nie odnaleziono Case sensitive = Rozmiar liter ma znaczenie List = Lista wpisów Date must be between 1970 and 2037 = Data musi być pomiędzy 1970 i 2037 up = wyżej down = niżej stop = stop Entry = Wpis name may not contain blanks = nazwa nie może zawierać spacji regular expressions = wyrażenie regularne Text fields are treated as %s = Pola tekstowe są wątkowane jako %s Subscribe to logbooks = Zasubskrybuj logbooki enable automatic email notifications = włącz automatyczne powiadamianie emailem Set all = Ustaw wszystko Set none = Nic nie ustawiaj Insert current time = Podaj aktualny czas Please enter hour for attribute '%s' = Podaj godzinę dla atrybutu '%s' Please enter minute for attribute '%s' = Podaj minuty dla atrybutu '%s' Please enter second for attribute '%s' = Podaj sekundy dla atrybutu '%s' No admin user has been defined in configuration file = Administrator nie został zdefiniowany w pliku konfiguracyjnym Duplicate = Duplikuj Cannot lookup server name = Nie można rozwiązać nazwy serwera Cannot connect to server = Nie można skontaktować się z serwerem %s wrote = %s napisał Quote = Cytat Please enter numeric value for year of attribute '%s' = Proszę podać numeryczna wartość dla roku atrybutu '%s' Preview = Pogląd FONT = Czcionka SIZE = Rozmiar COLOR = Kolor smiling = uśmiecha się happy = szczęśliwy winking = mruga big grin = duży zielony crying = płacze cool = spoko frowning = frowning confused = zawstydzony mad = zły pleased = spokojny tongue = język yawn = znudzony Encoding = kodowanie User "%s" not found in password file = Użytkownik "%s" nie został znaleziony w pliku konfiguracyjnym Cannot write to file %s = Nie można zapisywać do pliku %s Invalid user name "%s" or password for remote logbook = Niepoprawna nazwa użytkownika "%s" lub hasło do zdalnego logbooka Hide attachments = Ukryj załączniki Show only new entries = Pokaż tylko nowe wpisy Show all entries = Pokaż wszystkie wpisy New entries since = Nowe wpisy od An old ELOG entry has been updated = ELOG: Istniejący wpis został zaktualizowany A new ELOG entry has been submitted = ELOG: Nowy wpis został przesłany Invalid user name or password = Niepoprawna nazwa użytkownika lub hasło Upload image = Prześlij obrazek Please enter filename or URL = Proszę podaj nazwę pliku lub URL Maximum allowed file size is = Maksymalny dozwolony rozmiar pliku to Enter filename or URL = Podaj nazwę pliku lub URL Image uploaded successfully = Obrazek został przesłany pomyślnie Image "%s" uploaded successfully = Obrazek "%s" został przesłany pomyślnie HelpELCode = PomocELCode Cannot retrieve file from URL "%s" = Nie można pobrać pliku z URL "%s" Enter name of hyperlink = Podaj nazwę dla łącza Enter URL of hyperlink = Podaj adres URL dla łącza astonished = astonished Error in regular expression "%s" = Błąd w wyrażeniu regularnym "%s" File not found at URL "%s" = W adrsie "%s" URL plik nie został znaleziony Entry size too large for email notification = Rozmiar wpisu jest zbyt duży dla powiadomienia emailem Please specify a valid email address = Podaj poprawny adres email Cannot send email notification to "%s" = Nie można wysłać powiadomienia emailem do "%s" Error: Command "%s" is not allowed for user "%s" = Błąd: Polecenie "%s" jest niedozwolone dla użytkownika "%s" Shell execution not enabled via -x flag = Wykonywanie powłoki nie zostało włączone z flagą -x Enter heading level (1, 2 or 3) = Podaj poziom nagłówka (1, 2 lub 3) User name may not contain blanks = Nazwa użytkownika nie może zawierać spacji Empty password not allowed = Puste hasła nie są dozwolone Password may not contain blanks = Hasło nie może zawierać spacji Anonymous = Anonimowy Activation notice has been sent to %s = Informacja o aktywacji została przesłana do %s Your request has been forwarded to the administrator = Twoje żądanie zostało przesłane do administratora You will be notified by email upon activation of your new account = Zostaniesz powiadomiony emailem o aktywacji Twojego konta Select period = Wybierz okres Last week = Ostatni tydzień Last month = Ostatni miesiąc Last 3 Months = Ostatnie 3 miesiące Last 6 Months = Ostatnie 6 miesięcy Last Year = Ostatni rok Enter %s = Podaj %s Select %s = Wybierz %s Bold text CTRL+B = Pogrubiony tekst CTRL+B Italics text CTRL+I = Pochylony tekst CTRL+I Underlined text CTRL+U = Podkreślony tekst CTRL+U Centered text = Tekst wyśrodkowany Insert hyperlink = Wstaw łącze Insert email = Wstaw email Insert image CTRL+M = Wstaw obraz CTRL+M Insert quote = Wstaw quote Insert list CTRL+L = Wstaw listę CTRL+L Insert table = Wstaw tabelę Insert heading CTRL+H = Wstaw nagłówek CTRL+H Insert code CTRL+O = Wstaw kod CTRL+O Show the smiley bar = Pokaż emotki Insert current time/date = Wstaw aktualny czas/datę Entry is locked on local server and therefore skipped = Wpis jest zablokowany na lokalnym serwerze i zostanie pominięty Keep me logged in on this computer = Pozostaw mnie zalogowanym na tym komputerze for the next %d minutes = przez następnych %d minut for the next hour = przez następnych godzin for the next %d hours = przez następnych %d godzin for the next %d days = przez następnych %d dni keep original values = zachowaj oryginalne wartości Entry is locked = Wpis jest zablokowany Edit anyhow = Edytuj tak czy owak Insert horizontal line = Wstaw poziomą linię Insert anchor point = Wstaw punkt zaczepienia keep original text = zachowaj oryginalny tekst unspecified = nieokreślony To subscribe to any logbook, click on 'Config' in that logbook = Aby zasubskrybować jakikolwiek logbook kliknij przycisk 'Konfiguracja' w tym logbooku ELOG XML import = ELOG XML import Derive attributes from XML file = Pobierz atrybuty z pliku XML XML filename = Nazwa pliku XML Invalid date format = Niepoprawny format daty XML file does not contain %s element = Plik XML nie zawiera elementu %s XML import preview of %s = Pogląd XML importu %s XML Import = XML Import A old entry has been updated on %s = Stary wpis został zaktualizowany w %s First entry, Ctrl-Home = Pierwszy wpis, Ctrl+Home Previous entry, Ctrl-PgUp = Poprzedni wpis, Ctrl+PgUp Next entry, Ctrl-PgDn = Następny wpis, Ctrl+PgDn Last entry, Ctrl-End = Ostatni wpis, Ctrl+End ELOG import = Elog import Please choose format to import: = Proszę wybrać format importu Enter text = Wprowadź tekst or until I log out = albo zanim się wyloguję Make smaller = Mniejsze Original size = Rozmiar oryginalny Make larger = Większe Rotate left = Obrót w lewo Rotate right = Obróć w prawo Delete attachment = Skasuj załącznik Cannot create thumbnail, please check ImageMagick installation = Nie można utworzyć miniaturki, sprawdź instalację ImageMagick Attachments = Załączniki Error: Content length (%d) larger than maximum content length (%d) = Błąd: Długość zawartości (%d) większa od dozwolonej maksymalnej długości zawartości ELOG error = Elog błąd Only user %s can edit this entry = Tylko użytkownik %s może edytować ten wpis Export to = Eksportuj do Do not ignore first line = Nie ignoruj pierwszej linii Last %d hours = Ostatnie %d godzin Invalid URL = Błędny URL Attribute "%s" is not allowed in config file = Atrybut "%s" nie jest dozwolony w pliku konfiguracyjnym Only user %s can delete this entry = Tylko użytkownik %s może skasować ten wpis File system full, ELOG cannot continue to work = System plików jest pełny, ELOG nie może kontynuować pracy 3 Days = 3 Dni Last 3 Days = Ostatnie 3 dni Really submit this entry? = Naprawę przekazać ten wpis? Account activation for ELOG logbook "%s" = Aktywacja konta dla logbooka "%s" Account activation for ELOG on host "%s" = Aktywacja onta dla hosta "%s" Please click the URL below to activate following ELOG account = Proszę kliknąć w poniższy URL aby aktywować konto ELOG Activation URL = Link aktywacyjny An email has been sent to <%s> = Email został wysłany do <%s> Use that email to activate your account = Użyj tego emaila do aktywacji konta Registration request for ELOG logbook "%s" = Żądanie rejestracji do systemu ELOG logbook "%s" Registration request for ELOG on host "%s" = Żądanie rejestracji do systemu ELOG na hoście "%s" Are you sure you want to deactivate your own account? = Jesteś pewien, że chcesz dezaktywować swoje własne konto? Active = Aktywny Invalid activation code = Błędny kod aktywacji Error activating user = Błąd aktywacji użytkownika A password recovery email for user "%s" has been sent to %s = Email z przypomnieniem hasła został wysłany do "%s" User "%s" has no access to this logbook = Użytkownik "%s" nie ma prawa dostępu do tego logbooka Your account has been activated = Twoje konto zostało aktywowane Please subscribe to logbooks if you want to receive automatic email notifications = Proszę się zasubsrybować do logbooka, jeżeli chcesz otrzymywać automatyczne powiadomienia na email This is an automatically generated account recovery email for host %s = Ten email został automatycznie wygenerowany w celu odzyskania konta na koście %s Please click on following link to recover your account = Proszę kliknąć w poniższy link aby odzyskać konto Email address "%s" not registered = Adres email "%s" nie jest zarejestrowany User name "%s" not registered = Użytkownik "%s" nie jest zarejestrowany Error accessing password file = Błąd przy próbie dostępu do pliku z hasłami CSV (";" separated) + Text = CSV (";" rozdzielony) + Tekst Entry can only be deleted %1.2lg hours after creation = Wpis może być skasowany tylko przez %1.2lg godzin od utworzenia Drop attachments here... = Przeciągnij załączniki tutaj ... Insert Timestamp = Wstaw znacznik czasu Pending draft available = Dostępny jest projekt %d pending drafts available = dostępnych jest %d projektów Create new entry = Stwórz nowy wpis If you leave this page you will lose your unsaved changes = Jeżeli opuścisz tą stronę, stracisz wszystkie niezapisane zmiany Edited = Edytowany All time = Cały czas Draft = Projekt days = dni Draft saved at = Szkic zapisany o You might however then overwrite each other's modifications = Jednakże możesz nadpisać każdą inną modyfikację # #---- please translate following items and then remove this comment ----# # Draft entry created on %s by %s = Really delete this entry? = Restrict search to last = This is a draft message, edit and submit it to make it permanent = Do you want to restore your original text? = This will overwrite your current modifications. = Restore = elog-3.1.3-1/resources/eloghelp_spanish.html0000755000175000017500000000474012556103751020061 0ustar rogerroger ELOG Electronic Logbook - Ayuda
ELOG Electronic Logbook - Ayuda

ELOG (The Electronic Logbook ) puede usarse para almacenar y rescatar mensajes a traves de una interface Web. Segun la configuracion, el sistema ELog puede administrar uno o mas logbooks (algo asi como libros de bitacora) los que se guardan en secciones separadas del servidor.

Resumen de caracteristicas

Por defecto se muestra la ultima entrada en un logbook. Uno puede usar los botones de navegacion para ver el primero, anterior, siguiente o ultimo mensaje.

Si uno de los checkboxes proximo a uno de los campos atributo esta marcado, solo los mensajes con el mismo tipo de atributo seran mostrados por los botones de navegacion. Esta caracteristica puede usarse como un mecanismo de filtrado rapido para mostrar solo paginas de un autor o categoria determinada.

El boton Nuevo crea una nueva entrada. Con el boton Editar se puede editar un mensaje existente, si ello esta permitido en el archivo de configuracion. El boton Responder crea una respuesta a un mensaje existente, de modo similar a responder un email.

El boton Encontrar abre una pagina de consulta, donde los mensajes de un logbook pueden mostrarse de acuerdo a ciertas reglas de filtrado. Cada campo no vacio actua como un filtro adicional, el cual se combina con un and-logico con las otras reglas. Si no se escoje un filtro, se muestran todos los mesajes del logbook. Los botones Ultimo dia y Ultimos 10 muestran todos los mensajes de las ultimas 24 horas y los ultimos 10 mensajes, respectivamente.

Mas informacion

Para mas informacion, especialmente acerca de la configuracion de ELog, consultar la pagina de ELOG.


S. Ritt, 28 August 2001
elog-3.1.3-1/scripts/0000755000175000017500000000000013117320751013307 5ustar rogerrogerelog-3.1.3-1/scripts/im.js0000644000175000017500000000253012556103751014257 0ustar rogerroger/********************************************************************\ Name: md.js Created by: Stefan Ritt Contents: JavaScript code for ImageMagic interface inside ELOG $Id$ \********************************************************************/ var dummy = 0; var imReq; var elName; var thumbName; function im(name, thumb, image, cmd) { imReq = XMLHttpRequestGeneric(); elName = name; thumbName = thumb; imReq.onreadystatechange = onReady; imReq.open("GET","?cmd=im&req="+cmd+"&img="+image, true); imReq.send(null); } function onReady() { if (imReq.readyState == 4) { if (imReq.responseText != "" && imReq.responseText.search(/Fonts/) == -1) alert(imReq.responseText); o = document.getElementsByName(elName); if (o[0]) o[0].src = thumbName+'?'+dummy; if (o[1]) o[1].src = thumbName+'?'+dummy; for (i=0 ; i<8 ; i++) { o = document.getElementsByName(elName+'_'+i); if (o[0]) o[0].src = thumbName+'-'+i+'.png'+'?'+dummy; if (o[1]) o[1].src = thumbName+'-'+i+'.png'+'?'+dummy; } dummy++; } delete imReq; } function deleteAtt(idx) { submitted = true; document.form1.smcmd.value='delatt'+idx; document.form1.submit(); }elog-3.1.3-1/scripts/dnd.js0000644000175000017500000001627312556103751014430 0ustar rogerroger/********************************************************************\ Name: dnd.js Created by: Stefan Ritt Contents: JavaScript code for Drag & Drop interface \********************************************************************/ function XMLHttpRequestGeneric() { var request; try { request = new XMLHttpRequest(); // Firefox, Opera 8.0+, Safari } catch (e) { try { request = new ActiveXObject('Msxml2.XMLHTTP'); // Internet Explorer } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { alert('Your browser does not support AJAX!'); return undefined; } } } return request; } // asend does an AJAX call to send current form data to server function asend() { var f = document.form1; var r = XMLHttpRequestGeneric(); r.onreadystatechange = function() { if (r.readyState==4) in_asend = false; // after successful completion ... if (r.readyState==4 && r.status==200) { // restore original title document.title = page_title; // set "saved" message d = new Date(); e1 = document.getElementById('saved1'); e1.style.display = 'inline'; s = e1.innerHTML.substring(0, e1.innerHTML.length-8); e1.innerHTML = s+d.toTimeString().substring(0, 8); e2 = document.getElementById('saved2'); e2.innerHTML = e1.innerHTML; e2.style.display = 'inline'; // append edit_id (to prevent creation of new messages) if (r.responseText.substring(0,2) == 'OK') { if (document.getElementById('edit_id') == null) { var id = r.responseText.substring(3); var input = document.createElement('input'); input.type = 'hidden'; input.name = 'edit_id'; input.id = 'edit_id'; input.value = parseInt(id); document.form1.appendChild(input); } } } } r.open("Post", "."); // get all form fields var f = new FormData(document.form1); var t; // append text for CKEDITOR and textarea, respectively if (typeof f.delete == 'function') // not avalable in all browsers f.delete("Text"); if (typeof(CKEDITOR) != 'undefined') t = CKEDITOR.instances.Text.getData(); else if (document.form1.Text != undefined) t = document.form1.Text.value; if (t != undefined) f.append("Text", t); // add jcmd f.append("jcmd", "Save"); in_asend = true; r.send(f); } // getElementById function $id(id) { return document.getElementById(id); } var uploading_dropped_files = false; // upload file(s) after thei have been dropped function upload(files) { var formData = (!!window.FormData) ? new FormData() : null; // add all the other attachments that were previously added $( "input[name^='attachment']" ).each(function(idx, el) { formData.append($(el).attr('name'), $(el).attr('value')); }); formData.append('drop-count', files.length); // number of files dropped that should be sent for (var i = 0; i < files.length; i++) { if (!!window.FormData) { formData.append('next_attachment', parent.next_attachment); formData.append('encoding', "HTML"); formData.append('attfile', files[i]); parent.next_attachment += 1; } } formData.append('cmd', "Upload"); // Command for server to recognize this as an file upload if (!!window.FormData) { var URL = 'upload.html?next_attachment=' + parent.next_attachment; // set the flag so the chkupload validator doesn't trigger uploading_dropped_files = true; var submiter = $("input[value='Upload']"); var fileinput = $("input[type='File']"); // If we are uploading drag&drop files then ignore the validator submiter.on("click", function(e) { if(uploading_dropped_files == false) { return chkupload(); } return true; }); // We have finished uploading drag&drop files uploading_dropped_files = false; $.ajax({ xhr: function() { var xhr = new window.XMLHttpRequest(); // Start the progress bar progressJs().start(); //Upload progress xhr.upload.addEventListener("progress", function(evt){ if (evt.lengthComputable) { var percentComplete = evt.loaded / evt.total; //Update the progress bar progressJs().set(percentComplete); } }, false); return xhr; }, contentType: false, processData: false, type: 'POST', url: URL, data: formData, success: function(data) { // End the progress bar progressJs().end(); var attch = $(".attachment", $(data)); var attch_upload = $("#attachment_upload", $(data)); // add the new attachments to the current page $("#attachment_upload").before(attch.slice(-files.length)); // replace the attachment upload section $("#attachment_upload").replaceWith(attch_upload); }, fail: function() { // End the progress bar progressJs().end(); console.log("Error uploading files..."); } }); } } function dragover(e) { e.dataTransfer.dropEffect = 'copy'; e.preventDefault(); } function dragenter(e) { e.stopPropagation(); e.preventDefault(); $id('holder').style.border = '6px dashed #0c0'; } function dragleave(e) { e.stopPropagation(); e.preventDefault(); $id('holder').style.border = '6px dashed #ccc'; } function drop(e) { e.stopPropagation(); e.preventDefault(); $id('holder').style.border = '6px dashed #ccc'; upload(e.dataTransfer.files); } function dndInit() { document.body.addEventListener('dragover', function(e) { e.preventDefault(); }); document.body.addEventListener('drop', function(e) { e.preventDefault(); }); d = $id('holder'); d.addEventListener('dragover', dragover); d.addEventListener('dragenter', dragenter); d.addEventListener('dragleave', dragleave); d.addEventListener('drop', drop); } elog-3.1.3-1/scripts/load-ckeditor.js0000755000175000017500000001012112556103751016371 0ustar rogerroger// This function takes a string that should be translated // and asks the server for the translation to the server's locale. // Translation of the string is returned. // // NOTE: This function works SYNCRHONOUSLY function localize(str) { var URL = "?cmd=loc&value=" + str; return $.ajax({ type: "GET", url: URL, async: false }).responseText; } // After the page has loaded, load the Ckeditor and the attachment dropbox $(document).ready(function() { $('textarea').addClass("ckeditor"); // Need to wait for the ckeditor instance to finish initialization // because CKEDITOR.instances.editor.commands is an empty object // if you try to use it immediately after CKEDITOR.replace('editor'); CKEDITOR.on('instanceReady', function (ev) { var editor = ev.editor; // Make the editor bigger (at least 500px high and 80% of the viewport otherwise) //var width = Math.max(500, 0.7 * $(window).height() ); //editor.resize("100%", new String(width)); init_resize(); // Create a new command with the desired exec function var overridecmd = new CKEDITOR.command(editor, { exec: function(editor){ // Replace this with your desired save button code // alert(editor.document.getBody().getHtml()); window.top.document.form1.jcmd.value = "Submit"; if(window.top.chkform()) window.top.cond_submit(); } }); // Replace the old save's exec function with the new one ev.editor.commands.save.exec = overridecmd.exec; }); // There is a default listener on the submit button that we // need to get rid off in order to get custom upload working // without also firing an empty POST request CKEDITOR.on('dialogDefinition', function (ev) { // Take the dialog name and its definition from the event data. var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; // Check if the definition is from the dialog we're // interested in (the 'image2' and 'fileuploadDialog' dialog). if ( dialogName == 'image2' || dialogName == 'fileuploadDialog') { var dialogObj = dialogDefinition.dialog; dialogObj.on("show", function() { // This code will open the Upload tab. this.selectPage('Upload'); // replace the submit function with something useless dialogObj.getContentElement( 'Upload', 'upload' ).submit = function() { return false; }; }); } }); // Replace the textarea with the CKeditor CKEDITOR.replace('Text', { language: CKEditorLang }); // Workaround function for the drag and drop events, it disallows // dragstart and dragend events firing for each child elements of a specific elements. // In other words, events are only going to fire when an element is dragged over an element // and its children and when the item is dragged away from the element and its children $.fn.dndhover = function(options) { return this.each(function() { var self = $(this); var collection = $(); self.on('dragenter', function(event) { if (collection.size() === 0) { self.trigger('dndHoverStart'); } collection = collection.add(event.target); }); self.on('dragleave', function(event) { /* * Firefox 3.6 fires the dragleave event on the previous element * before firing dragenter on the next one so we introduce a delay */ setTimeout(function() { collection = collection.not(event.target); if (collection.size() === 0) { self.trigger('dndHoverEnd'); } }, 1); }); self.on('drop', function(event) { collection = $(); self.trigger('dndHoverEnd'); }); }); }; });elog-3.1.3-1/scripts/elcode.js0000755000175000017500000001517312556103751015117 0ustar rogerrogerfunction getSelection(text) { if (typeof text == 'undefined') return ""; if (browser == 'MSIE') { sel = document.selection; rng = sel.createRange(); rng.colapse; return rng.text; } else { if (text.selectionEnd > 0 && text.selectionEnd != text.selectionStart && text.value.charAt(text.selectionEnd-1) == ' ') return text.value.substring(text.selectionStart, text.selectionEnd-1); else return text.value.substring(text.selectionStart, text.selectionEnd); } } function replaceSelection(doc, text, newSelection, cursorPos) { if (browser == 'MSIE') { text.focus(); sel = doc.selection; rng = sel.createRange(); rng.colapse; is_sel = rng.text.length > 0; rng.text = newSelection; if (!is_sel) { rng.moveStart('character', -(newSelection.length-cursorPos)); rng.moveEnd('character', -(newSelection.length-cursorPos)); rng.select(); } } else { start = text.selectionStart; stop = text.selectionEnd; if (text.selectionEnd > 0 && text.selectionStart != text.selectionEnd && text.value.charAt(text.selectionEnd-1) == ' ') stop--; end = text.textLength; endtext = text.value.substring(stop, end); starttext = text.value.substring(0, start); oldTop = text.scrollTop; text.value = starttext + newSelection + endtext; if (start != stop) { text.selectionStart = start; text.selectionEnd = start + newSelection.length; } else { text.selectionStart = start + cursorPos; text.selectionEnd = text.selectionStart; } text.scrollTop = oldTop; } } function elcode(text, tag, value) { elcode1(text, tag, value, ''); } function elcode1(text, tag, value, selection) { if (selection == '') selection = getSelection(text); if (tag == '') str = selection + value; else if (tag == 'LIST') str = '[LIST]\r\n[*] ' + selection + '\r\n[/LIST]'; else if (tag == 'TABLE') str = '[TABLE border="1"]\r\nA|B\r\n|-\r\nC|D\r\n[/TABLE]'; else if (tag == 'LINE') str = selection + '[LINE]'; else if (tag == '*') str = '\r\n[*] '; else if (value == '') str = '['+tag+']' + selection + '[/'+tag+']'; else str = '['+tag+'='+value+']' + selection + '[/'+tag+']'; if (tag == '') pos = value.length; else if (tag == 'LIST') pos = 11; else if (tag == 'TABLE') pos = 19; else if (tag == '*') pos = 5; else if (value == '') pos = tag.length + 2; else pos = tag.length + value.length + 3; replaceSelection(document, text, str, pos); text.focus(); } function elcode2(doc, text, tag, value) { str = '['+tag+']' + value + '[/'+tag+']'; pos = str.length; replaceSelection(doc, text, str, pos); text.focus(); } function queryURL(text) { selection = getSelection(text); linkText = prompt(linkText_prompt, selection); linkURL = prompt(linkURL_prompt, 'http://'); elcode1(text, 'URL', linkURL, linkText); } function queryHeading(text) { selection = getSelection(text); heading = prompt(linkHeading_prompt, ''); tag = 'H' + heading; elcode1(text, tag, '', selection); } function insertTime(text) { var xmlHttp; try { xmlHttp = new XMLHttpRequest(); // Firefox, Opera 8.0+, Safari } catch (e) { try { xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); // Internet Explorer } catch (e) { try { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("Your browser does not support AJAX!"); return false; } } } xmlHttp.onreadystatechange = function() { if(xmlHttp.readyState == 4) { selection = getSelection(text); elcode1(text, '', xmlHttp.responseText, selection); } } xmlHttp.open("GET","?cmd=gettimedate",true); xmlHttp.send(null); } function elKeyInit() { document.onkeypress = elKeyPress; } function elKeyPress(evt) { evt = (evt) ? evt : window.event; var unicode = evt.charCode ? evt.charCode : evt.keyCode; var actualkey = String.fromCharCode(unicode); if (evt.ctrlKey && !evt.shiftKey && !evt.altKey) { if (browser == 'MSIE' || browser == 'Safari') { if (unicode == 10) unicode = 13; else unicode += 96; actualkey = String.fromCharCode(unicode); } if (actualkey == "b") { elcode(document.form1.Text, 'B',''); return false; } if (actualkey == "i") { elcode(document.form1.Text, 'I',''); return false; } if (actualkey == "u") { elcode(document.form1.Text, 'U',''); return false; } if (actualkey == "o") { elcode(document.form1.Text, 'CODE',''); return false; } if (actualkey == "l") { elcode(document.form1.Text, 'LIST',''); return false; } if (actualkey == "n") { elcode(document.form1.Text, '*',''); return false; } if (actualkey == "h") { queryHeading(document.form1.Text); return false; } if (actualkey == "m") { window.open('upload.html','','top=280,left=350,width=500,height=120,dependent=yes,menubar=no,status=no,scrollbars=no,location=no,resizable=yes'); return false; } if (actualkey == "p") { document.form1.jcmd.value = "Preview"; chkform(); cond_submit(); return false; } if (unicode == 13) { // return document.form1.jcmd.value = "Submit"; chkform(); cond_submit(); return false; } } return true; } function browse(evt) { evt = (evt) ? evt : window.event; var unicode = evt.charCode ? evt.charCode : evt.keyCode; var actualkey = String.fromCharCode(unicode); if (evt.ctrlKey && !evt.altKey) { if (browser == 'MSIE') { if (unicode == 10) unicode = 13; else unicode += 96; actualkey = String.fromCharCode(unicode); } if (actualkey == "$") { // home window.location.href = "?cmd_first.x=1"; return false; } if (actualkey == "#") { // end window.location.href = "?cmd_last.x=1"; return false; } if (actualkey == "!") { // page up window.location.href = "?cmd_previous.x=1"; return false; } if (actualkey == "\"") { // page down window.location.href = "?cmd_next.x=1"; return false; } } return true; } elog-3.1.3-1/scripts/ckeditor-config.js0000755000175000017500000000347212757305354016740 0ustar rogerroger/** * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.html or http://ckeditor.com/license */ CKEDITOR.editorConfig = function( config ) { // Define changes to default configuration here. For example: // config.language = 'fr'; // config.uiColor = '#AADC6E'; config.extraPlugins = 'timestamp,dndfiles,eqneditor,fileupload'; config.toolbarCanCollapse = true; // Toolbar configuration config.toolbar = [ { name: 'document', groups: [ 'mode', 'document', 'doctools' ], items: [ 'Source', '-', 'Save', 'Preview', 'Print', '-', 'Templates' ] }, { name: 'clipboard', groups: [ 'clipboard', 'undo' ], items: [ 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo' ] }, { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ], items: [ 'Find', 'Replace', '-', 'SelectAll', '-', 'Scayt' ] }, '/', { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ] }, { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ], items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl', 'Language' ] }, { name: 'links', items: [ 'Link', 'Unlink', 'Anchor' ] }, { name: 'insert', items: [ 'Image', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'FileUpload', 'Timestamp', 'EqnEditor' ] }, '/', { name: 'styles', items: [ 'Styles', 'Format', 'Font', 'FontSize' ] }, { name: 'colors', items: [ 'TextColor', 'BGColor' ] }, { name: 'tools', items: [ 'Maximize', 'ShowBlocks' ] }, { name: 'others', items: [ '-' ] }, { name: 'about', items: [ 'About' ] } ]; }; elog-3.1.3-1/scripts/progress/0000755000175000017500000000000013117320752015154 5ustar rogerrogerelog-3.1.3-1/scripts/progress/README.md0000644000175000017500000000471713117320752016444 0ustar rogerroger# ProgressJS > ProgressJs is a JavaScript and CSS3 library which help developers to create and manage progress bar for every objects on the page. ## How To Use 1) Include `progress.js` and `progressjs.css` in the page (use minified version from `minified` folder for production) 2) Execute following JavaScript code in the page: ```javascript //to set progress-bar for whole page progressJs().start(); //or for specific element progressJs("#targetElement").start(); ``` Use other methods to increase, decrease or set a auto-increase function for your progress-bar. Furthermore, you can change the template using `setOption` method. ## API Check the API and method usage with example here: https://github.com/usablica/progress.js/wiki/API ## Build First you should install `nodejs` and `npm`, then first run this command: `npm install` to install all dependencies. Now you can run this command to minify all static resources: make build ## Roadmap - Add `example` folder and provide examples - More browser compatibility + mobile/tablet device support - Add more templates ## Release History * **v0.1.0** - 2014-02-14 - First version - Increase, decrease and auto-increase functions - Ability to design and add templates ## Author **Afshin Mehrabani** - [Twitter](https://twitter.com/afshinmeh) - [Github](https://github.com/afshinm) - [Personal page](http://afshinm.name/) ## License > Copyright (C) 2012 Afshin Mehrabani (afshin.meh@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.elog-3.1.3-1/scripts/progress/progress.js0000644000175000017500000003700613117320752017364 0ustar rogerroger/** * Progress.js v0.1.0 * https://github.com/usablica/progress.js * MIT licensed * * Copyright (C) 2013 usabli.ca - Afshin Mehrabani (@afshinmeh) */ (function (root, factory) { if (typeof exports === 'object') { // CommonJS factory(exports); } else if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['exports'], factory); } else { // Browser globals factory(root); } } (this, function (exports) { //Default config/variables var VERSION = '0.1.0'; /** * ProgressJs main class * * @class ProgressJs */ function ProgressJs(obj) { if (typeof obj.length != 'undefined') { this._targetElement = obj; } else { this._targetElement = [obj]; } if (typeof window._progressjsId === 'undefined') window._progressjsId = 1; if (typeof window._progressjsIntervals === 'undefined') window._progressjsIntervals = {}; this._options = { //progress bar theme theme: 'blue', //overlay mode makes an overlay layer in the target element overlayMode: false, //to consider CSS3 transitions in events considerTransition: true }; } /** * Start progress for specific element(s) * * @api private * @method _createContainer */ function _startProgress() { //call onBeforeStart callback if (typeof this._onBeforeStartCallback != 'undefined') { this._onBeforeStartCallback.call(this); } //create the container for progress bar _createContainer.call(this); for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { _setProgress.call(this, this._targetElement[i]); } } /** * Set progress bar for specific element * * @api private * @method _setProgress * @param {Object} targetElement */ function _setProgress(targetElement) { //if the target element already as `data-progressjs`, ignore the init if (targetElement.hasAttribute("data-progressjs")) return; //get target element position var targetElementOffset = _getOffset.call(this, targetElement); targetElement.setAttribute("data-progressjs", window._progressjsId); var progressElementContainer = document.createElement('div'); progressElementContainer.className = 'progressjs-progress progressjs-theme-' + this._options.theme; //set the position percent elements, it depends on targetElement tag if (targetElement.tagName.toLowerCase() === 'body') { progressElementContainer.style.position = 'fixed'; } else { progressElementContainer.style.position = 'absolute'; } progressElementContainer.setAttribute("data-progressjs", window._progressjsId); var progressElement = document.createElement("div"); progressElement.className = "progressjs-inner"; //create an element for current percent of progress bar var progressPercentElement = document.createElement('div'); progressPercentElement.className = "progressjs-percent"; progressPercentElement.innerHTML = "1%"; progressElement.appendChild(progressPercentElement); if (this._options.overlayMode && targetElement.tagName.toLowerCase() === 'body') { //if we have `body` for target element and also overlay mode is enable, we should use a different //position for progress bar container element progressElementContainer.style.left = 0; progressElementContainer.style.right = 0; progressElementContainer.style.top = 0; progressElementContainer.style.bottom = 0; } else { //set progress bar container size and offset progressElementContainer.style.left = targetElementOffset.left + 'px'; progressElementContainer.style.top = targetElementOffset.top + 'px'; progressElementContainer.style.width = targetElementOffset.width + 'px'; if (this._options.overlayMode) { progressElementContainer.style.height = targetElementOffset.height + 'px'; } } progressElementContainer.appendChild(progressElement); //append the element to container var container = document.querySelector('.progressjs-container'); container.appendChild(progressElementContainer); _setPercentFor(targetElement, 1); //and increase the progressId ++window._progressjsId; } /** * Set percent for all elements * * @api private * @method _setPercent * @param {Number} percent */ function _setPercent(percent) { for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { _setPercentFor.call(this, this._targetElement[i], percent); } } /** * Set percent for specific element * * @api private * @method _setPercentFor * @param {Object} targetElement * @param {Number} percent */ function _setPercentFor(targetElement, percent) { var self = this; //prevent overflow! if (percent >= 100) percent = 100; if (targetElement.hasAttribute("data-progressjs")) { //setTimeout for better CSS3 animation applying in some cases setTimeout(function() { //call the onprogress callback if (typeof self._onProgressCallback != 'undefined') { self._onProgressCallback.call(self, targetElement, percent); } var percentElement = _getPercentElement(targetElement); percentElement.style.width = parseInt(percent) + '%'; var percentElement = percentElement.querySelector(".progressjs-percent"); var existingPercent = parseInt(percentElement.innerHTML.replace('%', '')); //start increase/decrease the percent element with animation (function(percentElement, existingPercent, currentPercent) { var increasement = true; if (existingPercent > currentPercent) { increasement = false; } var intervalIn = 10; function changePercentTimer(percentElement, existingPercent, currentPercent) { //calculate the distance between two percents var distance = Math.abs(existingPercent - currentPercent); if (distance < 3) { intervalIn = 30; } else if (distance < 20) { intervalIn = 20; } else { intervanIn = 1; } if ((existingPercent - currentPercent) != 0) { //set the percent percentElement.innerHTML = (increasement ? (++existingPercent) : (--existingPercent)) + '%'; setTimeout(function() { changePercentTimer(percentElement, existingPercent, currentPercent); }, intervalIn); } } changePercentTimer(percentElement, existingPercent, currentPercent); })(percentElement, existingPercent, parseInt(percent)); }, 50); } } /** * Get the progress bar element * * @api private * @method _getPercentElement * @param {Object} targetElement */ function _getPercentElement(targetElement) { var progressjsId = parseInt(targetElement.getAttribute('data-progressjs')); return document.querySelector('.progressjs-container > .progressjs-progress[data-progressjs="' + progressjsId + '"] > .progressjs-inner'); } /** * Auto increase the progress bar every X milliseconds * * @api private * @method _autoIncrease * @param {Number} size * @param {Number} millisecond */ function _autoIncrease(size, millisecond) { var self = this; var progressjsId = parseInt(this._targetElement[0].getAttribute('data-progressjs')); if (typeof window._progressjsIntervals[progressjsId] != 'undefined') { clearInterval(window._progressjsIntervals[progressjsId]); } window._progressjsIntervals[progressjsId] = setInterval(function() { _increasePercent.call(self, size); }, millisecond); } /** * Increase the size of progress bar * * @api private * @method _increasePercent * @param {Number} size */ function _increasePercent(size) { for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { var currentElement = this._targetElement[i]; if (currentElement.hasAttribute('data-progressjs')) { var percentElement = _getPercentElement(currentElement); var existingPercent = parseInt(percentElement.style.width.replace('%', '')); if (existingPercent) { _setPercentFor.call(this, currentElement, existingPercent + (size || 1)); } } } } /** * Close and remove progress bar * * @api private * @method _end */ function _end() { //call onBeforeEnd callback if (typeof this._onBeforeEndCallback != 'undefined') { if (this._options.considerTransition === true) { //we can safety assume that all layers would be the same, so `this._targetElement[0]` is the same as `this._targetElement[1]` _getPercentElement(this._targetElement[0]).addEventListener(whichTransitionEvent(), this._onBeforeEndCallback, false); } else { this._onBeforeEndCallback.call(this); } } var progressjsId = parseInt(this._targetElement[0].getAttribute('data-progressjs')); for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { var currentElement = this._targetElement[i]; var percentElement = _getPercentElement(currentElement); if (!percentElement) return; var existingPercent = parseInt(percentElement.style.width.replace('%', '')); var timeoutSec = 1; if (existingPercent < 100) { _setPercentFor.call(this, currentElement, 100); timeoutSec = 500; } //I believe I should handle this situation with eventListener and `transitionend` event but I'm not sure //about compatibility with IEs. Should be fixed in further versions. (function(percentElement, currentElement) { setTimeout(function() { percentElement.parentNode.className += " progressjs-end"; setTimeout(function() { //remove the percent element from page percentElement.parentNode.parentNode.removeChild(percentElement.parentNode); //and remove the attribute currentElement.removeAttribute("data-progressjs"); }, 1000); }, timeoutSec); })(percentElement, currentElement); } //clean the setInterval for autoIncrease function if (window._progressjsIntervals[progressjsId]) { //`delete` keyword has some problems in IE try { clearInterval(window._progressjsIntervals[progressjsId]); window._progressjsIntervals[progressjsId] = null; delete window._progressjsIntervals[progressjsId]; } catch(ex) { } } } /** * Create the progress bar container * * @api private * @method _createContainer */ function _createContainer() { //first check if we have an container already, we don't need to create it again if (!document.querySelector(".progressjs-container")) { var containerElement = document.createElement("div"); containerElement.className = "progressjs-container"; document.body.appendChild(containerElement); } } /** * Get an element position on the page * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966 * * @api private * @method _getOffset * @param {Object} element * @returns Element's position info */ function _getOffset(element) { var elementPosition = {}; if (element.tagName.toLowerCase() === 'body') { //set width elementPosition.width = element.clientWidth; //set height elementPosition.height = element.clientHeight; } else { //set width elementPosition.width = element.offsetWidth; //set height elementPosition.height = element.offsetHeight; } //calculate element top and left var _x = 0; var _y = 0; while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { _x += element.offsetLeft; _y += element.offsetTop; element = element.offsetParent; } //set top elementPosition.top = _y; //set left elementPosition.left = _x; return elementPosition; } /** * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically * * @param obj1 * @param obj2 * @returns obj3 a new object based on obj1 and obj2 */ function _mergeOptions(obj1, obj2) { var obj3 = {}; for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } return obj3; } var progressJs = function (targetElm) { if (typeof (targetElm) === 'object') { //Ok, create a new instance return new ProgressJs(targetElm); } else if (typeof (targetElm) === 'string') { //select the target element with query selector var targetElement = document.querySelectorAll(targetElm); if (targetElement) { return new ProgressJs(targetElement); } else { throw new Error('There is no element with given selector.'); } } else { return new ProgressJs(document.body); } }; /** * Get correct transition callback * Thanks @webinista: http://stackoverflow.com/a/9090128/375966 * * @returns transition name */ function whichTransitionEvent() { var t; var el = document.createElement('fakeelement'); var transitions = { 'transition': 'transitionend', 'OTransition': 'oTransitionEnd', 'MozTransition': 'transitionend', 'WebkitTransition': 'webkitTransitionEnd' } for (t in transitions) { if (el.style[t] !== undefined) { return transitions[t]; } } } /** * Current ProgressJs version * * @property version * @type String */ progressJs.version = VERSION; //Prototype progressJs.fn = ProgressJs.prototype = { clone: function () { return new ProgressJs(this); }, setOption: function(option, value) { this._options[option] = value; return this; }, setOptions: function(options) { this._options = _mergeOptions(this._options, options); return this; }, start: function() { _startProgress.call(this); return this; }, set: function(percent) { _setPercent.call(this, percent); return this; }, increase: function(size) { _increasePercent.call(this, size); return this; }, autoIncrease: function(size, millisecond) { _autoIncrease.call(this, size, millisecond); return this; }, end: function() { _end.call(this); return this; }, onbeforeend: function(providedCallback) { if (typeof (providedCallback) === 'function') { this._onBeforeEndCallback = providedCallback; } else { throw new Error('Provided callback for onbeforeend was not a function'); } return this; }, onbeforestart: function(providedCallback) { if (typeof (providedCallback) === 'function') { this._onBeforeStartCallback = providedCallback; } else { throw new Error('Provided callback for onbeforestart was not a function'); } return this; }, onprogress: function(providedCallback) { if (typeof (providedCallback) === 'function') { this._onProgressCallback = providedCallback; } else { throw new Error('Provided callback for onprogress was not a function'); } return this; } }; exports.progressJs = progressJs; return progressJs; })); elog-3.1.3-1/scripts/progress/progressjs.css0000644000175000017500000001223213117320752020067 0ustar rogerroger.progressjs-inner { width: 0; } .progressjs-progress { z-index: 9999999; } /* blue theme, like iOS 7 progress bar */ .progressjs-theme-blue .progressjs-inner { height: 2px; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; } .progressjs-theme-blue.progressjs-end { -webkit-transition: opacity 0.2s ease-out; -moz-transition: opacity 0.2s ease-out; -o-transition: opacity 0.2s ease-out; transition: opacity 0.2s ease-out; opacity: 0; } .progressjs-theme-blue .progressjs-percent { display: none; } /* blue theme with overlay layer, no percent bar */ .progressjs-theme-blueOverlay { background-color: white; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out; } .progressjs-theme-blueOverlay .progressjs-inner { height: 100%; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; } .progressjs-theme-blueOverlay.progressjs-end { opacity: 0 !important; } .progressjs-theme-blueOverlay .progressjs-percent { display: none; } /* blue theme with overlay layer, no percent bar */ .progressjs-theme-blueOverlay { background-color: white; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out; } .progressjs-theme-blueOverlay .progressjs-inner { height: 100%; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; } .progressjs-theme-blueOverlay.progressjs-end { opacity: 0 !important; } .progressjs-theme-blueOverlay .progressjs-percent { display: none; } /* Blue theme with border radius and overlay layer */ .progressjs-theme-blueOverlayRadius { background-color: white; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out; border-radius: 5px; } .progressjs-theme-blueOverlayRadius .progressjs-inner { height: 100%; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; border-radius: 5px; } .progressjs-theme-blueOverlayRadius.progressjs-end { opacity: 0 !important; } .progressjs-theme-blueOverlayRadius .progressjs-percent { display: none; } /* Blue theme with border radius and overlay layer */ .progressjs-theme-blueOverlayRadiusHalfOpacity { background-color: white; opacity: 0.5; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out; border-radius: 5px; } .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-inner { height: 100%; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; border-radius: 5px; } .progressjs-theme-blueOverlayRadiusHalfOpacity.progressjs-end { opacity: 0 !important; } .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-percent { display: none; } /* Blue theme with border radius, overlay layer and percent bar */ .progressjs-theme-blueOverlayRadiusWithPercentBar { background-color: white; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out; border-radius: 5px; } .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-inner { height: 100%; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; background-color: #3498db; border-radius: 5px; } .progressjs-theme-blueOverlayRadiusWithPercentBar.progressjs-end { opacity: 0 !important; } .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-percent { width: 70px; text-align: center; height: 40px; position: absolute; right: 50%; margin-right: -35px; top: 50%; margin-top: -20px; font-size: 30px; opacity: .5; } .progressjs-theme-blackRadiusInputs { height: 10px; border-radius: 10px; overflow: hidden; } .progressjs-theme-blackRadiusInputs .progressjs-inner { height: 2px; -webkit-transition: all 1s ease-out; -moz-transition: all 1s ease-out; -o-transition: all 1s ease-out; transition: all 1s ease-out; background-color: #34495e; } .progressjs-theme-blackRadiusInputs.progressjs-end { -webkit-transition: opacity 0.2s ease-out; -moz-transition: opacity 0.2s ease-out; -o-transition: opacity 0.2s ease-out; transition: opacity 0.2s ease-out; opacity: 0; } .progressjs-theme-blackRadiusInputs .progressjs-percent { display: none; }elog-3.1.3-1/Makefile0000644000175000017500000001254413076344200013265 0ustar rogerroger# Simple makefile for elogd # # S. Ritt, May 12th 2000 # install/clean section by Th. Bullinger, Apr. 26th, 2002 ############################################################# # # Directories for installation, modify if needed # ifndef PREFIX PREFIX = /usr/local endif ifndef MANDIR MANDIR = $(ROOT)$(PREFIX)/man endif ELOGDIR = $(ROOT)$(PREFIX)/elog DESTDIR = $(ROOT)$(PREFIX)/bin SDESTDIR = $(ROOT)$(PREFIX)/sbin RCDIR = $(ROOT)/etc/rc.d/init.d # flag for SSL support USE_SSL = 1 # flag for Kerberos support, please turn on if you need Kerberos USE_KRB5 = 0 # flag for LDAP support, please turn on if you need LDAP USE_LDAP = 0 ############################################################# # Default compilation flags unless stated otherwise. CFLAGS += -O3 -funroll-loops -fomit-frame-pointer -W -Wall -Wno-deprecated-declarations -Imxml CC = gcc IFLAGS = -kr -nut -i3 -l110 EXECS = elog elogd elconv OBJS = mxml.o crypt.o regex.o GIT_REVISION = src/git-revision.h BINOWNER = bin BINGROUP = bin # Option to use our own implementation of strlcat, strlcpy NEED_STRLCPY = 1 INSTALL = `which install` RM = /bin/rm -f OSTYPE = $(shell uname) ifeq ($(OSTYPE),SunOS) CC = gcc LIBS += -lsocket -lnsl CFLAGS = INSTALL = /usr/ucb/install RM = /usr/bin/rm -f endif ifeq ($(OSTYPE),OpenBSD) LIBS += -lcrypto endif ifeq ($(OSTYPE),Darwin) OSTYPE=darwin endif ifeq ($(OSTYPE),darwin) CC = cc BINOWNER = root BINGROUP = admin NEED_STRLCPY = CFLAGS += -I/opt/local/include endif ifeq ($(OSTYPE),FreeBSD) CC = gcc BINOWNER = root BINGROUP = wheel endif ifeq ($(OSTYPE),Linux) CC = gcc endif ifdef USE_SSL ifneq ($(USE_SSL),0) CFLAGS += -DHAVE_SSL LIBS += -lssl endif endif ifdef USE_KRB5 ifneq ($(USE_KRB5),0) CFLAGS += -DHAVE_KRB5 LIBS += -lkrb5 endif endif ifdef USE_LDAP ifneq ($(USE_LDAP),0) CFLAGS += -DHAVE_LDAP LIBS += -lldap endif endif ifdef NEED_STRLCPY OBJS += strlcpy.o endif WHOAMI = $(shell whoami) ifeq ($(WHOAMI),root) BINFLAGS = -o ${BINOWNER} -g ${BINGROUP} endif all: $(EXECS) # put current GIT revision into header file to be included by programs $(GIT_REVISION): src/elogd.c src/elog.c type git &> /dev/null; if [ $$? -eq 1 ]; then REV="unknown" ;else REV=`git log -n 1 --pretty=format:"%ad - %h"`; fi; echo \#define GIT_REVISION \"$$REV\" > $(GIT_REVISION) regex.o: src/regex.c src/regex.h $(CC) $(CFLAGS) -w -c -o regex.o src/regex.c crypt.o: src/crypt.c $(CC) $(CFLAGS) -w -c -o crypt.o src/crypt.c auth.o: src/auth.c $(CC) $(CFLAGS) -w -c -o auth.o src/auth.c mxml.o: mxml/mxml.c mxml/mxml.h $(CC) $(CFLAGS) -c -o mxml.o mxml/mxml.c strlcpy.o: mxml/strlcpy.c mxml/strlcpy.h $(CC) $(CFLAGS) -c -o strlcpy.o mxml/strlcpy.c elogd: src/elogd.c auth.o $(OBJS) $(GIT_REVISION) $(CC) $(CFLAGS) -o elogd src/elogd.c auth.o $(OBJS) $(LIBS) elog: src/elog.c $(OBJS) $(GIT_REVISION) $(CC) $(CFLAGS) -o elog src/elog.c $(OBJS) $(LIBS) debug: src/elogd.c auth.o $(OBJS) $(CC) -g $(CFLAGS) -O0 -o elogd src/elogd.c auth.o $(OBJS) $(LIBS) %: src/%.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) indent: for src in src/*.c; do \ d2u $$src; \ indent $(IFLAGS) $$src; \ u2d $$src; \ done ifeq ($(OSTYPE),CYGWIN_NT-5.1) loc: locext.exe for lang in resources/eloglang*; do \ ./locext.exe src/elogd.c $$lang; echo locext src/elogd.c $$lang;\ done else locext: src/locext.c loc: locext @for lang in resources/eloglang*; do \ ./locext src/elogd.c $$lang; echo locext src/elogd.c $$lang;\ done endif update: $(EXECS) @$(INSTALL) -v -m 0755 ${BINFLAGS} elogd $(SDESTDIR) install: $(EXECS) @$(INSTALL) -m 0755 -d $(DESTDIR) $(SDESTDIR) $(MANDIR)/man1/ $(MANDIR)/man8/ @$(INSTALL) -m 0755 -d $(ELOGDIR)/scripts/ $(ELOGDIR)/resources/ $(ELOGDIR)/ssl/ $(ELOGDIR)/themes/default/icons @$(INSTALL) -m 0755 -d $(ELOGDIR)/logbooks/demo @$(INSTALL) -m 0755 -d $(ELOGDIR)/logbooks/demo/2001 @$(INSTALL) -v -m 0755 ${BINFLAGS} elog elconv $(DESTDIR) @$(INSTALL) -v -m 0755 ${BINFLAGS} elogd $(SDESTDIR) @$(INSTALL) -v -m 0644 man/elog.1 man/elconv.1 $(MANDIR)/man1/ @$(INSTALL) -v -m 0644 man/elogd.8 $(MANDIR)/man8/ @$(INSTALL) -v -m 0644 scripts/*.js $(ELOGDIR)/scripts/ @echo "Installing CKeditor to $(ELOGDIR)/scripts/ckeditor" @cp -r scripts/* $(ELOGDIR)/scripts @echo "Installing resources to $(ELOGDIR)/resources" @$(INSTALL) -m 0644 resources/* $(ELOGDIR)/resources/ @if [ ! -f $(ELOGDIR)/ssl/server.crt ]; then \ $(INSTALL) -v -m 0644 ssl/server.crt $(ELOGDIR)/ssl/ ;\ fi @if [ ! -f $(ELOGDIR)/ssl/server.key ]; then \ $(INSTALL) -v -m 0644 ssl/server.key $(ELOGDIR)/ssl/ ;\ fi @echo "Installing themes to $(ELOGDIR)/themes" @cp -r themes/* $(ELOGDIR)/themes @echo "Installing example logbook to $(ELOGDIR)/logbooks/demo" @if [ ! -f $(ELOGDIR)/logbooks/demo/2001 ]; then \ $(INSTALL) -v -m 0644 logbooks/demo/2001/* $(ELOGDIR)/logbooks/demo/2001 ; \ fi @if [ ! -f $(ELOGDIR)/elogd.cfg ]; then \ $(INSTALL) -v -m 644 elogd.cfg $(ELOGDIR)/elogd.cfg ; \ fi ifeq ($(OSTYPE),darwin) @$(INSTALL) -v -m 0644 elogd.plist /Library/LaunchDaemons/ch.psi.elogd.plist @echo The elogd service can now be started with @echo " sudo launchctl load /Library/LaunchDaemons/ch.psi.elogd.plist" else @sed "s#\@PREFIX\@#$(PREFIX)#g" elogd.init_template > elogd.init @if [ ! -f $(RCDIR)/elogd ]; then \ $(INSTALL) -v -m 0755 -D elogd.init $(RCDIR)/elogd ; \ fi endif restart: $(RCDIR)/elogd restart clean: -$(RM) *~ $(EXECS) regex.o crypt.o auth.o mxml.o strlcpy.o locext