pax_global_header00006660000000000000000000000064124637267010014522gustar00rootroot0000000000000052 comment=d74c49fa58ac8276d9ee2f18595436b5dc34127a sympa-6.1.24~dfsg/000077500000000000000000000000001246372670100140075ustar00rootroot00000000000000sympa-6.1.24~dfsg/AUTHORS000066400000000000000000000006521246372670100150620ustar00rootroot00000000000000 SYMPA - Systeme de multipostage automatique Copyright © 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites Copyright © 2011, 2012, 2013, 2014 GIP RENATER You may distribute under the terms of the GNU General Public License Version 2 ( http://www.gnu.org/copyleft/gpl.html). sympa-6.1.24~dfsg/COPYING000066400000000000000000000432541246372670100150520ustar00rootroot00000000000000 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 Lesser 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 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) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General Public License instead of this License. sympa-6.1.24~dfsg/ChangeLog000066400000000000000000173014211246372670100155720ustar00rootroot00000000000000------------------------------------------------------------------------ r11778 | david.verdin | 2014-12-17 11:41:05 +0100 (mer. 17 dc. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] Preventing faulty newsletter sending. ------------------------------------------------------------------------ r11690 | david.verdin | 2014-11-14 16:38:25 +0100 (ven. 14 nov. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in [bug][Submitted by B. Marchal, univ. Lorraine] the loop_prevention_regex parameter was never taken into account. This was due to a o modifier in the regexp which resulted in the regexp being set at compile time and never changed after. Fixed by removing this modifier. ------------------------------------------------------------------------ r11563 | sikeda | 2014-10-17 09:17:36 +0200 (ven. 17 oct. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm [bug][#9542] [Submitted by M. Howe, Univ. of Oxford] SympaSOAP does not validate added addresses. ------------------------------------------------------------------------ r11562 | sikeda | 2014-10-17 03:34:08 +0200 (ven. 17 oct. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug][#9548][Submitted by M. Howe, Univ. of Oxford] do_del only logs single address successfully removed, even if multiple addresses are specified. ------------------------------------------------------------------------ r11428 | david.verdin | 2014-09-24 16:49:09 +0200 (mer. 24 sept. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/tools.pm [bug] When checking misaddressed commands in subject, the command length was not taken into account. Any message whose subjects started with unsubscribe, signoff or subscribe would be rejectd. Fixed by forcing test to match only if the subject contains two words exactly, including the command. We might miss wom better form subscribe commands but will have very less false positive. ------------------------------------------------------------------------ r11427 | david.verdin | 2014-09-24 16:31:44 +0200 (mer. 24 sept. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm [bug] DMARC protection mode was not correctly loaded in virtual hosts. Fixed by getting rid of double parsing of this parameter. ------------------------------------------------------------------------ r11398 | sympa-authors | 2014-09-17 17:19:31 +0200 (mer. 17 sept. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.23 ------------------------------------------------------------------------ r11369 | david.verdin | 2014-09-12 17:52:50 +0200 (ven. 12 sept. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [change][Submitted by Evol Tech, riseup.net] When searching a list through the web interface, Sympa would first load all the lists config before looking for the right lists. This is memory consuming when the number of lists grows. It is better to first search the list names and description, then load them (possible using the database cache only). Patch improved by testing the database backend Sympa uses, then building the query using a regular expression. ------------------------------------------------------------------------ r11368 | david.verdin | 2014-09-12 16:10:14 +0200 (ven. 12 sept. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm [bug][Submitted by O. Menkens] When failing to store a message in MySQL database, due to the size of the insert query exceeding the 'max_allowed_packet' value in my.cnf, the error report did not display the MySQL error because the statement handler error was wiped before being used in the Sympa log calls. Fixed by caching the error before logging it. ------------------------------------------------------------------------ r11360 | sympa-authors | 2014-09-11 16:23:11 +0200 (jeu. 11 sept. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/pt.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r11359 | david.verdin | 2014-09-11 13:07:41 +0200 (jeu. 11 sept. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po [dev] Committing updated catalogues. ------------------------------------------------------------------------ r11341 | sikeda | 2014-09-02 07:08:40 +0200 (mar. 02 sept. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] [Reported by M. Goebel, Eastern Michigan Univ.] Typos at several places in List.pm may crash Sympa. ------------------------------------------------------------------------ r11335 | sikeda | 2014-08-27 14:37:14 +0200 (mer. 27 aot 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] A typo not to show version number in template. It affects only to result of HELP command, though. ------------------------------------------------------------------------ r11334 | sikeda | 2014-08-27 03:17:32 +0200 (mer. 27 aot 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [-bug][#9413] [Submitted by G. Younger, Newcastle Univ.] Spurious "Use of uninitialized value" warnings in SympaSession.pm. ------------------------------------------------------------------------ r11333 | sikeda | 2014-08-25 03:44:14 +0200 (lun. 25 aot 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch R /branches/sympa-6.1-branch/src/etc (de /branches/sympa-6.1-branch/src/etc:11244) M /branches/sympa-6.1-branch/src/etc/Makefile.am A /branches/sympa-6.1-branch/src/etc/scenari/del.ownerdkim (de /branches/sympa-6.2-branch/default/scenari/del.ownerdkim:11331) [svn] Backporting a change from sympa-6.2-branch. ------------------------------------------------------------------------ r11331 | sikeda | 2014-08-24 04:11:23 +0200 (dim. 24 aot 2014) | 14 lignes Chemins modifis: M /branches/sympa-6.2-branch/default/Makefile.am A /branches/sympa-6.2-branch/default/scenari/del.ownerdkim Fision via: r11333 [feature] New scenarios to check DKIM signature are added: - add.authdkim - add.ownerdkim - del.authdkim - del.ownerdkim - remind.listmasterdkim - remind.ownerdkim - send.editordkim - subscribe.authdkim - subscribe.auth_notifydkim - subscribe.auth_ownerdkim - unsubscribe.authdkim - unsubscribe.auth_notifydkim ------------------------------------------------------------------------ r11328 | sikeda | 2014-08-19 09:51:32 +0200 (mar. 19 aot 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/lib/List.pm [svn] Backporting a bug fix from sympa-6.2-branch. ------------------------------------------------------------------------ r11327 | sikeda | 2014-08-19 09:42:10 +0200 (mar. 19 aot 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Sympa/List.pm Fision via: r11328 [bug] When dkim_add_signature_to parameter contains "list", DKIM signature won't be added. A typo has been corrected. ------------------------------------------------------------------------ r11272 | sikeda | 2014-07-29 17:19:43 +0200 (mar. 29 juil. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [svn] Backporting a fix from sympa-6.2-branch. ------------------------------------------------------------------------ r11271 | sikeda | 2014-07-29 17:14:29 +0200 (mar. 29 juil. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Sympa/ModDef.pm Fision via: r11272 [-bug] Crypt::OpenSSL::X509 was missing in module list. ------------------------------------------------------------------------ r11245 | sikeda | 2014-07-27 03:57:48 +0200 (dim. 27 juil. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/lib/tools.pm [svn] Backporting a few fixes from sympa-6.2-branch. ------------------------------------------------------------------------ r11244 | sikeda | 2014-07-27 03:35:43 +0200 (dim. 27 juil. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Message.pm Fision via: r11245 [-bug] Removed ineffective -rand option fed to openssl. Despite documents of OpenSSL describes, it has no effect. That's why LibreSSL deprecated this option: http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/lib/libssl/src/apps/smime.c?rev=1.25&content-type=text/x-cvsweb-markup ------------------------------------------------------------------------ r11231 | sikeda | 2014-07-24 16:55:40 +0200 (jeu. 24 juil. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Message.pm Fision via: r11245 [-bug] According to RFC 4871, new DKIM-Signature header field should not be appended to header but be prepended. ------------------------------------------------------------------------ r11155 | sikeda | 2014-07-12 06:39:54 +0200 (sam. 12 juil. 2014) | 7 lignes Chemins modifis: M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [dev] (r11154) Some fixes (partly on style) and improvement. - Added Japanese translation. - The parameter password_validation was not optional. - Data::Password module was not optional because it was loaded at startup. Use eval{require} instead of use. - @Data::Password::DICTIONARIES would grow by each call of password_validation. - Swtich feature and exported names wouldn't be used. ------------------------------------------------------------------------ r11154 | sikeda | 2014-07-12 05:31:32 +0200 (sam. 12 juil. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/web_tt2/error.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature][#9306] [Submitted by Evoltech, Riseup] Better password validation. When the user requests change of password via WWSympa or SympaSOAP, new password may be checked its strength. New parameter password_validation may be used to customize policy of password validation. See help text of the parameter for more details. ------------------------------------------------------------------------ r11147 | sikeda | 2014-07-11 05:36:47 +0200 (ven. 11 juil. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/javascript.tt2 [bug][#9270] [Submitted by A. Bezverkhyy] Fixing buggy getElementById behavior on IE8/9. ------------------------------------------------------------------------ r11144 | sikeda | 2014-07-09 16:43:57 +0200 (mer. 09 juil. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#9328][Submitted by F. Angebault] Back button prints an error "Webpage has expired" on IE9. On IE9, "Cache-Control: no-cache" HTTP response is treated as never keep content. Alternatively, "Cache-Control: max-age=0" may be used: This workaround may not affect to other browsers. For more details see item #9328 in bug tracker. ------------------------------------------------------------------------ r11136 | sikeda | 2014-07-08 09:46:30 +0200 (mar. 08 juil. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [bug][#9326] [Submitted by M. Howe, Univ. of Oxford] Search conditions in scenario of text filter were not excaped. Several characters in condition such as ".", "+" were treated as regexp metacharacter then search gave unexpected result or simlpy failed. ------------------------------------------------------------------------ r11135 | sikeda | 2014-07-08 09:02:46 +0200 (mar. 08 juil. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/lib/Scenario.pm [-bug] Check existence of parameter of expression [list->parameter] in scenario. ------------------------------------------------------------------------ r11128 | sikeda | 2014-07-06 06:11:26 +0200 (dim. 06 juil. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Scenario.pm Fision via: r11135 [-dev] Check existence of parameter of expression [list->parameter] in scenario. ------------------------------------------------------------------------ r11134 | sikeda | 2014-07-08 08:36:20 +0200 (mar. 08 juil. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#9300] [Sumitted by M. Howe, Univ. of Oxford] Sympa crashes if a list config does not have web_archive paragraph. In such case $list->{admin}{web_archive} may be an arrayref. If program accesses to this element as hashref, it will die with "Not a hash reference". Fixed by checking if the element is hashref. ------------------------------------------------------------------------ r11133 | sikeda | 2014-07-08 07:51:56 +0200 (mar. 08 juil. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#9262] [Originally submitted by Evoltech, Riseup] Prevent special list addresses from being added as list owner address. Check owner addresses to be added and reject ones falling under any of owner, return path etc. of the list. Patch was modified to check also VERP (bounce+) address pattern and suffixes in list_check_suffixes parameter. ------------------------------------------------------------------------ r11132 | sikeda | 2014-07-08 05:23:07 +0200 (mar. 08 juil. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Language.pm [bug][#9256] [Submitted by F. Angebault] "Manage your subscriptions" help page is only partially translated. ------------------------------------------------------------------------ r11070 | etiennemeleard | 2014-06-24 12:00:53 +0200 (mar. 24 juin 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature] Propagate choosen list descriptors (name, template, description ...) so that they can be used in create_list scenarios. ------------------------------------------------------------------------ r11069 | etiennemeleard | 2014-06-24 11:59:01 +0200 (mar. 24 juin 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [feature] Now CustomCondition can set the action to take (do_it, reject ...) by setting $_, this allows for complex, single-module CustomConditions. ------------------------------------------------------------------------ r11009 | sikeda | 2014-06-18 09:54:30 +0200 (mer. 18 juin 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in [bug] When lock_method is "nfs", Lock files in the spool are removed. Fixed by skipping files with ".lock" or ".NFSLock" extension. ------------------------------------------------------------------------ r10944 | david.verdin | 2014-06-13 15:17:23 +0200 (ven. 13 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in [bug] A missing dollar sign made sympa.pl crash when an automatic lst could not be created. ------------------------------------------------------------------------ r10911 | david.verdin | 2014-06-10 17:11:25 +0200 (mar. 10 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in [bug] When parameter dkim_signature_apply_on contained the value 'dkim_authenticated_messages', DKIM signing was used even for unsigned messages or messages containing an unvalid DKIM signature. Fixed by testing that DKIM verification passed before deciding if we need to add a DKIM signature. ------------------------------------------------------------------------ r10879 | sympa-authors | 2014-06-06 18:09:40 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.22 ------------------------------------------------------------------------ r10878 | david.verdin | 2014-06-06 18:06:45 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] Removing call to DAta::Dumper, useless and making message sending to fail. ------------------------------------------------------------------------ r10876 | david.verdin | 2014-06-06 17:52:05 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] Fixing typo: missing right curly bracket. ------------------------------------------------------------------------ r10875 | david.verdin | 2014-06-06 17:44:04 +0200 (ven. 06 juin 2014) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [bug] Various problems related to DMARC: 1- dmarc_protection_mode was not correctly taken as default for lists configuration because it was not split into an array by Conf.pm. Fixed in main and robot config. 2- Net::DNS can now be considered mandatory. Added to the module dependencies as such with the latest packaged version for RHEL 6. 3- Added debug and error logs while addressing DMARc issues. ------------------------------------------------------------------------ r10872 | sympa-authors | 2014-06-06 15:22:12 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.21 ------------------------------------------------------------------------ r10871 | sympa-authors | 2014-06-06 15:14:22 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10870 | sympa-authors | 2014-06-06 14:53:44 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10868 | etiennemeleard | 2014-06-06 12:52:44 +0200 (ven. 06 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] Fix viewmod bug if message contained (% token by reproducing archived messages custom tt2 tags. ------------------------------------------------------------------------ r10866 | etiennemeleard | 2014-06-06 12:27:32 +0200 (ven. 06 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/viewmod.tt2 [svn] Reverting commit that did not solve the problem ------------------------------------------------------------------------ r10865 | sympa-authors | 2014-06-06 12:22:13 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10863 | sympa-authors | 2014-06-06 11:57:28 +0200 (ven. 06 juin 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/eo.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/io.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10861 | etiennemeleard | 2014-06-06 11:42:55 +0200 (ven. 06 juin 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/viewmod.tt2 [-bug] Add complicated custom tt2 tags in viewmod template so that [% tokens that can be present in sent messages are not considered as tt2 syntax. ------------------------------------------------------------------------ r10860 | david.verdin | 2014-06-06 11:33:42 +0200 (ven. 06 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm [bug][#7318][Fixed by V. Bonamy, univ. Rouen] When usingUTF-8 encoded data, data were twice encoded before being setn by the SOAP server. Fixed by decoding from UTF-8 any text data. ------------------------------------------------------------------------ r10858 | david.verdin | 2014-06-06 10:58:13 +0200 (ven. 06 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm [bug] Scenarios were not evaluated for DKIM auth level, because DKIM signature was found invalifd. This was due - again - by the usage of MIME::Entity::as_string to store the message as string. Fixed by simply storing the raw data from message file instead of using this sub. ------------------------------------------------------------------------ r10829 | david.verdin | 2014-06-05 10:42:35 +0200 (jeu. 05 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [bug] When using DKIM, both DKIM and S/MIME signatures were broken. This was due to the usage of MIME::Entity->as_string that rewrapped the message body to 60 columns - thus changing the character string of the message body and probably also the \015\12 end of line required to get valid DKIM signatures. Fixed by storing the message body fed to Mail::DKIM and concatenating it to the new headers containing the DKIM signature. ------------------------------------------------------------------------ r10827 | david.verdin | 2014-06-04 15:14:25 +0200 (mer. 04 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm [bug] DKIM parameterswere not correctly loaded and dkim_signer_domain parameter was not correctly defaulted to the list service domain. ------------------------------------------------------------------------ r10825 | david.verdin | 2014-06-04 12:05:05 +0200 (mer. 04 juin 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] When using binary cache for list configs, if the main config (sympa.conf or wwsympa.conf) was changed, binary cache were still used. Problem: These bainary cache contain default values taken from main config. If these values were changed in main config, they were not updated in binary cache. Fixed by testing the last modification time for sympa.conf and wwsympa.conf against the binary_cache last update time. ------------------------------------------------------------------------ r10734 | sikeda | 2014-05-25 03:50:44 +0200 (dim. 25 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [-bug] tools::date_conv() ignored hour part. ------------------------------------------------------------------------ r10728 | sikeda | 2014-05-24 16:02:36 +0200 (sam. 24 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/mysql_alias_manager.pl.in [-dev] fixed non-fatal obsoleted syntax. ------------------------------------------------------------------------ r10727 | sikeda | 2014-05-24 16:00:56 +0200 (sam. 24 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/ldap_alias_manager.pl.in [bug] Fixed typos broke ldap_alias_manager.pl. ------------------------------------------------------------------------ r10725 | sikeda | 2014-05-24 02:49:31 +0200 (sam. 24 mai 2014) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwslib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] WWSympa may crash due to unknown subroutine when: - Anonymous user accesses to $wwsympa_url/signoff/LIST/EMAIL, i.e. specifying email but not logging-in. - User does not exist in user_table. This causes because wwslib::init_passwd() called in wwsympa.fcgi doesn't have qualified name. Use qualified name and do fix another logical bug on unknown variable. ------------------------------------------------------------------------ r10713 | sikeda | 2014-05-23 11:18:40 +0200 (ven. 23 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/etc/script/ldap_alias_manager.pl.in M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/web_tt2/review.tt2 M /branches/sympa-6.1-branch/wwsympa/Bounce.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/cookielib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [svn] Backport bug fix from sympa-6.2-branch. ------------------------------------------------------------------------ r10712 | sikeda | 2014-05-23 11:13:07 +0200 (ven. 23 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.2-branch/src/lib/List.pm Fision via: r10713 [-bug] con'd r10709) more fixes. ------------------------------------------------------------------------ r10710 | sikeda | 2014-05-23 10:37:29 +0200 (ven. 23 mai 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.2-branch/default/web_tt2/review.tt2 Fision via: r10713 [-bug] (con'd r10709) more fixes. ------------------------------------------------------------------------ r10709 | sikeda | 2014-05-23 10:26:33 +0200 (ven. 23 mai 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/cgi/wwsympa.fcgi.in M /branches/sympa-6.2-branch/src/lib/List.pm M /branches/sympa-6.2-branch/src/lib/Sympa/ListDef.pm M /branches/sympa-6.2-branch/src/lib/tools.pm M /branches/sympa-6.2-branch/src/libexec/ldap_alias_manager.pl.in M /branches/sympa-6.2-branch/src/sbin/sympa.pl.in M /branches/sympa-6.2-branch/src/sbin/task_manager.pl.in Fision via: r10713 [bug] Fixed several misspellings of the word "occurrence(s)" in sources. ------------------------------------------------------------------------ r10708 | sikeda | 2014-05-23 10:12:52 +0200 (ven. 23 mai 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Bounce.pm M /branches/sympa-6.2-branch/src/lib/Bulk.pm M /branches/sympa-6.2-branch/src/lib/List.pm M /branches/sympa-6.2-branch/src/lib/Log.pm M /branches/sympa-6.2-branch/src/lib/Sympa/DatabaseDescription.pm M /branches/sympa-6.2-branch/src/lib/cookielib.pm M /branches/sympa-6.2-branch/src/lib/mail.pm M /branches/sympa-6.2-branch/src/lib/tools.pm M /branches/sympa-6.2-branch/src/sbin/archived.pl.in M /branches/sympa-6.2-branch/src/sbin/bulk.pl.in M /branches/sympa-6.2-branch/src/sbin/sympa.pl.in Fision via: r10713 [dev] French spelling in English messages and variable names: "receipient" and "adresse". *NOTE*: On bulkmailer_table, a column is named "recEipients_bulkmailer"; it should not be changed. ------------------------------------------------------------------------ r10550 | sikeda | 2014-04-27 15:07:36 +0200 (dim. 27 avril 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm [-dev] (con'd r10541) Several new features contributed by S. Shipway. And a few cleanups. ------------------------------------------------------------------------ r10545 | sikeda | 2014-04-16 06:53:31 +0200 (mer. 16 avril 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-dev] (con'd r10541,10543) typo. ------------------------------------------------------------------------ r10544 | sikeda | 2014-04-16 06:15:23 +0200 (mer. 16 avril 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-dev] (con'd r10541,10543) typo. ------------------------------------------------------------------------ r10543 | sikeda | 2014-04-16 06:07:36 +0200 (mer. 16 avril 2014) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [dev] (con'd r10541) i18n concern: - Added description for parameter values. - Apply MIME encoding to anonymous From: header field. Note that dmarc_protection.mail was renamed to dmarc_protection.other_email. ------------------------------------------------------------------------ r10542 | sikeda | 2014-04-16 06:03:20 +0200 (mer. 16 avril 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [dev] Now tools::addrencode() can take optional "comment" argument. ------------------------------------------------------------------------ r10541 | sikeda | 2014-04-15 13:39:01 +0200 (mar. 15 avril 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [feature] [Submitted by S. Shipway, Univ. of Auckland] Workaround for aggressive DMARC policy such as yahoo.com. The patch adds option #3 of this DMARC FAQ: http://dmarc.org/faq.html#s_3 - New list config paragraph "dmarc_protection" to munge "From:" header and put original header content erc. into comment. ------------------------------------------------------------------------ r10540 | sikeda | 2014-04-13 10:56:51 +0200 (dim. 13 avril 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm [change] New parameters for merged messages. "part.description", "part.disposition", "part.encoding" and "part.type" may be used for each part of input messages. These are all-lowercase (except "part.description"). ------------------------------------------------------------------------ r10539 | sikeda | 2014-04-13 10:10:44 +0200 (dim. 13 avril 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/task_manager.pl.in [bug] If cache_list_db is "on" and list_table is empty, all users in user_table except listmaster will be purged. To prevent information loss, made purge_user_table task not to use list_table cache. ------------------------------------------------------------------------ r10538 | sikeda | 2014-04-12 10:05:43 +0200 (sam. 12 avril 2014) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/mail.pm [bug][#7289][Reported by D Launay, RENATER] Signed mail body are altered. Fixed by making following functions not to alter such parts: - Bulk::merge_msg() - List::_append_parts() Backported from sympa-6.2-branch. ------------------------------------------------------------------------ r10309 | sikeda | 2014-02-28 04:34:14 +0100 (ven. 28 fvr. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/lib/Bulk.pm M /branches/sympa-6.2-branch/src/lib/List.pm M /branches/sympa-6.2-branch/src/lib/mail.pm Fision via: r10538 [bug][#7289][Reported by D Launay, RENATER] Signed mail body are altered. Fixed by making following functions not to alter such parts: - Bulk::merge_msg() - List::_append_parts() ------------------------------------------------------------------------ r10537 | sikeda | 2014-04-10 16:10:01 +0200 (jeu. 10 avril 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/ja_JP/css.tt2 [-change] Added new font-families to locale-specific CSS template. ------------------------------------------------------------------------ r10536 | sikeda | 2014-04-10 16:08:22 +0200 (jeu. 10 avril 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [-change] Give times of sessions precision to second. ------------------------------------------------------------------------ r10535 | sikeda | 2014-04-10 15:50:12 +0200 (jeu. 10 avril 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [bug][#9105] [Originally submitted by M. Howe, Univ. of Oxford] Session for multiple robots sharing same cookie domain is broken. Fixed injected bug by fix for bug #6180: the sessions were limited to each virtual robot at the first access. The patch was modified a bit. ------------------------------------------------------------------------ r10468 | sympa-authors | 2014-03-21 11:48:20 +0100 (ven. 21 mars 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.20 ------------------------------------------------------------------------ r10467 | sympa-authors | 2014-03-21 11:41:36 +0100 (ven. 21 mars 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10459 | sympa-authors | 2014-03-21 10:38:12 +0100 (ven. 21 mars 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10458 | sympa-authors | 2014-03-21 10:16:33 +0100 (ven. 21 mars 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/af.po M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/eo.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/io.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/nn_NO.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/af.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/eo.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/gl.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/io.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/ko.po M /branches/sympa-6.1-branch/po-wwsympa/la.po M /branches/sympa-6.1-branch/po-wwsympa/ml.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/nn_NO.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po M /branches/sympa-6.1-branch/po-wwsympa/zh_TW.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10453 | sikeda | 2014-03-20 08:46:59 +0100 (jeu. 20 mars 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] Locale::Messages 1.16 or earlier does not have its own $VERSION variable. Check the version of Locale::TextDomain instead. ------------------------------------------------------------------------ r10452 | sikeda | 2014-03-20 08:39:18 +0100 (jeu. 20 mars 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] a typo in message. ------------------------------------------------------------------------ r10441 | sikeda | 2014-03-18 03:22:04 +0100 (mar. 18 mars 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [bug] Obsoleted parameter filesystem_encoding was not recognized during upgrade from the releases earlier than 5.3b.3. Fixed bug introduced in r5656 with Conf::Conf::Ignored_Conf. ------------------------------------------------------------------------ r10410 | sikeda | 2014-03-13 04:18:39 +0100 (jeu. 13 mars 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/po/af.po M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/eo.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/io.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/nn_NO.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/rm.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/af.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/eo.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/gl.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/io.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/ko.po M /branches/sympa-6.1-branch/po-wwsympa/la.po M /branches/sympa-6.1-branch/po-wwsympa/ml.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/nn_NO.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/web_help.pot M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po M /branches/sympa-6.1-branch/po-wwsympa/zh_TW.po [-feature] Committing recent translations from Pootle ------------------------------------------------------------------------ r10377 | sikeda | 2014-03-08 00:51:42 +0100 (sam. 08 mars 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_newaliases.pl.in [bug] sympa_newaliases fails to update aliases for virtual robot. Conf::load_conf() won't load robot config if no_db option is true. As a result, No virtual robots were recognized. ------------------------------------------------------------------------ r10355 | sikeda | 2014-03-05 05:58:57 +0100 (mer. 05 mars 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [bug] wwsympa/send_mail: Duplicate Message-ID: header field. do_send_mail() feeds mail_file() a Message-ID as 'headers' parameter, but mail_file() didn't check it. ------------------------------------------------------------------------ r10307 | sikeda | 2014-02-27 07:46:40 +0100 (jeu. 27 fvr. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm [bug] Mail merge: Parameters taken from mail headers may be overwritten by ones of each subpart. Initialize $data->{'headers'} only once at calling Bulk::merge_msg(). ------------------------------------------------------------------------ r10304 | sikeda | 2014-02-26 06:02:32 +0100 (mer. 26 fvr. 2014) | 36 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [-change] Updating default of anonymous_header_fields. List of header fields included in default (* were added): Authentication-Results: RFC7001 * DKIM-Signature: RFC6376 (DomainKeys) Disposition-Notification-To: RFC3798 (MDN) Injection-Info: RFC5536 (Netnews) * Organisation: non-standard * Organization: RFC5536 (Netnews) Original-Recipient: RFC3798 (MDN) * Originator: non-standard (Netnews) * Path: RFC5536 (Netnews) * Received: RFC5322 Received-SPF: RFC4408 (SPF) * Reply-To: RFC5322 Resent-Reply-To: RFC5322 * Return-Receipt-To: non-standard * X-Envelope-From: non-standard X-Envelope-To: non-standard X-Sender: non-standard X-X-Sender: non-standard n.b. "non-standard" fields are listed in I-D draft-palme-mailext-mail-attributes-01. These were omitted because they will be always overridden by Sympa: Errors-To: From: Message-Id: Resent-From: Resent-Message-Id: Resent-Sender: Sender: ------------------------------------------------------------------------ r10303 | sikeda | 2014-02-26 06:00:23 +0100 (mer. 26 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-change] List::distribute_msg(): When the header field is removed or replaced, corresponding Resent-*: fields will be removed to override information provided by forwarders. ------------------------------------------------------------------------ r10298 | sikeda | 2014-02-25 09:37:51 +0100 (mar. 25 fvr. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] (con'd r10297) typo. ------------------------------------------------------------------------ r10297 | sikeda | 2014-02-25 09:09:35 +0100 (mar. 25 fvr. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] Distributed messages may have duplicate content of some header fields, i.e. Errors-To:, Precedence: and Sender: (and From: and Message-ID: when anonymous_sender is enabled). Several content filtering systems will take off the score for such messages. Make sure that the fields will be overwritten by List::distribute_msg(). ------------------------------------------------------------------------ r10246 | sikeda | 2014-02-17 09:32:14 +0100 (lun. 17 fvr. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [-feature] Additional localised "Re:" prefixes in subject. "Antw:" (Dutch), "?\206?\145?\207?\128:", "?\206?\163?\207?\135?\206?\181?\207?\132:" (Greek), "Odp:" (Polish) and "YNT:" (Turkish). ------------------------------------------------------------------------ r10242 | sikeda | 2014-02-16 03:58:47 +0100 (dim. 16 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/listmaster_notification.tt2 M /branches/sympa-6.1-branch/web_tt2/error.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug][#9042] typo in error code for report::reject_report_web(). ------------------------------------------------------------------------ r10214 | sikeda | 2014-02-09 08:22:07 +0100 (dim. 09 fvr. 2014) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in [bug] On rebuilt archives, src attributes of HTML elements may be removed. Because Message::clean_html() was given Message object of message without X-Sympa-To: pseudo header. Give robot argument to this method. Changes: - Following methods/functions require the argument of Robot object: Archive::clean_archived_message(), Archive::clean_archive_directory(), Message::clean_html(), tools::sanitize_html(), tools::sanitize_html_file() and tools::sanitize_var(). - archived.pl: Now mhonarc will be given arguments with per-robot value of wwsympa_url parameter instead of site-default value. ------------------------------------------------------------------------ r10213 | sikeda | 2014-02-09 03:09:36 +0100 (dim. 09 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Spurious warnings "Argument "" isn't numeric in sprintf" and "Missing argument in sprintf". ------------------------------------------------------------------------ r10212 | sikeda | 2014-02-08 13:42:24 +0100 (sam. 08 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [bug][#9039][Reported by J. Moutinho] wwsympa: signoff crashes. wwsympa/signoff/somelistname dies with "Cannot quote a reference" error, since mail::mail_file() may take List object as 'list' item in $data argument. List along with HASH would be concerned. ------------------------------------------------------------------------ r10210 | sympa-authors | 2014-02-05 15:08:16 +0100 (mer. 05 fvr. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.19 ------------------------------------------------------------------------ r10209 | sympa-authors | 2014-02-05 14:57:35 +0100 (mer. 05 fvr. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10208 | sympa-authors | 2014-02-05 14:37:01 +0100 (mer. 05 fvr. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/eo.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/io.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/eo.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/io.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10207 | david.verdin | 2014-02-04 16:37:03 +0100 (mar. 04 fvr. 2014) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql [*change] Two new database fields appeared in this version and a field was modified. - The new fields are prev_id_session (varchar(30)) and refresh_date_session (int(11)). they are located in the session_table table. - The modified field is dkim_privatekey_bulkspool and is located in the bulkspool_table table. Its length went from varchar(1000) to varchar(2000). Sympa install using MySQL and SQLite backends will have no trouble at all, as the database structure is updated by Sympa. However, if you use Postgres, Oracle or Sybase, please have a look (respectively) at the create_db.Pg, create_db.Oracle or create_db.Sybase to check the definition of those fields. Please update your database structure before running Sympa. ------------------------------------------------------------------------ r10206 | david.verdin | 2014-02-04 16:23:34 +0100 (mar. 04 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm [change] Changing length of DKIM private key in database to ensure database creation scripts will be updated. ------------------------------------------------------------------------ r10205 | david.verdin | 2014-02-04 11:40:27 +0100 (mar. 04 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm [bug] new session fields (prev_id_session and refresh_date_session) were not declared in DatabaseDescription.pm. Consequently, they were not used in create_db_script.* update. ------------------------------------------------------------------------ r10204 | sikeda | 2014-02-03 13:58:42 +0100 (lun. 03 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/archived.pl.in [-dev] Some typos and minor bugs due to undefined variables. ------------------------------------------------------------------------ r10203 | sikeda | 2014-02-03 13:34:37 +0100 (lun. 03 fvr. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in [-dev] Refactoring to make succeeding fixes easy. ------------------------------------------------------------------------ r10202 | sikeda | 2014-02-01 09:09:24 +0100 (sam. 01 fvr. 2014) | 2 lignes Chemins modifis: A /branches/sympa-6.1-branch/po/eo.po A /branches/sympa-6.1-branch/po/io.po A /branches/sympa-6.1-branch/po/rm.po M /branches/sympa-6.1-branch/po-wwsympa/LINGUAS A /branches/sympa-6.1-branch/po-wwsympa/eo.po A /branches/sympa-6.1-branch/po-wwsympa/io.po A /branches/sympa-6.1-branch/po-wwsympa/rm.po [-dev] Added Catalan translation of online help to distribution. Added initial catalog files for Espreranto (eo), Ido (io) and Romansh (rm). ------------------------------------------------------------------------ r10201 | sikeda | 2014-01-31 09:17:34 +0100 (ven. 31 janv. 2014) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/web_tt2/help_faquser.tt2 M /branches/sympa-6.1-branch/web_tt2/renewpasswd.tt2 [-bug] wwsympa: A link to function "sendpasswd" which is no longer used. Fixed that link in help/faquser page and translation catalogs. Additionally, %action_type in Log.pm did not contain entry for "requestpasswd". ------------------------------------------------------------------------ r10200 | sikeda | 2014-01-29 09:58:07 +0100 (mer. 29 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am A /branches/sympa-6.1-branch/etc_README (de /branches/sympa-6.1-branch/src/etc/README_etc:10199) D /branches/sympa-6.1-branch/src/etc/README_etc [-change] Create subdirectories in $(sysconfdir) at installation time, and add description on usage of this directory. ------------------------------------------------------------------------ r10197 | sympa-authors | 2014-01-24 14:10:05 +0100 (ven. 24 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.18 ------------------------------------------------------------------------ r10196 | sympa-authors | 2014-01-24 13:54:33 +0100 (ven. 24 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10195 | sympa-authors | 2014-01-24 13:32:45 +0100 (ven. 24 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10194 | david.verdin | 2014-01-24 12:02:50 +0100 (ven. 24 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [bug] In scenarios, the field [msg->spam_status] was never taken into account because the message objects fields were not made available to scenarios. Fixed by adding a new test in Scenario::request_action. Now any Message field can be accessed in scenarios. ------------------------------------------------------------------------ r10193 | david.verdin | 2014-01-23 15:07:50 +0100 (jeu. 23 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm [change] Now the list of files parsed (in addition to config.tt2) when instantiating a family are defined in a new (sympa.conf and robot.conf parameter called 'parsed_family_files'. This parameter must contain a comma-separated list of file names. If these files exist, with the '.tt2' extension in a family, they will be parsed and added to the list directory. The default value of the parameter is 'message.footer,message.header,message.footer.mime,message.header.mime,info'. ------------------------------------------------------------------------ r10192 | sikeda | 2014-01-23 08:11:45 +0100 (jeu. 23 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/sympa.spec.in [-bug] (con'd r10159) Fix sympa.spec, too. ------------------------------------------------------------------------ r10191 | sikeda | 2014-01-23 08:10:23 +0100 (jeu. 23 janv. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am M /branches/sympa-6.1-branch/src/lib/Makefile.am [-bug] (More fix to r10159) There may not be write access under $(DESTDIR). Write previous_sympa_version under $(top_srcdir) instead. ------------------------------------------------------------------------ r10163 | sikeda | 2014-01-19 01:30:36 +0100 (dim. 19 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [bug] Mail-DKIM < 0.39 depended on Crypt::OpenSSL::Bignum. Updates dependency. ------------------------------------------------------------------------ r10159 | david.verdin | 2014-01-15 15:22:13 +0100 (mer. 15 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/src/lib/Makefile.am [bug] Ugly fix for make install bug: the important_changes.pl script was not correctly executed at the end of make install. Indeed, the Makefile.am "importantchanges" macro checked for the previous version number in Sympa binaries AFTER this version number had been substituted with the NEW version number. Then, previous and current version numbers had always the same values. None of the -sometimes vital- informations supposed to be displayed by this script were tehrefore displayed. The ugliness of the fix resides here: I stored the previous version number in a temporary file during the make and read it (then delete the file) during make install. ------------------------------------------------------------------------ r10158 | sikeda | 2014-01-15 05:23:07 +0100 (mer. 15 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in [-change] (con'd r9989, r10004) sympa.pl: make_alias_file will make multiple files by each sendmail_aliases path. ------------------------------------------------------------------------ r10129 | david.verdin | 2014-01-08 11:47:10 +0100 (mer. 08 janv. 2014) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [change] Custom vars values can take any character string as values (previously, spaces were forbidden). ------------------------------------------------------------------------ r10128 | sikeda | 2014-01-06 04:21:43 +0100 (lun. 06 janv. 2014) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [-bug] (reverted r10070) withdrawing partial fix which make things worse. It would rather be postponed. ------------------------------------------------------------------------ r10127 | sikeda | 2014-01-05 16:07:59 +0100 (dim. 05 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/list_rejected.tt2 M /branches/sympa-6.1-branch/mail_tt2/review.tt2 M /branches/sympa-6.1-branch/mail_tt2/stats_report.tt2 M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/web_tt2/nav.tt2 [-dev] More i18n'ed messages. ------------------------------------------------------------------------ r10126 | sikeda | 2014-01-05 03:26:34 +0100 (dim. 05 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-dev] (con'd r10076) Uniform subroutine names: "do_". ------------------------------------------------------------------------ r10125 | sikeda | 2014-01-05 03:24:17 +0100 (dim. 05 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] (con'd r10076) WWSympa crashes if user accesses a function with subroutine undefined. ------------------------------------------------------------------------ r10124 | sikeda | 2014-01-05 03:11:42 +0100 (dim. 05 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] (con'd r10076) remove duplicated keys in hash definitions. ------------------------------------------------------------------------ r10083 | sikeda | 2014-01-01 01:12:12 +0100 (mer. 01 janv. 2014) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/AUTHORS M /branches/sympa-6.1-branch/Makefile.am M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/doc/Makefile.am M /branches/sympa-6.1-branch/doc/man8/Makefile.am M /branches/sympa-6.1-branch/doc/sample/Makefile.am M /branches/sympa-6.1-branch/important_changes.pl M /branches/sympa-6.1-branch/mail_tt2/Makefile.am M /branches/sympa-6.1-branch/po/check_locales.pl M /branches/sympa-6.1-branch/soap/Makefile.am M /branches/sympa-6.1-branch/soap/SympaTransport.pm M /branches/sympa-6.1-branch/soap/sympa_soap_client.pl.in M /branches/sympa-6.1-branch/soap/sympa_soap_server.fcgi.in M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/Makefile.am M /branches/sympa-6.1-branch/src/alias_manager.pl.in M /branches/sympa-6.1-branch/src/aliaswrapper.c M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/etc/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/arc2webarc.pl.in M /branches/sympa-6.1-branch/src/etc/script/crypt_passwd.pl.in M /branches/sympa-6.1-branch/src/etc/script/init_comment.pl.in M /branches/sympa-6.1-branch/src/etc/script/p12topem.pl.in M /branches/sympa-6.1-branch/src/etc/script/testldap.pl.in M /branches/sympa-6.1-branch/src/etc/script/testlogs.pl.in M /branches/sympa-6.1-branch/src/etc/script/tpl2tt2.pl.in M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/Config_XML.pm M /branches/sympa-6.1-branch/src/lib/Datasource.pm M /branches/sympa-6.1-branch/src/lib/Family.pm M /branches/sympa-6.1-branch/src/lib/Fetch.pm M /branches/sympa-6.1-branch/src/lib/LDAPSource.pm M /branches/sympa-6.1-branch/src/lib/Language.pm M /branches/sympa-6.1-branch/src/lib/Ldap.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Lock.pm M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/src/lib/Makefile.am M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/Robot.pm M /branches/sympa-6.1-branch/src/lib/SQLSource.pm M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/src/lib/Sympa/Constants.pm.in M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm M /branches/sympa-6.1-branch/src/lib/Sympa/Template/Compat.pm M /branches/sympa-6.1-branch/src/lib/Task.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/WebAgent.pm M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/report.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/src/queue.c M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/sympa_newaliases-wrapper.c M /branches/sympa-6.1-branch/src/sympa_newaliases.pl.in M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/src/virtualwrapper.c M /branches/sympa-6.1-branch/web_tt2/Makefile.am M /branches/sympa-6.1-branch/wwsympa/Auth.pm M /branches/sympa-6.1-branch/wwsympa/Bounce.pm M /branches/sympa-6.1-branch/wwsympa/Makefile.am M /branches/sympa-6.1-branch/wwsympa/SharedDocument.pm M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/cookielib.pm M /branches/sympa-6.1-branch/wwsympa/icons/Makefile.am M /branches/sympa-6.1-branch/wwsympa/wwslib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [dev] Updating copyright notices. ------------------------------------------------------------------------ r10082 | david.verdin | 2013-12-31 12:05:40 +0100 (mar. 31 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/queue.c [dev] Reverting revision [#10043] which broke the queue programs (no mails were put in spools anymore). ------------------------------------------------------------------------ r10080 | sympa-authors | 2013-12-30 15:42:37 +0100 (lun. 30 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10079 | sympa-authors | 2013-12-30 15:26:01 +0100 (lun. 30 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10078 | sympa-authors | 2013-12-30 15:11:58 +0100 (lun. 30 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r10077 | sympa-authors | 2013-12-30 14:28:58 +0100 (lun. 30 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r10076 | sikeda | 2013-12-30 03:51:08 +0100 (lun. 30 dc. 2013) | 7 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [*bug] wwsympa: Non-privileged users can access to some restricted functions. - Any users can access to skinsedit function and execute it. - The remove_template function won't check argument. These are due to typos in some tables (hashes) defining functions. In addition, there are a few ineffective definitions in the tables. ------------------------------------------------------------------------ r10075 | sikeda | 2013-12-30 03:00:24 +0100 (lun. 30 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/edit_config.tt2 M /branches/sympa-6.1-branch/web_tt2/reviewbouncing.tt2 M /branches/sympa-6.1-branch/web_tt2/skinsedit.tt2 [-bug] inconsostent
. ------------------------------------------------------------------------ r10074 | sikeda | 2013-12-29 11:08:18 +0100 (dim. 29 dc. 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/command_report.tt2 M /branches/sympa-6.1-branch/mail_tt2/info_report.tt2 M /branches/sympa-6.1-branch/mail_tt2/review.tt2 M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/web_tt2/edit_list_request.tt2 M /branches/sympa-6.1-branch/web_tt2/review_family.tt2 M /branches/sympa-6.1-branch/web_tt2/search_user.tt2 M /branches/sympa-6.1-branch/web_tt2/suboptions.tt2 M /branches/sympa-6.1-branch/web_tt2/subscriber_table.tt2 M /branches/sympa-6.1-branch/web_tt2/suspend_request.tt2 M /branches/sympa-6.1-branch/wwsympa/wwslib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature] i18n of options for list parameters and subscriber options. - Options on edit_list page are shown by i18n'ed titles. - Only listmasters can view real config values. - Subscriber options on review pages, command results, subscriber option pages and so on are shown by i17n'ed titles (along with real option values). ------------------------------------------------------------------------ r10073 | sikeda | 2013-12-29 10:22:16 +0100 (dim. 29 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/css.tt2 [-change] Fix vertical-align attribute of element. ------------------------------------------------------------------------ r10063 | sikeda | 2013-12-27 11:39:00 +0100 (ven. 27 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [bug] MHonArc 2.6.18 should be used to avoid vulnerabilities. cf. CVE-2010-4524, CVE-2010-1677. ------------------------------------------------------------------------ r10062 | sikeda | 2013-12-27 05:02:16 +0100 (ven. 27 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-dev] (con'd r10051) also save and restore absolute_path flag. ------------------------------------------------------------------------ r10061 | sikeda | 2013-12-26 08:44:10 +0100 (jeu. 26 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/css.tt2 [-dev] small fix. ------------------------------------------------------------------------ r10060 | sikeda | 2013-12-26 06:21:03 +0100 (jeu. 26 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/active_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/css.tt2 M /branches/sympa-6.1-branch/web_tt2/get_closed_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/get_inactive_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/get_latest_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/get_pending_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/latest_arc.tt2 M /branches/sympa-6.1-branch/web_tt2/latest_d_read.tt2 M /branches/sympa-6.1-branch/web_tt2/latest_lists.tt2 M /branches/sympa-6.1-branch/web_tt2/ls_templates.tt2 M /branches/sympa-6.1-branch/web_tt2/search_user.tt2 M /branches/sympa-6.1-branch/web_tt2/subscriber_table.tt2 [bug] [#8506] [Reported by P. Zsigmond, Univ. of British Columbia] background-color of table cells are defined inconsistent. ------------------------------------------------------------------------ r10059 | sikeda | 2013-12-26 05:53:15 +0100 (jeu. 26 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/listmaster_notification.tt2 [-feature] i18n'ed css_updated listmaster notification. ------------------------------------------------------------------------ r10058 | sikeda | 2013-12-26 05:48:43 +0100 (jeu. 26 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/web_tt2/copy_template.tt2 M /branches/sympa-6.1-branch/web_tt2/edit_template.tt2 M /branches/sympa-6.1-branch/web_tt2/ls_templates.tt2 M /branches/sympa-6.1-branch/web_tt2/view_template.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature] (con'd r10051, r10056) wwsympa/serveradmin: per-language distrib templates is viewable. Additionally, Language names are shown instead of locale names. ------------------------------------------------------------------------ r10057 | sikeda | 2013-12-25 11:22:49 +0100 (mer. 25 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/copy_template.tt2 M /branches/sympa-6.1-branch/web_tt2/edit_list_request.tt2 M /branches/sympa-6.1-branch/web_tt2/pref.tt2 M /branches/sympa-6.1-branch/web_tt2/setlang.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-dev] (con'd r10056) More improvement. Elements with LanguageNeutral class would have lang/xml:lang attributes so that renderers may be able to choose proper font variants. ------------------------------------------------------------------------ r10056 | sikeda | 2013-12-24 15:49:07 +0100 (mar. 24 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/copy_template.tt2 M /branches/sympa-6.1-branch/web_tt2/css.tt2 M /branches/sympa-6.1-branch/web_tt2/edit_list_request.tt2 M /branches/sympa-6.1-branch/web_tt2/pref.tt2 M /branches/sympa-6.1-branch/web_tt2/setlang.tt2 [-bug] language boxes would better to be shown by universal fonts. e.g. Russian & Greek language names are displayed ugrily if they are rendered by East Asian fonts. ------------------------------------------------------------------------ r10055 | sikeda | 2013-12-24 15:40:35 +0100 (mar. 24 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/edit_list_request.tt2 [-dev] possible typo. ------------------------------------------------------------------------ r10054 | sikeda | 2013-12-24 15:30:11 +0100 (mar. 24 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Language.pm M /branches/sympa-6.1-branch/web_tt2/main.tt2 M /branches/sympa-6.1-branch/web_tt2/tt2_error.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Added "lagn" and "xml:lang" attributes to elements. New Language::LanguageTag() function. ------------------------------------------------------------------------ r10053 | sikeda | 2013-12-24 14:54:30 +0100 (mar. 24 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/title.tt2 [-dev] logo style was moved from inline style in title.tt2 to separate style in css.tt2. It may be needed for direction-flipping in near future, to support left-to-right writing systems (e.g. Arabic). ------------------------------------------------------------------------ r10052 | sikeda | 2013-12-24 14:34:23 +0100 (mar. 24 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/zh_TW/css.tt2 [-dev] typo. ------------------------------------------------------------------------ r10051 | sikeda | 2013-12-24 14:19:50 +0100 (mar. 24 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/web_tt2/Makefile.am M /branches/sympa-6.1-branch/web_tt2/css.tt2 A /branches/sympa-6.1-branch/web_tt2/ja_JP A /branches/sympa-6.1-branch/web_tt2/ja_JP/css.tt2 A /branches/sympa-6.1-branch/web_tt2/ko_KR A /branches/sympa-6.1-branch/web_tt2/ko_KR/css.tt2 M /branches/sympa-6.1-branch/web_tt2/main.tt2 A /branches/sympa-6.1-branch/web_tt2/zh_CN A /branches/sympa-6.1-branch/web_tt2/zh_CN/css.tt2 A /branches/sympa-6.1-branch/web_tt2/zh_TW A /branches/sympa-6.1-branch/web_tt2/zh_TW/css.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [*feature] Per-language css.tt2 will override any portion of main css, not fully replacing it. So they may be used for locale-specific customization. Background: Default css.tt2 specifies the font families covering Western scripts (Latin, Cyrillic, ...). East Asian users may prefer consistent font family supporting Western along with Eastern scripts (Han, Hangul, ...). ------------------------------------------------------------------------ r10050 | sikeda | 2013-12-24 14:15:35 +0100 (mar. 24 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/css.tt2 [-dev] whitespace normalization. ------------------------------------------------------------------------ r10049 | sikeda | 2013-12-24 14:13:21 +0100 (mar. 24 dc. 2013) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/css.tt2 M /branches/sympa-6.1-branch/web_tt2/css_ie.tt2 [bug] Several fixes on style sheets (CCS). - Fixed broken css.tt2: Special characters were replaced by entities. - Tune metrics of some items so that pages may be shown properly by most of modern browsers. - Added some workaround for IE bugs, though they are not bugs of Sympa. Now IE 7 or later more or less correctly render. Note: IE 6 or earlier is not. ------------------------------------------------------------------------ r10048 | sikeda | 2013-12-24 07:55:55 +0100 (mar. 24 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-feature] If the module CPAN is not installed, sympa_wizard.pl --check does never try to install modules. ------------------------------------------------------------------------ r10047 | sikeda | 2013-12-24 07:26:23 +0100 (mar. 24 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] DBD::ODBC is not checked by sympa_wizard.pl --check. Remove unused variables %opt_CPAN and %opt_features and use %cpan_modules. ------------------------------------------------------------------------ r10046 | sikeda | 2013-12-24 07:18:24 +0100 (mar. 24 dc. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] typo / change in package names. - DB_File is correct. - Msql-Mysql-modules is no longer used. ------------------------------------------------------------------------ r10045 | sikeda | 2013-12-23 09:18:58 +0100 (lun. 23 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/configure.ac [-dev] update address for bugreport (Authors, is it proper?) ------------------------------------------------------------------------ r10044 | sikeda | 2013-12-23 09:05:26 +0100 (lun. 23 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/po/xgettext.pl [-bug] po/xgettext.pl: Missed catalogue entries with spaces inside parenthese of gettext() calls. ------------------------------------------------------------------------ r10043 | sikeda | 2013-12-23 09:00:49 +0100 (lun. 23 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/queue.c [-bug] src/*queue.c: Suppress C compiler warnings ------------------------------------------------------------------------ r10042 | sikeda | 2013-12-23 08:42:50 +0100 (lun. 23 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [-bug] Messages generated from templates should have Message-Id field. ------------------------------------------------------------------------ r10041 | sikeda | 2013-12-23 08:30:43 +0100 (lun. 23 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in [-bug] Some MTAs decorate To: field of DSN as "mailbox
". Pick address only. ------------------------------------------------------------------------ r10040 | sikeda | 2013-12-23 07:59:13 +0100 (lun. 23 dc. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] Updated CPAN module dependencies: - Mail::Address was omitted. - Locale::TextDomain would be better to Local::Messages. ------------------------------------------------------------------------ r10039 | sikeda | 2013-12-23 07:44:13 +0100 (lun. 23 dc. 2013) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/scenari/send.editorkey M /branches/sympa-6.1-branch/src/etc/scenari/send.privateandnomultipartoreditorkey M /branches/sympa-6.1-branch/src/etc/scenari/send.publicnoattachment M /branches/sympa-6.1-branch/src/etc/scenari/send.publicnomultipart M /branches/sympa-6.1-branch/src/etc/scenari/spam_status.x-spam-status [change] (con'd r10038) Limit the header fields to be checked by scenarios. - In spam_status scenario, last occurrences of X-Spam-status and so on will be checked, since others are not trustworthy. - Also, the last X-sender field would be checked. n.b. I doubt if this field is trustworthy, though. - In send scenario, first occurrences of Content-Type and so on will be checked, since they must appear only at once. ------------------------------------------------------------------------ r10038 | sikeda | 2013-12-23 07:37:18 +0100 (lun. 23 dc. 2013) | 13 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [feature][bug][#8581][Reported by M. Ferrante] Scenari: Inconsistent handling of header fields in rule conditions. New features: - "[msg_header->field][index]" returns the value of particular field. Index may be negative. - "[msg_header->field]" still returns list of field values, additionally, ordering will be preserved. Fixes: - Conditions is_listmaster, is_owner, is_editor and is_subscriber can handle multiple values. - They also parse arguments as header field values to get address parts. Tests needed. ------------------------------------------------------------------------ r10037 | sikeda | 2013-12-23 05:59:49 +0100 (lun. 23 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/exclusion_table.tt2 [-dev] label emement related to widget. ------------------------------------------------------------------------ r10036 | sikeda | 2013-12-23 03:32:20 +0100 (lun. 23 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/serveradmin.tt2 [-dev] label emements related to widgets. ------------------------------------------------------------------------ r10035 | sikeda | 2013-12-23 03:27:14 +0100 (lun. 23 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/nav.tt2 M /branches/sympa-6.1-branch/web_tt2/serveradmin.tt2 [-change] Change "Logs" tab to "System log" to avoid confusion with action logs. ------------------------------------------------------------------------ r10034 | sikeda | 2013-12-23 03:22:30 +0100 (lun. 23 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] (con'd 10027) Update dependency MIME-EncWords to 1.014 which may not break UTF-16 headers. ------------------------------------------------------------------------ r10033 | sikeda | 2013-12-23 03:19:01 +0100 (lun. 23 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [-bug] typo in package name. ------------------------------------------------------------------------ r10032 | sikeda | 2013-12-23 02:27:57 +0100 (lun. 23 dc. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm [dev] removed meaningless use of MIME::WordDecoder. Note: MIME::WordDecoder had been almost obsoleted. Use MIME::EncWords if neccessary. ------------------------------------------------------------------------ r10031 | sikeda | 2013-12-23 02:18:22 +0100 (lun. 23 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [dev] Use CGI::Cookie constructor to make SOAP cookie. The "max-age" feature requires CGI >= 3.51. ------------------------------------------------------------------------ r10030 | sikeda | 2013-12-22 07:43:50 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#7840][Reported by P. Freyhult] Attribute values in ldap_2level_query must be escaped. ------------------------------------------------------------------------ r10029 | sikeda | 2013-12-22 07:17:09 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [bug] mail::fix_part(): Newline "\n" in text/plain body should be normalized to "\r\n" if content-transfer-encoding is BASE64. ------------------------------------------------------------------------ r10028 | sikeda | 2013-12-22 06:59:15 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm [bug] (con'd 10027) Message::fix_html_part() breaks texts encoded by several charsets such as UTF-16. Convert parts always except their charset is UTF-8 or US-ASCII. ------------------------------------------------------------------------ r10027 | sikeda | 2013-12-22 06:54:33 +0100 (dim. 22 dc. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [bug] Sympa may be crashed by the UTF-16 texts without BOM. It is due to a feature of Encode module. Update MIME-Charset to 1.010 to prevent crash. ToDo: Workaround for modules such as MHonArc, StripScripts not supporting UTF-16. ------------------------------------------------------------------------ r10026 | sikeda | 2013-12-22 06:14:50 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/listmaster_notification.tt2 M /branches/sympa-6.1-branch/src/sympa.pl.in [-bug] inconsistent notifications about 'automatic_list_creation_failed'. ------------------------------------------------------------------------ r10025 | sikeda | 2013-12-22 05:45:48 +0100 (dim. 22 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/wwslib.pm [change] [bug] [#7059] pidfile site parameters should be obsoleted. As PID file paths are fixed during configure and included in init script, those parameters are not usable. ------------------------------------------------------------------------ r10024 | sikeda | 2013-12-22 03:29:41 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/compose_mail.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] wwsympa/compose_mail: description about TT2 interpolation was shown even if merege_feature is not on. ------------------------------------------------------------------------ r10023 | sikeda | 2013-12-22 02:56:42 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am [-bug] "Unknown command @echo" till "make install" ------------------------------------------------------------------------ r10022 | sikeda | 2013-12-22 02:08:34 +0100 (dim. 22 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/user_notification.tt2 [bug][#7564] grammatical error in mail_tt2/user_notification.tt2 ------------------------------------------------------------------------ r10021 | sikeda | 2013-12-22 02:06:02 +0100 (dim. 22 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [bug] [#8558] Too short DKIM database field. Expaned dkim_privatekey_spool from 1000 to 2000 o, though, I don't know whether it is enough. ------------------------------------------------------------------------ r10020 | sikeda | 2013-12-21 07:52:18 +0100 (sam. 21 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] needless printf/sprintf ------------------------------------------------------------------------ r10019 | sikeda | 2013-12-21 07:05:06 +0100 (sam. 21 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [-bug] (con'd r9974, r10000) didn't work without Crypt-CipherSaber. ------------------------------------------------------------------------ r10018 | sikeda | 2013-12-21 04:30:14 +0100 (sam. 21 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/lib/SQLSource.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [svn] Incorporating a few bug fixes from sympa-cleanup branch. ------------------------------------------------------------------------ r8297 | rousse | 2012-12-18 12:42:56 +0100 (mar. 18 dc. 2012) | 1 ligne Chemins modifis: M /branches/sympa-cleanup/src/lib/Sympa/Datasource/SQL.pm Fision via: r10018 [dev] DBI is a mandatory module, no need to load it conditionally ------------------------------------------------------------------------ r8295 | rousse | 2012-12-18 12:39:53 +0100 (mar. 18 dc. 2012) | 1 ligne Chemins modifis: M /branches/sympa-cleanup/src/sbin/sympa_wizard.pl.in Fision via: r10018 [bug] FCGI is actually optional ------------------------------------------------------------------------ r10017 | sikeda | 2013-12-21 03:16:03 +0100 (sam. 21 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] do not remove CR as newline. Newline sequences may be LF, CRLF _or_ CR. Removing CR may miss newlines on some clients. ------------------------------------------------------------------------ r10016 | sikeda | 2013-12-21 02:50:14 +0100 (sam. 21 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm [bug] Bulk::next(): failure of locking spool may not be detected. DBI::execute() returns undef on errors, not -1. ------------------------------------------------------------------------ r10015 | sikeda | 2013-12-21 02:30:16 +0100 (sam. 21 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm [-bug] sympa_packet_priority parameters became robot parameters. They were defined as site-wide parameters. ------------------------------------------------------------------------ r10014 | sikeda | 2013-12-21 02:27:35 +0100 (sam. 21 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/wwsympa/wwslib.pm [-dev] Takes care of deprecated parameters. Add them to %confdef::old_params and %old_params in wwslib::load_config(). ------------------------------------------------------------------------ r10013 | sikeda | 2013-12-21 02:12:54 +0100 (sam. 21 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm [-dev] suppress warnings ------------------------------------------------------------------------ r10012 | sikeda | 2013-12-20 17:52:49 +0100 (ven. 20 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SharedDocument.pm [-bug] Reduce "Subroutine dup redefined at wwsympa/SharedDocument.pm line ..." warning. Won't use POSIX::strftime() but Language::gettext_strftime(). ------------------------------------------------------------------------ r10011 | sikeda | 2013-12-20 17:43:51 +0100 (ven. 20 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] typo. ------------------------------------------------------------------------ r10010 | sikeda | 2013-12-20 17:30:52 +0100 (ven. 20 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/digest_plain.tt2 [-feature] Adjust format of plain digests as RFC 1153. ------------------------------------------------------------------------ r10009 | sikeda | 2013-12-20 17:29:57 +0100 (ven. 20 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/digest.tt2 [-bug] Digest footer was included in MIME digest as if it was a proper message. Moved it to outside of multipart/digest part. ------------------------------------------------------------------------ r10008 | sikeda | 2013-12-20 09:29:35 +0100 (ven. 20 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/create_list_templates/confidential/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/discussion_list/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/hotline/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/html-news-letter/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/intranet_list/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/news-letter/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/private_working_group/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/public_web_forum/comment.tt2 [-bug][#8450] (con'd) more fixes. ------------------------------------------------------------------------ r10007 | sikeda | 2013-12-19 15:11:46 +0100 (jeu. 19 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/create_list_templates/confidential/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/discussion_list/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/hotline/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/html-news-letter/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/intranet_list/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/news-letter/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/private_working_group/comment.tt2 M /branches/sympa-6.1-branch/src/etc/create_list_templates/public_web_forum/comment.tt2 M /branches/sympa-6.1-branch/src/etc/mhonarc-ressources.tt2 M /branches/sympa-6.1-branch/web_tt2/arcsearch_form.tt2 M /branches/sympa-6.1-branch/web_tt2/automatic_lists_request.tt2 M /branches/sympa-6.1-branch/web_tt2/compose_mail.tt2 M /branches/sympa-6.1-branch/web_tt2/copy_template.tt2 M /branches/sympa-6.1-branch/web_tt2/create_list_request.tt2 M /branches/sympa-6.1-branch/web_tt2/d_editfile.tt2 M /branches/sympa-6.1-branch/web_tt2/d_upload.tt2 M /branches/sympa-6.1-branch/web_tt2/dump_scenario.tt2 M /branches/sympa-6.1-branch/web_tt2/ls_templates.tt2 M /branches/sympa-6.1-branch/web_tt2/manage_template.tt2 M /branches/sympa-6.1-branch/web_tt2/modindex.tt2 M /branches/sympa-6.1-branch/web_tt2/rename_list_request.tt2 M /branches/sympa-6.1-branch/web_tt2/request_topic.tt2 M /branches/sympa-6.1-branch/web_tt2/review.tt2 M /branches/sympa-6.1-branch/web_tt2/reviewbouncing.tt2 M /branches/sympa-6.1-branch/web_tt2/scenario_test.tt2 M /branches/sympa-6.1-branch/web_tt2/serveradmin.tt2 M /branches/sympa-6.1-branch/web_tt2/setlang.tt2 M /branches/sympa-6.1-branch/web_tt2/sigrequest.tt2 M /branches/sympa-6.1-branch/web_tt2/suboptions.tt2 M /branches/sympa-6.1-branch/web_tt2/suspend_request.tt2 M /branches/sympa-6.1-branch/web_tt2/viewmod.tt2 [bug][#8450] web_tt2: "for" attributes of several "label" elements did not refer proper widgets. ------------------------------------------------------------------------ r10006 | sikeda | 2013-12-19 09:49:04 +0100 (jeu. 19 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [dev] Insert spaces after commas of parameter items in viewlogs page so that they can be wrapped. ------------------------------------------------------------------------ r10005 | sikeda | 2013-12-19 09:29:06 +0100 (jeu. 19 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] DB log can contain the value of secure parameter "cookie". It should be removed from argument. ------------------------------------------------------------------------ r10004 | sikeda | 2013-12-19 09:20:16 +0100 (jeu. 19 dc. 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/alias_manager.pl.in M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/sympa_newaliases.pl.in [dev] (con'd r9989) Made parameters for alias_manager/sympa_newaliases be overridable by each robot. - sendmail_aliases - aliases_program - aliases_db_type ------------------------------------------------------------------------ r10003 | sikeda | 2013-12-18 11:50:26 +0100 (mer. 18 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [-dev] (con'd r9995, r9996) typos. ------------------------------------------------------------------------ r10002 | sikeda | 2013-12-18 11:47:12 +0100 (mer. 18 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa_newaliases.pl.in [-bug] (con'd r9989) Some newaliases utilities e.g. with Postfix cannot take arguments. ------------------------------------------------------------------------ r10001 | sikeda | 2013-12-18 04:28:39 +0100 (mer. 18 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [-bug] (con'd 9998) default_shared_quota must be optional. ------------------------------------------------------------------------ r10000 | sikeda | 2013-12-18 04:23:40 +0100 (mer. 18 dc. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [-bug] (con'd r9974) encrypted cookie won't work on 32-bit architecture. Simplified cookie generation and decryption processes. ------------------------------------------------------------------------ r9999 | sikeda | 2013-12-16 16:58:19 +0100 (lun. 16 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [dev] Reordered site/robot config parameters in confdef.pm. ------------------------------------------------------------------------ r9998 | sikeda | 2013-12-16 16:47:04 +0100 (lun. 16 dc. 2013) | 23 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [-bug] Fixed some inconsistent definitions in confdef. Fixes: - Null default values are forbidden: set "optional" flags. - "queuedistribute" had no default. Added it. - Made these can be overridden by robot.conf: sympa_priority default_list_priority request_priority owner_priority - "archive_default_index" should be editable. - typo in default of "clean_delay_tmpdir". - Synced query and advice items with trunk. More known bugs: - These parameters should be able to be overridden by robot.conf: domain sympa_packet_priority - This can NOT be overridden by robot.conf: password_case ------------------------------------------------------------------------ r9997 | sikeda | 2013-12-15 06:01:07 +0100 (dim. 15 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [dev] removed duplicated entries and added missing ones of @confdef::params. ------------------------------------------------------------------------ r9996 | sikeda | 2013-12-14 10:15:25 +0100 (sam. 14 dc. 2013) | 7 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [dev] some fixes to r9995. - Takes care of db types other than mysql & SQLite. - Restored update_db_field_types no-auto case (l.1391-1396). - Indentation fixes. Needs tests. ------------------------------------------------------------------------ r9995 | sikeda | 2013-12-14 10:07:27 +0100 (sam. 14 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [*bug][debian#642464] [Originally submitted by E Bouthenot, Debian Project] Initial installation or upgrade with SQLite fails. cf. ------------------------------------------------------------------------ r9994 | sikeda | 2013-12-14 03:05:57 +0100 (sam. 14 dc. 2013) | 24 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/wwsympa/wwslib.pm [bug] Some wwsympa.conf parameters are not defined in @confdef::params, and some are redundantly defined their defaults in wwslib::load_config(). Fixes: - These were added to @confdef::params: custom_archiver review_page_size viewlogs_page_size htmlarea_url - This was removed: robots (wwsympa.conf parameter) - Let wwslib::load_conf() get defaults from @confdef::params. More known bugs: - This inconsistently refers both wwsympa.conf and sympa.conf/robot.conf: arc_path - This refers sympa.conf/robot.conf by default and wwsympa.conf: cookie_domain - These refers sympa.conf/robot.conf, though they are defined as wwsympa.conf parameters: password_case title default_home ldap_force_canonical_email ------------------------------------------------------------------------ r9993 | sikeda | 2013-12-14 02:41:44 +0100 (sam. 14 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] archive_default_index should be limited to be "thrd" or "mail". ------------------------------------------------------------------------ r9992 | sikeda | 2013-12-13 16:37:35 +0100 (ven. 13 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [-change][Submitted by S Hornburg via Debian Project] Disable default of db_port parameter which was applicable to MySQL only. ------------------------------------------------------------------------ r9991 | sikeda | 2013-12-13 16:27:46 +0100 (ven. 13 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm [-change] Obsolete "localedir" site parameter which is never used. ------------------------------------------------------------------------ r9990 | sikeda | 2013-12-11 17:21:29 +0100 (mer. 11 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/Makefile.am [-dev] rules for sympa_newaliases(1) manpage. ------------------------------------------------------------------------ r9989 | sikeda | 2013-12-10 13:19:49 +0100 (mar. 10 dc. 2013) | 18 lignes Chemins modifis: M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/src/Makefile.am M /branches/sympa-6.1-branch/src/alias_manager.pl.in M /branches/sympa-6.1-branch/src/etc/script/ldap_alias_manager.pl.in M /branches/sympa-6.1-branch/src/etc/script/mysql_alias_manager.pl.in M /branches/sympa-6.1-branch/src/lib/confdef.pm A /branches/sympa-6.1-branch/src/sympa_newaliases-wrapper.c A /branches/sympa-6.1-branch/src/sympa_newaliases.pl.in [*change] Now alias maintenance utilities other than newaliases may be used without special configure options nor patch to alias_manager.pl. Changes: - aliaswrapper and virtualwrapper were deprecated and replaced with sympa_newaliases-wrapper. - New alias management program sympa_newaliases.pl which will typically be called by alias_manager.pl via sympa_newaliases-wrapper. - New site configuration parameters aliases_db_type and aliases_program will control behaviour of alias database maintenance. - configure script: - Options --with-sendmail_aliases and --with-virtual_aliases were deprecated. Use --with-aliases_file instead. - New options --with-makemap and --with-postalias, along with options --with-newaliases and --with-postmap are available. - Option --with-postmap_arg was removed. - Alias managers can handle postmap/makemap style maps (delimited by whitespace), not only newaliases style maps (delimited by colon). ------------------------------------------------------------------------ r9988 | david.verdin | 2013-12-06 17:37:06 +0100 (ven. 06 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] When sending a crypted message, it was possible that very few users would have provided their certificate to the list. In such a situation, if either all the the non VERP receipients array of receipients was empty, the message was not sent at all (the message distribution was stopped and the non VERP receipients were not distributed). Fixed by checking the number of receipients for each situation and issuing an info in the logs but without stopping the overall distribution. ------------------------------------------------------------------------ r9987 | david.verdin | 2013-12-06 17:32:45 +0100 (ven. 06 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [-change] Improved logs in mail.pm. ------------------------------------------------------------------------ r9986 | sikeda | 2013-12-06 13:35:02 +0100 (ven. 06 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] wwsympa crashes if update of session was (temporarily) failed. Create new session object instead of untied hash. ------------------------------------------------------------------------ r9985 | david.verdin | 2013-12-06 11:14:49 +0100 (ven. 06 dc. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [bug][Reported by S. Shipway, univ. Auckland] When merge was activated on a list, Sympa would merge even signed messages. Signed messages should not be altered in any way, so merge is deactivated when such a message is sent to a list. Note that an alternate solution would be to remove the incoming signature and make the list sign the message, if it has a certificate. This solution should be investigated in later versions of Sympa. ------------------------------------------------------------------------ r9984 | sikeda | 2013-12-06 10:23:44 +0100 (ven. 06 dc. 2013) | 14 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [bug][#7688] Supplemental Unicode characters beyond BMP can't be used on MySQL. As of MySQL 5.5.3, text types supported UTF-8 characters with 4 octets (a.k.a. utf8mb4). By them, additional characters such as CJK ideographs used in persons' names can be used. Change: Try to set 'utf8mb4' then 'utf8' as client-side character set. Note: - Server also must enable utf8mb4, or supplemental characters will be ignored silently. Add "character-set-server=utf8mb4" line to my.cnf. - Tables must be defined with "DEFAULT CHARACTER SET utf8mb4" option, or run "ALTER TABLE" queries to change default character sets of preexisting tables. ------------------------------------------------------------------------ r9983 | sikeda | 2013-12-06 07:42:48 +0100 (ven. 06 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [-bug][#8983] (con'd r9978) typo. ------------------------------------------------------------------------ r9982 | david.verdin | 2013-12-05 18:02:45 +0100 (jeu. 05 dc. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm [bug] When DKIM was activated, removing invalid DKIM signatures would break S/MIME signature. ------------------------------------------------------------------------ r9981 | sympa-authors | 2013-12-05 15:54:44 +0100 (jeu. 05 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r9980 | sympa-authors | 2013-12-05 15:30:39 +0100 (jeu. 05 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r9979 | sikeda | 2013-12-02 09:32:18 +0100 (lun. 02 dc. 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [dev] (con'd from r9966, r9976) Updated advice for sender_headers parameter. - Omit "Envelope-From" (I couldn't find reference to it) and "X-Sender" (it is not always same as envelope sender). - Add "X-Envelope-From" as an example. ------------------------------------------------------------------------ r9978 | sikeda | 2013-12-02 06:18:51 +0100 (lun. 02 dc. 2013) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [bug][#8983] MySQL: session charset is cleared. connect() sets "mysql_auto_reconnect" driver attribute to true when the processes are running under mod_perl or CGI environment so that ping() will always succeed and then "SET NAMES utf8" will be skipped. Fixed by resetting that attribute as soon as connect() succeeded. ------------------------------------------------------------------------ r9977 | sikeda | 2013-12-01 01:04:59 +0100 (dim. 01 dc. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/Language.pm [-bug] typos in sources ------------------------------------------------------------------------ r9976 | sikeda | 2013-11-30 14:17:35 +0100 (sam. 30 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm [dev] (con'd from r9966) Made default value of "sender_headers" parameter to include minimal set, i.e. "From". Added documentation about other possible options. ------------------------------------------------------------------------ r9975 | sikeda | 2013-11-30 02:41:50 +0100 (sam. 30 nov. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm [-bug] useless tests. ------------------------------------------------------------------------ r9974 | sikeda | 2013-11-30 01:23:01 +0100 (sam. 30 nov. 2013) | 12 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/SympaTransport.pm M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm M /branches/sympa-6.1-branch/wwsympa/cookielib.pm [bug][#6180][#6979][#7079][#8056] Web session will be broken by nearly simultaneous multiple HTTP requests, such as browsing an archive page with a few images. NOTE: - Database change is needed. - Optional Crypt-CipherSaber is needed to use encrypted cookie feature. Fixes: - Made transition between session IDs not to occur on each access; they occur with interval specified by cookie_refresh parameter. - Made raw session ID not to be disposed to clients; encrypted IDs changed by each request are used for cookie values. The first point prevents session to be broken, while it slightly weakens against the replay attack. The second point keeps inpredictability of session cookies. ------------------------------------------------------------------------ r9973 | sikeda | 2013-11-30 00:58:47 +0100 (sam. 30 nov. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [dev] Reverted r9970. ------------------------------------------------------------------------ r9972 | david.verdin | 2013-11-29 17:42:41 +0100 (ven. 29 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm [change] Now 'message.footer','message.header','message.footer.mime','message.header.mime','info' files are updated in existing lists when a family is instantiated. note that it will overwrite any other customization. ------------------------------------------------------------------------ r9971 | david.verdin | 2013-11-29 16:35:44 +0100 (ven. 29 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#8684][Reported by D. Fournier, CNRS] In some cases - such as changing a user email address through the Sympa admin > Users web interface - the email value was not set in the "update" config paragraph, leading to logs stating: 'Missing key "email" in param "update" in...'. ------------------------------------------------------------------------ r9970 | sikeda | 2013-11-29 15:35:22 +0100 (ven. 29 nov. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#6180] [Tentative workaround] Session breaks after viewing HTML from archives which references attachments. Although it can be broken by unintantional double-clicking, it is slightly effective workaround. ------------------------------------------------------------------------ r9969 | sikeda | 2013-11-29 14:49:13 +0100 (ven. 29 nov. 2013) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/AUTHORS M /branches/sympa-6.1-branch/README M /branches/sympa-6.1-branch/src/etc/mhonarc-ressources.tt2 M /branches/sympa-6.1-branch/src/etc/script/arc2webarc.pl.in M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/LDAPSource.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/sympa.spec.in M /branches/sympa-6.1-branch/web_tt2/help_arc.tt2 M /branches/sympa-6.1-branch/web_tt2/help_faquser.tt2 M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [dev] Fixed encoding of sources to UTF-8. Some notes --- - Comments in the sources should be written using UTF-8, not ISO-8859-1, EUC-JP or others. - Perl literals must not contain non-ASCII: They must be escaped, e.g. "\xE9". - In PODs, E escapes should be used if at all possible. Note that it can not be used in verbatim (indented) paragraphs: use UTF-8. - It is recommended that subversion commands (checkout, update etc.) are run under UTF-8 locales to freed coders from encoding mess: RCS revision tags such as $Date: 2015-01-13 17:04:20 +0100 (mar. 13 janv. 2015) $ can contain localised texts. Or, "C" locale may be used. ------------------------------------------------------------------------ r9968 | sikeda | 2013-11-29 14:36:40 +0100 (ven. 29 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/mhonarc-ressources.tt2 [-bug] language code of Czech is "cs". ------------------------------------------------------------------------ r9967 | david.verdin | 2013-11-28 17:53:50 +0100 (jeu. 28 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/Makefile.am A /branches/sympa-6.1-branch/web_tt2/exclusion_table.tt2 M /branches/sympa-6.1-branch/web_tt2/review.tt2 M /branches/sympa-6.1-branch/web_tt2/show_exclude.tt2 [-feature] Refactoring templates to have possibilities to restore subscription from exclusion tables in the review page. ------------------------------------------------------------------------ r9966 | sikeda | 2013-11-28 17:05:52 +0100 (jeu. 28 nov. 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm [feature] New site config parameter "sender_headers" to specify header fields by which message sender is detected. This is a enhancement to S. Shipway's improvement. RFC: What is the reasonable default for this parameter? ------------------------------------------------------------------------ r9965 | sikeda | 2013-11-28 16:59:15 +0100 (jeu. 28 nov. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm [dev] Make detection of envelope sender more secure ------------------------------------------------------------------------ r9964 | sikeda | 2013-11-28 16:57:26 +0100 (jeu. 28 nov. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm [-dev] small refactoring ------------------------------------------------------------------------ r9963 | david.verdin | 2013-11-27 17:42:40 +0100 (mer. 27 nov. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/review.tt2 M /branches/sympa-6.1-branch/web_tt2/show_exclude.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature][Reported by so many listmasters we lost the count] Exclusion table was just a display of the users excluded. list owners could not do anything to restore subscriptions; This page is now a form, similar to the review page, which allows to restore users subscriptions. ------------------------------------------------------------------------ r9962 | david.verdin | 2013-11-27 16:32:34 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#8895][Reported by F. Prichon] Users included from data sources can unsubscribe from a list provided the "unsusbscribe" scenario allows them. Unsubscription worked well, but when re-subscribing to the list, both the listmasters and the users received an error; this was because the user counter was not increased when re-including a subscriber. Fixed by changing the test and increase the counter when just removing someone from the exclusion table. ------------------------------------------------------------------------ r9961 | david.verdin | 2013-11-27 16:04:23 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/web_tt2/edit_config.tt2 [bug][#8712][Reported by L. Didry, CIRIL] In the main admin edition page, the database password was displayed as clear text. repalced by a series of stars. ------------------------------------------------------------------------ r9960 | david.verdin | 2013-11-27 15:49:15 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Lock.pm [bug][#8711][Submitted by a nice anonymous contributor aka "Luke"] When using NFS lock, timeout and stale_lock_timeout were inverted and the timout value was increased. ------------------------------------------------------------------------ r9959 | david.verdin | 2013-11-27 15:35:38 +0100 (mer. 27 nov. 2013) | 11 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/lib/Bulk.pm [feature][Submitted by S. Shipway, univ. Auckland] When mail merge is activated, new parameters can be used, taken from the mail headers: - subject - x-originating-ip - message-id - date - x-original-to - from - to - thread-topic - content-type ------------------------------------------------------------------------ r9958 | sympa-authors | 2013-11-27 14:55:15 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/admin.tt2 M /branches/sympa-6.1-branch/web_tt2/subscriber_table.tt2 [-change][submitted by S. Shipway, univ. Auckland] Updated templates so that new policy regarding subscribers and mesage moderation is taken into account. ------------------------------------------------------------------------ r9957 | sympa-authors | 2013-11-27 14:53:08 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/review.tt2 [-change] Removed lca references and "quiet" checked as default. ------------------------------------------------------------------------ r9956 | david.verdin | 2013-11-27 14:30:41 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/review.tt2 [feature][Submitted by S; shipway, univ. Auckland] Added quick access links in the review page to reach all users whoses emails start with a given letter. ------------------------------------------------------------------------ r9955 | david.verdin | 2013-11-27 13:39:52 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/SQLSource.pm M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in [feature][Submitted by S. Shipway, univ. Auckland] ODBC is now supported for email data sources. ------------------------------------------------------------------------ r9954 | david.verdin | 2013-11-27 13:26:57 +0100 (mer. 27 nov. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Message.pm [change][Submitted by S. shipway, univ. Auckland] When Sympa receives a mail without 'From' field, it tries to find the sender by parsing the 'Envelope-From', 'X-Sender' and 'return-Path' fields before giving up and moving the message to bad. ------------------------------------------------------------------------ r9953 | david.verdin | 2013-11-27 11:39:01 +0100 (mer. 27 nov. 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [change][Submitted by S. Shipway, univ. Auckland] several changes in privilegs to ease everyday lists moderation: - Owners and lismasters can moderate messages and shared repository - Editors can moderate subscriptions - 'del' and 'add' sceanrios are evaluated to make their result available in each page. ------------------------------------------------------------------------ r9952 | sikeda | 2013-11-20 05:05:48 +0100 (mer. 20 nov. 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#8916][Reported by Adam Bernstein, Electric Embers] wwsympa/search: Date is not displayed in human readable form in "other similar subscribers" results. Format date and update_date fields in tt2 parameters. ------------------------------------------------------------------------ r9951 | david.verdin | 2013-11-06 17:44:00 +0100 (mer. 06 nov. 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [feature] Now you can define a "scenari" directory in the lists family directory. These scenarii will be available for lists instantiated from this family. The "scenari" directory must be put directly in the family directory, not in the overall "families" directory. For example, if you want to define scenarii specific to the "staff" family, you must define a scenari directory in the /home/sympa/etc/families/staff/ directory. Not in /home/sympa/etc/families/. ------------------------------------------------------------------------ r9950 | david.verdin | 2013-10-25 16:48:10 +0200 (ven. 25 oct. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][Submitted by P. Aepli, univ. Genve] r9946 and r9949 fixes were not enough to fix multivalued attributes problem with Shibboleth. Philippe found the duplicated test and additional concatenation that were still causing problem. ------------------------------------------------------------------------ r9949 | david.verdin | 2013-10-25 16:25:40 +0200 (ven. 25 oct. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Error in attribute separator usage, interpolated as a variable. fixed by replacing interpolation by concatenation. ------------------------------------------------------------------------ r9948 | david.verdin | 2013-10-25 16:24:18 +0200 (ven. 25 oct. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][Reported by . Sahin, univ.Lyon 2] When List::new sub was called, sync_include_admin was systematically executed, except when specifically stated. If using a lot of owenr and editor datasources, this resulted in large amount of queries to these datasources when seraching lists or displaying the list of lists. This is now modulated. When calling get_lists, calling sync_include_admin is now optional and executed if the limits of the 'ttl' and 'ditribution_ttl' parameters are not exceeded. ------------------------------------------------------------------------ r9947 | david.verdin | 2013-10-25 11:19:23 +0200 (ven. 25 oct. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] Typo. ------------------------------------------------------------------------ r9946 | david.verdin | 2013-10-25 11:13:48 +0200 (ven. 25 oct. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][Reported by P. Aepli, univ. Genve] Multi-valued parameters obtained from Shibboleth IdP were badly parsed. This as due to the fact that Sympa stored additional parameters under the form: "key1=value1;key2=value2;etc." As Shibboleth uses semi-columns to separate values in multivalued attributes, values were misinterpreted as new - empy - attributes. Fixed by separating keys and values by totally unlikely strings. ------------------------------------------------------------------------ r9457 | david.verdin | 2013-07-04 14:11:22 +0200 (jeu. 04 juil. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/src/etc/Makefile.am A /branches/sympa-6.1-branch/src/etc/scenari/info.conceal (de /branches/sympa-6.2-branch/src/etc/scenari/info.conceal:9456) [bug] The "info.conceal" scenario was neither packaged nor installed. ------------------------------------------------------------------------ r9456 | david.verdin | 2013-07-04 14:04:41 +0200 (jeu. 04 juil. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/src/etc/Makefile.am A /branches/sympa-6.2-branch/src/etc/scenari/info.conceal Fision via: r9457 [bug] The "info.conceal" scenario was neither packaged nor installed. ------------------------------------------------------------------------ r9425 | sikeda | 2013-06-29 10:21:55 +0200 (sam. 29 juin 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] List::_append_parts(): It should not try to append header/footer to all parts of multipart/mixed and multipart/related, because secondary parts of these types would be "attachment"; On multipart/alternative all parts should be tried. Additionally, header/footer will be HTML-escaped if body type is text/html. ------------------------------------------------------------------------ r9424 | sikeda | 2013-06-28 02:42:53 +0200 (ven. 28 juin 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] headers append to message body are given additional newline. ------------------------------------------------------------------------ r9420 | sikeda | 2013-06-27 08:24:05 +0200 (jeu. 27 juin 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-dev] cosmetic changes. ------------------------------------------------------------------------ r9419 | david.verdin | 2013-06-26 09:34:29 +0200 (mer. 26 juin 2013) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] When adding footers, nothing appeared in the mail received by subscribers in the following condition: - footer_type was set to "append" - the mail was multipart/html This was due to the footer being added to the text/plain part only. Now it is added to the text/teml part as well. ------------------------------------------------------------------------ r9226 | sikeda | 2013-05-09 06:40:53 +0200 (jeu. 09 mai 2013) | 11 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#8466][#8468][Initially Submitted by Marton J., Budapesti M?\197?\177szaki ?\195?\169s Gazdas?\195?\161gtudom?\195?\161nyi Egyetem] Oracle cannot handle messages as long type. * DBI::Oracle has a default setting of LongReadLen=80. As a result, driver does not fetch a record in bulkspool_table containing a long column that have value longer than 80 characters. * DBI::Oracle raises error if messages larger than approximately 2600 o (4000 o in base64 encoding) was sent. Fixes: - Original patch was for Sympa 6.0.1; it was rewritten for sympa-6.1-branch. * Set $dbh->{'LongReadLen'} to be max_size * 2. * Give SQL the messages as bound parameters to avoid long text literals. * Rewritten patch also includes support for Sybase which needs similar concern. ------------------------------------------------------------------------ r9217 | sikeda | 2013-05-07 14:27:08 +0200 (mar. 07 mai 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [bug][#8628][Submitted by Marton J., Budapesti M?\197?\177szaki ?\195?\169s Gazdas?\195?\161gtudom?\195?\161nyi Egyetem] Inproper connection pool-handling: database user is not taken into account. Modified patch was applied. ------------------------------------------------------------------------ r9216 | sikeda | 2013-05-07 14:21:03 +0200 (mar. 07 mai 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle [dev] On Oracle, "long" field should be the last column of table, or succeeding columns allow only sequencial access. ------------------------------------------------------------------------ r9214 | sikeda | 2013-05-07 06:17:15 +0200 (mar. 07 mai 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [bug][#8629][Submitted by Marton J., BME, Hungary] Oracle date writer formula was broken: after noon, 1 day is added. ------------------------------------------------------------------------ r9207 | sikeda | 2013-05-02 04:04:31 +0200 (jeu. 02 mai 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite [bug][#8669] create_db.SQLite is not uptodate. ------------------------------------------------------------------------ r9189 | sikeda | 2013-04-25 13:53:00 +0200 (jeu. 25 avril 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bulk.pl.in [*bug] If merge_feature is on, whole or part of subscribers can be disposed by including "[% to %]" into messages. Fixed by removing 'receipients' item of bulk packets from the parameter passed to Bulk::merge_msg(). ------------------------------------------------------------------------ r9180 | sympa-authors | 2013-04-24 09:40:22 +0200 (mer. 24 avril 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/soap/sympa.wsdl [bug] [for backward compatibility] Soap which response format differs from wsdl, changing the wsdl in order to keep existing clients working ------------------------------------------------------------------------ r9178 | sikeda | 2013-04-24 08:32:42 +0200 (mer. 24 avril 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm [bug][#7318] Encoding problem on sympasoap. UTF-8 strings were encoded twice as latin-1 strings. ------------------------------------------------------------------------ r9131 | sikeda | 2013-04-18 05:32:56 +0200 (jeu. 18 avril 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/Marc/Search.pm [bug][#8662][Reported by A. Bernstein, Electric Embers] wwsympa: arcsearch crashes on non-UTF-8 bytes in HTML, with error "Malformed UTF-8 character (fatal)". Marc::Search uses Unicode internally to utilize case-insensitive match. Fixed by replacing ":utf8" input layer with ":encoding(utf8)", since former won't really check inputs. ------------------------------------------------------------------------ r9125 | sikeda | 2013-04-17 10:40:29 +0200 (mer. 17 avril 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/doc/Makefile.am M /branches/sympa-6.1-branch/doc/sample/Makefile.am M /branches/sympa-6.1-branch/important_changes.pl M /branches/sympa-6.1-branch/mail_tt2/Makefile.am M /branches/sympa-6.1-branch/po/check_locales.pl M /branches/sympa-6.1-branch/soap/Makefile.am M /branches/sympa-6.1-branch/soap/sympa_soap_client.pl.in M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/Makefile.am M /branches/sympa-6.1-branch/src/alias_manager.pl.in M /branches/sympa-6.1-branch/src/aliaswrapper.c M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/etc/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/arc2webarc.pl.in M /branches/sympa-6.1-branch/src/etc/script/crypt_passwd.pl.in M /branches/sympa-6.1-branch/src/etc/script/init_comment.pl.in M /branches/sympa-6.1-branch/src/etc/script/p12topem.pl.in M /branches/sympa-6.1-branch/src/etc/script/testldap.pl.in M /branches/sympa-6.1-branch/src/etc/script/tpl2tt2.pl.in M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/Config_XML.pm M /branches/sympa-6.1-branch/src/lib/Datasource.pm M /branches/sympa-6.1-branch/src/lib/Family.pm M /branches/sympa-6.1-branch/src/lib/Fetch.pm M /branches/sympa-6.1-branch/src/lib/LDAPSource.pm M /branches/sympa-6.1-branch/src/lib/Language.pm M /branches/sympa-6.1-branch/src/lib/Ldap.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Lock.pm M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/PlainDigest.pm M /branches/sympa-6.1-branch/src/lib/SQLSource.pm M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/src/lib/Sympa/Constants.pm.in M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm M /branches/sympa-6.1-branch/src/lib/Sympa/Template/Compat.pm M /branches/sympa-6.1-branch/src/lib/Task.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/WebAgent.pm M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/report.pm M /branches/sympa-6.1-branch/src/lib/time_utils.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/src/queue.c M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/src/virtualwrapper.c M /branches/sympa-6.1-branch/web_tt2/Makefile.am M /branches/sympa-6.1-branch/wwsympa/Auth.pm M /branches/sympa-6.1-branch/wwsympa/Bounce.pm M /branches/sympa-6.1-branch/wwsympa/Challenge.pm M /branches/sympa-6.1-branch/wwsympa/Makefile.am M /branches/sympa-6.1-branch/wwsympa/SharedDocument.pm M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/cookielib.pm M /branches/sympa-6.1-branch/wwsympa/wwslib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [dev] Replace street address of FSF with URL of description on license. ------------------------------------------------------------------------ r9124 | sikeda | 2013-04-17 05:51:29 +0200 (mer. 17 avril 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Lock.pm [bug][#8661][Reported by Y. Rouillard, INRA] Lock::remove_lock() doesn't properly destroy Lock object. Clear entire lock entry not only filehandle of it. ------------------------------------------------------------------------ r9104 | sikeda | 2013-04-16 14:54:37 +0200 (mar. 16 avril 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [bug][#7459][Submitted by Mitar] Tables in PostgreSQL user schema are not detected during upgrade process. ------------------------------------------------------------------------ r9082 | sikeda | 2013-04-10 14:41:16 +0200 (mer. 10 avril 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/COPYING M /branches/sympa-6.1-branch/Makefile.am M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/doc/Makefile.am M /branches/sympa-6.1-branch/doc/sample/Makefile.am M /branches/sympa-6.1-branch/important_changes.pl M /branches/sympa-6.1-branch/mail_tt2/Makefile.am M /branches/sympa-6.1-branch/soap/Makefile.am M /branches/sympa-6.1-branch/soap/sympa_soap_client.pl.in M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/Makefile.am M /branches/sympa-6.1-branch/src/alias_manager.pl.in M /branches/sympa-6.1-branch/src/aliaswrapper.c M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/etc/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/Makefile.am M /branches/sympa-6.1-branch/src/etc/script/arc2webarc.pl.in M /branches/sympa-6.1-branch/src/etc/script/crypt_passwd.pl.in M /branches/sympa-6.1-branch/src/etc/script/init_comment.pl.in M /branches/sympa-6.1-branch/src/etc/script/p12topem.pl.in M /branches/sympa-6.1-branch/src/etc/script/testldap.pl.in M /branches/sympa-6.1-branch/src/etc/script/tpl2tt2.pl.in M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/Config_XML.pm M /branches/sympa-6.1-branch/src/lib/Datasource.pm M /branches/sympa-6.1-branch/src/lib/Family.pm M /branches/sympa-6.1-branch/src/lib/Fetch.pm M /branches/sympa-6.1-branch/src/lib/LDAPSource.pm M /branches/sympa-6.1-branch/src/lib/Language.pm M /branches/sympa-6.1-branch/src/lib/Ldap.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Lock.pm M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/src/lib/Message.pm M /branches/sympa-6.1-branch/src/lib/PlainDigest.pm M /branches/sympa-6.1-branch/src/lib/SQLSource.pm M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/src/lib/Sympa/Constants.pm.in M /branches/sympa-6.1-branch/src/lib/Sympa/Template/Compat.pm M /branches/sympa-6.1-branch/src/lib/Task.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/WebAgent.pm M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/report.pm M /branches/sympa-6.1-branch/src/lib/time_utils.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/src/queue.c M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/sympa_wizard.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/src/virtualwrapper.c M /branches/sympa-6.1-branch/web_tt2/Makefile.am M /branches/sympa-6.1-branch/wwsympa/Auth.pm M /branches/sympa-6.1-branch/wwsympa/Bounce.pm M /branches/sympa-6.1-branch/wwsympa/Makefile.am M /branches/sympa-6.1-branch/wwsympa/SharedDocument.pm M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/cookielib.pm M /branches/sympa-6.1-branch/wwsympa/wwslib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#8066] [Submitted by X. Bachelot] FSF had moved. ------------------------------------------------------------------------ r9058 | sikeda | 2013-04-08 06:45:50 +0200 (lun. 08 avril 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in [bug] [#7825] [Reported by D. Marant, Universit Lille 1] spam_status scenario has no effect for messages sent to command addresses. Fixed a typo in sympa::DoCommand(). ------------------------------------------------------------------------ r9055 | sikeda | 2013-04-06 09:00:30 +0200 (sam. 06 avril 2013) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [bug] [#7526] [Submitted by Evili del Rio] SQL Filters always return false. Thouogh what SQLSource::fetch() returns may be arrayref of references to array of rows, it was treated as an arrayref of rows. ------------------------------------------------------------------------ r9041 | sikeda | 2013-04-04 17:25:09 +0200 (jeu. 04 avril 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/po/POTFILES.in [bug] [#7457] A file "wwsympa/Challemge.pm" missing in distribution was referred during undating sympa.pot. ------------------------------------------------------------------------ r9030 | sikeda | 2013-04-03 11:18:36 +0200 (mer. 03 avril 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/doc/man8/Makefile.am [-bug] typo. ------------------------------------------------------------------------ r9029 | sikeda | 2013-04-03 11:16:20 +0200 (mer. 03 avril 2013) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/doc/man8/Makefile.am M /branches/sympa-6.1-branch/doc/man8/alias_manager.pod.in M /branches/sympa-6.1-branch/doc/man8/archived.pod.in M /branches/sympa-6.1-branch/doc/man8/bounced.pod.in D /branches/sympa-6.1-branch/doc/man8/sympa.pod A /branches/sympa-6.1-branch/doc/man8/sympa.pod.in (de /branches/sympa-6.1-branch/doc/man8/sympa.pod:9026) [bug] [#8650] pod2man fails with non-ASCII characters in POD. Partially backported from 6.2-branch: Use E<...> for non-ASCII letters so that POD sources contain only ASCII. In conjunction corrected several descriptions out-of-date. ------------------------------------------------------------------------ r9027 | sikeda | 2013-04-03 06:37:18 +0200 (mer. 03 avril 2013) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympa_soap_server-wrapper.fcgi.c M /branches/sympa-6.1-branch/src/bouncequeue.c M /branches/sympa-6.1-branch/src/familyqueue.c M /branches/sympa-6.1-branch/src/queue.c M /branches/sympa-6.1-branch/wwsympa/wwsympa-wrapper.fcgi.c [bug] [#8658] [Submitted by Marc Schtz] Suppress compiler warnings. - In sympa_soap_server-wrapper.fcgi.c & wwsympa-wrapper.fcgi.c, main() are lacking return. Instead, returns the result of exec() if it failed. - Some syscalls were not accompanied by relevant headers. They are added. ------------------------------------------------------------------------ r8988 | sympa-authors | 2013-03-27 10:59:23 +0100 (mer. 27 mars 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm [bug] Fixed bad call to logging facility, was making processes crash ------------------------------------------------------------------------ r8708 | sympa-authors | 2013-02-20 11:52:47 +0100 (mer. 20 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r8707 | sympa-authors | 2013-02-20 11:36:19 +0100 (mer. 20 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r8706 | sympa-authors | 2013-02-20 11:23:10 +0100 (mer. 20 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/listmaster_notification.tt2 [bug] Fixed typo in error message ------------------------------------------------------------------------ r8595 | sympa-authors | 2013-02-05 15:57:24 +0100 (mar. 05 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.17 ------------------------------------------------------------------------ r8593 | sympa-authors | 2013-02-05 15:22:23 +0100 (mar. 05 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pt_BR.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r8590 | sympa-authors | 2013-02-05 15:04:23 +0100 (mar. 05 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/ja.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r8588 | sympa-authors | 2013-02-05 10:58:04 +0100 (mar. 05 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r8587 | sympa-authors | 2013-02-05 10:39:34 +0100 (mar. 05 fvr. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r8485 | david.verdin | 2013-01-24 14:38:37 +0100 (jeu. 24 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] wwsympa::prevent_visibility_bypass would crash when called in off-list context. ------------------------------------------------------------------------ r8482 | david.verdin | 2013-01-23 17:02:58 +0100 (mer. 23 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/Makefile.am [-bug] Forgot to add send.confidential in Makefile.am ------------------------------------------------------------------------ r8465 | david.verdin | 2013-01-22 13:18:20 +0100 (mar. 22 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/automatic_lists_management_request.tt2 [bug][Submitted by M. Overmeer, overmeer.net] Typo in a string prevented the family from being displayed. ------------------------------------------------------------------------ r8463 | david.verdin | 2013-01-22 11:18:33 +0100 (mar. 22 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/error.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] Modifiying error when visibility bypass is prevented. Switching from "list unknown" to "authorization reject" which is less confuding. ------------------------------------------------------------------------ r8457 | sikeda | 2013-01-19 02:59:19 +0100 (sam. 19 janv. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm [-bug] Use as_string() instead of confusing "". ------------------------------------------------------------------------ r8456 | sikeda | 2013-01-19 02:54:40 +0100 (sam. 19 janv. 2013) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm [-bug] reverted r8450: tt2->error() is actually an object. Enclosing "" stringifys it. ------------------------------------------------------------------------ r8454 | sympa-authors | 2013-01-18 17:08:17 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/footer.tt2 M /branches/sympa-6.1-branch/web_tt2/tt2_error.tt2 [change] Removing references to the Sympa version in web pages to avoid pages to be searched by bad guys willing to exploit known vulnerabilities on out of date servers. ------------------------------------------------------------------------ r8453 | david.verdin | 2013-01-18 16:17:05 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Adding a test to prevent wwsympa.fcgi from crashing when prevent_visibility_bypass is called out of list context. ------------------------------------------------------------------------ r8452 | david.verdin | 2013-01-18 16:04:55 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm [dev] Removing development traces. ------------------------------------------------------------------------ r8451 | david.verdin | 2013-01-18 15:52:26 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/Makefile.am A /branches/sympa-6.1-branch/src/etc/create_list_templates/confidential A /branches/sympa-6.1-branch/src/etc/create_list_templates/confidential/comment.tt2 A /branches/sympa-6.1-branch/src/etc/create_list_templates/confidential/config.tt2 A /branches/sympa-6.1-branch/src/etc/scenari/send.confidential [feature] New "confidential" list model. These lists are used for groups who don't want any publicity around their activities; All possible restrictions are applied to prevent unauthorized users to know these lists exist and to learn anything about them. ------------------------------------------------------------------------ r8450 | david.verdin | 2013-01-18 15:50:16 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm [-bug] Removed useless double quote around a variable name in a Log::do_log statement. ------------------------------------------------------------------------ r8449 | david.verdin | 2013-01-18 15:49:31 +0100 (ven. 18 janv. 2013) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/web_tt2/error.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] For lists which should not be visible to users, some actions still displayed the list side menus, granting informations regarding the list: number of users and identity of list owners and moderators. Fixed by short-cutting some parts of the code to prevent this display when an action is attempted. If privileges for an action are not granted and the list should not be visible to the user who requested them, the user is redirected to the main home page and list is decalred as "not found". In addition, subscribe and sigrequest will not display a form to unauthenticated users if, respectively, subscribe or unsubscribe scenarios are set to "closed", i.e.: any subscription or unsubscription request would be rejected. ------------------------------------------------------------------------ r7965 | david.verdin | 2012-11-19 10:41:47 +0100 (lun. 19 nov. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/archived.pl.in [bug][#8569][Submitted by T. Merritt, univ. Arizona] archived.pl did not systematically reaped the sendmail processes it created. Fixed by calling mail::reaper at the end of each loop. ------------------------------------------------------------------------ r7961 | sympa-authors | 2012-11-16 15:41:56 +0100 (ven. 16 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.16 ------------------------------------------------------------------------ r7960 | sympa-authors | 2012-11-16 15:20:25 +0100 (ven. 16 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7959 | sympa-authors | 2012-11-16 15:03:19 +0100 (ven. 16 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7957 | sympa-authors | 2012-11-16 12:20:24 +0100 (ven. 16 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/el.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7956 | sympa-authors | 2012-11-16 12:03:21 +0100 (ven. 16 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po-wwsympa/el.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7955 | david.verdin | 2012-11-16 11:39:25 +0100 (ven. 16 nov. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/PlainDigest.pm [bug][Reported by O. Germes and M. Thomas, univ. Rennes 1] When compiling a plain digest, attachments are subsituted by character strings indicating that there were attachments in the message. Any text part is converted to plain text and folded into 78 column text. Teh selection of parts to fold or to replace by text is based on the "Content-Type:" header of a part. In some cases (such as messages sent from opengroupware, for example) binary attachments are embedded in text/plain parts with a "content-disposition" header with value "attachment". Such parts should be substitued by replacement text but, as their Content-Type is "text/plain", Sympa tried to fold them to 78 columns. This led to a very long loop because the Text::Linefold module splits strings to be folded with special characters that your everyday PDF file contains thousands times. Fixed by testing, when dealing with a text/plain part, the value of the "content-disposition:" header. It if contains "attachment", the part is treated as a binary-content part. ------------------------------------------------------------------------ r7922 | sympa-authors | 2012-11-07 15:43:09 +0100 (mer. 07 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7921 | sympa-authors | 2012-11-07 15:24:48 +0100 (mer. 07 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/oc.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7920 | sympa-authors | 2012-11-07 15:05:28 +0100 (mer. 07 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/oc.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7918 | sympa-authors | 2012-11-07 12:03:03 +0100 (mer. 07 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7917 | david.verdin | 2012-11-06 18:22:36 +0100 (mar. 06 nov. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/Auth.pm [bug][#8852][Submitted by F. Prichon, univ. Lille 2] Ldap authentication failed in wwsympa with recent version of Convert-ASN1. Starting with version 0.25, Convert-ASN1 package has a 'use strict'. Ldap attrs were passed as a string in a ldap search call in wwsympa/Auth.pm thus making LDAP authentication fail. Fixed by passing attrs as an array, even when only one attribute is retrieved. ------------------------------------------------------------------------ r7667 | sympa-authors | 2012-10-04 16:52:39 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.15 ------------------------------------------------------------------------ r7666 | sympa-authors | 2012-10-04 16:46:11 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7665 | sympa-authors | 2012-10-04 16:29:26 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7663 | sympa-authors | 2012-10-04 16:14:49 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7660 | sympa-authors | 2012-10-04 15:56:15 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/af.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/gl.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7657 | david.verdin | 2012-10-04 15:08:47 +0200 (jeu. 04 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm [change] Reverting commit [7636]. This change could have lead to exclusion preventions by Sympa. Family does not have to be part of the key to allow multiple family unsubscritption as a dummy list name is given when family unsubscribing. ------------------------------------------------------------------------ r7643 | david.verdin | 2012-10-03 11:06:30 +0200 (mer. 03 oct. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/archived.pl.in [bug] When a user achieved to request a message deletion from archives without having the proper rights, archived.pl crashed because a return undef was then used outside a function context. Fixed by replacing it by a "next". ------------------------------------------------------------------------ r7641 | david.verdin | 2012-10-03 10:42:30 +0200 (mer. 03 oct. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] changing regexp modified in [#7564] which caused file uploads to crash wwsympa.fcgi. Fixed by double escaping antislashes in regexp declaration. ------------------------------------------------------------------------ r7636 | david.verdin | 2012-10-02 10:45:04 +0200 (mar. 02 oct. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm [change] Now the family_exclusion field is part of the exclusion_table primary_key. This allows users to be excluded from several families. ------------------------------------------------------------------------ r7627 | sikeda | 2012-09-29 02:09:35 +0200 (sam. 29 sept. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] More typos. ------------------------------------------------------------------------ r7619 | sikeda | 2012-09-25 05:55:05 +0200 (mar. 25 sept. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug] 'host' list parameter were used to determine robots. (Correction might be incomplete...) ------------------------------------------------------------------------ r7616 | sikeda | 2012-09-23 01:17:21 +0200 (dim. 23 sept. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/home.tt2 [-bug] typo in web tt2 template. ------------------------------------------------------------------------ r7603 | sikeda | 2012-09-15 15:41:53 +0200 (sam. 15 sept. 2012) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Config_XML.pm M /branches/sympa-6.1-branch/src/lib/Family.pm M /branches/sympa-6.1-branch/src/lib/admin.pm [*bug] Encoding issues on list creation. - If list template config.tt2 contains non-ASCII strings, they are double-encoded. - If list creation XML contains non-ASCII strings, they are double-encoded. Note: Now Config_XML::getHash() returns encoded values, not utf8-flagged values. So '>:utf8' layer should not be used. ------------------------------------------------------------------------ r7599 | sikeda | 2012-09-14 04:56:58 +0200 (ven. 14 sept. 2012) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Some spurious warning output to STDERR. mod_fcgid: stderr: Argument "" isn't numeric in numeric gt (>) at /usr/share/sympa/default/web_tt2/get_inactive_lists.tt2 line 30. mod_fcgid: stderr: Argument "" isn't numeric in sprintf at /usr/lib64/perl5/Sys/Syslog.pm line XXX. ------------------------------------------------------------------------ r7598 | sikeda | 2012-09-13 10:39:00 +0200 (jeu. 13 sept. 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/SQLSource.pm [-bug] DBI error message is output to STDERR. For example, if db_list_cache parameter is "on", a message "mod_fcgid: stderr: DBD::mysql::db do failed: Duplicate entry 'xxx' for key 'PRIMARY'" is shown on HTTPd error log, everytime list config is changed. It is not really failure. Set default 'PrintError' parameter of DBI handle to be false. ------------------------------------------------------------------------ r7577 | david.verdin | 2012-09-07 16:16:18 +0200 (ven. 07 sept. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [change] When an error occurs when creating a list through the web interface, the user stays on the list creation page with her previously filled list description still present. ------------------------------------------------------------------------ r7576 | david.verdin | 2012-09-07 16:14:50 +0200 (ven. 07 sept. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm [change] Now Sympa prevents users from creating a list whose local part of email address would be the local part used by sympa for mail commands. ------------------------------------------------------------------------ r7574 | sikeda | 2012-09-07 12:06:09 +0200 (ven. 07 sept. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature] Workaround for font-twitching by Internet Explorer 8 against some sites. cf. http://msdn.microsoft.com/library/cc288325%28v=vs.85%29.aspx ------------------------------------------------------------------------ r7573 | sikeda | 2012-09-06 04:42:23 +0200 (jeu. 06 sept. 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/Auth.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][Reported by R. Tassoni, National Library of Australia] Requests for new password with email address containing an apostrophe, SQL queries fail. [-bug] To: field of password notification message is empty if user have not logged in to Sympa yet. ------------------------------------------------------------------------ r7567 | sikeda | 2012-08-30 09:43:12 +0200 (jeu. 30 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Some actions (logout etc.) lead users home page, even if default_home parameter is set 'lists'. Now default_home is used in almost all cases. ------------------------------------------------------------------------ r7566 | sikeda | 2012-08-30 09:34:21 +0200 (jeu. 30 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/list_panel.tt2 [-bug] a bit inconsistent text decoration. ------------------------------------------------------------------------ r7565 | sikeda | 2012-08-29 04:28:09 +0200 (mer. 29 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/editfile.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature] Hide uneditable items on per-list editfile page. ------------------------------------------------------------------------ r7564 | sikeda | 2012-08-28 06:57:29 +0200 (mar. 28 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#6956] Uploading file is refused if any disallowed characters appear in its directory part. Relaxed regexps to allow any characters before / or \. ------------------------------------------------------------------------ r7563 | sikeda | 2012-08-28 03:28:09 +0200 (mar. 28 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#8462][Reported by S. Thomas] arcsearch_id can not find message if Message-ID contains a hyphen ("-") ------------------------------------------------------------------------ r7562 | sikeda | 2012-08-24 08:34:16 +0200 (ven. 24 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] Improved handling of mime message header/footer. ------------------------------------------------------------------------ r7561 | sikeda | 2012-08-24 08:24:52 +0200 (ven. 24 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#8456][Submitted by P Vandry] message headers and footers should not have filenames. ------------------------------------------------------------------------ r7560 | sikeda | 2012-08-23 07:09:14 +0200 (jeu. 23 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#8458][Reported by K. Clair] sympa.pl distribute dies if $msg->bodyhandle is undefined. Corrected typo in List.pm. ------------------------------------------------------------------------ r7559 | sikeda | 2012-08-10 06:56:53 +0200 (ven. 10 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/search_user.tt2 M /branches/sympa-6.1-branch/web_tt2/subscriber_table.tt2 [-bug] tagged items not translated. ------------------------------------------------------------------------ r7558 | sikeda | 2012-08-09 12:37:53 +0200 (jeu. 09 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [-bug] When user(s) are successfully added, a message "err List::delete_subscription_request() No pending subscription was found for users ..." is logged. It is not an error. Change loglevel from err to debug2. [-bug] A message "err List::parseCustomAttribute() Failed to parse XML data" is logged for the users not having custom attributes. ------------------------------------------------------------------------ r7557 | sikeda | 2012-08-09 11:56:33 +0200 (jeu. 09 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] list_check_smtp parameter was not robot-conscious. Give $robot parameters to admin::list_check_smtp(). ------------------------------------------------------------------------ r7556 | sikeda | 2012-08-09 11:49:40 +0200 (jeu. 09 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/mail_tt2/moderate.tt2 [-bug] tagged items not translated. ------------------------------------------------------------------------ r7555 | sikeda | 2012-08-08 07:55:54 +0200 (mer. 08 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/show_sessions.tt2 [-bug] tagged items not translated. ------------------------------------------------------------------------ r7554 | sikeda | 2012-08-07 04:38:16 +0200 (mar. 07 aot 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/home.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature] Changes on "Mailing lists categories" section of home page: If topics.conf contains only one topic with key "other", link to lists page is shown; topic argument 'topicsless' or 'other' means 'lists with topic "other" or without topics'. ------------------------------------------------------------------------ r7553 | sikeda | 2012-08-06 09:22:06 +0200 (lun. 06 aot 2012) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/create_list_request.tt2 [-feature] If only one list template is available, it will be selected on list_creation_request page. [-feature] If topics.conf contains only one topic with key "other", topics field won't be shown on list_creation_request page. Moreover, if topics.conf already contains "other" topic, it won't be shown twice. ------------------------------------------------------------------------ r7552 | sikeda | 2012-08-06 09:12:35 +0200 (lun. 06 aot 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm [-bug] Details of TT2 parse error did not logged. Stringify Template::Exception object using overloaded "". ------------------------------------------------------------------------ r7551 | sikeda | 2012-08-04 12:02:48 +0200 (sam. 04 aot 2012) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm [bug][#8451][Reported by R. Manola, UFES, Brazil] bulkmailer_table column "receipients_bulkmailer" too small. On Oracle, Pg or Sybase, if list of recipients exceeds 500 octets in its length, bulk mailer fails to store packet due to VARCHAR(500) column. On mysql, the limit of TEXT column is 2^16 octets. SQLite seems not to have such limitations. [bug] ARG_MAX checking did not consider entire command line arguments. ------------------------------------------------------------------------ r7544 | sikeda | 2012-07-31 08:58:01 +0200 (mar. 31 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#8449][Submitted by A. Bernstein, Electric Embers] subrequest and sigrequest accept invalid email addresses. ------------------------------------------------------------------------ r7535 | sympa-authors | 2012-07-27 15:38:17 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.14 ------------------------------------------------------------------------ r7534 | sympa-authors | 2012-07-27 15:30:43 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po-wwsympa/en.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7533 | sympa-authors | 2012-07-27 15:13:43 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/src/lib/Language.pm [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7532 | sympa-authors | 2012-07-27 13:57:24 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7531 | sympa-authors | 2012-07-27 13:40:56 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ca.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7530 | sympa-authors | 2012-07-27 13:21:50 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/en.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7529 | david.verdin | 2012-07-27 13:07:54 +0200 (ven. 27 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/LINGUAS M /branches/sympa-6.1-branch/po/Makefile.in.in M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po A /branches/sympa-6.1-branch/po/en.po D /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/LINGUAS A /branches/sympa-6.1-branch/po-wwsympa/en.po D /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/src/lib/Language.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [change] Now the English locale is the - more general - 'en' instead of 'en_US', saving people the need to install the en_Us if they don't use it. ------------------------------------------------------------------------ r7526 | sikeda | 2012-07-26 08:16:52 +0200 (jeu. 26 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/subscriber_table.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#6931][Reported by D. Pritts, Internet2] On the review page for the list without any subscribers, a message "ERROR (review) - List has no subscribers" is shown. It is not an error. ------------------------------------------------------------------------ r7524 | david.verdin | 2012-07-25 15:45:02 +0200 (mer. 25 juil. 2012) | 2 lignes Chemins modifis: A /branches/sympa-6.1-branch/src/lib/Sympa/DatabaseDescription.pm [dev] Adding a first version of DatabaseDescription.pm that is needed to update the database creation scripts. Will be later used in the SDM framework of the 6.2. ------------------------------------------------------------------------ r7523 | david.verdin | 2012-07-25 15:43:41 +0200 (mer. 25 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql [bug] Updating database creation scripts with correct indexes definition. ------------------------------------------------------------------------ r7512 | sikeda | 2012-07-24 10:52:37 +0200 (mar. 24 juil. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/tools.pm [-bug] Substring "[...]" in subject line is removed. Use more precise $tag_regexp. [-feature] Multiple "Re:" munged by some MUAs ("Re^2", "Re*2", "Re**2" & "Re[2]") is taken account by "Re:" munging feature. ------------------------------------------------------------------------ r7511 | sikeda | 2012-07-24 08:17:33 +0200 (mar. 24 juil. 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tt2.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature] Now WWSympa responds with "404 Not Found" status code, instead of sending notification to listmaster, if any random pages under /sympa/help/ are requested. Added new option 'has_header' for tt2::parse_tt2() to prepend newline to page body so that TT2 parse error can be handled before all HTTP headers have been sent. ------------------------------------------------------------------------ r7509 | david.verdin | 2012-07-23 13:00:44 +0200 (lun. 23 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm [bug] User firendly automatic lists were not robot-enabled. Fixed. ------------------------------------------------------------------------ r7507 | sympa-authors | 2012-07-20 17:21:44 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac [-release]Preparing version 6.1.13 ------------------------------------------------------------------------ r7506 | sympa-authors | 2012-07-20 17:18:30 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7505 | sympa-authors | 2012-07-20 16:59:13 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7504 | david.verdin | 2012-07-20 16:44:49 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/configure.ac [svn] Reversing the reversion to version 6.1.11 ------------------------------------------------------------------------ r7503 | david.verdin | 2012-07-20 16:42:05 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/configure.ac [svn] reverting to version 6.1.11 ------------------------------------------------------------------------ r7502 | david.verdin | 2012-07-20 16:40:58 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/NEWS M /branches/sympa-6.1-branch/configure.ac M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/web_help.pot M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [svn] Committing lost updates ------------------------------------------------------------------------ r7498 | sympa-authors | 2012-07-20 16:02:51 +0200 (ven. 20 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/ChangeLog M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7490 | sikeda | 2012-07-19 12:07:15 +0200 (jeu. 19 juil. 2012) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/help_admin.tt2 M /branches/sympa-6.1-branch/web_tt2/help_introduction.tt2 M /branches/sympa-6.1-branch/web_tt2/help_listconfig.tt2 M /branches/sympa-6.1-branch/web_tt2/serveradmin.tt2 [change] Some fixes on help --- - web_tt2/help_listconfig.tt2: Removed non-existing visibility scenari (private and semipublic). - web_tt2/serveradmin.tt2: changed link to translation FAQ. - web_tt2/help_introduction.tt2, web_tt2/help_admin.tt2: Removed dead links to example of charters. ------------------------------------------------------------------------ r7489 | sikeda | 2012-07-19 11:41:50 +0200 (jeu. 19 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature] By default, arcsearch (simple) searches in current month and in the previous non-empty one. ------------------------------------------------------------------------ r7488 | sikeda | 2012-07-19 07:07:50 +0200 (jeu. 19 juil. 2012) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa.pl.in [feature] Revived "Re:" munging. cf. Feature Request #1044. - Supports "Re:" and its equivalents: "AW:" (de); "?\208?\157?\208?\144:" (ru etc.); "Re:" (en, la etc.); "Rif:" (it); "SV:" (da, sv); "VS:" (fi). Additionally, command in subject with them can be recognized. - Multiple "Re:" and its equivalents in case of "Subject: AW: Re: quelque chose" will be truncated . - custom_subject will be placed at beginning of subject in case of "Subject: Another was Re: [list] Something". ------------------------------------------------------------------------ r7480 | etiennemeleard | 2012-07-17 11:54:30 +0200 (mar. 17 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/admin.pm [bug][#8425][Reported by Steve Shipway] List table was not populated upon command line list creation. ------------------------------------------------------------------------ r7479 | sikeda | 2012-07-17 07:50:31 +0200 (mar. 17 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm [*bug][#7733][Submitted by A. Bernstein, Electric Embers] SOAP 'add' function resets subscriber's password ------------------------------------------------------------------------ r7478 | etiennemeleard | 2012-07-16 18:22:22 +0200 (lun. 16 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug] Fixed list log search date regexp to accept dashes and removed admin notice if no list logs found (which can occur if no match). ------------------------------------------------------------------------ r7477 | etiennemeleard | 2012-07-16 18:13:16 +0200 (lun. 16 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/javascript.tt2 M /branches/sympa-6.1-branch/web_tt2/nav.tt2 M /branches/sympa-6.1-branch/web_tt2/viewlogs.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [bug][#1254][Reported by Olivier Duval] List logs page did not behave as expected, sorting failed and selecting a message to trace ended with a javascript error. ------------------------------------------------------------------------ r7476 | etiennemeleard | 2012-07-16 15:42:41 +0200 (lun. 16 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/css.tt2 [bug][#1130][Reported by Serge Aumont] Blockquote HTML tag had no margin, thus citation blocks in web archives were not always highlighted. ------------------------------------------------------------------------ r7475 | etiennemeleard | 2012-07-16 15:16:44 +0200 (lun. 16 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm [bug][#1031][Reported by Emmanuel Eyer] Lists remained in memory cache after purge in processes, so if a new list was created with the same name it "inherited" some outdated parameters, especially param_constraint entries when using list famillies. Also the list table was not purged. ------------------------------------------------------------------------ r7469 | sympa-authors | 2012-07-12 14:42:13 +0200 (jeu. 12 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/sympa.pot M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po [-feature]Committing updated catalogues from Pootle ------------------------------------------------------------------------ r7468 | sympa-authors | 2012-07-12 14:24:19 +0200 (jeu. 12 juil. 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/po/ar.po M /branches/sympa-6.1-branch/po/bg.po M /branches/sympa-6.1-branch/po/br.po M /branches/sympa-6.1-branch/po/ca.po M /branches/sympa-6.1-branch/po/cs.po M /branches/sympa-6.1-branch/po/de.po M /branches/sympa-6.1-branch/po/el.po M /branches/sympa-6.1-branch/po/en_US.po M /branches/sympa-6.1-branch/po/es.po M /branches/sympa-6.1-branch/po/et.po M /branches/sympa-6.1-branch/po/eu.po M /branches/sympa-6.1-branch/po/fi.po M /branches/sympa-6.1-branch/po/fr.po M /branches/sympa-6.1-branch/po/gl.po M /branches/sympa-6.1-branch/po/hu.po M /branches/sympa-6.1-branch/po/id.po M /branches/sympa-6.1-branch/po/it.po M /branches/sympa-6.1-branch/po/ja.po M /branches/sympa-6.1-branch/po/ko.po M /branches/sympa-6.1-branch/po/la.po M /branches/sympa-6.1-branch/po/ml.po M /branches/sympa-6.1-branch/po/nb_NO.po M /branches/sympa-6.1-branch/po/nl.po M /branches/sympa-6.1-branch/po/oc.po M /branches/sympa-6.1-branch/po/pl.po M /branches/sympa-6.1-branch/po/pt.po M /branches/sympa-6.1-branch/po/pt_BR.po M /branches/sympa-6.1-branch/po/ro.po M /branches/sympa-6.1-branch/po/ru.po M /branches/sympa-6.1-branch/po/sv.po M /branches/sympa-6.1-branch/po/tr.po M /branches/sympa-6.1-branch/po/vi.po M /branches/sympa-6.1-branch/po/zh_CN.po M /branches/sympa-6.1-branch/po/zh_TW.po M /branches/sympa-6.1-branch/po-wwsympa/ar.po M /branches/sympa-6.1-branch/po-wwsympa/bg.po M /branches/sympa-6.1-branch/po-wwsympa/br.po M /branches/sympa-6.1-branch/po-wwsympa/ca.po M /branches/sympa-6.1-branch/po-wwsympa/cs.po M /branches/sympa-6.1-branch/po-wwsympa/de.po M /branches/sympa-6.1-branch/po-wwsympa/el.po M /branches/sympa-6.1-branch/po-wwsympa/en_US.po M /branches/sympa-6.1-branch/po-wwsympa/es.po M /branches/sympa-6.1-branch/po-wwsympa/et.po M /branches/sympa-6.1-branch/po-wwsympa/eu.po M /branches/sympa-6.1-branch/po-wwsympa/fi.po M /branches/sympa-6.1-branch/po-wwsympa/fr.po M /branches/sympa-6.1-branch/po-wwsympa/hu.po M /branches/sympa-6.1-branch/po-wwsympa/id.po M /branches/sympa-6.1-branch/po-wwsympa/it.po M /branches/sympa-6.1-branch/po-wwsympa/ja.po M /branches/sympa-6.1-branch/po-wwsympa/nb_NO.po M /branches/sympa-6.1-branch/po-wwsympa/nl.po M /branches/sympa-6.1-branch/po-wwsympa/oc.po M /branches/sympa-6.1-branch/po-wwsympa/pl.po M /branches/sympa-6.1-branch/po-wwsympa/pt.po M /branches/sympa-6.1-branch/po-wwsympa/pt_BR.po M /branches/sympa-6.1-branch/po-wwsympa/ro.po M /branches/sympa-6.1-branch/po-wwsympa/ru.po M /branches/sympa-6.1-branch/po-wwsympa/sv.po M /branches/sympa-6.1-branch/po-wwsympa/tr.po M /branches/sympa-6.1-branch/po-wwsympa/vi.po M /branches/sympa-6.1-branch/po-wwsympa/zh_CN.po M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-feature]Committing latest translations from Pootle ------------------------------------------------------------------------ r7460 | david.verdin | 2012-07-11 11:21:19 +0200 (mer. 11 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.2-branch/wwsympa/wwsympa.fcgi.in Fision via: r7468 [bug] The automatic list families loading was done by directly casting the result &Conf::get_parameters, which led wwsympa to crash when no automatic list was dfined. ------------------------------------------------------------------------ r7453 | sikeda | 2012-07-10 05:07:41 +0200 (mar. 10 juil. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/Makefile.am [bug][#7690] If Sympa has been installed and if "make install" with DESTDIR argument is run for another version of Sympa source, sympa.conf & wwsympa.conf are created using defaults for installed version. Create temporary sympa_wizard.pl referring confdef.pm under source tree. ------------------------------------------------------------------------ r7451 | sikeda | 2012-07-06 11:21:24 +0200 (ven. 06 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympasoap.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/src/task_manager.pl.in M /branches/sympa-6.1-branch/wwsympa/archived.pl.in M /branches/sympa-6.1-branch/wwsympa/bounced.pl.in M /branches/sympa-6.1-branch/wwsympa/cookielib.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [-bug] Corrected minor typos ------------------------------------------------------------------------ r7449 | sikeda | 2012-07-06 06:33:47 +0200 (ven. 06 juil. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm [*bug][#8400][Submitted by M. Deranek] Issue #3 (#2 was solved): On SQLite, fields with 'NOT NULL' constraint won't be added so upgrade process will fail. Workaround is to assign DEFAULT value 0 or "" to such fields. ------------------------------------------------------------------------ r7448 | david.verdin | 2012-07-05 16:45:37 +0200 (jeu. 05 juil. 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch M /branches/sympa-6.1-branch/mail_tt2/user_notification.tt2 M /branches/sympa-6.1-branch/src/etc/Makefile.am A /branches/sympa-6.1-branch/src/etc/scenari/automatic_list_creation.family_owner (de /branches/userfriendly-autolists/src/etc/scenari/automatic_list_creation.family_owner:7447) M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/Family.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/admin.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/web_tt2/Makefile.am A /branches/sympa-6.1-branch/web_tt2/automatic_lists.tt2 (de /branches/userfriendly-autolists/web_tt2/automatic_lists.tt2:7447) A /branches/sympa-6.1-branch/web_tt2/automatic_lists_management_request.tt2 (de /branches/userfriendly-autolists/web_tt2/automatic_lists_management_request.tt2:7447) A /branches/sympa-6.1-branch/web_tt2/automatic_lists_request.tt2 (de /branches/userfriendly-autolists/web_tt2/automatic_lists_request.tt2:7447) A /branches/sympa-6.1-branch/web_tt2/family_signoff.tt2 (de /branches/userfriendly-autolists/web_tt2/family_signoff.tt2:7447) A /branches/sympa-6.1-branch/web_tt2/family_signoff_request.tt2 (de /branches/userfriendly-autolists/web_tt2/family_signoff_request.tt2:7447) A /branches/sympa-6.1-branch/web_tt2/family_signoff_request2.tt2 (de /branches/userfriendly-autolists/web_tt2/family_signoff_request2.tt2:7447) M /branches/sympa-6.1-branch/web_tt2/menu.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in [feature][Requested and funded by the CNRS] Automatic lists can now be manipulated through the sympa web interface, in a "user friendly" form. This is merely a web layer around the general autmatic lists feature. This feature is fully documented here: https://www.sympa.org/manual_6.2/user_friendly_autolists Additionnally, it is now possible to unsubscribe to all the lists of a given family in two clicks. ------------------------------------------------------------------------ r7447 | david.verdin | 2012-07-05 16:31:25 +0200 (jeu. 05 juil. 2012) | 2 lignes Chemins modifis: M /branches/userfriendly-autolists M /branches/userfriendly-autolists/ChangeLog M /branches/userfriendly-autolists/NEWS M /branches/userfriendly-autolists/configure.ac M /branches/userfriendly-autolists/doc/sample/config.include M /branches/userfriendly-autolists/po/LINGUAS M /branches/userfriendly-autolists/po/ar.po M /branches/userfriendly-autolists/po/bg.po M /branches/userfriendly-autolists/po/br.po M /branches/userfriendly-autolists/po/ca.po M /branches/userfriendly-autolists/po/cs.po M /branches/userfriendly-autolists/po/de.po M /branches/userfriendly-autolists/po/el.po M /branches/userfriendly-autolists/po/en_US.po M /branches/userfriendly-autolists/po/es.po M /branches/userfriendly-autolists/po/et.po M /branches/userfriendly-autolists/po/eu.po M /branches/userfriendly-autolists/po/fi.po M /branches/userfriendly-autolists/po/fr.po A /branches/userfriendly-autolists/po/gl.po (de /branches/sympa-6.1-branch/po/gl.po:7446) M /branches/userfriendly-autolists/po/hu.po M /branches/userfriendly-autolists/po/id.po M /branches/userfriendly-autolists/po/it.po M /branches/userfriendly-autolists/po/ja.po M /branches/userfriendly-autolists/po/ko.po M /branches/userfriendly-autolists/po/la.po M /branches/userfriendly-autolists/po/ml.po M /branches/userfriendly-autolists/po/nb_NO.po M /branches/userfriendly-autolists/po/nl.po M /branches/userfriendly-autolists/po/oc.po M /branches/userfriendly-autolists/po/pl.po M /branches/userfriendly-autolists/po/pt.po M /branches/userfriendly-autolists/po/pt_BR.po M /branches/userfriendly-autolists/po/ro.po M /branches/userfriendly-autolists/po/ru.po M /branches/userfriendly-autolists/po/sv.po M /branches/userfriendly-autolists/po/sympa.pot M /branches/userfriendly-autolists/po/tr.po M /branches/userfriendly-autolists/po/vi.po M /branches/userfriendly-autolists/po/zh_CN.po M /branches/userfriendly-autolists/po/zh_TW.po M /branches/userfriendly-autolists/po-wwsympa/POTFILES.in M /branches/userfriendly-autolists/po-wwsympa/ar.po M /branches/userfriendly-autolists/po-wwsympa/bg.po M /branches/userfriendly-autolists/po-wwsympa/br.po M /branches/userfriendly-autolists/po-wwsympa/ca.po M /branches/userfriendly-autolists/po-wwsympa/cs.po M /branches/userfriendly-autolists/po-wwsympa/de.po M /branches/userfriendly-autolists/po-wwsympa/el.po M /branches/userfriendly-autolists/po-wwsympa/en_US.po M /branches/userfriendly-autolists/po-wwsympa/es.po M /branches/userfriendly-autolists/po-wwsympa/et.po M /branches/userfriendly-autolists/po-wwsympa/eu.po M /branches/userfriendly-autolists/po-wwsympa/fi.po M /branches/userfriendly-autolists/po-wwsympa/fr.po A /branches/userfriendly-autolists/po-wwsympa/gl.po (de /branches/sympa-6.1-branch/po-wwsympa/gl.po:7446) M /branches/userfriendly-autolists/po-wwsympa/hu.po M /branches/userfriendly-autolists/po-wwsympa/id.po M /branches/userfriendly-autolists/po-wwsympa/it.po M /branches/userfriendly-autolists/po-wwsympa/ja.po M /branches/userfriendly-autolists/po-wwsympa/nb_NO.po M /branches/userfriendly-autolists/po-wwsympa/nl.po M /branches/userfriendly-autolists/po-wwsympa/oc.po M /branches/userfriendly-autolists/po-wwsympa/pl.po M /branches/userfriendly-autolists/po-wwsympa/pt.po M /branches/userfriendly-autolists/po-wwsympa/pt_BR.po M /branches/userfriendly-autolists/po-wwsympa/ro.po M /branches/userfriendly-autolists/po-wwsympa/ru.po M /branches/userfriendly-autolists/po-wwsympa/sv.po M /branches/userfriendly-autolists/po-wwsympa/tr.po M /branches/userfriendly-autolists/po-wwsympa/vi.po M /branches/userfriendly-autolists/po-wwsympa/web_help.pot M /branches/userfriendly-autolists/po-wwsympa/zh_CN.po M /branches/userfriendly-autolists/soap/sympa_soap_client.pl.in M /branches/userfriendly-autolists/soap/sympa_soap_server-wrapper.fcgi.c M /branches/userfriendly-autolists/soap/sympa_soap_server.fcgi.in M /branches/userfriendly-autolists/src/bulk.pl.in M /branches/userfriendly-autolists/src/etc/edit_list.conf M /branches/userfriendly-autolists/src/etc/mhonarc-ressources.tt2 M /branches/userfriendly-autolists/src/etc/script/create_db.Oracle M /branches/userfriendly-autolists/src/etc/script/create_db.Pg M /branches/userfriendly-autolists/src/etc/script/create_db.SQLite M /branches/userfriendly-autolists/src/etc/script/create_db.Sybase M /branches/userfriendly-autolists/src/etc/script/create_db.mysql M /branches/userfriendly-autolists/src/lib/Archive.pm M /branches/userfriendly-autolists/src/lib/Bulk.pm M /branches/userfriendly-autolists/src/lib/Commands.pm M /branches/userfriendly-autolists/src/lib/Conf.pm M /branches/userfriendly-autolists/src/lib/Family.pm M /branches/userfriendly-autolists/src/lib/List.pm M /branches/userfriendly-autolists/src/lib/Log.pm M /branches/userfriendly-autolists/src/lib/Message.pm M /branches/userfriendly-autolists/src/lib/PlainDigest.pm M /branches/userfriendly-autolists/src/lib/Scenario.pm M /branches/userfriendly-autolists/src/lib/Upgrade.pm M /branches/userfriendly-autolists/src/lib/admin.pm M /branches/userfriendly-autolists/src/lib/confdef.pm M /branches/userfriendly-autolists/src/lib/mail.pm M /branches/userfriendly-autolists/src/lib/report.pm M /branches/userfriendly-autolists/src/lib/tools.pm M /branches/userfriendly-autolists/src/sympa.pl.in M /branches/userfriendly-autolists/src/task_manager.pl.in M /branches/userfriendly-autolists/web_tt2/Makefile.am M /branches/userfriendly-autolists/web_tt2/arcsearch.tt2 M /branches/userfriendly-autolists/web_tt2/arcsearch_form.tt2 M /branches/userfriendly-autolists/web_tt2/edit_template.tt2 M /branches/userfriendly-autolists/web_tt2/editfile.tt2 M /branches/userfriendly-autolists/web_tt2/help_editlist.tt2 A /branches/userfriendly-autolists/web_tt2/help_suspend.tt2 (de /branches/sympa-6.1-branch/web_tt2/help_suspend.tt2:7446) M /branches/userfriendly-autolists/web_tt2/help_user.tt2 M /branches/userfriendly-autolists/web_tt2/javascript.tt2 M /branches/userfriendly-autolists/web_tt2/loginbanner.tt2 M /branches/userfriendly-autolists/web_tt2/manage_template.tt2 M /branches/userfriendly-autolists/web_tt2/subscriber_table.tt2 M /branches/userfriendly-autolists/web_tt2/suspend_request.tt2 M /branches/userfriendly-autolists/web_tt2/viewmod.tt2 M /branches/userfriendly-autolists/wwsympa/Marc/Search.pm M /branches/userfriendly-autolists/wwsympa/wwslib.pm M /branches/userfriendly-autolists/wwsympa/wwsympa-wrapper.fcgi.c M /branches/userfriendly-autolists/wwsympa/wwsympa.fcgi.in Fision via: r7448 [svn] Retrieving latest modifications from branch 6.1. ------------------------------------------------------------------------ r7443 | sikeda | 2012-07-04 11:24:59 +0200 (mer. 04 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/lib/Upgrade.pm Fision via: r7448, r7447 [*bug][#8400][Submitted by M. Deranek] Issue #1: On SQLite, data types should be normalized. Otherwise, upgrade process will fail. ------------------------------------------------------------------------ r7442 | david.verdin | 2012-07-03 16:28:56 +0200 (mar. 03 juil. 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/bulk.pl.in M /branches/sympa-6.1-branch/src/etc/script/create_db.Oracle M /branches/sympa-6.1-branch/src/etc/script/create_db.Pg M /branches/sympa-6.1-branch/src/etc/script/create_db.SQLite M /branches/sympa-6.1-branch/src/etc/script/create_db.Sybase M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql M /branches/sympa-6.1-branch/src/lib/Bulk.pm M /branches/sympa-6.1-branch/src/lib/Conf.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/Upgrade.pm M /branches/sympa-6.1-branch/src/lib/confdef.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/tools.pm Fision via: r7448, r7447 [*bug] The "dkim_header_list" sympa.conf parameter and "dkim > header_list" list parameter are in fact unapplicable using the Mail::DKIM module. This doesn't allow the list of headers to be modified. Consequently, anybody using these parameters could find the DKIM signature to be broken because the list of signed headers in the "h" tag of the DKIM signature would not match the actual headers used to compute the signature. Fixed by obsoleting this parameter. In addition, the algorithm computing the signature now use $dkim->PRINT instead of $dkim->load. Indeed, the load function computing time grows exponentially relatively to the message size; this is not the case when using PRINT directly. ------------------------------------------------------------------------ r7441 | sikeda | 2012-07-03 06:03:50 +0200 (mar. 03 juil. 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/loginbanner.tt2 Fision via: r7448, r7447 [-bug][#8417][Reported by Nicolas Vigier, hupstream] "Authentication help" link is empty if authentication_info_url is set. ------------------------------------------------------------------------ r7440 | sikeda | 2012-07-02 09:27:01 +0200 (lun. 02 juil. 2012) | 6 lignes Chemins modifis: M /branches/sympa-6.1-branch/soap/sympa_soap_client.pl.in M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [-bug] Correct some meaningless or harmful printf/sprintf. This fix is incomplete. Completed one should be done on 6.2.x. - [#7020] "load certificate" may go wrong in encrypted mailing lists [-bug] src/lib/mail.pm: format "%m" is not portable. Use "%s" with stringified $!. [-bug] wwsympa/wwsympa.fcgi.in: Content-Disposition HTTP header field did not have main value "attachment". ------------------------------------------------------------------------ r7439 | sikeda | 2012-06-28 05:27:44 +0200 (jeu. 28 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][#7540][Submitted by J.M. Kubeck, univ. J.F. Champollion] Sympa generates two cookies. Always use cookie_domain parameter instead 'localhost'. ------------------------------------------------------------------------ r7438 | sikeda | 2012-06-28 05:16:43 +0200 (jeu. 28 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm Fision via: r7448, r7447 [-change] reverted r7424. ------------------------------------------------------------------------ r7437 | sikeda | 2012-06-27 11:36:35 +0200 (mer. 27 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Archive.pm Fision via: r7448, r7447 [bug][#8377] Subscriber pictures not shown when email contains any punctuations. Now Archive::load_html_message() returns metadata with punctuations decoded. ------------------------------------------------------------------------ r7436 | sikeda | 2012-06-27 11:26:32 +0200 (mer. 27 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Log.pm M /branches/sympa-6.1-branch/src/lib/tools.pm Fision via: r7448, r7447 [-bug][#7649][#8115] "Use of uninitialized value" warning by RHEL6 Sys::Syslog. ------------------------------------------------------------------------ r7435 | sikeda | 2012-06-27 03:58:49 +0200 (mer. 27 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/List.pm Fision via: r7448, r7447 [-bug][#8197] Harmless "err List::get_subscriber() Unable to retrieve information from database for user" logs. do_log() statement was removed. ------------------------------------------------------------------------ r7434 | sikeda | 2012-06-25 09:33:43 +0200 (lun. 25 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwslib.pm Fision via: r7448, r7447 [-bug][#8317] Wrong duration for cookies. Correct duration of "1 week" and add "30 days" for backword compatibility. ------------------------------------------------------------------------ r7433 | sikeda | 2012-06-23 10:48:56 +0200 (sam. 23 juin 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][#7444][Submitted by A. Bernstein, Electric Embers] very long Message-id will break do_send_me() and do_view_source(). Commited a bit improved patch. ------------------------------------------------------------------------ r7432 | sikeda | 2012-06-23 09:50:34 +0200 (sam. 23 juin 2012) | 10 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/arcsearch.tt2 M /branches/sympa-6.1-branch/wwsympa/Marc/Search.pm Fision via: r7448, r7447 [*bug][#3918] HTML tags escaped in search results. Improved Marc::Search: - Now it also gives rich text information. arcsearch template was modified so that it would build HTML text from that information. - Strip HTML tags in the file generated from HTML message. Entity references are also unescaped. - Texts are handled as Unicode instead binary text to perform case-insensitive match on non-ASCII characters. ------------------------------------------------------------------------ r7431 | sikeda | 2012-06-22 11:34:23 +0200 (ven. 22 juin 2012) | 1 ligne Chemins modifis: M /branches/sympa-6.1-branch/src/etc/mhonarc-ressources.tt2 M /branches/sympa-6.1-branch/web_tt2/arcsearch_form.tt2 Fision via: r7448, r7447 [-bug] script was broken by MHonArc ------------------------------------------------------------------------ r7430 | sikeda | 2012-06-22 10:54:17 +0200 (ven. 22 juin 2012) | 13 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/mhonarc-ressources.tt2 M /branches/sympa-6.1-branch/web_tt2/arcsearch_form.tt2 M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [*bug] Fixed some bugs on arcsearch. - Submission to arcsearch with empty key_word causes "Missing argument key_word" error then takes user to empty page. Workaround is to check key_word by JavaScript. To enable it, archives must be reconstructed. - Escaped key_word is shown in search result and form entry. - The key_word with some characters (<>\\\*\$) causes "syntax_errors" error then takes user to empty page. Now these characters are also allowed. - Subjects in search result were decoded twice. - arcsearch_id could not search message-ID containing regexp metacharacters. Now regexp metacharacters are properly escaped. ------------------------------------------------------------------------ r7428 | sikeda | 2012-06-21 04:46:45 +0200 (jeu. 21 juin 2012) | 5 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][#8042] 'To' e-mail address(es) of 'list_created' or 'list_rejected' notification sent to listmaster(s) can be "encoded" due to line length. Disabled mime-encoding of 'To' header by mail::mail_file(), because this function will be given not addr-spec but email, and because mime-encoding of structured header should be done with special function such as tools::addrencode(). n.b. These notifications to listmaster would like to be compiled into 'listmaster_notification' template. ------------------------------------------------------------------------ r7427 | sikeda | 2012-06-19 15:32:48 +0200 (mar. 19 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/etc/script/create_db.mysql M /branches/sympa-6.1-branch/src/lib/Upgrade.pm Fision via: r7448, r7447 [bug][Reported by F.P. Goncharov via Debian Project] Sympa must create MySQL database & tables with charset utf8 instead default latin1. cf. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=574646 ------------------------------------------------------------------------ r7426 | sikeda | 2012-06-19 07:02:29 +0200 (mar. 19 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [-bug] correct start_time. ------------------------------------------------------------------------ r7425 | sikeda | 2012-06-19 06:47:32 +0200 (mar. 19 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug] HTTP Date & Last-Modified headers are not RFC-compliant, and strftime format %z is not portable. Modify format and use GMT. [-bug] Format of start_date & last_login_date are not gettext'ized. ------------------------------------------------------------------------ r7424 | sikeda | 2012-06-18 11:13:25 +0200 (lun. 18 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/SympaSession.pm Fision via: r7448, r7447 [bug] If 'cookie_domain' parameter is 'localhost' (default), when cookie was expired or HTTPd was restarted, user's cookie jar repeatedly sends session cookie with older session ID. Destroy cookie with default domain (no domain parameter) before sending new session id with explicit domain. ------------------------------------------------------------------------ r7423 | sikeda | 2012-06-17 03:18:28 +0200 (dim. 17 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][By E. Bouthenot, Debian Project] Properly fix CVE-2012-2352. http://bugs.debian.org/672893 ------------------------------------------------------------------------ r7422 | sikeda | 2012-06-17 02:02:09 +0200 (dim. 17 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/web_tt2/javascript.tt2 M /branches/sympa-6.1-branch/web_tt2/viewmod.tt2 Fision via: r7448, r7447 [-bug] Corrected typos. And removed ineffective code in src/sympa.pl.in. ------------------------------------------------------------------------ r7421 | sikeda | 2012-06-16 01:30:26 +0200 (sam. 16 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/tools.pm Fision via: r7448, r7447 [bug] Headers in S/MIME encrypted/decrypted/signed messages are broken. ------------------------------------------------------------------------ r7420 | sikeda | 2012-06-16 01:21:43 +0200 (sam. 16 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][Submitted by Mitar] static_content_url was not robot-specific. ------------------------------------------------------------------------ r7419 | sikeda | 2012-06-14 12:04:52 +0200 (jeu. 14 juin 2012) | 4 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/mail.pm Fision via: r7448, r7447 [*bug] mail::send_file() did not complement Date header. As a result, a message posted from WWSympa were archived as the newest messages when archive was reconstructed, and so on. ------------------------------------------------------------------------ r7417 | sikeda | 2012-06-12 10:34:15 +0200 (mar. 12 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [bug][#8378] Uploading subscriber pictures with uppercase extension does not work ------------------------------------------------------------------------ r7412 | sikeda | 2012-06-05 07:09:25 +0200 (mar. 05 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Upgrade.pm Fision via: r7448, r7447 [*bug][#8291][Reported by Heaton Anselm, Netuxo] --md5_encode_password does not take case sensitivity in account. Use Auth::password_fingerprint() instead of tools::md5_fingerprint(). ------------------------------------------------------------------------ r7410 | sikeda | 2012-06-02 04:32:42 +0200 (sam. 02 juin 2012) | 3 lignes Chemins modifis: M /branches/sympa-6.1-branch/src/lib/Archive.pm M /branches/sympa-6.1-branch/src/lib/Commands.pm M /branches/sympa-6.1-branch/src/lib/List.pm M /branches/sympa-6.1-branch/src/lib/PlainDigest.pm M /branches/sympa-6.1-branch/src/lib/Scenario.pm M /branches/sympa-6.1-branch/src/lib/report.pm M /branches/sympa-6.1-branch/src/lib/tools.pm M /branches/sympa-6.1-branch/src/sympa.pl.in M /branches/sympa-6.1-branch/wwsympa/wwsympa.fcgi.in Fision via: r7448, r7447 [*bug] fix in r7387 was not effective. Fixed again. Decode headers to UTF-8 with new function tools::decode_header which always returns scalar. To ensure value of head->get() is scalar, assign it to scalar variable then use it in other expressions. ------------------------------------------------------------------------ r7409 | sikeda | 2012-06-01 10:05:22 +0200 (ven. 01 juin 2012) | 2 lignes Chemins modifis: M /branches/sympa-6.1-branch/web_tt2/edit_template.tt2 M /branches/sympa-6.1-branch/web_tt2/editfile.tt2 M /branches/sympa-6.1-branch/web_tt2/manage_template.tt2 Fision via: r7448, r7447 [-bug] When message files or templates are edited, newline is added at end of them. Removed newline just before end tag of
sympa-6.1.24~dfsg/web_tt2/admin.tt2000066400000000000000000000114321246372670100171010ustar00rootroot00000000000000

[%|loc%]Casual administration[%END%]

    [% IF is_listmaster || is_owner %]
  • [%|loc%]Edit list config:[%END%] [%|loc%]Use it with care: it allows you to modify some of the list parameters. The list of the parameters you can modify depends on your privileges.[%END%]
  • [%|loc%]Customizing: [%END%] [%|loc%]Editing of various files and messages attached to your list.[%END%]
  • [% END %] [% IF is_listmaster || is_owner || may_del %]
  • [%|loc%]Manage subscribers:[%END%] [%|loc%]Allows you to add or delete list subscribers, moderate subscriptions and so on.[%END%]
  • [% IF conf.use_blacklist != 'none' %]
  • [%|loc%]Blacklist:[%END%] [%|loc%]Handles the set of black-listed mail addresses for this list.[%END%]
  • [% END %] [% END %] [% IF is_listmaster || is_owner %] [% IF is_archived %]
  • [%|loc%]Manage archives:[%END%] [%|loc%]Allows you to download and delete list archives.[%END%]
  • [% END %] [% IF is_listmaster || is_owner || ( is_editor && may_review ) %]
  • [%|loc%]Bounces:[%END%] [%|loc%]Manages non-delivery reports (also called bounces).[%END%]
  • [% END %] [% IF is_priv || is_listmaster %]
  • [%|loc%]Logs:[%END%] [%|loc%]A tool for exploring the list logs.[%END%]
  • [% END %] [% END %]
[% IF is_listmaster || is_owner || is_privileged_owner %]

[%|loc%]Drastic operations[%END%]

[% IF is_privileged_owner %]
[% IF list_conf.status == 'closed' %] [%|loc%]This list is currently closed. Clicking this button will make it active again.[%END%] [% ELSE %] [%|loc%]Completely removes the current list. Listmaster privileges are required to restore a list.[%END%] [% END %]

[% IF may_create_list %] [%|loc%]Allows you to change this list's name. Everything related to the list will be relabeled according to the new name, including the mail aliases and the web archives.[%END%] [% END %]

[% END %] [% IF is_listmaster || is_owner %]
[% IF shared == 'none' %] [%|loc%]Initializes the shared document web space.[%END%] [% ELSIF shared == 'exist' %] [%|loc%]Closes the shared document web space. It can be restored using "Restore shared" button.[%END%] [% ELSIF shared == 'deleted' %] [%|loc%]Restores the previously closed shared document web space.[%END%] [% END %]

[% END %] [% END %]
sympa-6.1.24~dfsg/web_tt2/admin_menu.tt2000066400000000000000000000213071246372670100201270ustar00rootroot00000000000000 [%|loc%]List Administration Panel[%END%] [% IF action == 'review' %] [%|loc%]Subscribers[%END%] [% ELSE %] [% IF is_owner %] [%|loc%]Subscribers[%END%] [% END %] [% END %] [% IF action == 'edit_list_request' %] [%|loc%]Edit List Config[%END%] [% ELSE %] [%|loc%]Edit List Config[%END%] [% END %] [% IF action == 'modindex' %] [%|loc%]Moderate[%END%] [% ELSE %] [% IF is_editor %] [%|loc%]Moderate[%END%] [% ELSE %] [%|loc%]Moderate[%END%] [% END %] [% END %] [% IF action == 'editfile' %] [%|loc%]Customizing[%END%] [% ELSE %] [% IF is_owner %] [%|loc%]Customizing[%END%] [% END %] [% END %] [% IF is_archived %] [% IF action == 'arc_manage' %] [%|loc%]Manage Archives[%END%] [% ELSE %] [% IF is_owner %] [%|loc%]Manage Archives[%END%] [% END %] [% END %] [% END %] [% IF action == 'reviewbouncing' %] [%|loc%]Bounces[%END%] [% ELSE %] [% IF is_owner %] [%|loc%]Bounces[%END%] [% END %] [% END %] [% IF shared == 'none' %] [% IF is_privileged_owner %] [%|loc%]Create Shared[%END%] [% ELSE %] [%|loc%]Create Shared[%END%] [% END %] [% ELSIF shared == 'deleted' %] [%|loc%]Restore shared[%END%] [% ELSIF shared == 'exist' %] [%|loc%]Delete Shared[%END%] [% ELSE %] [% shared %] [% END %] [% IF list_conf.status == 'closed' %] [% IF is_listmaster %] [%|loc%]Restore List[%END%] [% ELSE %] [%|loc%]Restore List[%END%] [% END %] [% ELSIF is_privileged_owner %] [%|loc%]Remove List[%END%] [% ELSIF is_owner %] [%|loc%]Remove List[%END%] [% END %] [% IF may_create_list %] [%|loc%]Rename List[%END%] [% ELSE %] [%|loc%]Rename List[%END%] [%END%] [% IF action == 'edit_list_request' %] [%|loc%]List Definition[%END%] [%|loc%]Sending/Receiving[%END%] [%|loc%]Privileges[%END%] [%|loc%]Archives[%END%] [%|loc%]Bounce Settings[%END%] [%|loc%]Miscellaneous[%END%] [%|loc%]Data Source[%END%] [% END %] [% IF action == 'edit_attributes' %] [%|loc%]Attribute optionnal/required[%END%] [% END %] sympa-6.1.24~dfsg/web_tt2/arc.tt2000066400000000000000000000011761246372670100165620ustar00rootroot00000000000000

[%|loc%]List archive[%END%] [%|loc%]Help[%END%]


[% IF file_handle %] [% PROCESS $file_handle %] [% END %]
sympa-6.1.24~dfsg/web_tt2/arc_manage.tt2000066400000000000000000000027751246372670100201000ustar00rootroot00000000000000

[%|loc%]Archive Management[%END%] [%|loc%]Help[%END%]


[%|loc%]Select below Archives months you want to delete or download (ZiP format):[%END%]



sympa-6.1.24~dfsg/web_tt2/arc_protect.tt2000066400000000000000000000010101246372670100203050ustar00rootroot00000000000000

[%|loc%]This button aims at protecting mailing lists archives against Spam Harvester.[%END%] sympa-6.1.24~dfsg/web_tt2/arcsearch.tt2000066400000000000000000000103141246372670100177420ustar00rootroot00000000000000

[%|loc%]Result of your search in the archive[%END%] [% list %]:

[%|loc%]Search field:[%END%] [% FOREACH u = directories %] [% u %] - [% END %]

[%|loc(key_word)%]Parameters of these search make on "%1":[%END%]
[% IF how == 'phrase' %] [%|loc%](This sentence,[%END%] [% ELSIF how == 'any' %] [%|loc%](All of these words,[%END%] [% ELSE %] [%|loc%](Each of these words,[%END%] [% END %] [% IF case == 'off' %] [%|loc%]case insensitive[%END%] [% ELSE %] [%|loc%]case sensitive[%END%] [% END %] [% IF match == 'partial' %] [%|loc%]and checking on part of word)[%END%] [% ELSE %] [%|loc%]and checking on entire word)[%END%] [% END %]


[% IF age == 'new' %]

[%|loc%]Newest messages first[%END%]

[% ELSE %]

[%|loc%]Oldest messages first[%END%]

[% END %]

[% IF body %]
[%|loc(body_count)%]%1 hits on message Body[%END%]
[% END %] [% IF subj %]
[%|loc(subj_count)%]%1 hits on message Subject field[%END%]
[% END %] [% IF from %]
[%|loc(from_count)%] %1 hits on message From field[%END%]
[% END %] [% IF date %]
[%|loc(date_count)%]%1 hits on message Date field[%END%]
[% END %]
[% IF body %] [% END %] [% IF subj %] [% END %] [% IF from %] [% END %] [% IF date %] [% END %] [% FOREACH u = directories %] [% END %] [% IF continue %] [% END %]


[%|loc%]Based on Marc-Search, search engine of MHonArc archives[%END%]

[%|loc(archive_name)%]Return to archive %1[%END%]
sympa-6.1.24~dfsg/web_tt2/arcsearch_form.tt2000066400000000000000000000075031246372670100207730ustar00rootroot00000000000000

[%|loc%]List archive[%END%]

[%|loc%]Advanced archive search[%END%] [%|loc%]Help[%END%]

[%|loc%]Search field:[%END%] [% archive_name %]
[%|loc%]Search:[%END%]
[%|loc%]Prefer:[%END%]
[%|loc%]Case:[%END%]
[%|loc%]Check:[%END%]
[%|loc%]Layout:[%END%]
[%|loc%]Search area:[%END%]


sympa-6.1.24~dfsg/web_tt2/auto_signoff.tt2000066400000000000000000000005011246372670100204670ustar00rootroot00000000000000

[%|loc%]Unsubscription request[% END %]

[%|loc(signing_off_email,list)%]You clicked a link to unsubscribe the address %1 from list %2.[% END %]

[%|loc(list)%]A confirmation was just sent to this address. By clicking the link it contains, you will be definitively unsubscribed from list %1[% END %]

sympa-6.1.24~dfsg/web_tt2/automatic_lists.tt2000066400000000000000000000002631246372670100212150ustar00rootroot00000000000000

[%|loc%]Automatic lists result[% END %]

The list created would be [% list_name %]

sympa-6.1.24~dfsg/web_tt2/automatic_lists_management_request.tt2000066400000000000000000000005661246372670100251670ustar00rootroot00000000000000

[%|loc%]Automatic lists management[% END %]

[%|loc(automatic_lists_description.family_name)%]The automatic lists are based on the %1 family[% END %]

[% FOREACH p = automatic_lists_description.class %]

Found class [% p.name %]

[%END%]
sympa-6.1.24~dfsg/web_tt2/automatic_lists_request.tt2000066400000000000000000000016601246372670100227670ustar00rootroot00000000000000

[% family.display %]

[%|loc%]In this form, you will be able to create and / or access lists created on the basis of parameters you will defined.[% END %]

[% FOREACH p = family.description.class %]

[% p.stamp %]

[% p.description %]

[% FOREACH i = p.instances %]
[%END%] [%END%]
sympa-6.1.24~dfsg/web_tt2/blacklist.tt2000066400000000000000000000030201246372670100177530ustar00rootroot00000000000000

[%|loc%]Blacklist management[%END%]


[%|loc%]Operation requested by users which email is listed in the blacklist file are rejected. The blacklist is in use for the following operation:[%END%] [% conf.use_blacklist %].

[%|loc%]Syntax:[%END%]
  • [%|loc%]only one email or expression by line[%END%]
  • [%|loc%]char # introduce a comment[%END%]
  • [%|loc%]char * match any string. Example: "*@spammer-domain.com" match any sender from that exact domain[%END%]
  • [%|loc%]only one char * is autorized in a line[%END%]
[%end%]

[% IF rows == '0' %] [%|loc%]The current blacklist is empty[%END%] [% ELSE %] [%|loc(rows)%]The current blacklist contains %1 line(s)[%END%] [% END %]

[% IF rows < '10' %] [% rows = '10' %] [% END %] [% IF rows > '20' %] [% rows = '20' %] [% END %] [% rows = rows+2 %]

[% IF list %] [% END %]
[% IF saved %] [%|loc%]Template saved[%END%] ([% time %])

[% END %]
sympa-6.1.24~dfsg/web_tt2/button_footer.tt2000066400000000000000000000002451246372670100207020ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/button_header.tt2000066400000000000000000000004771246372670100206430ustar00rootroot00000000000000
sympa-6.1.24~dfsg/web_tt2/ca.tt2000066400000000000000000000002101246372670100163640ustar00rootroot00000000000000[% TRY %] [% PROCESS "${custom_action}.tt2" %] [% CATCH file %] File Error! [% error.info %] [% CATCH %] [% error %] [% END %] sympa-6.1.24~dfsg/web_tt2/change_email.tt2000066400000000000000000000004371246372670100204100ustar00rootroot00000000000000 [%|loc%]You will receive a n email, with a confirmation link.[%END%]
[%|loc%]To confirm your email address change, go to your email account and click on the provided link[%END%] sympa-6.1.24~dfsg/web_tt2/change_email_request.tt2000066400000000000000000000006051246372670100221550ustar00rootroot00000000000000 [%|loc%]Changing your email address is a sensitive operation so we need to verify your email.[%END%]
[%|loc(new_email)%]To this end we have sent you an email to this address: %1 with a validation link.[%END%]
[%|loc%]You shoukd check your mailbox now.[%END%] sympa-6.1.24~dfsg/web_tt2/choosepasswd.tt2000066400000000000000000000015621246372670100205160ustar00rootroot00000000000000 [%|loc%]You need to choose a password for your WWSympa environment. You will need this password to perform privileged operations.[%END%]






sympa-6.1.24~dfsg/web_tt2/close_list.tt2000066400000000000000000000006561246372670100201570ustar00rootroot00000000000000[% list %] [%|loc%]list has been closed. Its subscribers have been deleted from the subscriber database. The list is no longer public on the site.[%END%]

[% IF auto_aliases %] [%|loc%]List aliases have been removed.[%END%] [% ELSE %] [%|loc%]You should remove list aliases manually[%END%] [% END %] sympa-6.1.24~dfsg/web_tt2/compose_mail.tt2000066400000000000000000000112331246372670100204570ustar00rootroot00000000000000
[% IF !subaction %]

[%|loc%]Sending a message to the list[%END%] [%|loc%]Help[%END%]


[% END %]
[% IF subaction == "html_news_letter" %]

[%|loc%]Sending an html page to the list [%END%] [%|loc%]Help[%END%]


[% END %]
[%|loc(user.email)%]From: %1[%END%]
[%|loc(mailto)%]To: %1[%END%]
[% IF subaction == "html_news_letter" %] [% END %]
[% SET counter = 0 %] [% SET stringto = '' %] [% FOREACH r = recipients %] [% IF counter == 0 %] [% stringto = r.value.local_to _ ' ' _ r.value.domain_to %] [% counter = 1 %] [% ELSE %] [% stringto = stringto _ ',' _ r.value.local_to _ ' ' _ r.value.domain_to %] [% END %] [% END %] [% IF request_topic %]
[%|loc%]This list is configured to require topic(s).[%END%]
[%|loc%]Please select one or more topic(s) that corresponds to your message:[%END%]
[% FOREACH t = available_topics %]
[% END %]
[% END %] [% IF !subaction %] [% balise_email = '['_'%'_' user.email '_'%'_']' %] [% balise_fingerprint = '['_'%'_' user.fingerprint '_'%'_']' %]
[% IF merge_feature %]
[%|loc%]Messages customization: use the template syntax:[%END%] TT2
[%|loc%]Below are some examples of TT2 parameters usable in messages.[%END%]
  • [% listname %][%|loc%]: the listname; always available.[%END%]
  • [% robot %][%|loc%]: the name of the host the list is intalled on; always available.[%END%]
  • [% user.email %][%|loc%]: the user email; always available.[%END%]
  • [% user.gecos %][%|loc%]: the user name associated to her email; always available.[%END%]
  • [% user.friendly_date %][%|loc%]: the - human readable - user's subscription date; always available.[%END%]
  • [% user.custom_attribute.title.value %][%|loc%]: can be anything you like; available if you defined a user custom attribute named "title" (see the list configuration, section "Miscellaneous").[%END%]
  • [% user.custom_attribute.name.value %][%|loc%]: can be anything you like; available if you defined a user custom attribute named "name".[%END%]
  • [% user.custom_attribute.organization.value %][%|loc%]: can be anything you like; available if you defined a user custom attribute named "organization".[%END%]
[% END %] [% END %] [% IF subaction == "html_news_letter" %]

[% END %]
sympa-6.1.24~dfsg/web_tt2/copy_template.tt2000066400000000000000000000065761246372670100206730ustar00rootroot00000000000000

[%|loc%]Copying template[%END%]


[%|loc%]Input template[%END%] [% template_name %]
[% SWITCH scope -%] [% CASE 'distrib' %] [%|loc%]default[%END%] [%|loc%](this template is the default included in the distribution)[%END%] [% CASE 'site' %] [%|loc%]site[%END%] [%|loc%](this template is the default used by all robots unless redefined for a specific robot)[%END%] [% CASE 'robot' %] [%|loc%]robot[%END%] [%|loc(robot)%](this template is the default for all lists of robot %1 unless it is redefined for a specific list)[%END%] [% CASE 'list' %] [%|loc%]list[%END -%] [%|loc(list,robot)%](this template is defined for list %1@%2)[%END%] [% CASE %] [% scope %] [% END %]
[%- IF tpl_lang == 'default' -%] [%|loc%]default[%END%] [%|loc%](This template is the default for all languages unless it is redefined for a specific language)[%END%] [%- ELSE -%] [%tpl_lang_title%] [%- END %]
[%|loc%]Output template[%END%]



[% IF scope == 'list' %] [% END %]


[% template_content %]
sympa-6.1.24~dfsg/web_tt2/create_list.tt2000066400000000000000000000010501246372670100203020ustar00rootroot00000000000000
[% IF status == 'open' %] [%|loc%]Your list is created.[%END%]
[%|loc%]You can configure it via the admin button beside.[%END%]
[% IF auto_aliases %] [%|loc%]Aliases have been installed.[%END%] [% END %] [% ELSE %] [%|loc%]Your list creation request is registered. You can now modify its configuration using the admin button but the list will be unusable until the listmaster validates it.[%END%] [% END %]
sympa-6.1.24~dfsg/web_tt2/create_list_request.tt2000066400000000000000000000127701246372670100220650ustar00rootroot00000000000000
[% IF get_which_owner.size > 0 %]

[%|loc%]Copy an existing list.[%END%]


[%|loc%]You can create a list, using an existing list as a template; the list configuration of the source list will be copied to create the new one. Note that neither list members, nor archives or shared documents are duplicated. The source lists you can use are limited to lists you own.[%END%] [%|loc%]After the list is created, you will be able to adjust it's configuration.[%END%]
[% END %]

[%|loc%]Create a list using a template[%END%] [%|loc%]Help[%END%]


[%|loc%]You can request a new list creation with the following form.You will have to choose a list template that will preset most of the list parameters according to the list usage you plan. [%END%] [%|loc%]After the list is created, you will be able to adjust it's configuration.[%END%]
[%|loc%]Help[%END%]
[%|loc%]Help[%END%] [%|loc%]Owner:[%END%] [% user.email %]
[%|loc%]Help[%END%]
    [% FOREACH template = list_list_tpl %] [% IF template.value.comment %]
  • [% PROCESS $template.value.comment %]
  • [% ELSE %]
  • [% END %] [% END %]

[%|loc%]Help[%END%]
[% SET single_topic = "other" %] [% FOREACH topic = list_of_topics %] [% IF loop.size > 1 || (topic.key && topic.key != "other") %] [% SET single_topic = "" %] [% LAST %] [% ELSIF topic.value.sub %] [% FOREACH subtopic = topic.value.sub %] [% SET single_topic = "" %] [% LAST %] [% END %] [% END %] [% END %] [% IF single_topic != "" %] [% ELSE %] [%|loc%]Help[%END%]
[% END %] [%|loc%]Help[%END%]

sympa-6.1.24~dfsg/web_tt2/css.tt2000066400000000000000000002141511246372670100166040ustar00rootroot00000000000000[% IF css == 'style.css' || custom_css || session.custom_css -%] [% IF custom_css || session.custom_css -%] [% FOREACH color IN ['color_0' 'color_1' 'color_2' 'color_3''color_4' 'color_5' 'color_6' 'color_7' 'color_8''color_9' 'color_10' 'color_11' 'color_12' 'color_13' 'color_14' 'color_15'] -%] [% IF session.$color %][% SET $color = session.$color %][% END -%] /* [% color %]: [% $color %] */ [% END -%] [% END -%] @media screen { } * { border: 0px solid [% color_1 %]; color: [% color_2 %]; font-family:"Trebuchet MS", Myriad, "Gill Sans", "Century Gothic", "Bitstream Vera Sans", verdana, lucida, arial, helvetica, sans-serif; padding: 0; margin: 0; } [%# This is a definition of font families to cover languages as much as possible rather than quality. -%] .LanguageNeutral, .LanguageNeutral option { font-family: arial, sans-serif; } html, body { height:100%; padding: 0; margin: 0; } body { font-size: 62.5%; background: [% color_9 %]; } ul { margin: 0 0 0 4em; } ol { margin: 0 0 0 4em; } dl { margin: 0 0 0 1em; } /* ########## Blocks, id and class ########## */ /*Top anchor*/ #TopAnchor { position: absolute; top: 0; } /*Global container*/ #Canvas { min-height: 95%; position:relative; width: 98%; margin: 0 0.8em -5em 0.8em; /* if IE <= 7 zoom:1; */ zoom: 1; } /* Menus Block*/ #Menus { float:left; width: 23.5em; border: 0px solid [% color_1 %]; margin-top:1em; padding-top: 0px; text-align: left; } /*Content Block*/ #Stretcher { margin-left:25em; margin-right: 0; margin-top:1em; text-align: left; } #Paint { vertical-align:top; text-align: center; } #Header { position:relative; background: none; border: none; } #ActionHeader { background: [% color_9 %]; border: 1px solid [% color_4 %]!important; font-size: 1em; text-align: left; vertical-align: top; /* padding-bottom: 0px; */ padding-top: 5px; padding-left: 5px; padding-right: 5px; padding-bottom: 5px; margin-bottom: 2em; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } #ActionHeader #MainMenuLinks { border: none; } #ActionHeader span.search_form { margin-bottom:1em; } #Clock { float: right; padding-right: 3px; vertical-align:top; } #Login { text-align: left; padding-left: 5px; vertical-align:top; } #Search input.textbox { width: 90%; } #logo { position:absolute; top:0.5em; left:4em; z-index:510; } #logo img { float: left; } /*Robot title*/ #Title { position:relative; text-align: center; font-size:1.8em; font-weight: bold; padding:1em 0; margin-top:0em; margin-left:5em; z-index:520; } #error { font-size: 1em; } select { background-color: [% color_13 %]; } select[multiple] { vertical-align: top; } .menuInactive2 { font-size: 1em; line-height:1.4em; text-decoration: none; font-weight:normal; } #FormLabel { font-size: 1.2em; font-weight: bold; } #ArcCalendar { padding: 1em 0; } ul#MainMenuLinks { border-bottom:1px solid [% color_8 %]; float:right; width:100%; margin-top:1em; margin-left: 0; } ul#MainMenuLinks li { list-style: none; display: inline; } ul#MainMenuLinks li a.MainMenuLinks { background: [% color_8 %]; color:[% color_9 %]; } ul#MainMenuLinks li a.MainMenuLinks:hover{ background: [% color_6 %]; color:[% color_8 %]; } #toggleMenu { float: right; padding-right: 3px; } td.adminmenu { background: [% color_9 %]; text-align: center; } .text_left { text-align: left; } .text_right { text-align: right!important; } td.text_right { text-align: right; } .text_center { text-align: center; } .text_justify { text-align: justify; } .without_padding { padding:0!important; } /* list_panel and list_admin_menu .tt2 block*/ .list_panel, #list_admin_menu { font-weight: bold; border: 1px solid [% color_12 %]; padding:0.5em; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } .list_panel { font-size: 1.2em; margin-bottom:0.5em } #list_admin_menu { font-size: 1.4em; } .list_panel ul, #list_admin_menu ul { list-style-type: none; margin:0 0.5em } .list_panel ul li, #list_admin_menu ul li { font-size:1em; line-height:1.4em; margin-top:0.3em; } .list_panel span { font-weight: normal!important; } .list_panel ul li a:hover, #list_admin_menu ul li a:hover { color:[% color_4 %]; text-decoration:none; } .list_panel ul li ul, #list_admin_menu ul li ul { font-size:0.9em; } .list_panel ul li ul li, #list_admin_menu ul li ul li{ font-size:0.9em; line-height:1.2em; margin-top:0em; } /* list type on the list creation page */ #list_type { margin-left: 5em; list-style-type: none; } #list_type dd { margin-left: 1em; } img { border: 0px; } /* Font */ .smaller { font-size: smaller; } .larger { font-size: larger; } span.center { text-align: center; } /* review.tt2 cels */ td.review_cels { text-align: center; padding:0 0.2em; } td.review_cels_mail { text-align: left; padding:0 0 0 0.2em; } .search_form, .edit_list_request_help { float: right; } .search_form { text-align: center; } #home_rss_news { float: right; } #home_rss_news input { background: [% color_4 %]; border: 1px solid [% color_4 %]; color: [% color_5 %]; padding:0.05em 1em; margin:0; text-decoration: none; font-size: 0.9em; font-weight: 600; letter-spacing:0.1em; /* if IE zoom:1;*/ zoom: 1; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } #home_rss_news input:hover { background: [% color_5 %]; color: [% color_4 %]; } #home_search_list { clear: both; } #home_search_list form fieldset { display: inline; vertical-align: top; } #home_container { padding: 10px; margin: 0; border: 1px dashed [% color_2 %]; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } /* Not yet implemented .news_container { background-color: [% color_3 %]; border: 1px solid [% color_2 %]; -moz-border-radius: 4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; padding: 5px; } */ .edit_list_request_enum { margin: 1em; padding-left: 1em; text-align: left; font-size: 1em; } /* menu button which allowed to manage your list*/ .list_admin { font-size: 1em; } /*colors*/ .color0 { background-color: [% color_0 %]; } [%# for compatibility: use .color_light. #%] .color4 { background-color: [% color_9 %]; } [%# for comatibility: use .bg_color_error. #%] .color7 { background-color: [% color_7 %]; } .color_light { background-color: [% color_9 %]; } .color_dark { color: [% color_9 %]; } .bg_color_dark { background-color: [% color_9 %]; } .color_bg { color: [% color_13 %]; } .bg_color_bg { background-color: [% color_13 %]; } .bg_color_error { background-color: [% color_7 %]; } .list_menu_links { font-weight: bold; font-size: 1.2em; } a.list_menu_links:hover { color:[% color_4 %]; text-decoration:none; } .mailing_lists_menu { padding: 10px; margin: 5px 25px 5px 25px; float: left; font-size: 1.1em; text-align: left; } ul.no_style { list-style: none; } .align_top { vertical-align: top; } /* explanation block of the template "ls_template.tt2" */ #template_editor { margin-top: 10px; } #template_editor ul { padding-left: 5px; } #template_editor li { margin: 2px; } /*block of the color table*/ #color_table { border: 1px dotted; padding-top: 3px; padding-left: 3px; padding-bottom: 4px; } #color_table th { border: solid 1px; text-align: center; padding: 1px; } #color_table td { border: 1px solid [% color_12 %]; padding-left: 10px; } #color_table .separator { border: none; padding: 0px; background-color: [% color_5 %]; } #color_table tr#color_table_title th { background-color: [% color_9 %]; } /* ul of the "help.tt2" template */ #help { padding-left: 10px; padding-top: 5px; margin-bottom: 20px; } #help li { padding: 1px; } /** menu "ul" "li" style **/ #help_editfile, #blaklist { margin: 1em; } #help_editfile ul, #blacklist ul { padding-left: 1em; margin-top: 0.5em; margin-bottom: 0.5em; } /*bounce level color*/ .bounce_level2 { background-color: #F00; } .bounce_level1 { background-color: #FF8C00; } /* set_pending_list_request.tt2 template*/ ul#set_pending_radio { display: inline; list-style-type: none; } ul#set_pending_radio li { display: inline; margin-right: 1em; margin-left: 1em; } form { line-height: 1.8em; } form input, form textarea, form select { margin: 4px 0px; padding: 1px; font-size: 1em; } form#logs_form label, form.bold_label label { font-weight: bold; } form#bold_label input { margin-left: 5px; margin-top: 5px; } form input[type="radio"], form input[type="checkbox"] { vertical-align: middle; border:none; } #global_mailing_lists { text-align: center; } #show_cert { border: 1px solid; } #show_cert ul { list-style-type: none; padding-top: 5px; margin-bottom: 5px; } #show_cert ul li { line-height: 1em; padding-left: 3px; } #cp_template { border: 1px solid; padding: 3px; background-color: [% color_0 %]; width: 410px; } #cp_template fieldset { display: inline; border: 1px solid; vertical-align: top; margin-left: 3px; } #cp_template legend, #cp_template label { padding-left: 3px; } /* pre */ pre.code { font-family: monospace; } #Footer { width:98%; height:1.5em; line-height: 1.5em; position:relative; margin: 0 0.8em; background: [% color_12 %]; font-size: 1.1em; text-align: center; padding-top:0.5em; padding-bottom:0.5em; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius: 10px; -webkit-border-top-right-radius: 10px; -webkit-border-bottom-left-radius:0px; -webkit-border-bottom-right-radius:0px; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; border-radius:10px 10px 0 0; } #Footer a { color:[% color_5 %]; font-family: serif!important; font-weight:bold; font-variant: small-caps!important; } #Footer img { margin:0 0.5em 0 0; height:20px; width:20px; vertical-align:middle; } .clearfooter { height: 3em; clear: both; } #Identity { text-align: left; font-size: 1.2em; font-weight: bold; overflow: hidden; } .Help { display: none; position: absolute; border: 1px dotted [% color_2 %]; z-index: 1000; background: [% color_13 %]; } #rows_nb { font-size: 0.8em; } #page_size { float: right; margin-bottom: 20px; padding-bottom: 20px; } #setlang { background: [% color_5 %]; padding: 5px; text-align: center; font-weight: bold; margin-bottom:2em!important; border:1px solid [% color_8 %]; border-width: 0 1px 1px 0; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } #setlang form { letter-spacing:0.1em; padding: 0.5em 0; border:1px solid [% color_12 %]; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } .MenuBlock { background: [% color_5 %]; padding:5px; text-align: left; margin-bottom: 1em; border:1px solid [% color_8 %]; border-width: 0 1px 1px 0; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } .MenuBlock p strong { font-size: 1.4em; } .MenuBlock h1 { background: [% color_8 %]; color:[% color_5 %]; text-align: center; font-size: 1.2em; letter-spacing:0.1em; padding:0.1em 0; margin:0 0 1em 0; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius: 10px; -webkit-border-top-right-radius: 10px; -webkit-border-bottom-left-radius:0px; -webkit-border-bottom-right-radius:0px; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; } .MenuBlock strong { text-indent: 0px; font-size: 1.2em; padding-bottom:10px; } .ContentBlock { position: relative; font-size: 1.1em; margin: 0; background: [% color_5 %]; padding: 10px; text-align: left; height: 100%; border:1px solid [% color_8 %]; border-width: 0 1px 1px 0; border-top:1px solid [% color_5 %]; z-index:310; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } .ContentBlock p { padding: 10px; font-size: 1.1em; } .ContentBlock > * { padding: 0px; border-bottom: 0px dotted [% color_1 %]; text-align: left; } .ContentBlock * a { border-bottom: 1px dotted [% color_8 %]; text-align: left; } .ContentBlock * a:hover { text-decoration:none; } .ContentBlock * a img { border: 0px; } a img.Pictures { height:30px; } a:hover img.Pictures { height: 80px; } .sub_pictures { margin: 2em; } /* pictures block on the suboptions form*/ #pictures_block { text-align: center; height: 120px ; } #pictures_block div { margin: 1em; float: left; } #large_picture { height: 80px; } #pictures_block #large img{ height: 80px; } #pictures_block #small img{ height: 30px; margin-top: 50px; } .navBar { position:relative; text-align: center; margin: 1em 0 0 0; font-size: 1.2em; /* if IE <= 6 zoom:1; */ zoom: 1; } .navBar .MainMenuLinks { padding:2px 5px 1px 5px!important; position: relative; top: -1px; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius: 10px; -webkit-border-top-right-radius: 10px; -webkit-border-bottom-left-radius:0px; -webkit-border-bottom-right-radius:0px; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; border-radius:10px 10px 0 0; } .displayNone { display: none; height:0px; width:0px; } .printNone {} .title { position: absolute; top: 60px; font-size: 1.6em; text-indent: 30px; } .customMenu { text-indent: 0px; } a.dingbat { text-align: center; } .dingbat { text-indent: 0px; text-align: center; font-weight: 100; } .button { background:[% color_9 %] !important; border: 3px solid; padding: 0px 2px 0px 2px; margin:2px; border-top-color: [% color_6 %]; border-right-color: [% color_1 %]; border-bottom-color: [% color_1 %]; border-left-color: [% color_6 %]; } .button:hover { background: [% color_9 %]; border-top-color: [% color_1 %]; border-right-color: [% color_6 %]; border-bottom-color: [% color_6 %]; border-left-color: [% color_1 %]; } .smalltext { font-size: 0.8em; } .smalltext a { font-size: 0.8em; } .smallblacktext { color: [% color_12 %]; font-size: 1.2em; } .mediumtext { font-size: 1.2em; } .largetext { font-size: 1.6em; } /* ########## Elements ########## */ .MainMenuLinks { border: 1px solid [% color_8 %]; background: [% color_9 %]; color:[% color_8 %] ; text-decoration: none; padding: 0 5px!important; margin: 0; vertical-align: baseline; cursor:pointer; -moz-border-radius:10px; -KHTML-border-radius:10px; -webkit-border-radius:8px; -icab-border-radius:10px; border-radius:10px; } a.MainMenuLinks { border: 1px solid [% color_8 %]; background: [% color_8 %]; color:[% color_9 %]; text-decoration: none; padding: 0px 5px 0px 5px; /* if IE <= 7 zoom:1; */ zoom: 1; } a.MainMenuLinks:hover { border: 1px solid [% color_6 %]; border-bottom:[% color_8 %]; background: [% color_6 %]!important; color: [% color_8 %]!important; text-decoration: none; padding: 0px 5px 0px 5px; } a.MainMenuLinksCurrentPage { border: 1px solid [% color_8 %]; background: [% color_9 %]; border-bottom: 1px solid [% color_9 %]; color: [% color_8 %]; text-decoration: none; padding: 3px 5px 1px 5px; position: relative; top: -1px; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius: 10px; -webkit-border-top-right-radius: 10px; -webkit-border-bottom-left-radius:0px; -webkit-border-bottom-right-radius:0px; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; border-radius:10px 10px 0 0; /* if IE <= 7 zoom:1; */ zoom: 1; } a.MainMenuLinksCurrentPage:hover { border: 1px solid [% color_8 %]; background: [% color_9 %]; border-bottom: 1px solid [% color_9 %]; color: [% color_8 %]; text-decoration: none; } input.MainMenuLinks { margin-top: 4px; } input.MainMenuLinks:hover{ background: [% color_8 %]!important; color: [% color_9 %]!important; border: 1px solid [% color_8 %]; } a.actionMenuLinks { border: 1px solid [% color_8 %]; background: [% color_9 %]; color:[% color_8 %] ; text-decoration: none; padding: 1px 5px !important; margin: 0 0.5em 0 0; cursor:pointer; -moz-border-radius:10px; -KHTML-border-radius:10px; -webkit-border-radius:8px; -icab-border-radius:10px; border-radius:10px; white-space: nowrap; } a.actionMenuLinks:hover { background: [% color_8 %]; color: [% color_9 %]; text-decoration: none; } a.actionMenuLinksCurrentPage { border: 1px solid [% color_2 %]; background: [% color_6 %]; color: [% color_8 %]; text-decoration: none; padding: 0px 10px 0px 10px; white-space: nowrap; } a.actionMenuLinksCurrentPage:hover{ border: 1px solid [% color_2 %]; background: [% color_6 %]; text-decoration: none; padding: 0px 10px 0px 10px; } a.actionMenuLinksInactive { border-bottom: 1px solid [% color_2 %]; border-right: 1px solid [% color_10 %]; border-top: 1px solid [% color_10 %]; border-left: 1px solid [% color_10 %]; color: [% color_10 %]; text-decoration: none; padding: 0px 10px 0px 10px; white-space: nowrap; } a.actionMenuLinksInactive:hover { border-bottom: 1px solid [% color_2 %]; border-right: 1px solid [% color_10 %]; border-top: 1px solid [% color_10 %]; border-left: 1px solid [% color_10 %]; color: [% color_10 %]; text-decoration: none; padding: 0px 10px 0px 10px; } #actionnuLinksInactive { border: 0px solid [% color_2 %]; text-decoration: none; padding: 0px 0px 0px 3px; } /* a.ArcMenuLinks { border: 1px solid [% color_2 %]; text-decoration: none; padding: 0px 10px 0px 10px; } */ a.ArcMenuLinks, a.ArcMenuLinks:visited, a.ArcMenuLinks:link { border-bottom: 2px solid [% color_8 %]; border-right: 2px solid [% color_8 %]; border-top: 1px solid [% color_8 %]; border-left: 1px solid [% color_8 %]; font-weight:bold; color: [% color_8 %]; background-color:[% color_13 %]; text-decoration: none; padding: 0px 10px 0px 10px; -moz-border-radius:5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } a.ArcMenuLinks:hover { background: none; color: [% color_8 %]; } a.ArcMenuLinks:active { border: 1px solid [% color_2 %]; background: [% color_6 %]; color: [% color_8 %]; text-decoration: none; padding: 0px 10px 0px 10px; } a.ArcMenuLinksCurrentPage { border: 1px solid [% color_2 %]; background: [% color_6 %]; color: [% color_8 %]; text-decoration: none; padding: 0px 10px 0px 10px; -moz-border-radius:5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } a.ArcMenuLinksCurrentPage:hover { border: 1px solid [% color_2 %]; background: [% color_6 %]; text-decoration: none; padding: 0px 10px 0px 10px; } a.ArcMenuLinksInactive, a.ArcMenuLinksInactive:hover { border-bottom: 2px solid [% color_2 %]; border-right: 2px solid [% color_2 %]; border-top: 1px solid [% color_2 %]; border-left: 1px solid [% color_2 %]; color: [% color_2 %]; text-decoration: none; padding: 0px 10px 0px 10px; -moz-border-radius:5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } a.ArcMenuLinksSortActive, a.ArcMenuLinksSortActive:hover, a.ArcMenuLinksSortInactive:hover { border: 1px solid [% color_4 %]; background: [% color_5 %]; color: [% color_4 %]; text-decoration: none; padding: 0px 10px 0px 10px; -moz-border-radius:5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } a.ArcMenuLinksSortInactive { border: 1px solid [% color_4 %]; background: [% color_4 %]; color: [% color_5 %]; text-decoration: none; padding: 0px 10px 0px 10px; -moz-border-radius:5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } #ArcMenuLinksInactive { border: 0px solid [% color_2 %]; text-decoration: none; padding: 0px 0px 0px 3px; } /* Not yet implemented. .ArcActions { float:right; margin-top: -2.9em; margin-right:0.5em } .ArcActions form { border: none!important; } */ .block { border: 1px dotted [% color_2 %]; padding: 7px; margin-top:0; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } .block form, .block .form { border:1px solid [% color_11 %]; margin:0.5em 0; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } .block form fieldset { padding:0.5em; } .block .form { padding: 0.5em; } form.inline { display: inline; border-width: 0 !important; margin: 0; padding: 0; } form.inline .MainMenuLinks { margin-top: 1px; } .block blockquote { margin: 16px 40px; } h1.block, h2.block, h3.block, h4.block, h5.block, h6.block { text-align: center; background: [% color_9 %]; } .block span.default { color: [% color_1 %]; padding-left: 2em; font-weight: bold; } h7 { font-size: 0.8em; font-style: italic; } h7 strong { font-size: 1em; } .listTitle { position:relative; height: 1%; width: 99.5%; text-align: center; border: solid [% color_4 %]; border-width: 0 1px 1px 0; padding: 2px 0px; background: [% color_13 %]; font-size:1.3em; margin:0 auto; -moz-border-radius:10px; -KHTML-border-radius:10px; -webkit-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } .listTitle a, .listTitle a:hover { font-weight:bold; letter-spacing: 0.1em; text-decoration:none; background:none!important; border:none!important; } .listTitle a { color: [% color_2 %]; } .listTitle a:hover { color: [% color_8 %]; } .listTitle.description { font-size:1em; font-weight:bold; margin-top:0em; margin-bottom:1.3em; background:none!important; border:none; } .listTitle.description span { color:[% color_4 %]; } #ErrorBlock { margin:0; padding:0; position:absolute; left:0; top:0; z-index:600; background: [% color_4 %]; opacity:0.4; width:100%; height:100%; } #ErrorMsg { position:absolute; width:60%; margin:20% 19%; z-index:610!important; border: 5px ridge [% color_12 %]; font-family:serif; font-size: 1.6em; font-weight: bold; text-align:center; color:[% color_5 %]!important; background-color: [% color_12 %]; padding:1em 0.5em; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; } /* Not yet implemented. .formError { float:right; background: [% color_7 %]; z-index:610!important; border: 2px ridge [% color_12 %]; padding:1em 0.5em; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; display:none; } */ #ErrorMsg a { color: [% color_4 %]!important; } #ErrorMsg a:hover { color: [% color_5 %]!important; } #ErrorMsg .MainMenuLinks { color:[% color_4 %]; background-color:[% color_5 %]; border:2px outset [% color_4 %]; padding: 0 1.8em!important; margin-top:1em; font-size: 0.7em; font-weight:bold; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; } #ErrorMsg .MainMenuLinks:hover { background-color:[% color_4 %]!important; border:2px inset [% color_5 %]; color:[% color_5 %]!important; } #noticeMsg { position: absolute; top:20%;left:25%;right:25%; z-index: 700!important; border: 2px ridge [% color_12 %]; font-family:serif; font-size: 1.6em; font-weight: bold; text-align:center; color:[% color_4 %]!important; background-color: [% color_13 %]; padding:1em 0.5em; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; } #noticeMsg .MainMenuLinks { color:[% color_12 %]; background-color:[% color_5 %]; border:2px outset [% color_12 %]; padding: 0 2em!important; margin-top:1em; font-size: 0.8em; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; } #noticeMsg .MainMenuLinks:hover { background-color:[% color_12 %]!important; border:2px inset [% color_5 %]; color:[% color_5 %]!important; } /* Not yet implemented. div.messageContent { text-align: left; color:[% color_5 %]!important; font-size: 0.8em; font-weight: normal; height: 400px; overflow-y: scroll; } div.hiddenform { position:absolute; width:40%; background: [% color_3 %]; z-index:610!important; border: 2px ridge [% color_12 %]; padding:1em 0.5em; -moz-border-radius:20px; -webkit-border-radius:20px; -KHTML-border-radius:20px; -icab-border-radius:20px; border-radius:20px; display:none; } */ p.listenum { margin: 0 0 0.5em 1.5em; padding:0.3em 0 0 0.5em!important; font-size:0.9em; } ul.listenum { list-style: none; margin: 0 0 0 1em } ul.listenum li { padding: 4px; } ul.listenum li a.actionMenuLinks { background: [% color_8 %]; color: [% color_9 %]; font-size:1.1em; padding:0em 0.4em!important; } ul.listenum li a.actionMenuLinks:hover { background: [% color_9 %]; color:[% color_8 %]; border: 1px solid [% color_9 %]; padding:0em 0.4em!important; } div.admin_cmd { float:left; margin: 0em 0.3em 0.1em 0; padding: 0 0.3em; background: [% color_4 %]; border: 1px solid [% color_4 %]; -moz-border-radius:6px; -webkit-border-radius:6px; -KHTML-border-radius:6px; -icab-border-radius:6px; border-radius:6px; } div.admin_cmd a { color: [% color_5 %]; font-size:0.7em!important; font-weight:bold; border: none; } div.admin_cmd a:hover { color: [% color_4 %]; border: none; } div.admin_cmd:hover { background: [% color_5 %]; } ul li.menuLinksCurrentPage a { color:[% color_4 %]!important; } ul.calendar { margin-left:0!important; padding-left:0!important; } ul.calendar a { font-size: 1em; } ul.calendar li { list-style: none; display: inline; padding: 0px 1px 0px 2px; line-height: 2em; font-size: 1em; text-decoration: none; border: 1px solid [% color_12 %]; -moz-border-radius:4px; -webkit-border-radius:4px; -KHTML-border-radius:4px; -icab-border-radius:4px; border-radius:4px; } ul li.calendarLinks { border: 1px solid [% color_4 %]; background: [% color_5 %]; } ul li.calendarLinks a { color: [% color_4 %]; border: none; } ul li.calendarLinks:hover { background: [% color_4 %]; border: 1px solid [% color_4 %]; } ul li.calendarLinks a:hover { color: [% color_5 %]!important; } ul li.calendarLinksCurrentPage, ul li.calendarLinksCurrentPage:hover { border: 1px solid [% color_4 %]!important; } ul li.calendarLinksCurrentPage, ul li.calendarLinksCurrentPage a { background: [% color_4 %]; color: [% color_5 %]!important; border-bottom:1px solid [% color_4 %]!important; } ul li.calendarLinksCurrentPage:hover, ul li.calendarLinksCurrentPage a:hover { background: [% color_5 %]; color: [% color_4 %]!important; border-bottom:1px solid [% color_4 %]!important; } ul li.calendarLinksInactive { background: #eee; border: 1px solid #888; color: #888; } ul li.calendarYear { background: [% color_12 %]; border: 1px solid [% color_12 %]; color: [% color_5 %]; } a img { border: 0px; } a { text-decoration: none; } a:link { text-decoration: none; } a:visited { text-decoration: none; } a:hover { color: [% color_8 %]; text-decoration:underline; background:none; } abbr, acronym, .info { border-bottom: 1px dotted [% color_8 %]; cursor: help; } h1 a:hover { text-indent: 30px; font-size: 1.6em; padding-bottom:10px; } p a:hover { text-indent: 30px; } p a:first-letter { font-size: 1.6em; font-weight: bold; } p { text-indent: 0px; } p.spacer { clear: both; } code { font-weight: bold; } .retraitita { background-color: [% color_13 %]; border: 1px dashed [% color_1 %]; padding: 2px 2px 2px 2px; margin: 5px 5px 5px 5px; } span.retraitita { background-color: [% color_13 %]; border: 1px dashed [% color_1 %]; padding: 5px 10px 5px 10px; margin: 5px 5px 5px 5px; display: block; } h1 { text-indent: 0px; margin-top: 10px; font-size: 1.8em; } h2 { color: [% color_4 %]; text-indent: 0px; margin-top: 10px; font-size: 1.4em; } h3 { color: [% color_12 %]; text-indent: 10px; margin-top: 10px; font-size: 1.2em; } h4 { color: [% color_8 %]; text-indent: 20px; margin-top: 10px; font-size: 1.2em; } h5 { color: [% color_8 %]; text-indent: 30px; margin-top: 15px; margin-bottom: 2px; font-size: 1em; } h6 { color: [% color_8 %]; text-indent: 40px; margin-top: 10px; font-size: 1em; } /* input */ a.input { background: [% color_12 %]; border: 1px solid [% color_12 %]; color: [% color_13 %]; padding: 0.2em 0.3em; margin-left:0.2em; text-decoration: none; vertical-align: middle; font-size: 0.9em; font-weight:bold; font-variant:small-caps; /* if IE zoom:1;*/ zoom: 1; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } a.input:hover { background: [% color_13 %]; color: [% color_12 %]; } h2 a.input { font-size: 0.7em; } input { background: [% color_13 %]; border: 1px solid [% color_2 %]; padding: 0px; } input[type="radio"], input[type="checkbox"] { margin-bottom: 0; } textarea { background: [% color_13 %]; border: 1px solid [% color_2 %]; padding: 3px; } textarea:hover { background: [% color_13 %]; } textarea.desc { width: 75%; max-width: 500px; } textarea.textbox { background: [% color_6 %]; border: 1px solid [% color_2 %]; padding: 3px; } textarea.textbox:hover { background: [% color_6 %]; } input.textbox { background: [% color_6 %]; border: 1px solid [% color_2 %]; padding: 3px; } input.textbox:hover { background: [% color_6 %]; } input.button { background:[% color_9 %]!important; border: 1px solid; padding: 0px 2px 0px 2px; margin:2px; border-top-color: [% color_6 %]; border-right-color: [% color_1 %]; border-bottom-color: [% color_1 %]; border-left-color: [% color_6 %]; } input.button:hover { background: [% color_9 %]; border-top-color: [% color_1 %]; border-right-color: [% color_6 %]; border-bottom-color: [% color_6 %]; border-left-color: [% color_1 %]; } /* Tables */ /* default table style */ table.table_style, table.listOfItems, table.ls_template { border: 1px solid; border-width: 0 2px 2px 0; background-color:[% color_14 %]; width: auto; margin: 10px; font-size: 1em; -moz-border-radius: 5px; -webkit-border-radius:5px; -KHTML-border-radius:5px; -icab-border-radius:5px; border-radius:5px; } table.table_style th { font-weight: bold; background-color: [% color_9 %] } table.listOfItems { overflow: scroll; } table.listOfItems td { font-size: smaller; padding: 5px; border:1px solid [% color_12 %]; } table.listOfItems tr { border:1px solid [% color_12 %]; } table.listOfItems th { background-color: [% color_12 %]; color:[% color_5 %]; padding:0 2px; border:solid 1px; text-align: center; white-space: nowrap; } table.listOfItems th a { color:[% color_5 %]; } table.listOfItems th a:hover { color: [% color_4 %]; } table.listOfItems th.sortby { background-color: [% color_5 %]; color:[% color_12 %]; } table.pending_lists { border: 1px solid; width: auto; margin: 10px; } table.pending_lists th { padding: 3px; border: 1px solid; } table.pending_lists th.title_pending { padding: 10px; } table.ls_template th { padding: 3px; border: solid 1px; background: [% color_9 %]; text-align: center; } /*pref.tt2 template - maybe to delete*/ table.preferences { border: none; border-spacing: 0.5em; font-size: 1em; } table.preferences th { font-weight: normal; white-space: nowrap; } #table_container { overflow: auto; } /* test relookage */ #bandeau_top { position: relative; width:98%; min-height:2.5em; margin: 0 0.8em; background: [% color_12 %]; clear:both; -moz-border-radius: 0 0 10px 10px; -webkit-border-top-left-radius:0; -webkit-border-top-right-radius:0; -webkit-border-bottom-left-radius:10px; -webkit-border-bottom-right-radius:10px; -KHTML-border-radius:0 0 10px 10px; -icab-border-radius:0 0 10px 10px; border-radius:0 0 10px 10px; z-index:2000; } #bandeau_top #Identity { float:left; width:30%; text-align: left; font-family:serif!important; font-size: 1em; font-weight: bold; color:[% color_5 %]!important; overflow: hidden; margin-left:1em; line-height: 1.3em; } #bandeau_top #Identity_not_connect { width:99%; text-align: right; font-size: 1em; font-weight: bold; font-family:serif!important; color:[% color_5 %]!important; overflow: hidden; margin-left:1em; clear:none; } #bandeau_top #Identity_not_connect label { color:[% color_5 %]!important; font-family:serif!important; line-height: 2.6em; } #bandeau_top #Identity span { font-size: 0.9em; font-weight: normal; color:[% color_5 %]!important; } #bandeau_top .prefs { float:left; font-size: 1.1em!important; font-family:serif!important; text-align:center; font-weight: bold; width:33%; /* margin-top:0.3em; */ line-height: 2.6em; } #bandeau_top a { font-weight: bold; font-family:serif!important; color:[% color_5 %]!important; } #bandeau_top a:hover { background:none; border: none; color:[% color_4 %]!important; text-decoration:none; } #bandeau_top .login { float:right; text-align:right!important; font-family:serif!important; color:[% color_5 %]!important; width:33%; } [%# * Two lines, 1.4em in their height, will be shifted up. * The height of form on right side will be solved by the height of label * defined above, which is (2.8em) minus (approx. 0.25em), approximately 2.6em. -%] #bandeau_top .remember { float:left; font-size: 0.9em; line-height: 1.4em; font-family:serif!important; text-align:left; font-weight: bold; color:[% color_5 %]!important; width:25%; } #bandeau_top #Identity_not_connect .remember { margin-top:-2.8em; } #bandeau_top .remember a { margin-right:1em; } #bandeau_top .MainMenuLinks { font-size:0.9em; font-weight:600; letter-spacing:0.1em; color:[% color_4 %]; background-color:[% color_5 %]; border:1px solid [% color_4 %]; padding: 0 5px ; margin-top:0.5em; margin-right:0.5em; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } #bandeau_top .MainMenuLinks:hover { background-color:[% color_4 %]!important; border:1px solid [% color_4 %]; color:[% color_5 %]!important; } /* A modified version of A List Apart - http://alistapart.com/articles/slidingdoors2/ */ #nav { position:relative; float:left; width:100%; margin:-1.6em 0 0 0.5em; clear:right; z-index:350; } #nav ul { height:1.4em; margin:0; padding: 0; list-style:none; } #nav ul li { float:left; min-width:0.7em; display:inline; margin:0 0 0.2em 0.5em; padding:0.1em 0.2em; border:1px solid [% color_8 %]; border-width: 0 1px 0 0; border-bottom:1px solid [% color_5 %]; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius:9px; -webkit-border-top-right-radius:9px; -webkit-border-bottom-left-radius:0; -webkit-border-bottom-right-radius:0; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; border-radius:10px 10px 0 0; } #nav li.MainMenuLinks { font-weight:normal; background-color:[% color_6 %]; border-bottom:1px solid [% color_6 %]; padding-top:0.2em!important; } #nav li.MainMenuLinks a { color:[% color_8 %]; } #nav li.MainMenuLinks a:hover { text-decoration:none!important; color:[% color_5 %]; } #nav li.MainMenuLinks:hover { background-color: [% color_4 %]; border:1px solid [% color_8 %]; border-width: 0 1px 0 0; border-bottom:1px solid [% color_4 %]; z-index:300; } #nav li.MainMenuLinksCurrentPage { font-weight:normal; background-color: [% color_5 %]; } #nav li.MainMenuLinksCurrentPage a{ color:[% color_8 %]; padding:0.1em 0.4em; border:none; } #nav li.MainMenuLinksCurrentPage a:hover{ text-decoration:none!important; color:[% color_8 %]; } #nav li.MainMenuLinksCurrentPage:hover { border-bottom:1px solid [% color_5 %]; border-right: 1px solid [% color_8 %]; z-index:300; } #nav li.nolink{ background-color: [% color_5 %]; color:[% color_12 %]; min-width:0.9em; padding:0.2em 0.4em 0em 0.4em; -moz-border-radius:10px 10px 0 0; -webkit-border-top-left-radius:9px; -webkit-border-top-right-radius:9px; -webkit-border-bottom-left-radius:0; -webkit-border-bottom-right-radius:0; -KHTML-border-radius:10px 10px 0 0; -icab-border-radius:10px 10px 0 0; border-radius:10px 10px 0 0; } #nav > ul a {width:auto;} /* Commented Backslash Hack hides rule from IE5-Mac \*/ #nav a {float:none;} /* End IE5-Mac hack */ /* not for IE5.x or IE6 */ #subnav { background: none; position:relative; float:left; width:99.9%; margin:0 0 0 0; z-index:400!important; } #subnav ul { font-size:0.9em; margin:1em 0 0 2em; padding: 0; list-style:none; } #subnav ul li { float:left; display:inline; margin:0 0.5em 0.2em 0.2em; background-color:[% color_5 %]; -moz-border-radius:10px; -KTML-border-radius:10px; -webkit-border-radius:8px; -icab-border-radius:10px; border-radius:10px; } #subnav ul li.MainMenuLinks{ background-color:[% color_9 %]; padding:0 1em!important; border:1px outset [% color_8 %]; } #subnav ul li.MainMenuLinks a { text-decoration:none; color:[% color_2 %]; } #subnav ul li.MainMenuLinks a:hover, #subnav ul li.MainMenuLinks:hover { color:[% color_5 %]; } #subnav ul li.MainMenuLinks:hover{ text-decoration:none; background-color:[% color_4 %]!important; border:1px outset [% color_4 %]; } #subnav ul li.MainMenuLinksCurrentPage{ background-color:[% color_5 %]; padding:0 1em!important; color: [% color_4 %]; border:1px inset [% color_8 %]; } #subnav ul li.MainMenuLinksCurrentPage a { color: [% color_4 %]; text-decoration:none; } .columns { -moz-column-width:40%; -moz-column-count:2; -moz-column-gap:1em; -moz-column-rule: 1px solid black; -webkit-column-width:40%; -webkit-column-count:2; -webkit-column-gap:1em; -webkit-column-rule: 1px solid black; -icab-column-width:40%; -icab-column-count:2; -icab-column-gap:1em; -icab-column-rule: 1px solid black; column-width:40%; column-count:2; column-gap:1em; column-rule: 1px solid black; } /* Not yet implemented. .columns div { -moz-column-break-inside: avoid!important; -webkit-column-break-inside: avoid!important; column-break-inside: avoid!important; } */ .menu_search { width:100%; border: 1px solid [% color_8 %]; border-width: 0 1px 1px 0; background:[% color_5 %]; padding:0.5em 0 0.5em 0; text-align:center; } .menu_search input { padding: 0 1px; } .menu_search input.MainMenuLinks { margin-left:0; border: 1px solid [% color_4 %]; background: [% color_5 %]; color:[% color_4 %]; padding:0!important; font-size:0.9em; font-weight:600; letter-spacing:0em; -moz-border-radius:10px; -webkit-border-radius:10px; -KHTML-border-radius:10px; -icab-border-radius:10px; border-radius:10px; } .menu_search input.MainMenuLinks:hover { border: 1px solid [% color_4 %]; background: [% color_4 %]!important; color:[% color_5 %]!important; } span.bottom_page { font-size: 0.8em; font-weight:bold; } .top { float:right; margin:0.5em 1em 1.5em 1em; } .noborder { border: none!important; } /********************/ /* For the calendar */ /********************/ .divCal { position: absolute; border: 1px [% color_9 %]; background: [% color_9 %]; z-index: 500; } .divCal a{ text-decoration: none; width: 100%; } .divCal table { font-size: 12px; font-family: Tahoma, Geneva, Verdana, sans-serif; text-align: center; margin: 0px; width: 140px; } .divCal td { margin: 0px; border: 1px solid [% color_8 %]; } .divCal .zoneTitre { font-size: 12px; font-family: Tahoma, Geneva, Verdana, sans-serif; text-align: center; margin: 0px; background: [% color_9 %]; } .divCal .zoneNav { font-size: 10px; font-family: Tahoma, Geneva, Verdana, sans-serif; text-align: center; margin: 0px; cursor: pointer; } .divCal .zoneMois { font-family: Tahoma, Geneva, Verdana, sans-serif; width: 70px; margin: 0px; } .divCal .zoneAnnee { font-size: 10px; font-weight: bold; text-align: right; margin: 0px; width: 100%; } .divCal .nSemaine { font-family: Tahoma, Geneva, Verdana, sans-serif; width: 30px; margin: 0px; color: [% color_4 %]; } .divCal div{ margin: 0px; } .divCal .td { color: [% color_5 %]; } /*par defaut*/ .divCal .tdx .enWeekend { background: [% color_8 %]; } .divCal .tdx .enFeriee { background: [% color_8 %]; } .divCal .tdx .enMois { color: black; font-weight: bold; } .divCal .tdx .aujourdhui { border: 2px solid [% color_7 %]; } .divCal .tdxNow { color: black; font-weight: bold; } .divCal .tdxNow:hover { background: [% color_4 %]; } .divCal .tdx:hover { background: [% color_3 %]; } .divCal .tdx:hover .enWeekend { background: [% color_6 %]; } .divCal .tdx:hover .enMois { color: [% color_2 %]; font-weight: bold; background: [% color_6 %]; } /*pour firefox */ /* Line height not to be overridden by locale-specific styles. */ table, #MainMenuLinks, #nav, #subnav, .input, .listTitle, .listTitle.description, .MainMenuLinks, .MainMenuLinksCurrentPage, .actionMenuLinks, .actionMenuLinksCurrentPage, .actionMenuLinksInactive, .ArcMenuLinks, .ArcMenuLinksCurrentPage, .ArcMenuLinksInactive, .ArcMenuLinksSortActive, .ArcMenuLinksSortInactive { line-height: normal; } [% END %] sympa-6.1.24~dfsg/web_tt2/css_ie.tt2000066400000000000000000000034571246372670100172660ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/d_control.tt2000066400000000000000000000050301246372670100177710ustar00rootroot00000000000000 [%|loc%]Up to higher level directory[%END%]

[%|loc(visible_path)%]Access control for the document %1[%END%] [%|loc%]Help[%END%]


[%|loc(owner)%]Owner: %1[%END%]
[%|loc(doc_date)%]Last update: %1[%END%]
[%|loc(doc_title)%]Description: %1[%END%]





[% IF set_owner %]


[% END %] sympa-6.1.24~dfsg/web_tt2/d_editfile.tt2000066400000000000000000000055661246372670100201140ustar00rootroot00000000000000

[% IF url %]

[%|loc(visible_path)%]Edition of the bookmark %1[%END%] [%|loc%]Help[%END%]

[% ELSE %]

[%|loc(visible_path)%]Edition of the file %1[%END%] [%|loc%]Help[%END%]

[% END %] [%|loc(doc_owner)%]Owner: %1[%END%]
[%|loc(doc_date)%]Last update: %1[%END%]
[%|loc(desc)%]Description: %1[%END%]



[% IF !directory %]
[% IF url %]
[% ELSE %]    [% END %]   [% IF url %] [% ELSE %] [% END %]
[% END %]

[% IF !url %] [% IF textfile %]


[% END %] [% END %] sympa-6.1.24~dfsg/web_tt2/d_install_shared.tt2000066400000000000000000000015251246372670100213120ustar00rootroot00000000000000

[% FOREACH f = list_file %] [% f %]

[% END %]
[% FOREACH elt = id %] [% END %]
sympa-6.1.24~dfsg/web_tt2/d_properties.tt2000066400000000000000000000061131246372670100205100ustar00rootroot00000000000000 [%|loc%]Up to higher level directory[%END%] [% IF url %]

[%|loc(visible_path)%]Properties of the bookmark %1[%END%] [%|loc%]Help[%END%]

[% ELSIF directory %]

[%|loc(visible_path)%]Properties of the directory %1[%END%] [%|loc%]Help[%END%]

[% ELSE %]

[%|loc(visible_path)%]Properties of the file %1[%END%] [%|loc%]Help[%END%]

[% END %]
[%|loc(doc_owner)%]Owner: %1[%END%]
[%|loc(doc_date)%]Last update: %1[%END%]
[%|loc(desc)%]Description: %1[%END%]





sympa-6.1.24~dfsg/web_tt2/d_read.tt2000066400000000000000000000366241246372670100172410ustar00rootroot00000000000000 [% IF file %] [% INSERT $file IF file %] [% ELSE %] [% IF path %] [%|loc%]Up to higher level directory[%END%] [% END %] [% IF expert_page %] [%|loc%]User mode[%END%] [%|loc%]Expert mode[%END%] [% ELSE %] [% IF has_dir_rights %] [%|loc%]User mode[%END%] [%|loc%]Expert mode[%END%] [% END %] [% END %] [% IF path %]

[%|loc%]Listing of folder[%END%] [% visible_path %] [%|loc%]Help[%END%]

[% IF expert_page %] [% IF may_edit %] [%|loc%]delete[%END%] [%|loc%]properties[%END%] [% END %] [% IF may_control %] [%|loc%]access[%END%] [% END %] [% END %]
[%|loc(doc_owner)%]Owner: %1[%END%]
[%|loc(doc_date)%]Last update: %1[%END%]
[% IF doc_title %] [%|loc(doc_title)%]Description: %1[%END%]

[% END %]
[% ELSE %]

[%|loc%]Listing of root folder[%END%] [%|loc%]Help[%END%]

[% END %]
[% IF order_by != 'order_by_doc' %] [% ELSE %] [% END %] [% IF order_by != 'order_by_author' %] [% ELSE %] [% ELSE %] [% END %] [% IF order_by != 'order_by_date' %] [% ELSE %] [% IF is_editor %] [% END %] [% END %] [% IF empty %] [% ELSE %] [% IF sort_subdirs %] [% FOREACH s = sort_subdirs %] [% IF escaped_path %] [% IF expert_page %] [% IF s.edit %] [% ELSE %] [% END %] [% IF s.control %] [% ELSE %] [% END %] [% IF s.edit %] [% ELSE %] [% END %] [% IF is_editor %] [% END %] [% END %] [% END %] [% END %] [% IF sort_files %] [% FOREACH f = sort_files %] [% IF f.moderate %] [% IF expert_page %] [% END %] [% ELSE %] [% END %] [% IF f.html %] [% ELSIF f.url %] [% ELSE %] [% IF f.moderate %] [% IF expert_page %] [% END %] [% ELSE %] [% END %] [% END %] [% IF f.moderate %] [% IF expert_page %] [% END %] [% ELSE %] [% END %] [% IF f.moderate %] [% IF expert_page %] [% END %] [% ELSE %] [% END %] [% IF expert_page %] [% IF f.edit %] [% ELSE %] [% END %] [% IF f.control %] [% ELSE %] [% END %] [% IF f.edit %] [% ELSE %] [% END %] [% IF is_editor %] [% IF f.moderate %] [% IF expert_page %] [% END %] [% ELSE %] [%END%] [%END%] [% END %] [% END %] [% END %] [% END %]
[%|loc%]Document[%END%] [%|loc%]Document[%END%][%|loc%]Author[%END%] [%|loc%]Author[%END%] [% END %] [% IF order_by != 'order_by_size' %] [%|loc%]Size (Kb)[%END%] [%|loc%]Size (Kb)[%END%][%|loc%]Last update[%END%] [%|loc%]Last update[%END%] [% END %] [% IF expert_page %] [%|loc%]Edit[%END%] [%|loc%]Delete[%END%] [%|loc%]Access[%END%] [%|loc%]Properties[%END%][%|loc%]Moderation[%END%]
[%|loc%]Empty folder[%END%]
[% ELSE %] [% END %] [% s.escaped_title %] [% s.doc %] [% IF s.author_known %] [% s.author_mailto %] [% ELSE %] [%|loc%]Unknown[%END%] [% END %]   [% s.date %]   [% IF escaped_path %] [%|loc%]delete[%END%] [% ELSE %] [%|loc%]delete[%END%] [% END %]     [% IF escaped_path %] [%|loc%]access[%END%] [% ELSE %] [%|loc%]access[%END%] [% END %]   [% IF escaped_path %] [%|loc%]properties[%END%] [% ELSE %] [%|loc%]properties[%END%] [% END %]    
[% IF escaped_path %] [% ELSE %] [% END %] [% f.escaped_title %] [% f.doc %] [% f.escaped_title %] [% f.anchor %] [% IF escaped_path %] [% ELSE %] [% END %] [% f.escaped_title %] [% f.doc %] [% IF escaped_path %] [% ELSE %] [% END %] [% f.escaped_title %] [% f.doc %] [%|loc%]to moderate[%END%] [% IF f.author_known %] [% f.author_mailto %] [% ELSE %] [%|loc%]Unknown[%END%] [%END%]   [% IF !f.url %] [% f.size %] [% END %] [% f.date %]   [% IF !f.url %] [% f.size %] [% END %] [% f.date %] [% IF escaped_path %] [%|loc%]edit[%END%] [% ELSE %] [%|loc%]edit[%END%] [% END %] [% IF escaped_path %] [%|loc%]delete[%END%] [% ELSE %] [%|loc%]delete[%END%] [% END %]     [% IF escaped_path %] [%|loc%]access[%END%] [% ELSE %] [%|loc%]access[%END%] [% END %]   [% IF escaped_path %] [%|loc%]properties[%END%] [% ELSE %] [%|loc%]properties[%END%] [% END %]   [%|loc%]moderate[%END%]  

[% IF expert_page %] [% IF may_edit %] [% IF total_edit %]
[% IF path %] [%|loc(visible_path)%]Create a new folder inside folder %1[%END%] [% ELSE %] [%|loc%]Create a new folder inside root folder[%END%] [% END %]

[% END %]
[%|loc%]Create a new file[%END%]

[%|loc%]Add a bookmark[%END%]




[% IF total_edit %]


[% END %] [% END %] [% END %] [% END %] sympa-6.1.24~dfsg/web_tt2/d_upload.tt2000066400000000000000000000023561246372670100176050ustar00rootroot00000000000000 [%|loc(shortname)%]the file %1 already exists[%END%]



sympa-6.1.24~dfsg/web_tt2/dump_scenario.tt2000066400000000000000000000033211246372670100206370ustar00rootroot00000000000000 [% IF result %]
[% IF result == 'success' %] [%|loc%]scenario is created but not loaded in the list config[%END%] [% ELSIF result == 'success_new_name' %] [%|loc%]new scenario is created but not loaded in the list config. Edit list config if you need to apply it for that list[%END%] [% ELSIF result == 'unchanged' %] [%|loc%]new scenario is equal to previous one. Nothing done.[%END%] [% END %]
[% ELSE %]

[% pname %] [% scenario_name %]

([%|loc%]path:[%END%] [% scenario_path %])

[% END %] sympa-6.1.24~dfsg/web_tt2/dumpvars.tt2000066400000000000000000000002351246372670100176510ustar00rootroot00000000000000

[% html_dumpvars %]
sympa-6.1.24~dfsg/web_tt2/edit_attributes.tt2000066400000000000000000000030461246372670100212060ustar00rootroot00000000000000 [% IF list_conf.custom_attribute.size > 0 %]
[%|loc%]Additional information[%END%] [% FOREACH k IN list_conf.custom_attribute %] [% END %]
[% SET m = subscriber.custom_attribute.item(k.id).value %] [% IF k.type == 'string' %] [% ELSIF k.type == 'integer' %] [% ELSIF k.type == 'text' %] [% ELSIF k.type == 'enum' %] [% ELSE %] [% subscriber.custom_attribute.item(k.id).value %] [% END %] [% IF k.optional == 'required' %]*[% END %]
[%|loc%]*: Required element[%END%]
[% END %] sympa-6.1.24~dfsg/web_tt2/edit_config.tt2000066400000000000000000000042721246372670100202670ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/edit_list_request.tt2000066400000000000000000000700671246372670100215520ustar00rootroot00000000000000

[%|loc%]Configuring the list[%END%] [%|loc%]Help[%END%]


[% IF !group %] [%|loc%]You can choose below a subset of parameters to edit:[%END%] [% ELSE %]
[% FOREACH p = param %] [% IF p.may_edit != 'hidden' %] [% IF p.changed == '1' %]
[% ELSE %]
[% END %] [% IF p.type == 'scenario' %] [% IF is_listmaster %]  [%|loc%]scenario source[%END%] [% END %] [% END %] [%|loc%]Help[%END%]

[% IF p.title %] [% p.title %] [% IF is_listmaster %] ([% p.name %]) [% END %] [% ELSE %] [% p.name %] [% END %] [% IF is_listmaster %] [% IF p.default == '1' %] [%|loc%](default)[%END%] [% END %] [% END %]

[% IF p.occurrence == 'multiple' %] [% IF p.type == 'enum' %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% FOREACH enum = p.value %] [% IF enum.value.selected == '1' %] [% IF enum.value.title %] [% enum.value.title %] [% ELSE %] [%|optdesc('',is_listmaster)%][% enum.key %][%END%] [% END %] [% END %] [% END %] [% END %] [% ELSE %] [% o_INDEX = 0 %] [% FOREACH o = p.value %]
[% IF p.type == 'paragraph' %] [% FOREACH key = o.value %] [% IF key.may_edit != 'hidden' %] [% IF key.type == 'enum' %] [% IF key.may_edit == 'write' %] [% ELSIF key.may_edit == 'read' %] [% FOREACH enum = key.value %] [% IF enum.value.selected == '1' %] [% IF enum.value.title %][% enum.value.title %][% ELSE %][%|optdesc('',is_listmaster)%][% enum.key %][%END%][% END %] [% END %] [% END %] [% END %]
[% ELSIF key.type == 'datasource' %] [% IF key.may_edit == 'write' %]
[% ELSIF key.may_edit == 'read' %] [% FOREACH source = key.value %] [% IF source.value.selected == '1' %] [% source.value.title %][% IF is_listmaster && source.value.name %] ([% source.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSE %] [% IF key.may_edit == 'write' %] [% IF key.field_type == 'password' %] [% ELSE %] [% END %] [% ELSIF key.may_edit == 'read' %] [% IF key.field_type == 'password' %] [% key.hidden_field %] [% ELSE %] [% key.value %] [% END %] [% END %] [% key.unit %]
[% END %] [% END %] [% END %] [% ELSE %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% o.value %] [% END %] [% o.unit %]
[% END %] [% o_INDEX = o_INDEX + 1 %] [% END %] [%END%]
[% ELSE %] [% IF p.type == 'scenario' %] [% IF p.may_edit == 'write' %]
[% ELSIF p.may_edit == 'read' %] [% FOREACH scenario = p.value %] [% IF scenario.value.selected == '1' %] [% scenario.value.web_title %][% IF is_listmaster && scenario.value.name %] ([% scenario.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF p.type == 'task' %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% FOREACH task = p.value %] [% IF task.value.selected == '1' %] [% task.value.title %][% IF is_listmaster && task.vakue.name %] ([% task.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF p.type == 'datasource' %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% FOREACH source = p.value %] [% IF source.value.selected == '1' %] [% source.value.title %][% IF is_listmaster && source.value.name %] ([% source.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF p.type == 'paragraph' %] [% FOREACH key = p.value %] [% IF key.may_edit != 'hidden' %] [% IF key.type == 'scenario' %] [% IF key.may_edit == 'write' %]
[% ELSIF key.may_edit == 'read' %] [% FOREACH scenario = key.value %] [% IF scenario.value.selected == '1' %] [% scenario.value.web_title %][% IF is_listmaster && scenario.value.name %] ([% scenario.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF key.type == 'task' %] [% IF key.may_edit == 'write' %] [% ELSIF key.may_edit == 'read' %] [% FOREACH task = key.value %] [% IF task.value.selected == '1' %] [% task.value.title %][% IF is_listmaster && task.value.name %] ([% task.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF key.type == 'datasource' %] [% IF key.may_edit == 'write' %] [% ELSIF key.may_edit == 'read' %] [% FOREACH source = key.value %] [% IF source.value.selected == '1' %] [% source.value.title %][% IF is_listmaster && source.value.name %] ([% source.value.name %])[% END %] [% END %] [% END %] [% END %] [% ELSIF key.type == 'enum' %] [% IF key.may_edit == 'write' %] [% IF key.occurrence == 'multiple' %] [% END %] [% FOREACH enum = key.value %] [% ELSE %] >[%|optdesc('',is_listmaster)%][% enum.key %][%END%] [% END %] [% END %]
[% ELSIF key.may_edit == 'read' %] [% FOREACH enum = key.value %] [% IF enum.value.selected == '1' %] [% IF enum.value.title %] [% IF p.name == 'lang' %] [% enum.value.title %] [% ELSE %] [% enum.value.title %] [% END %] [% ELSE %] [%|optdesc('',is_listmaster)%][% enum.key %][%END%] [% END %] [% END %] [% END %] [% END %] [% ELSE %] [% IF key.may_edit == 'write' %] [% ELSIF key.may_edit == 'read' %] [% key.value %] [% END %] [% key.unit %]
[% END %] [% END %] [% END %] [% ELSIF p.type == 'enum' %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% FOREACH enum = p.value %] [% IF enum.value.selected == '1' %] [% IF enum.value.title %] [% IF p.name == 'lang' %] [% enum.value.title %] [% ELSE %] [% enum.value.title %] [% END %] [% ELSE %] [%|optdesc('',is_listmaster)%][% enum.key %][%END%] [% END %] [% END %] [% END %] [% END %] [% ELSE %] [% IF p.may_edit == 'write' %] [% ELSIF p.may_edit == 'read' %] [% p.value %] [% END %] [% p.unit %]
[% END %] [% END %]
[% IF p.default == '1' %] [%|loc%]default[%END%] [% END %]

[% END %] [% END %] [% IF is_form_editable == '1' %] [% END %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/edit_template.tt2000066400000000000000000000044031246372670100206310ustar00rootroot00000000000000

[%|loc%]Template edition system[%END%]


  • [%|loc%]Template name: [%END%] [% template_name %]
  • [%|loc%]Type: [%END%] [% SWITCH webormail -%] [% CASE 'web' %][%|loc%]web[%END -%] [% CASE 'mail' %][%|loc%]mail[%END -%] [% CASE %][% webormail -%] [% END %]
  • [%|loc%]Path: [%END%] [% template_path %]
  • [%|loc%]Scope: [%END%] [%- SWITCH scope -%] [% CASE 'site' %] [%|loc%]site[%END%] [%|loc%](this template is the default used by all robots unless redefined for a specific robot)[%END%] [% CASE 'robot' %] [%|loc%]robot[%END%] [%|loc(robot)%](this template is the default for all lists of robot %1 unless it is redefined for a specific list)[%END%] [% CASE 'list' %] [%|loc%]list[%END -%] [%|loc(list,robot)%](this template is defined for list %1@%2)[%END%] [% CASE %] [% scope %] [% END %]
  • [%|loc%]Language: [%END%] [%- IF tpl_lang == 'default' -%] [%|loc%]default[%END%] [%|loc%](This template is the default for all languages unless it is redefined for a specific language)[%END%] [%- ELSE -%] [% tpl_lang_title %] [%- END %]




[% IF list %] [% END %] [% IF saved %] [%|loc%]Template saved[%END%] ([% time %])

[% END %]
sympa-6.1.24~dfsg/web_tt2/editfile.tt2000066400000000000000000000117521246372670100176030ustar00rootroot00000000000000

[%|loc%]Edit list templates[%END%] [%|loc%]Help[%END%]

[% IF file %]

[% IF complete %][%|loc(complete)%]Edit the file %1[%END%][% ELSE %][%|loc(file)%]Edit the file %1[%END%][% END %]


[% ELSE %]
[%|loc%]You can edit several messages/files associated with your list:[%END%]

[% item1 = 'homepage' item2 = 'info' %] [% IF files.$item1 || files.$item2 %]

[%|loc%]HTML pages[%END%]

[% IF files.$item1 %] [%|loc%]Edit[%END%] [%|loc%]List homepage: HTML text to describe the list. It is printed on the right-hand side of the main list page. (default for this is the list description)[%END%]
[% END %] [% IF files.$item2 %] [%|loc%]Edit[%END%] [%|loc%]List description: This text is sent as an answer to the mail command INFO. It can also be included in the Welcome message.[%END%]
[% END %] [% END %] [% item1 = 'welcome.tt2' item2 = 'reject.tt2' item3 = 'remind.tt2' item4 = 'invite.tt2' %] [% IF files.$item1 || files.$item2 || files.$item3 || files.$item4 %]

[%|loc%]Automatic messages[%END%]

[% IF files.$item1 %] [%|loc%]Edit[%END%] [%|loc%]Welcome message: This message is sent to new subscribers. It can be a full MIME structured message (only for MIME gurus).[%END%]
[% END %] [% IF files.$item2 %] [%|loc%]Edit[%END%] [%|loc%]Rejection message: when a message is rejected by list editor, a notification can be sent to the original author. You may prepare various rejection messages.[%END%]
[% END %] [% IF files.$item3 %] [%|loc%]Edit[%END%] [%|loc%]Remind message: This message is sent to each subscriber when using the command REMIND. It's very useful to help people who are confused about their own subscription emails or people who are not able to unsubscribe themselves.[%END%]
[% END %] [% IF files.$item4 %] [%|loc%]Edit[%END%] [%|loc%]Subscribing invitation message: sent to a person if someone uses the INVITE command to invite someone to subscribe.[%END%]
[% END %] [% END %] [% item1 = 'message.footer' item2 = 'message.header' %] [% IF files.$item1 || files.$item2 %]

[%|loc%]Added in distributed messages[%END%]

[% IF files.$item1 %] [%|loc%]Edit[%END%] [%|loc%]Message footer: If this file is not empty, it is added as a MIME attachment at the end of each message distributed to the list.[%END%]
[% END %] [% IF files.$item2 %] [%|loc%]Edit[%END%] [%|loc%]Message header: If this file is not empty, it is added as a MIME attachment at the beginning of each message distributed to the list.[%END%]
[% END %] [% END %] [% item1 = 'bye.tt2' item2 = 'removed.tt2' item3 = 'your_infected_msg.tt2' %] [% IF files.$item1 || files.$item2 || files.$item3 %]

[%|loc%]More[%END%]

[% IF files.$item1 %] [%|loc%]Edit[%END%] [%|loc%]Unsubscribe message: This message is sent when users leave the list.[%END%]
[% END %] [% IF files.$item2 %] [%|loc%]Edit[%END%] [%|loc%]Deletion message: This message is sent to users when you remove them from the list using the DEL command (unless you hit the Quiet button).[%END%]
[% END %] [% IF files.$item3 %] [%|loc%]Edit[%END%] [%|loc%]Virus infection message: This message is sent to the sender of a message in which a virus was found.[%END%]
[% END %] [% END %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/editsubscriber.tt2000066400000000000000000000102411246372670100210170ustar00rootroot00000000000000

[%|loc%]Subscriber information[%END%]



[% IF current_subscriber.custom_attribute %] [% SET subscriber = current_subscriber ; PROCESS edit_attributes.tt2 ; END %] [%|loc%]Subscribed since:[%END%] [% current_subscriber.date %]
[%|loc%]Last update:[%END%] [% current_subscriber.update_date %]


[% current_subscriber.lang %]
[% IF pictures_display %] [% current_subscriber.escaped_email %]'s picture
[% IF current_subscriber.escaped_email == user.email %] [%|loc%]Changing your picture for this list[%END%]
[% END %] [% END %] [% IF additional_fields %] [% FOREACH field = additional_fields %] [% IF field.value.type == 'enum' %]
[% ELSE %]
[% END %] [% END %] [% END %]
[% IF current_subscriber.bounce %]
[%|loc%]Bouncing address[%END%]
[% IF current_subscriber.escaped_bounce_address %] [% current_subscriber.escaped_bounce_address %]
[% END %] [%|loc%]Status:[%END%] [% current_subscriber.bounce_status %] ([% current_subscriber.bounce_code %])
[%|loc%]Bounce count:[%END%] [% current_subscriber.bounce_count %]
[%|loc%]Period:[%END%] [%|loc(current_subscriber.first_bounce,current_subscriber.last_bounce)%]from %1 to %2[%END%]
[%|loc%]View last bounce[%END%]

[% END %]
sympa-6.1.24~dfsg/web_tt2/error.tt2000066400000000000000000000303131246372670100171410ustar00rootroot00000000000000
[%###################-%] [%#### INTERN ERROR -%] [%###################-%] [% FOREACH i_err = intern_errors %] [%|loc(i_err.action)%]INTERNAL SERVER ERROR (%1)[%END-%] - [% IF i_err.msg == 'month_not_found' %][%|loc(i_err.month)%]Unable to find month '%1'[%END%] [% ELSIF i_err.msg == 'inaccessible_archive' %][%|loc(i_err.year_month)%]Archives from %1 are not accessible[%END%] [% ELSIF i_err.msg == 'unable_to_load_list_of_topics' %][%|loc%]Unable to load list topics.[%END%] [% ELSIF i_err.msg == 'unable_to_load_create_list_templates' %][%|loc%]Unable to load create_list templates.[%END%] [% ELSIF i_err.msg == 'sync_include_failed' %][%|loc%]Failed to inlude members[%END%] [% ELSIF i_err.msg == 'sync_include_admin_failed' %][%|loc%]Failed to include list admins[%END%] [% ELSIF i_err.msg == 'no_owner_defined' %][%|loc%]No owner is defined for the list[%END%] [% ELSIF i_err.msg == 'exportation_failed' %][%|loc(i_err.listname)%]The exportation failed for list '%1'[%END%] [% ELSIF i_err.msg == 'cannot_unzip' %][%|loc(name)%]Cannot unzip file '%1'.[%END%] [% ELSIF i_err.msg == 'auth_msg_failed' %][%|loc(i_err.key)%]Unable to access the message authenticated with key %1[%END%] [% ELSIF i_err.msg == 'no_identified_user' %][%|loc%]Failed to get your email address from the authentication service.[%END%] [% ELSIF i_err.msg == 'err_404' %][%|loc(i_err.key)%]File not found.[%END%] [% ELSIF i_err.msg == 'db_error' %][%|loc%]Database error.[%END%] [% ELSIF i_err.msg == 'db_update_failed' %][%|loc(i_err.key)%]Failed to update database.[%END%] [% ELSIF i_err.msg == 'file_update_failed' %][%|loc(i_err.key)%]Failed to update a file.[%END%] [% ELSIF i_err.msg == 'create_list' %][%|loc(i_err.listname)%]Failed creating list '%1'. The list might already exist or listname might include forbidden characters.[%END%] [% ELSIF i_err.msg == 'unable_to_rename_list' %][%|loc(i_err.listname,i_err.new_listname)%]Unable to rename list '%1' to '%2'.[%END%] [% END %] [% END %] [%###################-%] [%#### SYSTEM ERROR -%] [%###################-%] [% FOREACH s_err = system_errors %] [%|loc(s_err.action)%]SYSTEM ERROR (%1)[%END-%] [% IF s_err.msg == 'a' %] [% END %] [% END %] [%###################-%] [%#### USER ERROR -%] [%###################-%] [% FOREACH u_err = user_errors %] [%|loc(u_err.action)%]ERROR (%1) [%END-%] - [% IF u_err.msg == 'wrong_param' %][%|loc()%]Wrong parameters[%END%] [% ELSIF u_err.msg == 'unknown_action' %][%|loc()%]Unknown action[%END%] [% ELSIF u_err.msg == 'syntax_errors' %][%|loc(u_err.params)%]Syntax errors with the following parameters: %1[%END%] [% ELSIF u_err.msg == 'authorization_reject' %][%|loc()%]Authorization rejected. Maybe you forgot to log in?[%END%] [% ELSIF u_err.msg == 'unknown_list' %][%|loc(u_err.list)%]List %1 not found[%END%] [% ELSIF u_err.msg == 'unknown_robot' %][%|loc(u_err.new_robot)%]%1: unknown robot[%END%] [% ELSIF u_err.msg == 'unknown_family' %][%|loc(u_err.family)%]%1: unknown family[%END%] [% ELSIF u_err.msg == 'already_login' %][%|loc(u_err.email)%]You are already logged in as %1[%END%] [% ELSIF u_err.msg == 'passwd_reminder_not_allowed' %][%|loc%]You can not get a password reminder; probably because your password is managed outside Sympa (Single Sign-On system or LDAP directory).[%END%] [% ELSIF u_err.msg == 'no_email' %][%|loc%]Please provide email address[%END%] [% ELSIF u_err.msg == 'missing_arg' %][%|loc(u_err.argument)%]Missing argument %1[%END%] [% ELSIF u_err.msg == 'wrong_value' %][%|loc(u_err.argument)%]Wrong value for parameter %1[%END%] [% ELSIF u_err.msg == 'no_user' %][%|loc%]You need to login[%END%] [% ELSIF u_err.msg == 'incorrect_email' %][%|loc(u_err.email)%]Address "%1" is incorrect[%END%] [% ELSIF u_err.msg == 'incorrect_passwd' %][%|loc%]Provided password is incorrect[%END%] [% ELSIF u_err.msg == 'init_passwd' %][%|loc%]You did not choose a password, request a reminder of the initial password[%END%] [% ELSIF u_err.msg == 'ldap_user' %][%|loc%]Your password is stored in an LDAP directory, therefore Sympa cannot post you a reminder[%END%] [% ELSIF u_err.msg == 'nb_days_to_much' %][%|loc(u_err.nb_days)%]The period is too long (%1 days)[%END%] [% ELSIF u_err.msg == 'no_subscriber' %][%|loc%]List has no subscribers[%END%] [% ELSIF u_err.msg == 'no_page' %][%|loc(u_err.page)%]No page %1[%END%] [% ELSIF u_err.msg == 'no_filter' %][%|loc%]Missing filter[%END%] [% ELSIF u_err.msg == 'not_subscriber' %] [% IF u_err.email %] [%|loc(u_err.email)%]Not subscribed: %1[%END%] [% ELSE %][%|loc(u_err.list)%]You are not subscribed to list %1[%END%] [% END %] [% ELSIF u_err.msg == 'custom_attribute' %][%|loc%]Check the additional information[%END%] [% ELSIF u_err.msg == 'not_available_reception_mode' %][%|loc(u_err.reception_mode)%]%1 is not an available reception mode[%END%] [% ELSIF u_err.msg == 'file_not_editable' %][%|loc(u_err.file)%]%1: file not editable[%END%] [% ELSIF u_err.msg == 'already_subscriber' %][%|loc(u_err.list)%]You are already subscribed to the list %1[%END%] [% ELSIF u_err.msg == 'user_already_subscriber' %][%|loc(u_err.email,u_err.list)%]%1 is already subscribed to the list %2[%END%] [% ELSIF u_err.msg == 'no_passwd' %][%|loc%]Please provide your password[%END%] [% ELSIF u_err.msg == 'diff_passwd' %][%|loc%]The passwords you typed do not match[%END%] [% ELSIF u_err.msg == 'passwd_validation' %][%|loc(u_err.reason)%]The password you typed does not match this sites standards of strength: %1. Please pick a stronger password.[%END%] [% ELSIF u_err.msg == 'wrong_input_path' %][%|loc(u_err.tpl)%]Provided path is incorrect for template '%1'[%END%] [% ELSIF u_err.msg == 'cannot_open_file' %][%|loc(u_err.path)%]Cannot open file '%1'[%END%] [% ELSIF u_err.msg == 'listname_needed' %][%|loc%]You need to provide list name[%END%] [% ELSIF u_err.msg == 'already_moderated' %][%|loc%]Failed to moderate a message; it was probably moderated by another moderator[%END%] [% ELSIF u_err.msg == 'msg_topic_missing' %][%|loc%]Tagging message is required for this list[%END%] [% ELSIF u_err.msg == 'empty_archives' %][%|loc%]Archives are empty for this list[%END%] [% ELSIF u_err.msg == 'didnt_change_anything' %][%|loc%]You did not select an action to perform[%END%] [% ELSIF u_err.msg == 'no_bounce_user' %][%|loc(u_err.email)%]No bounce for user %1[%END%] [% ELSIF u_err.msg == 'no_bounce_subscriber' %][%|loc%]List has no bouncing subscribers[%END%] [% ELSIF u_err.msg == 'no_parameter_edited' %][%|loc%]No parameter was edited[%END%] [% ELSIF u_err.msg == 'config_changed' %][%|loc(u_err.email)%]Config file has been modified by %1. Cannot apply your changes[%END%] [% ELSIF u_err.msg == 'topic_other' %][%|loc%]Topic "other" is a reserved word[%END%] [% ELSIF u_err.msg == 'mandatory_parameter' %][%|loc(u_err.p_name)%]Parameter '%1' is mandatory. Ignoring deletion.[%END%] [% ELSIF u_err.msg == 'p_family_controlled' %][%|loc(u_err.param)%]Parameter '%1' must have values[%END%] [% ELSIF u_err.msg == 'p_family_wrong' %][%|loc(u_err.param,u_err.val)%]Parameter '%1' has got wrong value: '%2'[%END%] [% ELSIF u_err.msg == 'already_closed' %][%|loc(u_err.listname)%]The list '%1' is already closed[%END%] [% ELSIF u_err.msg == 'not_closed' %][%|loc(u_err.listname)%]The list '%1' is not closed[%END%] [% ELSIF u_err.msg == 'incorrect_listname' %][%|loc(u_err.bad_listname)%]'%1': bad listname[%END%] [% ELSIF u_err.msg == 'list_already_exists' %][%|loc(u_err.new_listname)%]'%1' list already exists[%END%] [% ELSIF u_err.msg == 'listname_matches_aliases' %][%|loc(u_err.new_listname)%]Incorrect listname '%1': matches one of service aliases[%END%] [% ELSIF u_err.msg == 'failed_to_install_aliases' %][%|loc(u_err.listname)%]Failed to remove list aliases for list %1[%END%] [% ELSIF u_err.msg == 'no_such_document' %][%|loc(u_err.path)%]%1: No such file or directory[%END%] [% ELSIF u_err.msg == 'empty_document' %] [%|loc(u_err.path)%]Unable to read %1: empty document[%END%] [% ELSIF u_err.msg == 'no_shared' %] [%|loc%]There is no shared documents[%END%] [% ELSIF u_err.msg == 'shared_empty' %] [%|loc%]The shared document space is empty[%END%] [% ELSIF u_err.msg == 'cannot_describe_shared_directory'%][%|loc%]The shared directory cannot have any description[%END%] [% ELSIF u_err.msg == 'no_description' %] [%|loc%]No description specified[%END%] [% ELSIF u_err.msg == 'no_doc_to_describe' %] [%|loc(u_err.path)%]Unable to describe, the document '%1' does not exist[%END%] [% ELSIF u_err.msg == 'synchro_failed' %][%|loc%]Data has changed on disk. Cannot apply your changes[%END%] [% ELSIF u_err.msg == 'incorrect_name' %][%|loc(u_err.name)%]%1: incorrect name[%END%][%IF u_err.reason%][%|loc%]:[%END%] [% u_err.reason %][%END%] [% ELSIF u_err.msg == 'no_content' %][%|loc%]Failed: your content is empty[%END%] [% ELSIF u_err.msg == 'cannot_overwrite' %] [%|loc(u_err.path)%]Cannot overwrite file %1:[%END%] [% u_err.reason %] [% ELSIF u_err.msg == 'doc_already_a_dir' %] [%|loc(u_err.path)%]A directory named '%1' already exists:[%END%] [% u_err.reason %] [% ELSIF u_err.msg == 'doc_already_exist' %] [%|loc(u_err.name)%]This is an already existing document: '%1'[%END%] [% ELSIF u_err.msg == 'no_name' %][%|loc%]No name specified[%END%] [% ELSIF u_err.msg == 'shared_full' %][%|loc%]The document repository exceed disk quota.[%END%] [% ELSIF u_err.msg == 'cannot_upload' %] [%|loc(u_err.path)%]Cannot upload file %1:[%END%] [% u_err.reason %] [% ELSIF u_err.msg == 'index_html' %][%|loc(u_err.dir)%]You're not authorized to upload an INDEX.HTML in %1[%END%] [% ELSIF u_err.msg == 'no_uploaded_file' %][%|loc%]The upload failed, try it again[%END%] [% ELSIF u_err.msg == 'directory_no_copied' %][%|loc(u_err.name,u_err.reason)%]Directory %1 and its contents could not be copied: %2[%END%] [% ELSIF u_err.msg == 'file_no_copied' %][%|loc(u_err.name,u_err.reason)%]File %1 was not copied: %2 [%END%] [% ELSIF u_err.msg == 'full_directory' %][%|loc(u_err.directory)%]Failed: %1 not empty[%END%] [% ELSIF u_err.msg == 'missing_cert' %][%|loc%]No certificate for this list[%END%] [% ELSIF u_err.msg == 'no_topic' %][%|loc%]This list has no message topic[%END%] [% ELSIF u_err.msg == 'msg_topic_missing' %][%|loc%]Tag message with topic is required for this list[%END%] [% ELSIF u_err.msg == 'no_entry' %][%|loc(u_err.email)%]No entry for user '%1'[%END%] [% ELSIF u_err.msg == 'select_month' %][%|loc%]Please select archive months[%END%] [% ELSIF u_err.msg == 'no_soap_service' %][%|loc%]No SOAP service[%END%] [% ELSIF u_err.msg == 'auth_failed' %][%|loc%]Authentication failed[%END%] [% ELSIF u_err.msg == 'list_not_open' %][%|loc(u_err.status)%]Service unavailable because list status is '%1'[%END%] [% ELSIF u_err.msg == 'template_exists' %][%|loc(u_err.argument)%]This Template '%1' already exists[%END%] [% ELSIF u_err.msg == 'cannot_delete' %][%|loc(u_err.file_del)%]Cannot delete this file '%1'[%END%] [% ELSIF u_err.msg == 'invalid_filename' %][%|loc(u_err.filename)%]Invalid filename: '%1'[%END%] [% ELSIF u_err.msg == 'change_member_email_failed_included' %][%|loc(u_err.listname)%]Failed to update member email in list '%1', list owner has been notified.[%END%] [% ELSIF u_err.msg == 'change_admin_email_failed_included' %][%|loc(u_err.listname)%]Failed to update admin email in list '%1', list owner has been notified.[%END%] [% ELSIF u_err.msg %][%u_err.msg%] [% END %] [% END %] [%#####################-%] [%# AUTHORIZATION ERROR-%] [%#####################-%] [% FOREACH auth = auth_rejects %] [%|loc(auth.action)%]AUTHORIZATION REJECT (%1)[%END-%] [% IF auth.change_email_failed %][%|loc(auth.listname)%]Could not change your subscription address for the list '%1' because your new address is not allowed to subscribe/unsubscribe:[%END%][% END %] [% SET reason = auth.msg -%] [% IF reason == 'edit_right' %][% SET role = auth.role -%][% SET right = auth.right -%][% END -%] [% PROCESS authorization_reject.tt2 -%] [% IF auth.login %]
[%|loc%]You need to login[%END%][% END %] [% END %]
sympa-6.1.24~dfsg/web_tt2/exclusion_table.tt2000066400000000000000000000034421246372670100211730ustar00rootroot00000000000000
[% IF is_listmaster || is_owner || may_add %] [% END %] [% IF user.email %] [% IF exclude_users %] [% FOREACH exc = exclude_users %] [% IF is_listmaster || is_owner || may_add %] [% END %] [% END %] [% ELSE %]

[%|loc%]No user excluded.[% END %]

[% END %] [% END %]
«»[%|loc%]Email[%END%] [%|loc%]Since[%END%]
 [% exc.email %]   [% exc.since %] 
[% IF may_add %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/family_signoff.tt2000066400000000000000000000002371246372670100210060ustar00rootroot00000000000000

[%|loc%]Global unsubscription[% END %]

[%|loc(signing_off_email,family)%]You successfully unsubscribed the address %1 from family %2.[% END %]

sympa-6.1.24~dfsg/web_tt2/family_signoff_request.tt2000066400000000000000000000005431246372670100225560ustar00rootroot00000000000000

[%|loc%]Global unsubscription request[% END %]

[%|loc(signing_off_email,family)%]You clicked a link to unsubscribe the address %1 from family %2.[% END %]

[%|loc(family)%]A confirmation was just sent to this address. By clicking the link it contains, you will be definitively unsubscribed from all the lists from family %1[% END %]

sympa-6.1.24~dfsg/web_tt2/footer.tt2000066400000000000000000000004401246372670100173040ustar00rootroot00000000000000
 
sympa-6.1.24~dfsg/web_tt2/get_closed_lists.tt2000066400000000000000000000024001246372670100213320ustar00rootroot00000000000000
[%|loc%]Closed lists[%END%] [% FOREACH list = closed %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
«» [%|loc%]list name[%END%] [%|loc%]list subject[%END%] [%|loc%]Requested by[%END%]
[% list.key %] [% list.value.subject %] [% list.value.by %]
sympa-6.1.24~dfsg/web_tt2/get_inactive_lists.tt2000066400000000000000000000024111246372670100216650ustar00rootroot00000000000000
[% FOREACH list = inactive_lists %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
[%|loc%]Creation date[%END%] [%|loc%]Most recent message[%END%] [%|loc%]Listname[%END%] [%|loc%]Subject[%END%] [%|loc%]Number of subscribers[%END%] [%|loc%]Number of messages[%END%] [%|loc%]Send scenario[%END%] [%|loc%]Creator[%END%] [%|loc%]Owners[%END%] [%|loc%]Editors[%END%]
[% list.creation_date %] [% IF list.last_message_epoch > 0 %] [% list.last_message_date %] [% ELSE %] [%|loc%]none so far[%END%] [% END %] [% list.name %] [% list.subject %] [% list.subscribers_count %] [% list.msg_count %] [% list.send_scenario %] [% list.creator %] [% list.owners %] [% list.editors %]
sympa-6.1.24~dfsg/web_tt2/get_latest_lists.tt2000066400000000000000000000012131246372670100213560ustar00rootroot00000000000000
[% FOREACH list = latest_lists %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
[%|loc%]Creation date[%END%] [%|loc%]Listname[%END%] [%|loc%]Subject[%END%]
[% list.creation_date %] [% list.name %] [% list.subject %]
sympa-6.1.24~dfsg/web_tt2/get_pending_lists.tt2000066400000000000000000000015431246372670100215140ustar00rootroot00000000000000
[%|loc%]Pending lists[%END%] [% FOREACH list = pending %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
[%|loc%]list name[%END%] [%|loc%]list subject[%END%] [%|loc%]Requested by[%END%] [%|loc%]Requested date[%END%]
[% list.key %] [% list.value.subject %] [% list.value.by %] [% list.value.date %]
sympa-6.1.24~dfsg/web_tt2/help.tt2000066400000000000000000000023111246372670100167350ustar00rootroot00000000000000
[% IF help_topic %] [% PROCESS "help_${help_topic}.tt2" IF help_topic %]
[% ELSE %]

[%|helploc%]Subscriber, moderator and owner documentation[%END%]

[%|helploc%]In this documentation, you will find:[%END%]

[%|helploc(path_cgi)%]If you want to perform a particular task, take a look at the list of all available features in the mailing list management software Sympa.[%END%]
[%|helploc(path_cgi)%]If you experience any problem, please refer to the users FAQ or to the administrators FAQ.[%END%]

[% END %]
sympa-6.1.24~dfsg/web_tt2/help_admin.tt2000066400000000000000000000772131246372670100201220ustar00rootroot00000000000000

[%|helploc%]Mailing lists - Owner and moderator guide[%END%]

[%|helploc%]Introduction: who is in charge of managing mailing lists?[%END%]

[%|helploc%]Reminder: a mailing list service involves four types of roles:[%END%]

  • [%|helploc%]listmaster;[%END%]
  • [%|helploc%]owner;[%END%]
  • [%|helploc%]moderator;[%END%]
  • [%|helploc%]subscriber.[%END%]

[%|helploc(path_cgi)%]Refer to the description of each role to know more about this.[%END%]

[%|helploc%]Requesting the creation of a mailing list[%END%]

[%|helploc%]The list creation request can be subject to conditions. Even though you meet those conditions, the list creation will nevertheless be subject to approval by the listmasters.[%END%]

[%|helploc%]To request the creation of a mailing list, do as follows:[%END%]

  1. [%|helploc(path_cgi,conf.host)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]In the top menu, click on the 'Create list' link.[%END%]
    [%|helploc%]If this link does not display, it means that you do not have the privileges required to create a list.[%END%]
  3. [%|helploc(conf.host)%]Give your list a name (only enter the name without the '@' and the domain name; example: languages_spanish and not languages_spanish@%2).[%END%]
    [%|helploc%]Do not use any spaces, accents or specials characters in list names: those characters might cause problems.[%END%] [%|helploc%]Choose an explicit yet short name: think of the subscribers who will have to type this name every time they will send a message to the list! If you manage a set of lists, you can prefix your lists' names with a common prefix; thus they will be sorted together and will be easily recognizable (example: xx-users@%2, xx-hotline@%2, etc.).[%END%]
  4. [%|helploc%]Choose a list type among the predefined types (the predefined types are only examples of typical configurations that can be changed by the list owners after creation; it is even possible to configure the list beyond the options offered in the list administration module, by asking the listmasters).[%END%]
  5. [%|helploc%]Enter a subject for your list. This subject will display as a header for all the list pages, and will also be visible on list index pages (list of lists, list of your subscriptions, etc.) and in the browser title bar.[%END%]
  6. [%|helploc%]Choose a topic in the 'Topics' drop-menu.[%END%]

    [%|helploc%]If no topic suits your needs, you can request the creation of a new topic by asking the listmasters.[%END%]

  7. [%|helploc%]Enter a description for your list. This description will display on the list information page and in the 'Subscribers Charter' sent by email to each new subscriber, under the 'List subject' heading. This description may involve explanations about the following issues:[%END%]
    • [%|helploc%]object of the list and targets;[%END%]
    • [%|helploc%]topics discussed;[%END%]
    • [%|helploc%]operation of the list (liabilities, status of the list, etc.);[%END%]
    • [%|helploc%]rules applying;[%END%]
    • [%|helploc%]description of the typical subscribers (their occupations, the projects they manage, their nationalities, etc.).[%END%]

    [%|helploc%]You can format your list description with HTML tags. Be careful: if your description is long, cut it with manual line breaks (ENTER key of your keyboard); otherwise, it might not be entirely visible in your browser window.[%END%]

  8. [%|helploc%]Click on the 'Submit your creation request' button.[%END%]

[%|helploc%]A message displays to inform you that your list creation request has been sent to the listmasters and that from now on, you can modify the list by clicking on the 'Admin' button. However, the message warns you that the list will be actually installed and made visible on the server only after approval by a listmaster.[%END%]

[%|helploc%]After this, you will have to wait for the list creation to be approved of by one of the listmasters. Then you will receive a notice message entitled 'Creation of the nameofthelist list', informing you that your list was actually created.[%END%]

[%|helploc%]Last, subscribe to your list: creating a list or becoming its owner or moderator does not mean that you are automatically subscribed to it![%END%]

[%|helploc%]Managing a list[%END%]

[%|helploc%]To manage a list you own, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]

    [%|helploc%]If you are subscribed to the list with several addresses, use the address with which you requested the list creation.[%END%]

  2. [%|helploc%]Go to the information page of the list you want to manage.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Admin' link.[%END%]

[%|helploc%]To browse the sections of the administration module, click on the links below the 'Admin' link, in the left menu.[%END%]

[%|helploc%]Those different sections allow you to:[%END%]

[%|helploc%]The options available in the 'Moderate' submenu allow you to:[%END%]

[%|helploc%]Configuring the list[%END%]

[%|helploc(path_cgi)%]To learn how to configure the list, please refer to the documentation about list configuration.[%END%]

[%|helploc%]Customizing the list[%END%]

[%|helploc%]From this page, you can edit a number of files relating to your list, among which:[%END%]

  • [%|helploc%]typical messages sent to subscribers in particular occasions:[%END%]
    • [%|helploc%]welcome message: this message is the notice sent to people who just subscribed. You should write a charter for your list and add it in this welcome message. You can create a structured MIME message (reserved to the MIME format experts);[%END%]
    • [%|helploc%]unsubscribe message: this message is sent to people unsubscribing from the list;[%END%]
    • [%|helploc%]deletion message: this message is sent to people you unsubscribe from the list (DEL command), especially because their address caused bounces;[%END%]
    • [%|helploc%]remind message: this message is sent to subscribers as a personalized reminder when using the REMIND command. This command is essential to the good management of your list since many bounces are due to people whose current address is not their subscription address anymore, or even who forgot that they were subscribed to the list;[%END%]
    • [%|helploc%]subscribing invitation message: this message is sent to people you invite to subscribe to the list using the INVITE command;[%END%]
    • [%|helploc%]notice of message rejected by the moderator: this message is sent to the sender of a message rejected by the moderator;[%END%]
    • [%|helploc%]notice of message rejected because of a virus: this message is sent to the sender of a message in which a virus was found.[%END%]
  • [%|helploc%]miscellaneous files:[%END%]
    • [%|helploc%]list description: the list description is sent by email in return to the INFO command. By default, it is also included in the welcome message (subscription notice). It is not the same as the list presentation page displayed on the mailing list web interface;[%END%]
    • [%|helploc%]list homepage: this description is available on the information page of the list. It can be in HTML format. Even though you do not use this format, use <br /> tags to indicate line breaks;[%END%]
    • [%|helploc%]message header: when available, it is added as a MIME attachment at the beginning of each message distributed to the list;[%END%]
    • [%|helploc%]message footer: when available, it is added as a MIME attachment at the end of each message distributed to the list.[%END%]

[%|helploc%]By default, Sympa uses default files; in this case, the specific files corresponding to your list are empty. To edit a file, choose it from the drop-down list and click on the 'Edit' button. You will have the possibility to change the 'From' field (sender), the 'Subject' field (subject line) and the message body.[%END%]

[%|helploc%]Be careful: the values between square brackets are variables. Do not change them, unless you really know what you are doing...[%END%]

[%|helploc%]Managing the shared document web space[%END%]

[%|helploc%]By default, lists have no shared document web space. Thus, you need to create it. To do that, go to the list administration module and click on the 'Create shared' link.[%END%]

[%|helploc(path_cgi)%]To allow subscribers to publish documents in the shared document web space, you need to change default rights: in the list administration module, click on 'Edit list config' and then on 'Privileges'. At the bottom of the page, there is a drop-down list entitled 'Who can edit'; choose the 'Restricted to subscribers (private)' option.[%END%]

[%|helploc(path_cgi)%]Be careful: if you created folders before changing those rights, the folders will still be unwritable. If you want to make them writable, you will have to change access rights for each folder.[%END%]

[%|helploc(path_cgi)%]You might also want to set up quotas for the shared document web space on the 'Privileges' page of the 'Edit list config' section.[%END%]

[%|helploc(path_cgi)%]To know everything about the management of the shared document web space (how to organize it, change access rights, name documents, etc.), refer to the 'Using the shared document web space' section of the User guide.[%END%]

[%|helploc%]To deny access to the shared document web space, click on 'Delete shared' in the 'Admin' submenu. Then you will still have the possibility to reopen it by clicking on 'Restore shared'. Deleting and restoring the shared document web space has no impact on the documents it contains.[%END%]

[%|helploc%]Managing subscribers[%END%]

[%|helploc%]This section lets you browse the list of all the list subscribers. For each subscriber, the following information is available:[%END%]

  • [%|helploc%]email address;[%END%]
  • [%|helploc%]domain of the email address (@cru.fr, @sympa.org, @fai.com, etc.);[%END%]
  • [%|helploc%]picture (if that feature was activated for the list);[%END%]
  • [%|helploc%]name (according to the subscription method, it is not always displayed);[%END%]
  • [%|helploc%]message delivery mode;[%END%]
  • [%|helploc%]data source indicating the origin in case the subscriber is included in the list and not directly subscribed;[%END%]
  • [%|helploc%]date of subscription to the list;[%END%]
  • [%|helploc%]latest update of the subscriber options.[%END%]

[%|helploc%]By default, each page displays 25 subscribers. You can browse through the pages by using the browsing arrows or display more subscribers on each page. You may also wish to sort subscribers according to different criteria by clicking on the corresponding column header.[%END%]

[%|helploc%]To search a subscriber, enter all or part of his/her email address or name in the input field and click on the 'Search' button.[%END%]

[%|helploc%]You can subscribe other people to the list from this page:[%END%]

  • [%|helploc%]To add a single person, enter his/her email address in the input field and click on the 'Add' button.[%END%]
  • [%|helploc%]To add several persons, click on the 'Multiple add' button. In the input field that displays, enter the email addresses and names of the people you want to subscribe to the list and click on 'Add subscribers'.[%END%]

[%|helploc%]If you want to subscribe people without letting them know through a notice, tick the 'Quiet' box. However, it is far better to warn people that you subscribe them![%END%]

[%|helploc(conf.email,conf.host)%]Even though you have the possibility to subscribe people to your mailing lists, it is always much better that people take the necessary steps to subscribe themselves to the list. You can also invite people to subscribe to the list through the INVITE command: send an email to %1@%2 and type the following command in the message body: invite nameofthelist emailaddress (example: invite list_example john.doe(@)fai.com).[%END%]

[%|helploc%]To accept or reject the subscription requests to the list, click on 'Pending subscriptions'. The list of all people who requested subscription to the list displays. To accept or reject a request, tick the box in front of a person's name and click on the button of your choice.[%END%]

[%|helploc%]To send a subscription reminder to all subscribers, click on the 'Remind all' button. The subscription reminder message contains:[%END%]

  • [%|helploc%]the name of the list to which the subscriber is subscribed;[%END%]
  • [%|helploc%]the email address with which the subscriber is subscribed;[%END%]
  • [%|helploc%]the subscriber's list password;[%END%]
  • [%|helploc%]a link to the information page of the list;[%END%]
  • [%|helploc%]a clickable address in order to allow subscribers to unsubscribe from the list.[%END%]

[%|helploc(path_cgi)%]You can also configure an automatic subscription reminder through the 'Miscellaneous' page of the 'Edit list config' section.[%END%]

[%|helploc%]To unsubscribe subscribers from this page, select them by ticking the boxes in front of their names and click on the 'Delete selected email addresses' button.[%END%]

[%|helploc%]If you do not want to notify the subscribers, tick the 'Quiet' box. However, this is not advisable, except in the case of bouncing subscribers.[%END%]

[%|helploc%]Tip: to select all subscribers at once, first make sure that they are all displayed on the page, and then click on the 'Toggle selection' button: all subscribers will be selected in a single click![%END%]

[%|helploc%]To change a subscriber's subscriber options, click on his/her email address.[%END%]

[%|helploc%]From this page, you can change the name, email address and message delivery mode of the subscriber. You can also unsubscribe him/her.[%END%]

[%|helploc%]If the subscriber is bouncing, another form displays under the 'Subscriber information' form:[%END%]

[%|helploc%]The information displayed involves:[%END%]

  • [%|helploc%]the type of error (in English);[%END%]
  • [%|helploc%]the number or errors;[%END%]
  • [%|helploc%]the period during which errors occurred.[%END%]

[%|helploc(path_cgi)%]You can check the latest error or reset errors. If you reset errors, the subscriber's score will be reset to zero.[%END%]

[%|helploc%]To manage bouncing addresses more easily, go to the 'Bounces' page of the list administration module.[%END%]

[%|helploc%]Managing bounces[%END%]

[%|helploc%]When there is a problem with subscribers' email addresses (obsolete email addresses, addresses temporarily unavailable when messages were sent, inbox quota exceeded, etc.), the percentage of bouncing addresses displays in the left menu under the text 'Error rate'. To check the bouncing addresses, go to the 'Bounces' page of the list administration module.[%END%]

[%|helploc%]The Sympa software automatically manages bouncing subscribers: according to the number of errors and to the traffic on the list, bouncing subscribers are either notified, unsubscribed, or their score is reset to zero when their address stops bouncing.[%END%]

[%|helploc%]To make addresses stop bouncing, select them by ticking their boxes and click on the 'Reset errors for selected users' button. If the error remains, the addresses will be reported as bouncing again as soon as a message is posted on the list.[%END%]

[%|helploc%]You can also unsubscribe subscribers whose addresses are still bouncing: too many bouncing addresses cause a considerable load on the mailing list server. To unsubscribe subscribers, select them by ticking the boxes in front of their names and click on the 'Delete selected email addresses' button.[%END%]

[%|helploc%]Tip: to select all subscribers at once, first make sure that they are all displayed on the page, and then click on the 'Toggle selection' button: all subscribers will be selected in a single click![%END%]

[%|helploc%]Moderating messages sent to the list[%END%]

[%|helploc%]When a list is moderated, all messages have to be approved of by one of the moderators before being distributed to the list. After sending a message to a list, subscribers are automatically notified by email that their message will be moderated. As for moderators, they also receive a notice message from Sympa, which includes the message to moderate.[%END%]

[%|helploc%]You can perform moderating tasks either by email, by answering the messages received from Sympa, or through the mailing list web interface. To do that, click on the 'Message' link in the 'Moderate' submenu: you are brought to a page that displays all messages to be moderated (the most recent messages are on top of page). To read a message, click on its subject.[%END%]

[%|helploc%]You have two options:[%END%]

  • [%|helploc%]authorize the message distribution on the list;[%END%]
  • [%|helploc%]reject the message and notify sender: when you reject a message, it is better to notify its sender...[%END%]

[%|helploc%]All moderators can decide to distribute a message or not. However, the first moderator to process the message will carry the day. You will be notified by Sympa if you try to process a message that has already been moderated. When there are several moderators, it is easier to moderate messages from the mailing list web interface: thus, you will be able to view all messages remaining to be moderated.[%END%]

[%|helploc%]Whatever the date and time of moderation, the date and time of sending of the message do not change. Thus, if the distribution of the message is allowed with a lot of delay, it is possible to receive a message dated January 1st on December 31st![%END%]

[%|helploc%]In case the message was rejected with a notice, the subscriber who had sent it is notified by email. You can customize the message he/she gets.[%END%]

[%|helploc%]Checking the 'Reject without notification' checkbox allows you to prevent the message author from receiving a notification.[%END%]

[%|helploc%]Checking the 'Add to blacklist' checkbox both skip the rejection notification and adds the message author to the list own blacklist. You can manage the blacklist via the 'edit blacklist' button at the bottom of the page.[%END%]

[%|helploc%]If you wish to customize the rejection message that is sent to a message author, you can do so via the 'Manage rejection messages' button. The message management page will let define a set of rejection messages and define the default one.[%END%]

[%|helploc(path_cgi)%]Reminder: you can add or remove moderators through the list administration module. To do that, from the list information page, click on 'Admin', on 'Edit list config', and then on 'List definition'.[%END%]

[%|helploc(path_cgi)%]It is also possible to process messages after their distribution on the list; this can be useful when a list is not moderated. If you want to delete a message, search for it in the online message archive and click on the 'Tag this mail for deletion' button in the upper right corner of the message. A confirmation message displays; click on 'OK'. The message will be deleted after a few seconds. Be careful: this operation is irreversible!!! If you delete a message, you will not be able to retrieve it.[%END%]

[%|helploc%]Managing the message archive[%END%]

[%|helploc%]The mailing list management robot Sympa can generate downloadable compressed archives (.ZIP format) of the messages sent to the list. To download those archives, select the months that interest you in the list and click on the 'Download Zip file' button.[%END%]

[%|helploc%]Tip: in the 'Archive selection' list, to select all months during which messages have been sent, click on the first month, hold down the SHIFT key and click on the last month of the list.[%END%]

[%|helploc%]You will receive a file of the type 'nameofthelist_archive.zip' containing folders named 'nameofthelist_year-month' (example: list_example_2005-06) for each month. Inside each folder, the file names are numbers representing their position in the chronology of the messages sent (example: the file named '1' contains the first message sent in the month). The message files have no extension; use the text editor of your choice (Notepad, WordPad, Vim, etc.) to open them. Each file represents an entire message (full header).[%END%]

[%|helploc%]From the 'Manage archive' page, you can also delete messages (deletion month by month and not message by message). To do that, select the months that interest you in the list and click on the 'Delete selected months' button.[%END%]

[%|helploc%]Be careful: this operation is irreversible!!! When you click on 'Delete selected months', keep in mind that you are not only deleting an archive file, but also the entire message archive of the selected month!!![%END%]

[%|helploc%]Renaming the list[%END%]

[%|helploc%]In the list administration module, click on 'Rename list'. Enter the name of your choice and click on the 'Rename this list' button. A confirmation message displays; click on 'OK'.[%END%]

[%|helploc%]If you change your mind, you will only have to redo the inverse operation in order to retrieve the former list name.[%END%]

[%|helploc%]Be careful: when you rename a list, you have to let the listmasters know; otherwise, the change will not be effective.[%END%]

[%|helploc%]A few tips to name your lists:[%END%]

  • [%|helploc%]Do not use any spaces, accents or specials characters in list names: those characters might cause problems.[%END%]
  • [%|helploc%]Choose an explicit yet short name: think of the subscribers who will have to type this name every time they will send a message to the list![%END%]
  • [%|helploc(conf.host)%]If you manage a set of lists, you can prefix your lists' names with a common prefix; thus they will be sorted together and will be easily recognizable (example: xx-users@%1, xx-hotline@%1.fr, etc.).[%END%]

[%|helploc%]Deleting the list[%END%]

[%|helploc%]In the list administration module, click on 'Remove list'. A confirmation message displays; click on 'OK'.[%END%]

[%|helploc%]Be careful: if you delete the list, you will not be able to reopen it yourself!!! If you want to reopen the list, you will have to ask the listmasters.[%END%]

[%|helploc%]In real terms, what are the consequences of a list deletion?[%END%]

  • [%|helploc%]All subscribers are immediately unsubscribed automatically (including owners and moderators).[%END%]
  • [%|helploc%]The message archive is preserved but no one can access it anymore.[%END%]
  • [%|helploc%]The documents published in the shared document web space are preserved but no one can access them anymore.[%END%]
  • [%|helploc%]Only the listmasters have the power to reopen the list; if they do, the former list subscribers will automatically be subscribed again.[%END%]

[%|helploc%]Be careful: due to a slight latency, the list pages remain accessible for a while if you know their addresses. Then they will become fully unavailable.[%END%]

[%|helploc%]If you want the list and all the associated files to be deleted permanently, you will have to ask the listmasters.[%END%]

[%|helploc%]Good practices[%END%]

[%|helploc%]In order to have dynamic lists, you must be deeply involved in their exchanges: if a list is neither controlled nor enlivened by its owners, it may result in a loss of quality in its messages and it might even end up vanishing...[%END%]

[%|helploc%]The use of email in general and for mailing lists is bound by a set of precise rules necessary to share pleasant exchanges: the "Netiquette". As a list owner or moderator, it is your role to have subscribers respect it. You will find the general principles of the Netiquette, as well as many links on the page of the Wikipedia dedicated to the Netiquette.[%END%]

[%|helploc%]You should write a charter for your list to define all the rules that apply to its use:[%END%]

  • [%|helploc%]allowed, tolerated and forbidden topics;[%END%]
  • [%|helploc%]writing rules (for example to specify the languages in which the subscribers are to post, to ban "SMS language", etc.);[%END%]
  • [%|helploc%]rules applying when sending messages (frequency, attachments, etc.);[%END%]
  • [%|helploc%]liabilities of subscribers regarding the netiquette;[%END%]
  • [%|helploc%]rights and responsibilities of the subscribers;[%END%]
  • [%|helploc%]information about retention of the messages sent to the list;[%END%]
  • [%|helploc%]legal information and privacy policy;[%END%]
  • [%|helploc%]sanctions applying to those who would not respect the list charter;[%END%]
  • [%|helploc%]etc.[%END%]

[%|helploc%]In order to have all subscribers read the list charter, you should include it in the welcome message they get after subscribing. To do that, you need to customize that message through the 'Customize' page of the list administration module.[%END%]

[%|helploc%]When available, the 'Owner and moderator charter' provides list owners and moderators with all the necessary information to carry out their roles. This Charter involves all the rights and responsibilities of owners and moderators.[%END%]


sympa-6.1.24~dfsg/web_tt2/help_arc.tt2000066400000000000000000000310101246372670100175600ustar00rootroot00000000000000

[%|helploc%]Reading the list archive online[%END%]

[%|helploc%]To read the messages sent to a list, you have two options:[%END%]

  • [%|helploc%]read the messages you receive in your email inbox;[%END%]
  • [%|helploc%]read the list archive online (through the mailing list web interface).[%END%]

[%|helploc%]To read the list archive online, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list of which you want to read the archive.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Archive' link.[%END%]
  4. [%|helploc%]The message index of the current month displays.[%END%]

[%|helploc%]Presentation of messages[%END%]

[%|helploc%]The page always displays the latest messages; thus if the list was idle during the current month, the messages of the latest active month are displayed.[%END%]

[%|helploc%]When there are too many messages to be displayed on a single page, only the latest messages display by default. To change page, click on the arrow links around the current page's number:[%END%]

  • [%|helploc%]The '<' arrow displays older messages.[%END%]
  • [%|helploc%]The '>' arrow displays newer messages.[%END%]
  • [%|helploc%]The '<<' link displays the oldest messages.[%END%]
  • [%|helploc%]The '>>' link displays the latest messages.[%END%]

[%|helploc%]Chronological and thread order[%END%]

[%|helploc%]By default, messages are displayed as threads: they are gathered according to their subject. This way, all answers to a given message are displayed just below the thread's first message and are indented for better readability.[%END%]

[%|helploc%]Be careful: it is possible for two messages to have the same subject without being related; however, they will be displayed as if they belonged to the same thread.[%END%]

[%|helploc%]To display messages chronologically, click on the 'Chronological' link.[%END%]

[%|helploc%]Be careful: this display setting applies as long as you read the messages of a particular month, but if you change month, the default display setting 'Thread' will apply again.[%END%]

[%|helploc%]Browsing through the messages[%END%]

[%|helploc%]To read a message, click on its subject. Then you can browse through the messages sent to the list by clicking on the arrows surrounding the words 'Chronological' and 'Thread':[%END%]

  • [%|helploc%]By clicking on the arrows surrounding the word 'Chronological', you read messages according to their chronological order.[%END%]
  • [%|helploc%]By clicking on the arrows surrounding the word 'Thread', you read messages as a thread (i.e. gathered according to their subject).[%END%]
  • [%|helploc%]The left arrows let you browse through older messages.[%END%]
  • [%|helploc%]The right arrows let you browse through newer messages.[%END%]

[%|helploc%]Other messages belonging to the same thread as the message displayed are listed at its bottom. Click on their subject to read them.[%END%]

[%|helploc%]To come back to the messages index, click on 'Chronological' or 'Thread' according to whether you want to display the index as a chronological history or as a thread. When there are too many messages to be displayed on a single page, you are brought to the index page displaying the message you just read, and not to the page displaying the latest messages.[%END%]

[%|helploc%]To look at another month's archive, click on the number of the month in the calendar at the top left of your screen. Every month during which messages have been sent has a clickable number.[%END%]

[%|helploc%]When you are on a message page, the calendar does not display.[%END%]

[%|helploc%]Content of the messages: links and attachments[%END%]

[%|helploc%]Normally, every link contained in a message is clickable: just click on it and it will bring you to the linked resource.[%END%]

[%|helploc%]Be careful: when you click on a link contained in a message, you leave the mailing list environment. If you do not want to do that, open the link in a new window or tab. Leaving the mailing list environment does not mean that you log out: your logout depends on the value you set for the 'Connection expiration period' option on the 'Your preferences' page.[%END%]

[%|helploc%]When a file is attached to a message, it is displayed at the bottom of the message. To download it, you only have to click on its name.[%END%]

[%|helploc%]Replying to a message from the mailing list web interface[%END%]

[%|helploc%]You can reply to a message directly from the mailing list web interface. To do this, click on the 'Reply' button on the top right of the message.[%END%]

[%|helploc%]Be careful: after clicking on the 'Reply' button, you will not be able to change the recipient anymore; thus be careful when choosing between the message's sender and the list (i.e. all subscribers)![%END%]

[%|helploc%]This method is not very flexible: from the mailing list web interface, you can not add or change some recipients, add an attachment or format the messages you send. Thus you might prefer to use an email client or a webmail.[%END%]

[%|helploc%]Searching in a list archive[%END%]

[%|helploc%]If you want to find a particular message or if you look for information on a topic, you can perform a search in the list archive. To do that, you have two options:[%END%]

  • [%|helploc%]perform a simple search;[%END%]
  • [%|helploc%]perform a search with the advanced search mode.[%END%]
[%|helploc%]Performing a simple search[%END%]

[%|helploc%]Before starting to use the simple search mode, you should know that advanced search is much more flexible than simple search and will suit your needs better if you do not know precisely what you are looking for.[%END%]

[%|helploc%]To perform a simple search, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list of your interest.[%END%]
    [%|helploc%]It is impossible to perform a search in the archive of several lists at once. If you want to do it nevertheless, you will need to repeat the search for each list.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Archive' link. A message index page displays.[%END%]
    [%|helploc%]You can perform searches from index pages and from message pages.[%END%]
  4. [%|helploc%]Go to the message index of the month in which you want to search. To do that, use the calendar displayed at the top left of your screen.[%END%]
    [%|helploc%]The search will process the messages of the month you chose and of the previous month during which messages have been sent. If you do not know when the message was sent, use the advanced search mode.[%END%]
  5. [%|helploc%]Enter your keywords in the search box in the upper right corner of your screen.[%END%]
    [%|helploc%]Be careful: the search engine will process your input as a sentence. If you want to search with several non contiguous words, use the advanced search mode.[%END%]
  6. [%|helploc%]Click on the 'Search' button.[%END%]

[%|helploc%]The messages matching your search display. To know more, please refer to the 'Search results' section.[%END%]

[%|helploc%]Performing a search with the advanced search mode[%END%]

[%|helploc%]As the advanced search mode is much more powerful and flexible than the simple search mode, it is much easier to use. To perform an advanced search, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list of your interest.[%END%]
    [%|helploc%]It is impossible to perform a search in the archive of several lists at once. If you want to do it nevertheless, you will need to repeat the search for each list.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Archive' link. A message index page displays.[%END%]
    [%|helploc%]You can perform searches from index pages and from message pages.[%END%]
  4. [%|helploc%]Click on the 'Advanced search' button below the search box.[%END%]
  5. [%|helploc%]Enter your keywords in the search box.[%END%]
  6. [%|helploc%]Choose your search options.[%END%]
    [%|helploc%]Tip: in the 'Extend search field' list, to select all months for which an archive is available, click on the first month, hold down the SHIFT key and click on the last month of the list. Although it is not indicated in the list, the current month will of course be processed as well.[%END%]
  7. [%|helploc%]Click on the 'Search' button.[%END%]

[%|helploc%]The messages matching your search display. To know more, please refer to the 'Search results' section.[%END%]

[%|helploc%]Search results[%END%]

[%|helploc%]By default, the messages displayed on the results page are sorted from the latest to the oldest and each page displays up to ten results. With the advanced search mode, you can change these settings to match your preferences.[%END%]

[%|helploc%]When there are many results, they are sometimes displayed on several pages. Do not get caught by this: if you can not find the message you are looking for, make sure that there are no other results page before giving up...[%END%]

[%|helploc%]When you specify a large search range, sometimes only a part of the messages is processed in a first step of search. In this case, a button labeled 'Continue search' displays below results. Click on it to search in the rest of messages. If messages are very numerous, sometimes you will need to perform this action several times.[%END%]

[%|helploc%]For each message, the information displayed on the results page is the following:[%END%]

  • [%|helploc%]message subject;[%END%]
  • [%|helploc%]sending date;[%END%]
  • [%|helploc%]sender;[%END%]
  • [%|helploc%]a few lines of context containing the searched keywords.[%END%]

[%|helploc%]To read one of the messages matching your search, you only need to click on its subject.[%END%]

[%|helploc%]Be careful: with Internet Explorer, if you go and read a message from the results page, you will not be able to use the 'Back' button of your browser to come back to that page. We advise you to use a modern browser such as Firefox. If you can not use a browser other than Internet Explorer, we advise you to open messages in a new window (right click on links and choose 'Open in New Window') in order to preserve your search results.[%END%]

sympa-6.1.24~dfsg/web_tt2/help_editfile.tt2000066400000000000000000000033051246372670100206060ustar00rootroot00000000000000
[%|helploc%]Service messages description :[%END%]
  • [%|helploc%]Welcome message: This message is sent to new subscribers. It can be a full MIME structured message (only for MIME gurus).[%END%]
  • [%|helploc%]Unsubscribe message: This message is sent when users leave the list using the UNSUBSCRIBE command.[%END%]
  • [%|helploc%]Deletion message: This message is sent to users when you remove them from the list using the DEL command (unless you hit the Quiet button.[%END%]
  • [%|helploc%]Remind message: This message is sent to each subscriber when using the command REMIND. It's very useful to help people who are confused about their own subscription emails or people who are not able to unsubscribe themselves.[%END%]
  • [%|helploc%]Subscribing invitation message: sent to a person if someone uses the INVITE command to invite someone to subscribe.[%END%]
[%|helploc%]Other files/pages description:[%END%]
  • [%|helploc%]List homepage: HTML text to describe the list. It is printed on the right-hand side of the main list page. (default for this is the list description)[%END%]
  • [%|helploc%]List description: This text is sent as an answer to the mail command INFO. It can also be included in the Welcome message.[%END%]
  • [%|helploc%]Message header: If this file is not empty, it is added as a MIME attachment at the beginning of each message distributed to the list.[%END%]
  • [%|helploc%]Message footer: same as Message header, but attached at the end of the message.[%END%]
sympa-6.1.24~dfsg/web_tt2/help_editlist.tt2000066400000000000000000000424771246372670100206570ustar00rootroot00000000000000 [% FOREACH p = param %] [% p.value.title %] ([% p.key %])[%|helploc%]:[%END%]
[% IF p.key == 'add' %] [%|helploc%]Privilege for adding (ADD command) a subscriber to the list[%END%] [% ELSIF p.key == 'anonymous_sender' %] [%|helploc%]To hide the sender's email address before distributing the message. It is replaced by the provided email address.[%END%] [% ELSIF p.key == 'archive' %] [%|helploc%]Privilege for reading mail archives and frequency of archiving[%END%] [% ELSIF p.key == 'available_user_options' %] [%|helploc%]The available_user_options parameter starts a paragraph to define available options for the subscribers of the list.[%END%]

  • reception modelist ([%|helploc%]Default value: reception mail,notice,digest,digestplain,summary,nomail[%END%])

    [%|helploc%]modelist is a list of modes (mail, notice, digest, digestplain, summary, nomail), separated by commas. Only these modes will be allowed for the subscribers of this list. If a subscriber has a reception mode not in the list, sympa uses the mode specified in the default_user_options paragraph.[%END%]
[% ELSIF p.key == 'bounce' %] [%|helploc%]This paragraph defines bounce management parameters:[%END%]

  • warn_rate ([%|helploc%]Default value: bounce_warn_rate robot parameter[%END%])

    [%|helploc%]The list owner receives a warning whenever a message is distributed and the number (percentage) of bounces exceeds this value.[%END%]

  • halt_rate ([%|helploc%]Default value: bounce_halt_rate robot parameter[%END%])

    [%|helploc%]NOT USED YET. If bounce rate reaches the halt_rate, messages for the list will be halted, i.e. they are retained for subsequent moderation.[%END%]

  • expire_bounce_task ([%|helploc%]Default value:[%END%] daily)

    [%|helploc%]Name of the task template used to remove old bounces. Useful for removing bounced subscriber email. If some messages are distributed without receiving new bounce messages, the subscriber's email seems to be OK again. Active if task_manager.pl is running.[%END%]
[% ELSIF p.key == 'bouncers_level1' %] [%|helploc%]The Bouncers_level1 paragraphs defines the automatic behavior of bounce management.
Level 1 is the lower level of bouncing users[%END%]

  • rate ([%|helploc%]Default value:[%END%] 45)

    [%|helploc%]Each bouncing user have a score (from 0 to 100).[%END%] [%|helploc%]This parameter defines a lower limit for each category of bouncing users.For example, level 1 begins from 45 to level_2_treshold.[%END%]

  • action ([%|helploc%]Default value:[%END%] notify_bouncers)

    [%|helploc%]This parameter defines which task is automaticaly applied on level 1 bouncers.[%END%]

  • notification ([%|helploc%]Default value:[%END%] owner)

    [%|helploc%]When automatic task is executed on level 1 bouncers, a notification email can be send to listowner or listmaster.[%END%]

[% ELSIF p.key == 'bouncers_level2' %] [%|helploc%]The Bouncers_levelX paragraphs defines the automatic behavior of bounce management.
Level 2 is the highest level of bouncing users[%END%]

  • rate ([%|helploc%]Default value:[%END%] 80)

    [%|helploc%]Each bouncing user have a score (from 0 to 100).[%END%] [%|helploc%]This parameter defines the score range defining each category of bouncing users.For example, level 2 is for users with a score between 80 and 100.[%END%]

  • action ([%|helploc%]Default value:[%END%] notify_bouncers)

    [%|helploc%]This parameter defines which task is automaticaly applied on level 2 bouncers.[%END%]

  • notification ([%|helploc%]Default value:[%END%] owner)

    [%|helploc%]When automatic task is executed on level 2 bouncers, a notification email can be send to listowner or listmaster.[%END%]

[% ELSIF p.key == 'cookie' %] [%|helploc%]This parameter is a confidential item for generating authentication keys for administrative commands (ADD, DELETE, etc.). This parameter should remain concealed, even for owners. The cookie is applied to all list owners, and is only taken into account when the owner has the auth parameter.[%END%] [% ELSIF p.key == 'custom_header' %] [%|helploc%]This parameter is optional. The headers specified will be added to the headers of messages distributed via the list. As of release 1.2.2 of Sympa, it is possible to put several custom header lines in the configuration file at the same time.[%END%] [% ELSIF p.key == 'custom_subject' %] [%|helploc%]This parameter is optional. It specifies a string which is added to the subject of distributed messages (intended to help users who do not use automatic tools to sort incoming messages). This string will be surrounded by [] characters.[%END%] [% ELSIF p.key == 'default_user_options' %] [%|helploc%]The default_user_options parameter starts a paragraph to define a default profile for the subscribers of the list.[%END%]

  • reception notice | digest | summary | nomail | mail

    [%|helploc%]Mail reception mode.[%END%]

  • visibility conceal | noconceal

    [%|helploc%]Visibility of the subscriber with the REVIEW command.[%END%]
[% ELSIF p.key == 'del' %] [%|helploc%]This parameter specifies who is authorized to use the DEL command.[%END%] [% ELSIF p.key == 'digest' %] [%|helploc%]Definition of digest mode. If this parameter is present, subscribers can select the option of receiving messages in multipart/digest MIME format, or as a plain text digest. Messages are then grouped together, and compiled messages are sent to subscribers according to the frequency selected with this parameter.[%END%] [% ELSIF p.key == 'editor' %] [%|helploc%]Editors are responsible for moderating messages. If the mailing list is moderated, messages posted to the list will first be passed to the editors, who will decide whether to distribute or reject it.[%END%]
[%|helploc%]FYI: Defining editors will not make the list moderated; you will have to set the "send" parameter.[%END%]
[%|helploc%]FYI: If the list is moderated, any editor can distribute or reject a message without the knowledge or consent of the other editors. Messages that have not been distributed or rejected will remain in the moderation spool until they are acted on.[%END%] [% ELSIF p.key == 'expire_task' %] [%|helploc%]This parameter states which model is used to create an expire task. An expire task regularly checks the subscription or resubscription date of subscribers and asks them to renew their subscription. If they don't they are deleted.[%END%] [% ELSIF p.key == 'footer_type' %] [%|helploc%]List owners may decide to add message headers or footers to messages sent via the list. This parameter defines the way a footer/header is added to a message.[%END%]

  • footer_type mime

    [%|helploc%]The default value. Sympa will add the footer/header as a new MIME part.[%END%]

  • footer_type append

    [%|helploc%]Sympa will not create new MIME parts, but will try to append the header/footer to the body of the message. Predefined message-footers will be ignored. Headers/footers may be appended to text/plain messages only.[%END%]
[% ELSIF p.key == 'host' %] [%|helploc%]Domain name of the list, default is the robot domain name set in the related robot.conf file or in file /etc/sympa.conf.[%END%] [% ELSIF p.key == 'include_file' %] [%|helploc%]Include subscribers from this file. The file should contain one e-mail address per line (lines beginning with a "#" are ignored).[%END%] [% ELSIF p.key == 'include_ldap_2level_query' %] [%|helploc%]This paragraph defines parameters for a two-level query returning a list of subscribers. Usually the first-level query returns a list of DNs and the second-level queries convert the DNs into e-mail addresses. This feature requires the Net::LDAP (perlldap) PERL module.[%END%] [% ELSIF p.key == 'include_ldap_query' %] [%|helploc%]This paragraph defines parameters for a query returning a list of subscribers. This feature requires the Net::LDAP (perlldap) PERL module.[%END%] [% ELSIF p.key == 'include_list' %] [%|helploc%]Include subscribers from other list. All subscribers of list listname become subscribers of the current list. You may include as many lists as required, using one include_list listname line for each included list. Any list at all may be included; you may therefore include lists which are also defined by the inclusion of other lists. Be careful, however, not to include list A in list B and then list B in list A, since this will give rise to an infinite loop.[%END%] [% ELSIF p.key == 'include_remote_sympa_list' %] [%|helploc%]Sympa can contact another Sympa service using HTTPS to fetch a remote list in order to include each member of a remote list as subscriber. You may include as many lists as required, using one include_remote_sympa_list paragraph for each included list. Be careful, however, not to give rise to an infinite loop resulting from cross includes.[%END%]

[%|helploc%]For this operation, one Sympa site acts as a server while the other one acs as client. On the server side, the only setting needed is to give permission to the remote Sympa to review the list. This is controlled by the review scenario.[%END%] [% ELSIF p.key == 'include_sql_query' %] [%|helploc%]This parameter is used to begin a paragraph defining the SQL query parameters. [%END%] [% ELSIF p.key == 'lang' %] [%|helploc%]This parameter defines the language used for the list. It is used to initialize a user's language preference; Sympa command reports are extracted from the associated message catalog.[%END%] [% ELSIF p.key == 'max_size' %] [%|helploc%]Maximum size of a message in 8-bit bytes.[%END%] [% ELSIF p.key == 'owner' %] [%|helploc%]Owners are managing subscribers of the list. They may review subscribers and add or delete email addresses from the mailing list. If you are a privileged owner of the list, you can choose other owners for the mailing list. Privileged owners may edit a few more options than other owners. [%END%] [% ELSIF p.key == 'priority' %] [%|helploc%]The priority with which Sympa will process messages for this list. This level of priority is applied while the message is going through the spool. The z priority will freeze the message in the spool.[%END%] [% ELSIF p.key == 'remind' %] [%|helploc%]This parameter specifies who is authorized to use the remind command.[%END%] [% ELSIF p.key == 'remind_return_path' %] [%|helploc%]Same as welcome_return_path, but applied to remind messages.[%END%] [% ELSIF p.key == 'remind_task' %] [%|helploc%]This parameter states which model is used to create a remind task. A remind task regularly sends subscribers a message which reminds them of their list subscriptions.[%END%] [% ELSIF p.key == 'reply_to_header' %] [%|helploc%]The reply_to_header parameter starts a paragraph defining what Sympa will place in the Reply-To: SMTP header field of the messages it distributes.[%END%]

  • value sender | list | all | other_email ([%|helploc%]Default value:[%END%] sender)

    [%|helploc%]This parameter indicates whether the Reply-To: field should indicate the sender of the message (sender), the list itself (list), both list and sender (all) or an arbitrary e-mail address (defined by the other_email parameter).[%END%]

    [%|helploc%]Note: it is inadvisable to change this parameter, and particularly inadvisable to set it to list. Experience has shown it to be almost inevitable that users, mistakenly believing that they are replying only to the sender, will send private messages to a list. This can lead, at the very least, to embarrassment, and sometimes to more serious consequences.[%END%]

  • other_email [%|helploc%]an_email_address[%END%]

    [%|helploc%]If value was set to other_email, this parameter defines the e-mail address used.[%END%]

  • apply respect | forced ([%|helploc%]Default value:[%END%] respect)

    [%|helploc%]The default is to respect (preserve) the existing Reply-To: SMTP header field in incoming messages. If set to forced, Reply-To: SMTP header field will be overwritten.[%END%]
[% ELSIF p.key == 'review' %] [%|helploc%]This parameter specifies who can access the list of members. Since subscriber addresses can be abused by spammers, it is strongly recommended that you only authorize owners or subscribers to access the subscriber list. [%END%] [% ELSIF p.key == 'send' %] [%|helploc%]This parameter specifies who can send messages to the list.[%END%]

[% ELSIF p.key == 'shared_doc' %] [%|helploc%]This paragraph defines read and edit access to the shared document repository.[%END%] [% ELSIF p.key == 'spam_protection' %] [%|helploc%]There is a need to protect Sympa web sites against spambots which collect email addresses from public web sites. Various methods are available in Sympa and you can choose to use them with the spam_protection and web_archive_spam_protection parameters. Possible value are:[%END%]

  • javascript: [%|helploc%]the address is hidden using a javascript. A user who enables javascript can see a nice mailto address where others have nothing.[%END%]
  • at: [%|helploc%]the @ char is replaced by the string " AT ".[%END%]
  • none: [%|helploc%]no protection against spammer.[%END%]
[% ELSIF p.key == 'subject' %] [%|helploc%]This parameter indicates the subject of the list, which is sent in response to the LISTS mail command. The subject is a free form text limited to one line.[%END%] [% ELSIF p.key == 'subscribe' %] [%|helploc%]The subscribe parameter defines the rules for subscribing to the list.[%END%] [% ELSIF p.key == 'topics' %] [%|helploc%]This parameter allows the classification of lists. You may define multiple topics as well as hierarchical ones. WWSympa's list of public lists uses this parameter.[%END%] [% ELSIF p.key == 'ttl' %] [%|helploc%]Sympa caches user data extracted using the include parameter. Their TTL (time-to-live) within Sympa can be controlled using this parameter. The default value is 3600[%END%] [% ELSIF p.key == 'unsubscribe' %] [%|helploc%]This parameter specifies the unsubscription method for the list. Use open_notify or auth_notify to allow owner notification of each unsubscribe command.[%END%] [% ELSIF p.key == 'visibility' %] [%|helploc%]This parameter indicates whether the list should feature in the output generated in response to a LISTS command or should be shown in the list overview of the web-interface.[%END%] [% ELSIF p.key == 'web_archive' %] [%|helploc%]Defines who can access the web archive for the list.[%END%] [% ELSIF p.key == 'web_archive_spam_protection' %] [%|helploc%]Idem spam_protection is provided but it can be used only for web archives. Access requires a cookie, and users must submit a small form in order to receive a cookie before browsing the archives. This blocks all robot, even google and co.[%END%] [% ELSIF p.key == 'welcome_return_path' %] [%|helploc%]If set to unique, the welcome message is sent using a unique return path in order to remove the subscriber immediately in the case of a bounce.[%END%] [% ELSIF p.key == 'msg_topic' %] [%|helploc%]This paragraph defines a topic used to tag a message of a list, named by msg_topic.name ("other" is a reserved word), its title is msg_topic.title. The msg_topic.keywords entry is optional and allows automatic tagging. This should be a list of keywords, separated by ','.[%END%] [% ELSIF p.key == 'msg_topic_keywords_apply_on' %] [%|helploc%]This parameter indicates which part of the message is used to perform automatic tagging.[%END%] [% ELSIF p.key == 'msg_topic_tagging' %] [%|helploc%]This parameter indicates if the tagging is optional or required for a list.[%END%] [% ELSE %] [%|helploc%]No Comment[%END%] [% END %]
[% END %] sympa-6.1.24~dfsg/web_tt2/help_faqadmin.tt2000066400000000000000000000031121246372670100205750ustar00rootroot00000000000000

[%|helploc%]Administrators Frequently Asked Questions[%END%]

[%|helploc%]A subscriber does not receive the list messages[%END%]

[%|helploc%]To find the cause of the problem, do as follows:[%END%]

  1. [%|helploc%]Check that the user is really subscribed to the list on the 'Manage subscribers' page.[%END%]
  2. [%|helploc%]Check the subscriber's message delivery mode; the page about the subscriber is available by clicking on his/her email address from the members list. When the delivery mode is the 'Digest' mode, it is normal that the subscriber does not receive messages as soon as they are sent.[%END%]
  3. [%|helploc%]Check that the subscriber's email address is not bouncing on the 'Bounces' page. A subscriber email address is considered to be bouncing when messages sent to it generate error logs (called "bounces"). These errors can be temporary (inbox full, mail server unavailable) or permanent (no email account for the user). In any case, the mailing list server automatically manages the deletion of addresses generating too many errors.[%END%]
  4. [%|helploc%]Try and send a message yourself to the subscriber in order to check if he/she receives it.[%END%]
  5. [%|helploc%]As a last resort, contact the listmaster, who will check the mail server logs in order to find the cause of the problem.[%END%]
sympa-6.1.24~dfsg/web_tt2/help_faquser.tt2000066400000000000000000000174161246372670100204770ustar00rootroot00000000000000

[%|helploc%]Users Frequently Asked Questions[%END%]

[%|helploc%]You can not subscribe to a list[%END%]

[%|helploc%]This problem may be due to the following reasons:[%END%]

  • [%|helploc%]The list owners forgot to process your subscription request: to err is human, and your request might have been lost among many other messages! Resubmit your request before contacting list owners directly.[%END%]
  • [%|helploc%]Subscription to the list is reserved to a certain category of people. To know more, contact list owners.[%END%]

[%|helploc%]You can not log on to the mailing list web interface[%END%]

[%|helploc%]This problem may be due to the following reasons:[%END%]

  • [%|helploc(path_cgi)%]You have no password. To be given a password, follow the First login? link from the homepage. You will receive your new password by email.[%END%]
  • [%|helploc(path_cgi)%]You entered an incorrect password. If you forgot your password, you can reset it. To do so, follow the Lost password? link from the homepage.[%END%]
  • [%|helploc%]You are not using the correct username (the email address with which you are subscribed to the list).[%END%]

[%|helploc%]In order to avoid any mistake when typing your password, you can type it into another application (such as your email client) and copy and paste it into your web browser.[%END%]

[%|helploc%]You do not receive (all) messages sent to a list[%END%]

[%|helploc%]This problem can have multiple reasons:[%END%]

  • [%|helploc%]You have never been subscribed to the list[%END%]:
    • [%|helploc%]Maybe you made a mistake in your email address when you tried to subscribe.[%END%]
    • [%|helploc%]Maybe you are subscribed with an email address other than the one you are checking.[%END%]
    • [%|helploc%]Maybe your subscription request was rejected by the list owners.[%END%]
    [%|helploc(path_cgi)%]In any case, try and subscribe again.[%END%]
  • [%|helploc%]You are not subscribed to the list anymore[%END%]:
    • [%|helploc(path_cgi)%]If your address has been bouncing for a while, you might have been automatically unsubscribed by the system (or even by the list owners). Try and subscribe again after ensuring that your email address will not be in trouble again.[%END%]
    • [%|helploc%]If you have not respected the different rules applying to the mailing list, the list owners might have "banned" you...[%END%]
    • [%|helploc(path_cgi)%]You might also have been arbitrarily unsubscribed by an ill-intentioned person, in case the list is not configured to send a confirmation request for any subscription and unsubscription request... In that case, try and subscribe again.[%END%]
  • [%|helploc(path_cgi)%]Your delivery mode does not allow you to receive messages: for example, it is the case with the 'Nomail' delivery mode.[%END%]
  • [%|helploc%]Your inbox is full. Be careful: when your inbox is not completely full, you receive only small messages, which makes it difficult to understand what actually is the problem... Furthermore, if your email address causes trouble on a regular basis, you might be unsubscribed by the list owners or by the system. Thus, you should clean your inbox frequently.[%END%]
  • [%|helploc(path_cgi)%]Your inbox is subject to some restrictions: it does not allow you to receive messages with attachments, bans some types of attachments or limits the maximum size of incoming messages; in that case, we advise you to choose the Urlize delivery mode.[%END%]

[%|helploc%]You can not send messages to a list[%END%]

[%|helploc%]This problem can have multiple reasons:[%END%]

  • [%|helploc%]You have never been subscribed to the list.[%END%]
  • [%|helploc%]You are not subscribed to the list anymore.[%END%]
  • [%|helploc%]You are using another address than the one with which you are subscribed to the list.[%END%]
  • [%|helploc%]If the list is a moderated list, the distribution of the message depends on the moderators' availability: they can not monitor the list night and day! Thus the distribution of your message might be a bit delayed.[%END%]
  • [%|helploc(conf.host)%]If the list is a moderated list, your message might have been rejected by a moderator. In case you received no notice, you can possibly send a message to nameofthelist-request@%1 to ask for an explanation.[%END%]
  • [%|helploc%]The message you are trying to send does not fulfill the conditions to be distributed on the list: it may be too large, contain a forbidden type of attachment or even contain any type of attachment (in case attachments are forbidden on the list).[%END%]
  • [%|helploc%]The problem may also come from your email account:[%END%]
    • [%|helploc%]The mail server is temporarily unavailable.[%END%]
    • [%|helploc%]Your inbox is full and it prevents you from sending new messages.[%END%]
    • [%|helploc%]Your inbox is subject to some restrictions: it does not allow you to send messages with attachments, bans some types of attachments or limits the maximum size of outgoing messages.[%END%]
  • [%|helploc%]Last, you might have made a mistake in the list address when sending your message![%END%]

[%|helploc%]You can not unsubscribe from a list[%END%]

[%|helploc%]This problem can have multiple reasons:[%END%]

  • [%|helploc%]You are using another address than the one with which you are subscribed to the list.[%END%]
  • [%|helploc%]You are subscribed through a dynamic data source (examples: databases, LDAP directories, etc.) which does not allow you to unsubscribe. Contact the list owners to know more about this.[%END%]
  • [%|helploc%]The list owners forgot to process your unsubscription request: to err is human, and your request might have been lost among many other messages! Resubmit your request before contacting list owners directly.[%END%]

[%|helploc%]You want to contact list owners[%END%]

[%|helploc(conf.host)%]The list owners and moderators' names are mentioned in the left menu. However, you should never write directly to a list owner or moderator: first, the person to whom you are writing might be absent, and furthermore, it is better to inform all owners and moderators of your request. When you have a question or remark, the address you should write to is: nameofthelist-request@%1 (replace 'nameofthelist' by the name of the list).[%END%]

sympa-6.1.24~dfsg/web_tt2/help_introduction.tt2000066400000000000000000000263511246372670100215500ustar00rootroot00000000000000

[%|helploc%]Mailing lists - General introduction[%END%]

[%|helploc%]What is a mailing list?[%END%]

[%|helploc%]A mailing list is a distribution list allowing a group of subscribers to automatically receive by email all messages sent to the list: every message sent to the list by a subscriber is received by all the other subscribers. When subscribed to a mailing list, it is possible to send messages, to reply to them or to read them without contributing (i.e. to "lurk").[%END%]

[%|helploc%]Special cases:[%END%]

  • [%|helploc%]It is sometimes possible to send messages to a mailing lit without having subscribed to it. However, you need to be subscribed to a list to receive its messages.[%END%]
  • [%|helploc%]It is sometimes impossible to send messages to the list even though you are actually subscribed to it: it is the case for announcement lists, which are used to transmit information from a unique sender to a large number of recipients.[%END%]

[%|helploc%]Interest of mailing lists[%END%]

[%|helploc%]People subscribe to a mailing list (sometimes abbreviated in ML) to be informed about a particular topic and to take part in exchanges about it. Examples are:[%END%]

  • [%|helploc%]mailing list for all the employees of a company;[%END%]
  • [%|helploc%]mailing list reserved to the participants in a project;[%END%]
  • [%|helploc%]mailing list dedicated to a class of students;[%END%]
  • [%|helploc%]mailing list about the latest news in computer security;[%END%]
  • [%|helploc%]mailing list of mutual aid between handymen;[%END%]
  • [%|helploc%]mailing list restricted to a family and dedicated to the organization of large family gatherings;[%END%]
  • [%|helploc%]and so on![%END%]

[%|helploc%]Types of mailing lists[%END%]

[%|helploc%]There are thousands of mailing lists of all kinds on the Internet: public or private, free or not, with subscription subject to conditions or not, etc. Those lists may have from a dozen up to several thousand members.[%END%]

[%|helploc%]According to the way they work, we can distinguish between two types of lists:[%END%]

  • [%|helploc%]Announcements lists allow subscribers to receive messages without being allowed to post some themselves. In fact, those messages are newsletters: electronic magazines, daily services (daily horoscope, daily weather report, daily security alert, etc.), update notices about a website, etc. With this type of mailing list, the information flows from a unique sender to a large number of recipients.[%END%]
  • [%|helploc%]Discussion lists allow all subscribers to take part in exchanges. Those lists can be moderated or not:[%END%]
    • [%|helploc%]In a moderated discussion list, messages are transmitted to all subscribers after approval by one of the list moderators. Moderation is a token of quality for the list. For example, it ensures that subscribers will not receive off-topic messages, unsolicited commercial messages (spams), messages containing large attachments, etc.[%END%]
    • [%|helploc%]In a non moderated discussion list, messages are transmitted to all subscribers as soon as the mailing list management robot receives them.[%END%]

[%|helploc%]Features[%END%]

[%|helploc%]Once subscribed to a mailing list service, you can:[%END%]

[%|helploc%]How the mailing list service works: roles and responsibilities[%END%]

[%|helploc%]A mailing list service involves four types of roles:[%END%]

  • [%|helploc%]listmaster;[%END%]
  • [%|helploc%]owner;[%END%]
  • [%|helploc%]moderator;[%END%]
  • [%|helploc%]subscriber.[%END%]

[%|helploc%]It is possible to have several roles at once (for example, you can be an owner and a moderator of a list and be subscribed to several others).[%END%]

[%|helploc%]Listmasters[%END%]

[%|helploc%]Listmasters are in charge of the management of the mailing list service. Their duties:[%END%]

  • [%|helploc%]manage the mailing list server (deployment, maintenance, etc.);[%END%]
  • [%|helploc%]define the general orientations of the mailing list service:[%END%]
    • [%|helploc%]who will be allowed to ask for the creation of a new list,[%END%]
    • [%|helploc%]which options will be available to manage lists (scenario definition),[%END%]
    • [%|helploc%]what the default files will contain (creation of templates),[%END%]
    • [%|helploc%]what will the mailing list web interface look like;[%END%]
  • [%|helploc%]set the way the mailing list service should be used and document those rules through providing charters to subscribers, moderators and owners;[%END%]
  • [%|helploc%]approve of requests of mailing list creations;[%END%]
  • [%|helploc%]temporarily replace list owners when necessary; on the other hand, listmasters are not supposed to take the place of moderators.[%END%]

[%|helploc%]List owners and moderators can turn to listmasters when they face a problem not dealt with by the documentation or for any comment. However, in order not to flood listmasters with messages, it is recommended that subscribers rather turn to list owners.[%END%]

[%|helploc%]Owners[%END%]

[%|helploc%]The list owner is generally its creator or, failing him/her, the person who requested the list creation or who became responsible for it. His/her role:[%END%]

[%|helploc%]A list can have several owners. However, the 'Privileged' profile is reserved to the list's creator; other owners have a 'Normal' profile, which has fewer prerogatives.[%END%]

[%|helploc%]Moderators[%END%]

[%|helploc(path_cgi)%]Moderators are appointed by the list owner. They are in charge of controlling the relevancy of the messages sent to the list: after reading them, they choose to accept or to reject them . Moderation occurs before the message is actually sent to subscribers. Rejection of a message is possibly followed by a notice to the sender in order to explain the reason for that rejection.[%END%]

[%|helploc%]A list can have one or several moderators; generally, the list owner is also a moderator.[%END%]

[%|helploc%]This concerns only moderated lists.[%END%]

[%|helploc%]Regulatory framework[%END%]

[%|helploc%]The use of a mailing list service means respecting a number of rules:[%END%]

  • [%|helploc%]In most mailing list services, subscribers receive a 'List subscribers charter' on subscription. Then they are obliged to respect all the rules contained in that charter.[%END%]
  • [%|helploc%]If available, list owners and moderators have to conform to the 'Owner and moderator charter'.[%END%]
  • [%|helploc%]The use of mailing lists naturally means respecting the rules of good practices as regards email (the "Netiquette").[%END%]

[%|helploc(path_cgi)%]To know more, refer to the section dedicated to good practices for subscribers and to the section about good practices for owners and moderators.[%END%]


sympa-6.1.24~dfsg/web_tt2/help_listconfig.tt2000066400000000000000000000511771246372670100211740ustar00rootroot00000000000000

[%|helploc%]Configuring the list[%END%]

[%|helploc%]As the list configuration is quite complex, it is divided in several parts with a separate page for each part:[%END%]

[%|helploc%]We advise you to make configuration changes very progressively: thus, if the result does not match your expectations, it will be easier to backtrack.[%END%]

[%|helploc%]The list administration module offers you many options to configure your list. However, these options might not match your own needs perfectly. To cure this problem, you can ask the listmasters to create new options (within the limits of what they can do, naturally...): access and/or rights limited to some categories of people according to the place they log on from, the domain of their email address, the user group they belong to, etc.[%END%]

[%|helploc%]List definition[%END%]

[%|helploc%]In the 'Subject of the list' area, you can change the subject you had chosen when creating the list. This subject displays as a header for all the list pages, and is also be visible on list index pages (list of lists, list of your subscriptions, etc.) and in the browser title bar.[%END%]

[%|helploc%]You can also change the 'Visibility of the list'. The options available are the following:[%END%]

  • [%|helploc%]conceal except for subscribers (conceal) - default option;[%END%]
  • [%|helploc%]intranet access (intranet);[%END%]
  • [%|helploc%]no conceal (noconceal);[%END%]
  • [%|helploc%]conceal even for subscribers (secret);[%END%]

[%|helploc%]If you want to limit the list visibility according to other criteria, you should ask the listmasters: they may be able to create a new option matching your needs (example: list visible only by members of a user group, of an Internet domain, etc.).[%END%]

[%|helploc%]In the 'Owners' and 'Moderators' areas, you can add owners and moderators to the list or change their personal information:[%END%]

  • [%|helploc%]For each owner/moderator, you have to indicate an email address and a name.[%END%]
  • [%|helploc%]You can also add the information of your choice in the 'Private information' input box (phone number, function, etc.); this information will only be accessible by listmasters and list owners with a 'Privileged' profile.[%END%]
  • [%|helploc%]You can change the message delivery mode (the only options available on this page are 'mail' and 'nomail').[%END%]
  • [%|helploc%]The 'Profile' parameter can not be changed: the 'Privileged' profile is reserved to the list creator (other owners have a 'Normal' profile).[%END%]

[%|helploc%]Be careful: becoming owner or moderator of a list does not mean that you are automatically subscribed to that list. Thus owners and moderators have to subscribe themselves to the list.[%END%]

[%|helploc%]To delete owners/moderators, clear the content of the input boxes relating to the person you want to delete and click on the 'Update' button.[%END%]

[%|helploc%]You can also change the list topic as well as its language. If you change the list language, all predefined messages will be sent in the chosen language (be careful: subject to the availability of a translation!).[%END%]

[%|helploc%]You can not change the Internet domain of the list: only the listmasters can change this parameter.[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

[%|helploc%]Sending/receiving[%END%]

[%|helploc%]From this page, you can decide who will be allowed to post messages on the list.[%END%]

[%|helploc%]In the 'Digest frequency' area, you can define how frequently compiled messages are sent (Digest and Summary delivery modes): in the list, select all the days for which you want digests to be sent. Then choose a delivery time for digests (please avoid choosing a time between 11 p.m. and midnight).[%END%]

[%|helploc%]In the 'Available subscription options' list, select all the subscriber options you want to offer to your subscribers. By default, all options are selected.[%END%]

[%|helploc%]The 'Reply address' area allows you to define the default recipients of any reply to a message sent to the list:[%END%]

  • [%|helploc%]With the 'All' value, the reply is sent to both the message sender AND the list.[%END%]
  • [%|helploc%]With the 'List' value, the reply is sent to the list.[%END%]

    [%|helploc%]Be careful: use this option carefully! Experience shows that subscribers do not always check the address to which they send their reply. Thus, they might send private messages to the whole list when trying to reply to a single person...[%END%]

  • [%|helploc%]With the 'Other_email' value, the reply is sent to a predefined address. If you choose this option, you will have to indicate an email address in the 'Other email address' input box.[%END%]
  • [%|helploc%]With the 'Sender' value, the reply is sent to the message sender. This is the option you should probably choose.[%END%]

[%|helploc%]The 'Respect of existing header field' drop-down list allows you to choose how the 'Reply-To' SMTP header field will be processed in incoming messages. The 'Respect' option preserves that field while the 'Forced' option allows it to be overwritten.[%END%]

[%|helploc%]Last, the 'Subject tagging' option lets you choose the text included before the subject of all messages sent to the list: this allows subscribers to sort their messages easily, to use message filters on them in order to process messages automatically, etc. By default, this text consists of the list name surrounded by square brackets (the square brackets are automatically added by the system, thus it is useless that you add them yourself).[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

[%|helploc%]Privileges[%END%]

[%|helploc%]From this page, you can decide:[%END%]

  • [%|helploc%]who can view list information. The following options are available:[%END%]
    • [%|helploc%]for anyone (open) - default option;[%END%]
    • [%|helploc%]restricted to subscribers (private).[%END%]
  • [%|helploc%]who can subscribe to the list. The following options are available:[%END%]
    • [%|helploc%]subscription request confirmed (auth);[%END%]
    • [%|helploc%]need authentication (notification is sent to owners) (auth_notify);[%END%]
    • [%|helploc%]requires authentication then owner approval (auth_owner);[%END%]
    • [%|helploc%]subscribe is impossible (closed);[%END%]
    • [%|helploc%]restricted to local domain users (intranet);[%END%]
    • [%|helploc%]local domain users or owner approval (intranetorowner);[%END%]
    • [%|helploc%]for anyone without authentication (open) - default option;[%END%]
    • [%|helploc%]anyone, notification is sent to list owner (open_notify);[%END%]
    • [%|helploc%]anyone, no welcome message (open_quiet);[%END%]
    • [%|helploc%]owner approval (owner);[%END%]
    • [%|helploc%]requires S/MIME signed (smime);[%END%]
    • [%|helploc%]requires S/MIME signed or owner approval (smimeorowner).[%END%]
      [%|helploc%]You should always choose an option involving the 'auth' parameter: this way, the system requires confirmation from the future subscriber by email before subscribing him/her to the list. This avoids subscriptions with invalid email addresses and guarantees that no one can be subscribed to the list without knowing it.[%END%]
  • [%|helploc%]who can unsubscribe from the list. The following options are available:[%END%]
    • [%|helploc%]need authentication (auth);[%END%]
    • [%|helploc%]authentication requested, notification sent to owner (auth_notify);[%END%]
    • [%|helploc%]impossible (closed);[%END%]
    • [%|helploc%]open (open) - default option;[%END%]
    • [%|helploc%]open with mail confirmation, owner is notified (open_notify);[%END%]
    • [%|helploc%]owner approval (owner).[%END%]

      [%|helploc%]You should always choose an option involving the 'auth' parameter: this way, the system requires confirmation from the subscriber by email before unsubscribing him/her from the list. This prevents ill-intentioned people from unsubscribing others without letting them know.[%END%]

  • [%|helploc%]who can invite people to subscribe to the list. The following options are available:[%END%]
    • [%|helploc%]closed (closed);[%END%]
    • [%|helploc%]list owner, no authentication (owner);[%END%]
    • [%|helploc%]restricted to subscribers (private) - default option;[%END%]
    • [%|helploc%]public (public).[%END%]
  • [%|helploc%]who can review subscribers. The following options are available:[%END%]
    • [%|helploc%]no one can review (closed);[%END%]
    • [%|helploc%]restricted to subscribers or local domain users (intranet);[%END%]
    • [%|helploc%]listmaster only (listmaster);[%END%]
    • [%|helploc%]only owner (and listmaster) (owner) - default option;[%END%]
    • [%|helploc%]restricted to subscribers (private);[%END%]
    • [%|helploc%]anyone can do it! (public).[%END%]
      [%|helploc%]You should in no case make the members list accessible to anyone. The 'Restricted to subscribers' option may be interesting in order to allow subscribers to communicate with each others without posting messages on the list. However, this is not appropriate in the case of an announcement list involving subscribers having no particular relationship.[%END%]

[%|helploc%]From this page, you can also define the access rights applying to the shared document web space ('Shared documents' section of the list, accessible through a link in the left menu). You can define both the read and write access rights for the documents: [%END%]

  • [%|helploc%]The following options are available in the 'Who can view' drop-down list:[%END%]
    • [%|helploc%]restricted to subscribers or local domain users (intranet);[%END%]
    • [%|helploc%]restricted to list owners (owner);[%END%]
    • [%|helploc%]restricted to subscribers (private) - default option;[%END%]
    • [%|helploc%]public documents (public).[%END%]
  • [%|helploc%]The following options are available in the 'Who can edit' drop-down list:[%END%]
    • [%|helploc%]restricted to list owners (owner) - default option;[%END%]
    • [%|helploc%]moderated for subscribers (editor);[%END%]
    • [%|helploc%]restricted to subscribers (private);[%END%]
    • [%|helploc%]public documents (public).[%END%]

[%|helploc%]The 'Quota' input box allows you to define a maximum size not to be exceeded for the shared document web space. This size does not represent the maximum size of one document published in the shared document web space, but the maximum size for all documents published on the list. It is expressed in kilobytes. When a subscriber tries to publish a too large document regarding the space left, he/she gets an error message.[%END%]

[%|helploc(path_cgi)%]To know more about the management of the shared document web space (how to organize it, change access rights, name documents, etc.), please refer to the 'Using the shared document web space' section of the User guide.[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

[%|helploc%]Archive[%END%]

[%|helploc%]From this page, you can decide who can access the online list archive (messages readable on the mailing list web interface). The following options are available:[%END%]

  • [%|helploc%]closed (closed);[%END%]
  • [%|helploc%]restricted to local domain users (intranet);[%END%]
  • [%|helploc%]listmaster (listmaster);[%END%]
  • [%|helploc%]owner (owner);[%END%]
  • [%|helploc%]moderator (moderator);[%END%]
  • [%|helploc%]subscribers only (private);[%END%]
  • [%|helploc%]public (public).[%END%]

[%|helploc%]The 'Quota' input box allows you to define a maximum size not to be exceeded for the message archive. This size is expressed in kilobytes. List owners are notified when the archive size reaches 95 % of the allowed size. When the maximum size is reached, later messages are not archived.[%END%]

[%|helploc%]Even though messages stop being archived, it is naturally still possible to send messages to the list.[%END%]

[%|helploc(conf.email,conf.host)%]It is also possible to access the list archive by email, by sending %1@%2 the following command: get nameofthelist log.yearmonth (example: get list_example log.200507). Then you receive a compilation of all messages sent during the chosen month. This compilation is sent in plain text and contains HTML tags instead of the original formatting; it also involves full headers for each message. The 'Text archives' parameter allows you to define:[%END%]

  • [%|helploc%]who is allowed to receive the list message archive by email;[%END%]
  • [%|helploc%]how frequently these archive files will be created. For example, if the frequency is set to 'month', all messages distributed to the list in a month will be gathered in a single archive file.[%END%]

[%|helploc%]If this parameter is not defined, the list will have no archive accessible by email. Be careful: only the listmasters have the power to change this parameter.[%END%]

[%|helploc%]It is possible to send messages encrypted in S/MIME to the list. The 'Archive encrypted mails as cleartext' option allows you to define how those messages will be archived:[%END%]

  • [%|helploc%]The 'Decrypted' option archives the message after removing the encryption.[%END%]
  • [%|helploc%]The 'Original' option archives the message under its original encrypted form.[%END%]

[%|helploc%]This option applies to both the text archive and the online archive, as well as to the compilations sent to those who chose the 'Digest' delivery mode.[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

[%|helploc%]Bounces[%END%]

[%|helploc%]"Bounces" represent the subscribers whose address is in error, i.e. subscribers who can not receive the messages sent to the list. This can be due to many reasons: obsolete email addresses, addresses temporarily unavailable when messages were sent, inbox quota exceeded, etc.[%END%]

[%|helploc%]The 'Bounce management' section defines two rates:[%END%]

  • [%|helploc%]The warn rate indicates the bouncing email addresses rate from which the list owner will receive a notice entitled 'Bounce rate too high' inviting him/her to delete bouncing subscribers from the list.[%END%]
  • [%|helploc%]The halt rate indicates the bouncing email addresses rate from which the message distribution will automatically be stopped up to the resolution of the problem (generally through the deletion of bouncing subscribers).[%END%]

[%|helploc%]The 'Management of bouncers, 1st level' and 'Management of bouncers, 2nd level' sections allow you to perform automatic tasks with regard to bouncing subscribers. You can define:[%END%]

  • [%|helploc%]the score ranges that define level 1 and level 2 bouncers. By default, level 1 bouncers have a score comprised between 45 and 74 and level 2 bouncers have a score comprised between 75 and 100;[%END%]

    [%|helploc%]The score depends on the number, type and frequency of errors. If the bouncing time is too short or if there have not been many errors, the bouncer is not given a score.[%END%]

  • [%|helploc%]the task to perform towards the subscribers concerned: no task, notice, deletion from the list;[%END%]
  • [%|helploc%]the person to be notified when a task is carried out: no one, the list owners, the listmasters. The notice sent when a task is carried out involves the names of all the subscribers concerned as well as precise information about the task.[%END%]

[%|helploc(path_cgi)%]To manage bounces (reset errors for some subscribers, unsubscribe bouncing subscribers, request a subscription reminder, etc.), go to the 'Bounces' page of the list administration module.[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

[%|helploc%]Miscellaneous[%END%]

[%|helploc%]The 'Periodical subscription expiration task' option allows you to define an automatic expiration deadline for subscriptions to the list: on a regular basis (example: once a year), subscribers will receive a message asking them to renew their subscription to the list. If they don't, they will automatically be unsubscribed. This procedure ensures that all people subscribed to the list are really concerned and interested.[%END%]

[%|helploc%]The 'Periodical subscription reminder task' option allows you to send subscription reminders to list members on a regular basis.[%END%]

[%|helploc%]The 'email address protection method' option ensures that the subscribers' email addresses will not be collected by robots for spamming purposes. This option applies to all the list pages.[%END%]

[%|helploc%]On this page, you can also view information about the last update of the list (who made it and when), as well as the number of configuration change since the list was created.[%END%]

[%|helploc%]BE CAREFUL: do not forget to click on the 'Update' button on bottom of page to save all your changes.[%END%]

sympa-6.1.24~dfsg/web_tt2/help_mail_commands.tt2000066400000000000000000000075561246372670100216400ustar00rootroot00000000000000

[%|helploc%]List of the commands of the Sympa mail interface[%END%]

[%|helploc(conf.email ,conf.host)%]All commands are to be sent at %1@%2.[%END%]

[%|helploc%]It is possible to send several commands in a single message. Commands are to be entered in the message body (one command per line).[%END%]

[%|helploc%]Commands for users[%END%]

[%|helploc%]HELP: receive a list of all available commands[%END%]
[%|helploc%]LISTS: receive a list of all lists managed on the server[%END%]
[%|helploc%]WHICH: receive a list of all lists to which you are subscribed[%END%]
[%|helploc%]CONFIRM key: confirm sending of a message (according to the way the list is configured)[%END%]
[%|helploc%]QUIT: indicates the end of the commands (used to ignore a signature)[%END%]

[%|helploc%]INFO list: get information about the list[%END%]
[%|helploc%]REVIEW list: receive a list of all list members[%END%]
[%|helploc%]SUBSCRIBE list name: subscription (or subscription confirmation) to the list[%END%]
[%|helploc%]INVITE list email: invite someone to subscribe to the list[%END%]
[%|helploc%]UNSUBSCRIBE list email: unsubscribe from the list. The email address is required only if you want to unsubscribe with an address other than the address with which you send the message[%END%]
[%|helploc%]UNSUBSCRIBE * email: unsubscribe from all the lists to which you are subscribed[%END%]

[%|helploc%]SET list NOMAIL: suspend receipt of the list's messages[%END%]
[%|helploc%]SET list DIGEST: receive messages in digest mode[%END%]
[%|helploc%]SET list DIGESTPLAIN: receive messages in digest mode (plain text)[%END%]
[%|helploc%]SET list SUMMARY: only receive the message list[%END%]
[%|helploc%]SET list NOTICE: only receive the message subjects[%END%]
[%|helploc%]SET list MAIL: normal message delivery mode[%END%]
[%|helploc%]SET list CONCEAL: become unlisted (hidden subscriber address)[%END%]
[%|helploc%]SET list NOCONCEAL: subscriber address visible via REView[%END%]

[%|helploc%]INDEX list: receive the list of the archive files[%END%]
[%|helploc%]GET list file: receive a file of the list archive[%END%]
[%|helploc%]LAST list: receive the list's most recent message[%END%]

[%|helploc%]Commands for list owners[%END%]

[%|helploc%]ADD list email name: add a member to the list[%END%]
[%|helploc%]DEL list email: remove a subscriber from the list[%END%]
[%|helploc%]STATS list: check the statistics for the list[%END%]
<[%|helploc%]code>REMIND list: send to all subscribers a personalized reminder with the address with which he/she is subscribed to the list[%END%]

[%|helploc%]Commands for list moderators[%END%]

[%|helploc%]DISTRIBUTE list key: approve of a message[%END%]
[%|helploc%]REJECT list key: reject a message to be moderated[%END%]
[%|helploc%]MODINDEX list: check the list of messages to be moderated[%END%]

sympa-6.1.24~dfsg/web_tt2/help_sendmsg.tt2000066400000000000000000000217101246372670100204610ustar00rootroot00000000000000

[%|helploc%]Sending a message[%END%]

[%|helploc%]When you are subscribed to a list, you receive all messages subscribers send. You can reply to those messages or send some yourself.[%END%]

[%|helploc%]Sending a message with an email client[%END%]

[%|helploc(conf.host)%]To send a new message, it is very simple: from your email client or a webmail, send a message to the list address. This address consists of the list name followed by the suffix '@%1' (example: psycho_cognitive(@)%1).[%END%]

[%|helploc%]Be careful: you need to send the message from the address with which you subscribed to the list, otherwise, your message might be rejected.[%END%]

[%|helploc%]Sending a message from the mailing list environment[%END%]

[%|helploc%]You can also log on to the mailing list environment, go to the information page of the list to which you want to send a message and click on the 'Post' link in the left menu.[%END%]

[%|helploc%]This method is not very flexible: from the mailing list web interface, you can not add or change some recipients, add an attachment or format the messages you send.[%END%]

[%|helploc%]Replying to a message[%END%]

[%|helploc%]To reply to a message sent to a list, do as for any message that would have been sent to you in private. However, be careful: some lists are configured to send any reply to the list by default, i.e. to all the subscribers. If you only want to reply to the message sender, make sure the recipient of your message is the right one![%END%]

[%|helploc%]You can also reply to a message directly from the mailing list environment. However, it is far simpler and more functional to reply using an email client or a webmail...[%END%]

[%|helploc%]A few rules[%END%]

[%|helploc%]Sending messages to a list makes you liable as an author. Furthermore, if you send a message to a list, it will be read by all the subscribers and you are likely to strike up conversations with them. Thus, to use the mailing list service within the law and share pleasant and respectful exchanges, you have to respect a comprehensive set of rules.[%END%]

[%|helploc%]Before you start writing to a list[%END%]

[%|helploc%]It is better to always respect an observation period of a few days after subscribing, prior to sending any message. This will allow you to gather useful information in order not to make a blunder:[%END%]

  • [%|helploc%]Who can send messages to the list?[%END%]
  • [%|helploc%]How frequently can you send messages without disturbing the other subscribers?[%END%]
  • [%|helploc%]What are the topics discussed?[%END%]
  • [%|helploc%]Are off-topic messages tolerated?[%END%]
  • [%|helploc%]What is the tone used? Is it allowed to joke or is the list very formal?[%END%]
  • [%|helploc%]Is the sending of attachments allowed/tolerated? Within what limits?[%END%]

[%|helploc%]Some lists require subscribers to introduce themselves to the other contributors at the time of their subscription, others on the first message sent to the list, and a third category (most generally the largest lists) consider this to be useless and annoying... Study the uses and take appropriate action![%END%]

[%|helploc%]Privacy[%END%]

[%|helploc(path_cgi)%]All messages sent to the list are kept in the list archive. Thus, the simple fact of sending a message constitutes an express authorization of distribution and reproduction in the archive. However, you can request the deletion of any message you sent, whether directly from the list archive ('Tag this mail for deletion' button) or by contacting the list owners.[%END%]

[%|helploc%]If you send a message to the list, your email address will naturally display in your message header and in the list archive. However, unless otherwise specified, your email address and the other data you provided when you subscribed will not be disclosed to any other third party without your agreement.[%END%]

[%|helploc(path_cgi)%]In most countries, your personal data is protected through a number of laws. For example, in the United States, the Privacy Act of 1974 applies. In the European Union, the "Directive 95/46/EC of the European Parliament and of the Council of 24 October 1995 on the protection of individuals with regard to the processing of personal data and on the free movement of such data" and national laws arising from it apply. To know more about this, please contact the list owners.[%END%]

[%|helploc%]As for any correspondence, you must sign your messages. On professional mailing lists, it is customary to mention the name of the organization you belong to and your job title alongside your name. However, ask yourself whether it is relevant to give your complete details (address, telephone number, etc.): they will remain available at any time in the list archive...[%END%]

[%|helploc%]Do never send information about other people without their express agreement.[%END%]

[%|helploc%]Good practices[%END%]

[%|helploc%]When asking a question on a list, it is customary to post a summary of all answers obtained.[%END%]

[%|helploc%]When you reply to a message sent on the list, it is up to you to decide whether you will reply on the list or in private. This might depend on the interest of your reply for the other subscribers...[%END%]

[%|helploc%]Always use descriptive subjects for your messages. On some lists, typical subjects for messages are even predefined and it is compulsory to "tag" messages using one of them (examples of typical objects: [summary], [urgent], [administrative], [question], etc.).[%END%]

[%|helploc%]Some kinds of messages are not welcome on mailing lists: advertisement, spamming, commercial messages, virus warnings, test messages, political or religious messages, hoaxes, flaming, privacy invasions, messages damaging, misleading or in any way defamatory, harassing, offensive, abusive, infringing, racist, obscene or profane, promoting discrimination, violence or hatred for any reason, contrary to good morals, or more generally illegal.[%END%]

[%|helploc%]Unconstructive and mean-spirited messages (example: remarks about spelling mistakes) and other personal attacks towards other contributors are either not welcome on lists. If you really need to tell unpleasant or offensive things to someone, you had better do it in a private message... On most of the mailing lists, it is also frowned on to feed the trolls (topics or posts deliberately incorrect, intended to provoke readers).[%END%]

[%|helploc%]Generally, a list uses only one language for the exchanges between contributors. Respect this rule even though you are not a native speaker of the language used. Also try to respect the elementary rules of grammar and spelling, ban "SMS language" and proofread yourself before posting your message![%END%]

[%|helploc%]When sending a message, you may want to add one or several attachments. However, be careful to respect a few elementary rules:[%END%]

  • [%|helploc%]Make sure that attachments are accepted on the list to which you send your message.[%END%]
  • [%|helploc%]When attachments are allowed, remain reasonable: too many or too large attachments may disturb the other subscribers, for example by flooding their inboxes.[%END%]

[%|helploc(path_cgi)%]If you want to share documents with other list members, you had probably better upload them in the 'Documents' section of the list.[%END%]

[%|helploc%]The use of email in general and for mailing lists is bound by a set of precise rules necessary to share pleasant exchanges: the "Netiquette". You will find the general principles of the Netiquette, as well as many links on the page of the Wikipedia dedicated to the Netiquette.[%END%]

sympa-6.1.24~dfsg/web_tt2/help_shared.tt2000066400000000000000000000433431246372670100202750ustar00rootroot00000000000000

[%|helploc%]Using the shared document web space[%END%]

[%|helploc%]Some lists have a shared document web space where subscribers can download and upload documents: this space is available through the 'Shared documents' section.[%END%]

[%|helploc%]Presentation of the documents in the shared document web space[%END%]

[%|helploc%]To access the 'Shared documents' section of a list, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list of your interest.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Shared documents' link.[%END%]

[%|helploc%]The 'Shared documents' section can contain three types of resources: folders, files and bookmarks.[%END%]

  • [%|helploc(icons_url)%]Folders are preceded by the icon .[%END%]
    • [%|helploc%]To browse a folder, click on its name.[%END%]
    • [%|helploc%]To go back up a level, click on the 'Up to higher level directory' link in the upper right corner of your screen.[%END%]
  • [%|helploc%]Files are preceded by icons related to each type of file. You can download and upload some.[%END%]
  • [%|helploc(icons_url)%]Bookmarks are preceded by the icon . They consist of shortcuts providing a single-click access to a particular website. If you click on a bookmark label, the website linked will open in a new window.[%END%]

[%|helploc%]The functions of editing and creation of documents, when they are available to you, are accessible through the Expert mode. To switch to expert mode, click on the Expert mode button on top of page.[%END%]

[%|helploc%]The list owner or the documents authors can choose to restrict the access rights to some files/folders. Both the reading and writing rights can be restricted:[%END%]

  • [%|helploc%]When a folder is not writable, you can neither upload nor create documents in it.[%END%]
  • [%|helploc%]When a folder is not readable, you can not browse it (you get an error message).[%END%]

[%|helploc%]Folders are sorted separately from files and bookmarks, and display before them. By default, documents are sorted ascendingly according to the 'Document' column.[%END%]

[%|helploc%]Be careful: alphanumeric sort distinguishes uppercase from lowercase, thus all documents which have a name starting with an uppercase character display first, sorted in alphabetical order, and then, all documents which have a name starting with a lowercase character, sorted in alphabetical order. It is the same for folders.[%END%]

[%|helploc%]You can sort documents and folders according to criteria other than the name of the document/folder: they can also be sorted according to their author, their size and their last update date. To sort documents according to the criterion of your choice, click on the name of the corresponding column.[%END%]

[%|helploc%]Downloading documents from the shared document web space[%END%]

[%|helploc%]To download a document from a list, do as follows:[%END%]

  1. [%|helploc%]Go to the 'Shared documents' section of the list of your interest.[%END%]
  2. [%|helploc%]Browse the folder containing the file you want to download.[%END%]
  3. [%|helploc%]Select the 'expert mode'.[%END%]
  4. [%|helploc%]Click on the name of the file to save it on your hard disk.[%END%]

    [%|helploc%]Be careful: files available in formats likely to open in a web browser will. To download this type of files, you had rather make a right click on their names and choose 'Save target as...', 'Save link as...', etc., according to your browser. This can affect, among others, files of formats .HTM/HTML, .PDF, .PNG, .TXT, .SWF, etc. (this behavior varies according to your browser and settings).[%END%]

[%|helploc%]Uploading documents in the shared document web space[%END%]

[%|helploc%]Creating a folder on a list[%END%]

[%|helploc%]To create a folder on a list, do as follows:[%END%]

  1. [%|helploc%]Go to the 'Shared documents' section of the list of your interest.[%END%]
  2. [%|helploc%]Browse the folder in which you want to create a folder.[%END%]
  3. [%|helploc%]Switch to expert mode.[%END%]
  4. [%|helploc%]Enter the folder name in the text field close to the 'Create a new folder inside [name of the current folder]' label.[%END%]
  5. [%|helploc%]Click on the 'Create' button.[%END%]
[%|helploc%]Uploading a file on a list[%END%]

[%|helploc%]To upload a file on a list, do as follows:[%END%]

  1. [%|helploc%]Go to the 'Shared documents' section of the list of your interest.[%END%]
  2. [%|helploc%]Browse the folder in which you want to upload your file. Create a new folder if necessary.[%END%]
  3. [%|helploc%]Switch to expert mode.[%END%]
  4. [%|helploc%]Click on the 'Browse' button below the 'Upload a file inside the folder [name of the current folder]' label and choose the file you want to upload; after selecting it, click on the 'Open' button. Your file path then displays in the input box close to the 'Browse' button.[%END%]
  5. [%|helploc%]Click on the 'Publish' button.[%END%]

[%|helploc%]Be careful: list owners may define quotas, that is to say a maximum size not to be exceeded for the shared document web space. If you try to upload or create a too large document with regard to the space left, you will get the following error message: "The document repository exceed disk quota".[%END%]

[%|helploc%]In order to avoid any list overload, try and delete useless files as you go along. A good organization of the list will allow you to manage the shared document web space more easily. To save some space, you can also publish your files in compressed formats.[%END%]

[%|helploc%]Publishing a bookmark on a list[%END%]

[%|helploc%]To publish a bookmark on a list, do as follows:[%END%]

  1. [%|helploc%]Go to the 'Shared documents' section of the list of your interest.[%END%]
  2. [%|helploc%]Browse the folder in which you want to publish your bookmark. Create a new folder if necessary.[%END%]
  3. [%|helploc%]Switch to expert mode.[%END%]
  4. [%|helploc%]In the 'title' field, enter a descriptive name for the website to be linked through the bookmark.[%END%]
  5. [%|helploc%]In the 'URL' field, enter or paste the website's URL.[%END%]
  6. [%|helploc%]Click on the 'Add' button.[%END%]
[%|helploc%]Creating a file on a list[%END%]

[%|helploc%]To create a file on a list, do as follows:[%END%]

  1. [%|helploc%]Go to the 'Shared documents' section of the list of your interest.[%END%]
  2. [%|helploc%]Browse the folder in which you want to create your file. Create a new folder if necessary.[%END%]
  3. [%|helploc%]Switch to expert mode.[%END%]
  4. [%|helploc%]Enter a file name in the input box close to the 'Create a new file' label.[%END%]
  5. [%|helploc%]Click on the 'Create' button.[%END%]

[%|helploc%]You are brought to the file creation page.[%END%]

[%|helploc%]Enter or paste the text you want to put in your file in the 'Edit the file /nameofthefile' text area, and then click on the 'Publish' button.[%END%]

[%|helploc%]Important: the only files that can be created online on the lists are plain text files. On the other hand, it is impossible to create office documents (.DOC, .XLS, .PPT, .RTF, .ODT, etc.), .PDF, images, etc.[%END%]

[%|helploc%]You can also replace the file, describe it or rename it. To know more about these features, refer to the 'Editing or deleting documents in the shared document web space' section.[%END%]

[%|helploc%]Editing or deleting documents in the shared document web space[%END%]

[%|helploc%]In addition to uploading and downloading documents, you can also act on files and folders that are already online:[%END%]

  • [%|helploc%]by changing their access rights (read and write);[%END%]
  • [%|helploc%]by editing them;[%END%]
  • [%|helploc%]by deleting them.[%END%]
[%|helploc%]Changing access rights[%END%]

[%|helploc%]You can change read and/or write access rights to folders and files. This has several advantages:[%END%]

  • [%|helploc%]Denying write access to a folder avoids proliferation of files published by unauthorized persons. To keep control of the list, sometimes it is more sensible to deny write access to the root folder of the shared document web space. It is also possible to offer a writable folder and to lock it at a given date or time, for example to control delivery of works by students.[%END%]
  • [%|helploc%]Denying write access to a file ensures that no one will be able to modify it. For example, this is the most suitable option for teachers willing to put at their students' disposal a set of documents, such as exam questions.[%END%]
  • [%|helploc%]Denying read access to a folder makes it possible to store several confidential documents without having to change read access rights individually for each of them.[%END%]
  • [%|helploc%]Denying read access to a file ensures its confidentiality. For example, a teacher can ask his/her students to upload their "exam copies" on a list and to restrict read access to their files in order to prevent other students from looking at them.[%END%]

[%|helploc%]You are allowed to change access rights only to documents you uploaded or created yourself on the lists (one exception: list owners can change access rights to any document published on the lists; this does not concern moderators).[%END%]

[%|helploc%]To change access rights for a document, click on the 'Access' text in front of the document's name, in the 'Access' column. You are brought to the access rights editing page.[%END%]

[%|helploc%]Choose options from the drop-down lists 'Read access' and 'Edit access'.[%END%]

[%|helploc%]Though it isn't mentionned in any of the options, note that the document owner (most of the time the person that published it) keeps the write and read rights on this document whatever happens (unless the list owner changes the document owner).[%END%]

[%|helploc%]You can also change the document's owner, for example to allow another person to edit it online, or to indicate the actual author of a document if it has been published by someone else.[%END%]

[%|helploc%]Editing folders, files or bookmarks[%END%]

[%|helploc%]To edit a document, click on the 'Edit' text in front of the document's name in the 'Edit' column.[%END%]

[%|helploc%]According to the type of document you edit, you have different possibilities:[%END%]

  • [%|helploc%]If the document is a folder, you can only describe it or rename it.[%END%]

    [%|helploc%]The description of a document is visible in the upper left corner when editing it. The description of folders is also visible there when browsing the folder.[%END%]

  • [%|helploc%]If the document is a bookmark, you can also change the URL specified.[%END%]
  • [%|helploc%]If the document is a file, you can also replace the existing file with a file of your choice. To do that, click on the 'Browse' button below the 'Replace the file nameofthefile with your file' text and choose the file you want to publish; after selecting it, click on the 'Open' button. Your file path then displays in the input box close to the 'Browse' button. Click on the 'Publish' button.[%END%]

    [%|helploc%]Whatever the name of the new file, the file published on the list will keep its original name. If you want the file replacement to be followed by a change of name, you will also have to rename the file published on the list.[%END%]

  • [%|helploc%]Last, if the document is a plain text file, you can change its content online: enter or paste the text you want to put in your file in the 'Edit the file /nameofthefile' text area, and then click on the 'Publish' button.[%END%]

[%|helploc%]Any click on a button related to an input box only validates the changes specified in that box. To make several changes, you need to click on each button corresponding to your choices.[%END%]

[%|helploc%]Some buttons immediately bring you back to the page of the folder containing the document, while others perform the update without bringing you to another page. To go back to the folder page without changing anything, click on the 'Up to higher level directory' button.[%END%]

[%|helploc%]Deleting folders, files or bookmarks[%END%]

[%|helploc%]To delete any type of document, click on the 'Delete' text in front of the document's name in the 'Delete' column. A confirmation message displays in order to let you go back on your decision: once deleted, the document will not be retrievable anymore.[%END%]

[%|helploc%]If there is no 'Delete' text in front of the document's name, you do not have write access rights to the document.[%END%]

[%|helploc%]It is impossible to delete a folder which still contains documents: before deleting a folder, you need to empty it entirely first.[%END%]

[%|helploc%]A few tips to organize the shared document web space[%END%]

[%|helploc%]If you are one of the people likely to organize the list and create files and folders, be far-sighted: think that the list will maybe develop in a considerable manner and that it will maybe be used for several years.[%END%]

[%|helploc%]Here are a few suggestions to prevent a list from developing in an anarchic manner:[%END%]

  • [%|helploc%]If the list is to contain the same kinds of resources at regular intervals, choose an organization by month or by year (or any other duration according to your needs).[%END%]

    [%|helploc%]Example: if the list is meant to collect student works, those students will attend the same lessons and make the same works from a year to another. Thus, it might be interesting to create a folder for each academic year at the root of the shared document web space: this will allow students to take a look at the previous year's works and lessons (provided that teachers restrict access to sensitive resources). This can be completed by subfolders for each lesson or each teacher within each year folder.[%END%]

  • [%|helploc%]If the list is a collaborative list destined to all members of a department, you had better choose a project-based organization.[%END%]
  • [%|helploc%]If the list aims at exchanging information, choose a topic-based organization.[%END%]
  • [%|helploc%]You can also choose an organization by person, by team, etc., and even combine all those solutions![%END%]

[%|helploc%]In order to avoid problems, choose carefully the names of files and folders you publish on lists: give them explicit yet short names and avoid spaces, accents, hyphens and special characters.[%END%]

sympa-6.1.24~dfsg/web_tt2/help_suspend.tt2000066400000000000000000000023541246372670100205050ustar00rootroot00000000000000

[%|helploc%]Manage your subscriptions[%END%]

[%|helploc%]How does the suspension work?[%END%]

[%|helploc%]In order to suspend your subscription to one or more lists, follow these steps:[%END%]

  • [%|helploc%]Select a start date through the calendar that appears when you click on the "start date" field;[%END%]
  • [%|helploc%]if you wish, you can specify the date when you want your subscription to resume. Do it using the "end date" field. You can click on "indefinite." In this case, you will have to return to this page to resume your subscription;[%END%]
  • [%|helploc%]Select the lists which you want to suspend your subscription for. The "Toogle selection" button allows you to invert the selection;[%END%]
  • [%|helploc%]Click on "Suspend my subscriptions" to confirm the suspension.[%END%]

[%|helploc%]To resume your subscription to one or more lists, follow these steps:[%END%]

  • [%|helploc%]Select the list(s) you want to reactivate. The "Toogle selection" button allows you to invert the selection;[%END%]
  • [%|helploc%]Click "Resume".[%END%]
sympa-6.1.24~dfsg/web_tt2/help_user.tt2000066400000000000000000000535561246372670100200140ustar00rootroot00000000000000

[%|helploc%]Mailing lists - User Guide[%END%]

[%|helploc%]How the mailing list service works[%END%]

[%|helploc%]The mailing-list service is managed by a mailing-list software: Sympa. This software comes with a web mailing list environment.[%END%]

[%|helploc%]To perform actions related to mailing lists (subscribe, change your options, etc.), you have two options:[%END%]

  • [%|helploc%]log on to the web environment;[%END%]
  • [%|helploc(conf.email,conf.host)%]send commands by email to the Sympa mailing list manager at %1@%2.[%END%]

[%|helploc%]To send a command to Sympa, do as follows:[%END%]

  • [%|helploc%]If you send a single command, type it into the subject line of your email and leave its body blank.[%END%]
  • [%|helploc%]If you send several commands, leave the subject line of your email blank and type all the commands in the email body. Be careful: Sympa will not process your message unless you respect the following rules:[%END%]
    • [%|helploc%]Write every command on a new line.[%END%]
    • [%|helploc%]Send your message in plain text, not in HTML (no formatting).[%END%]
    • [%|helploc%]Your message can not contain anything else than Sympa commands (no signature block).[%END%]

[%|helploc(path_cgi)%]A description of all the commands you can send to Sympa is available at %1/help/mail_commands.[%END%]

[%|helploc%]Subscribing to mailing lists[%END%]

[%|helploc%]Subscribing to a mailing list is very simple:[%END%]

  1. [%|helploc%]Choose the address with which you want to subscribe to the list.[%END%]

    [%|helploc%]You should choose an address you can check frequently and which offers a large storage capacity for your email: some lists distribute many messages, which sometimes contain large attachments.[%END%]

    [%|helploc%]Of course you can subscribe to the same list with several email addresses. Then you will need to redo the whole process with a different email address.[%END%]

  2. [%|helploc(conf.email,conf.host)%]Send a message to %1@%2 from the address you want to subscribe to the list.[%END%]

    [%|helploc%]Sympa is not a person but a mailing list management robot. Thus it is useless to send it loving words! ;-)[%END%]

  3. [%|helploc%]In the subject line of your message, type in: subscribe nameofthelist Firstname Name (replace 'nameofthelist' by the name of the list you want to subscribe to and indicate your own first name and name).[%END%]
  4. [%|helploc%]Leave the message body blank.[%END%]

    [%|helploc%]To save some time, you can also send several commands in a single message. To do that, follow the instructions available in the How the mailing list service works section.[%END%]

[%|helploc(conf.host)%]After this, you will receive a message telling you whether your request was accepted or not: if the subscription to the list is subject to any approval, the list owner may choose not to subscribe you. If so, do not send several other requests: it is useless as the result will remain the same. You can possibly send a message directly to the list owner (nameofthelist-request@%1) to explain why you really want to subscribe to the list...[%END%]

[%|helploc%]Note: you will sometimes be asked to confirm your subscription request before it can be processed. If so, please conform to the instructions contained in the message you receive.[%END%]

[%|helploc%]According to the type of list (list with subscription subject to conditions or not) and to the availability of the list owner, you may not receive the notice immediately. It is useless to send several requests.[%END%]

[%|helploc%]If your request is accepted, the message you receive confirms your subscription to the list. This message (the list Charter) contains several pieces of essential information:[%END%]

  • [%|helploc%]your list password. This password is the same for all the lists you subscribed to with a single email address. You can change it online after logging on to the mailing list environment;[%END%]
  • [%|helploc%]detailed information about the list: its purpose, the Internet address at which the message archive is available, etc.[%END%]
  • [%|helploc%]the rules applying to the list and its members: allowed and forbidden topics, netiquette, legal information, privacy policy, etc.[%END%]

[%|helploc%]You should keep your subscription notice: you may need it later to remember your password or to send a precise command to Sympa (example: signoff command). More generally, we advise you to keep all your subscription notices to mailing lists.[%END%]

[%|helploc%]You can also subscribe to a list through the mailing list web interface. To do that, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list you want to subscribe to.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Subscribe' link.[%END%]

[%|helploc%]Logging on to the mailing list environment[%END%]

[%|helploc%]To log on to the mailing list environment, use the authentication form displayed on top of the left column of the web interface. When you are logged on, your email address and user profile (subscriber, moderator or owner) are displayed there.[%END%]

[%|helploc%]The authentication process varies according to your personal situation:[%END%]

  • [%|helploc%]If the organization offering the mailing list service uses single sign-on technology (unique account and unique authentication, for example through the CAS system), you will preferably log on with your unique account. To do that, click on the 'Go' button next to the text 'Authentication [name of the system used]'. Then, type in your login and password to log on to the authentication server.[%END%]

    [%|helploc%]If you have already logged on to another service using the unique authentication system, your authentication is automatic. Refresh page if necessary.[%END%]

  • [%|helploc%]If the unique authentication process does not apply to you, you can use your list password. In this case, log on through the classic method: enter the email address with which you subscribed to the list as a login and your list password in the 'Password' field.[%END%]
  • [%|helploc%]If you do not remember your list password, click on 'Lost password?'. After you've provided your email address, a mail with a validation URL will be sent at that address.[%END%]

  • [%|helploc%]If the unique authentication process does not apply to you and you do no have a list password yet, click on 'First login?' and type in your email address. A confirmation URL will be sent at that address. Then you will be able to choose your password.[%END%]

[%|helploc%]Remember: the list password is a special password you will only use for the mailing list service.[%END%]

[%|helploc%]Checking your subscriptions[%END%]

[%|helploc%]To see all the lists you subscribed to, you need to log on first. Then a list of all your lists, including a short description for each of them, will be displayed in the 'Your lists' form on the left column.[%END%]

[%|helploc%]To look at a list information page, click on its name. The information page includes a description of the list (object, rules applying when sending a message, etc.), which length varies according to the list.[%END%]

[%|helploc%]Form this information page, you can:[%END%]

[%|helploc%]The number of people subscribed to the list is permanently displayed in the left menu. To review the list members, click on the 'Review members' link in the left menu (if the list-owner decided to deny access to the members list, this link is not available). The subscribers list displays and shows the email address and name of each of the subscribers (the indication of the name depends on the subscription method used by the subscribers).[%END%]

[%|helploc%]By default, each page displays 25 subscribers. You can browse through the pages by using the browsing arrows or display more subscribers per page. You may also wish to sort subscribers according to their email address, domain or name by clicking on the corresponding column header.[%END%]

[%|helploc(conf.host)%]The names of the list owners and moderators are displayed in the left menu. You should never write directly to a list owner or moderator. If you want to ask a question or make a comment, you should use the following address: nameofthelist-request@%1 (replace 'nameofthelist' by the name of the list in question).[%END%]

[%|helploc%]To know when you subscribed to the list and when you last updated your subscriber options, click on the 'Subscriber options' link in the left menu.[%END%]

[%|helploc%]Managing your preferences[%END%]

[%|helploc%]To allow you to use lists more easily, you can define a number of personal preferences. There are two types of preferences you can change:[%END%]

  • [%|helploc%]your subscriber options, which can vary according to the list;[%END%]
  • [%|helploc%]your general preferences, which apply to the entire Sympa mailing list environment.[%END%]

[%|helploc%]Changing your subscriber options[%END%]

[%|helploc%]Your subscriber options can vary from a list to another. To change them, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list for which you want to change your subscriber options.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Subscriber options' link.[%END%]
  4. <[%|helploc%]strong>Choose a message delivery mode (those options are mutually exclusive, thus you can not select several of them):[%END%]
    • [%|helploc%]digest MIME format: instead of receiving the list messages in a normal manner, you will get a digest of them on a regular basis. This digest compiles a group of messages from the list, using multipart/digest MIME format. The digest frequency is set up by the list owner.[%END%]
    • [%|helploc%]digest plain text format: instead of receiving the list messages in a normal manner, you will get a digest of them on a regular basis. This digest compiles a group of messages from the list, using plain text format. The digest frequency is set up by the list owner.[%END%]
    • [%|helploc%]summary mode: instead of receiving the list messages in a normal manner, you will get a list of them on a regular basis. To read the messages, you will need to browse the online list archive.[%END%]
    • [%|helploc%]notice mode: with this mode, you will receive all the messages with a blank body: this way you are informed of every message sent to the list real time, without risk of flooding your inbox.[%END%]
    • [%|helploc%]no mail (useful for vacations): this mode makes it possible not to receive the messages of the list. It is especially useful when you have no access to your email for a long time and want to remain subscribed to the list nevertheless.[%END%]
    • [%|helploc%]text only mode: this mode allows you to receive only the text version (text/plain) of messages sent in both formats (plain text and HTML).[%END%]
    • [%|helploc%]HTML only mode: this mode allows you to receive only the HTML version (text/html) of messages sent in both formats.[%END%]
    • [%|helploc%]urlize mode: this mode allows you not to receive attached documents. However these documents are available in the list archive and you can access them through a URL provided in the message.[%END%]
    • [%|helploc%]you do not receive your own posts: this mode allows you not to receive a copy of your own messages.[%END%]
    • [%|helploc%]standard (direct reception): this mode is the default delivery mode; it cancels any other delivery mode.[%END%]
    • [%|helploc%]suspended: this mode allows you to suspend your subscription to one or more lists for a specified period or not. Unlike unsubscription, you can keep track of your subscription and reactivate it at any time by visiting the "Manage your subscription" section.[%END%]
  5. [%|helploc%]Choose a visibility option:[%END%]
    • [%|helploc%]listed in the list review page: your name and email address will be displayed in the members list (if the list owner allowed subscribers to review the list members).[%END%]
    • [%|helploc%]concealed: your name and email address will not be displayed in the members list. However you email address will be visible in the list archive if you send messages.[%END%]
  6. [%|helploc%]Click on the 'Update' button.[%END%]

[%|helploc%]Changing your general preferences[%END%]

[%|helploc%]The general preferences apply to all your subscriptions as well as to the way your Sympa mailing list web interface displays. To change your preferences, do as follows:[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]In the form displayed on top of the left column, click on the 'Your preferences' link.[%END%]
  3. [%|helploc%]Change your preferences.[%END%]
  4. [%|helploc%]Click on 'Submit' for every option you change.[%END%]

[%|helploc%]You can change:[%END%]

  • [%|helploc%]your name; if you subscribe to a list from the mailing list server web interface, the 'Name' field will automatically be filled in in the members list;[%END%]
  • [%|helploc%]the language in which the Sympa web interface is displayed (you can change language on every page of the web interface; your choice will remain even though you change the interface language on another page than the 'Preferences' page;[%END%]
  • [%|helploc%]the lifetime of the cookie placed on your computer by Sympa ('Connection expiration period'). By default, the session expires when you close your browser; if you use the mailing list service a lot, we advise you to choose a longer duration;[%END%]

    [%|helploc%]A cookie is a small file a web server stores on your hard disk, most generally temporarily, in order to identify you as a user of its service. It contains a few pieces of personal information about you: name, email address, latest logon time, etc.[%END%]

  • [%|helploc%]the email address with which you subscribed to the lists (if you subscribed with several email addresses, the address to be replaced will be the one you logged on with);[%END%]

    [%|helploc%]Be careful: this will change your subscriptions to all your lists. If you want to change address for a single mailing list, you had better unsubscribe from that list and subscribe again with the right email address.[%END%]

  • [%|helploc%]your list password.[%END%]

[%|helploc%]The 'Your other email addresses' section acts like an email address change.[%END%]

[%|helploc%]Searching for a mailing list[%END%]

[%|helploc%]You may need to search for a mailing list. To do that, you have three options:[%END%]

  • [%|helploc(path_cgi)%]browse the different sections displayed on the list environment homepage;[%END%]
  • [%|helploc%]search for a list via the search box: the searched string will return all the lists whose name or description matches your search criteria (descriptions of the lists generally consist of a short sentence);[%END%]
  • [%|helploc(path_cgi)%]click on the 'List of lists' tab on top of page to display all available lists.[%END%]

[%|helploc%]According to the domain to which your email address belongs (example: cru.fr, fai.com, etc.) and to the location you log on from, you will not have access to the same lists. However you can subscribe to a list that does not display if you know its name. To do this, use your email client.[%END%]

[%|helploc%]Reading a list archive online[%END%]

[%|helploc(path_cgi)%]Please refer to the archive documentation.[%END%]

[%|helploc%]Sending a message[%END%]

[%|helploc(path_cgi)%]Please refer to the documentation about sending messages.[%END%]

[%|helploc%]Using the shared document web space[%END%]

[%|helploc(path_cgi)%]Please refer to the shared document web space documentation.[%END%]

[%|helploc%]Suspending or resuming your subscription of each list[%END%]

[%|helploc(path_cgi)%]Please refer to the subscription management documentation.[%END%]

[%|helploc%]Unsubscribing from lists[%END%]

[%|helploc%]To unsubscribe from a list, do as follows:[%END%]

  1. [%|helploc(conf.email,conf.host)%]From the address with which you subscribed to the list, send a message to %1@%2.[%END%]
  2. [%|helploc%]In the subject line of your email, type in: unsubscribe nameofthelist (replace 'nameofthelist' by the name of the list you want to unsubscribe from).[%END%]
  3. [%|helploc%]Leave the message body blank.[%END%]

    [%|helploc%]To save some time, you can also send several commands in a single message. To do that, follow the instructions available in the How the mailing list service works section.[%END%]

[%|helploc%]You can also unsubscribe through the mailing list web interface (you will need to repeat the operation for each list you want to unsubscribe from):[%END%]

  1. [%|helploc(path_cgi)%]Go to the list environment homepage and log on.[%END%]
  2. [%|helploc%]Go to the information page of the list you want to unsubscribe from.[%END%]
  3. [%|helploc%]In the left menu, click on the 'Unsubscribe' link.[%END%]

sympa-6.1.24~dfsg/web_tt2/help_user_options.tt2000066400000000000000000000055321246372670100215560ustar00rootroot00000000000000

[%|helploc%]This is a description of the receivie modes available in Sympa. These options are mutually exclusive, which means that you can't set two different receive modes at the same time. Only some of the modes might be available to specific mailing lists.[%END%]

  • [%|helploc%]Digest[%END%]
    [%|helploc%]Instead of receiving individual mail messages from the list, the subscriber will periodically receive batched messages in a Digest. This Digest compiles a group of messages from the list, using the multipart/digest MIME format.[%END%]

    [%|helploc%]The sending interval for these Digests is defined by the list owner.[%END%]

  • [%|helploc%]DigestPlain[%END%]
    [%|helploc%]Similar to the Digest option in that the subscriber will periodically receive batched messages in a Digest. With DigestPlain the Digest is sent in a plain text format, with all attachments stripped out. DigestPlain is useful if your email software doesn't display multipart/digest format messages well.[%END%]

    [%|helploc%]The sending interval for these Digests is defined by the list owner.[%END%]

  • [%|helploc%]Summary[%END%]
    [%|helploc%]Instead of receiving individual mail messages from the list, the subscriber will periodically receive a list of messages. This mode is very close to the Digest reception mode but the subscriber receives only the list of messages.[%END%]

  • [%|helploc%]Nomail[%END%]
    [%|helploc%]This mode is used when a subscriber no longer wishes to receive mail from the list, but nevertheless wishes to retain the ability to post to the list. This mode therefore prevents the subscriber from unsubscribing and subscribing later on.[%END%]

  • Txt
    [%|helploc%]This mode is used when a subscriber wishes to receive mails sent in both HTML and plain text formats only in plain text format.[%END%]

  • Html
    [%|helploc%]This mode is used when a subscriber wishes to receive mails sent in both HTML and plain text formats only in HTML format.[%END%]

  • Urlize
    [%|helploc%]This mode is used when a subscriber does not want to receive attached files. The attached files are replaced by a URL leading to the file stored on the list site.[%END%]

  • Not_me
    [%|helploc%]This mode is used when a subscriber does not want to receive copies of messages that he or she has sent to the list.[%END%]

  • Normal
    [%|helploc%]This option is used mainly to cancel the nomail, summary or digest modes. If the subscriber was in nomail mode, he or she will again receive individual mail messages from the list.[%END%]

sympa-6.1.24~dfsg/web_tt2/home.tt2000066400000000000000000000043041246372670100167410ustar00rootroot00000000000000

[%|loc%]Mailing lists server[%END%]

[%|loc%]This server provides you access to your mailing list environment. Starting from this web page, you can perform subscription options, unsubscription, archive, list moderation, list configuration.[%END%]

[%|loc%]Mailing lists categories[%END%]


[% SET single_topic = "other" %] [% FOREACH topic = topics %] [% IF topic.id && topic.id != "other" && topic.id != "topicsless" %] [% SET single_topic = "" %] [% LAST %] [% ELSIF topic.sub %] [% FOREACH subtopic = topic.sub %] [% SET single_topic = "" %] [% LAST %] [% END %] [% END %] [% END %] [% IF single_topic != "" %] [% ELSE %]
    [% END %] [% END %]
[% END %]

sympa-6.1.24~dfsg/web_tt2/info.tt2000066400000000000000000000004221246372670100167410ustar00rootroot00000000000000 [% IF homepage_content %] [% homepage_content %] [% ELSE %]

[%|loc%]List informations[%END%]

[% info_content %]

[% END %] sympa-6.1.24~dfsg/web_tt2/install_pending_list.tt2000066400000000000000000000014021246372670100222120ustar00rootroot00000000000000

[%|loc%]Listname:[%END%][% list %]
[%|loc%]Subject:[%END%] [% list_subject %]
[%|loc%]List requested by[%END%] [% list_request_by %] [%|loc(list_request_date)%]on %1[%END%]


[% IF is_listmaster %] [% IF auto_aliases %] [%|loc%]Aliases have been installed.[%END%] [% END %] [% END %] sympa-6.1.24~dfsg/web_tt2/ja_JP/000077500000000000000000000000001246372670100163405ustar00rootroot00000000000000sympa-6.1.24~dfsg/web_tt2/ja_JP/css.tt2000066400000000000000000000037741246372670100175760ustar00rootroot00000000000000[%# $Id: $ -%] [%# This style sheet will override any portion of main CSS, not fully replacing it. So this may be used for locale-specific customization. -%] /* begin ja_JP/css.tt2 */ @media screen { } [%# Enlarge fonts. %] body { font-size: 69%; line-height: 1.5; } [%# Overriding font families. Problems such as below will be solved: - Default css.tt2 specifies the font families covering Western scripts (Latin, Cyrillic, ...). East Asian users may prefer consistent font family supporting Western along with Eastern scripts (Han, Hiragana, ...). - Internet Explorer 8 bug: pulsatory motion of East Asian texts in links when their font size are around 9 to 11 pt and when font family is not specified explicitly. - Windows 7 bug: "Batang", a Korean font, is used for generic family "serif". Family names are ordered by platforms: Mac OS X and iOS (localized names are ignored by Safari), MacOS classic (if any), Windows (Vista or later, then XP/2000 or earlier), Red Hat/Fedora, Debian/Ubuntu, and others if any. -%] [%# Overriding default font family. %] * { font-family: "游ゴシック", YuGothic, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "ヒラギノ 角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "Yu Gothic", "メイリオ", Meiryo, "MS Pゴ シック", "VL Pゴシック", "VL PGothic", "TakaoExゴシック", TakaoExGothic, "IPAex ゴシック", IPAexGothic, "IPA Pゴシック", IPAPGothic, sans-serif; } [%# Overriding "serif!important" %] #bandeau_top #Identity, #bandeau_top #Identity_not_connect, #bandeau_top #Identity_not_connect label, #bandeau_top .prefs, #bandeau_top a, #bandeau_top .login, #bandeau_top .remember { font-family: "游明朝", YuMincho, "ヒラギノ明朝 ProN W3", "Hiragino Mincho ProN", "ヒラギノ明朝 Pro W3", "Hiragino Mincho Pro", "Yu Mincho", "MS P明朝", "さざなみ明朝", "Sazanami Mincho", "TakaoEx明朝", TakaoExMincho, "IPAex明朝", IPAexMincho, "IPA P明朝", IPAPMincho, serif !important; } /* end ja_JP/css.tt2 */ sympa-6.1.24~dfsg/web_tt2/javascript.tt2000066400000000000000000001076061246372670100201700ustar00rootroot00000000000000 [% IF use_htmlarea %] [% END %] sympa-6.1.24~dfsg/web_tt2/ko_KR/000077500000000000000000000000001246372670100163625ustar00rootroot00000000000000sympa-6.1.24~dfsg/web_tt2/ko_KR/css.tt2000066400000000000000000000015661246372670100176150ustar00rootroot00000000000000[%# $Id: $ -%] [%# This style sheet will override any portion of main CSS, not fully replacing it. So this may be used for locale-specific customization. -%] /* begin ko_KR/css.tt2 */ @media screen { } [%# Overriding default font family. %] * { font-family:"Apple SD Gothic Neo Medium", AppleGothic, "맑은 고딕", "Malgun Gothic", "돋움 Regular", Dotum, UnDotum, "백묵 돋움", "Baekmuk Dotum", "은 돋움 Regular", "Un Dotum", sans-serif; } [%# Overriding "serif!important". %] #bandeau_top #Identity, #bandeau_top #Identity_not_connect, #bandeau_top #Identity_not_connect label, #bandeau_top .prefs, #bandeau_top a, #bandeau_top .login, .remember { font-family:"나눔명조", "Nanum Myeongjo", AppleMyungjo, "바탕 Regular", Batang, UnBatang, "백묵 바탕 Regular", "Baekmuk Batang", "은 바탕 Regular", "Un Batang", serif !important; } /* end ko_KR/css.tt2 */ sympa-6.1.24~dfsg/web_tt2/latest_arc.tt2000066400000000000000000000014251246372670100201330ustar00rootroot00000000000000

[% IF count %] [%|loc(count)%] The %1 most recent messages [%END%] [% ELSE %] [%|loc%] Recent messages [%END%] [% END %] [% IF for %] [%|loc(for)%] for %1 days [%END%] [% END %]


[% FOREACH a = archives %] [% END %]
[%|loc%]Date[%END%] [%|loc%]Subject[%END%] [%|loc%]From[%END%]
[% a.date %] [% a.subject %] [% a.from %]
sympa-6.1.24~dfsg/web_tt2/latest_d_read.tt2000066400000000000000000000031021246372670100205760ustar00rootroot00000000000000

[% IF count %] [%|loc(count)%] The %1 most recent shared documents [%END%] [% ELSE %] [%|loc%] Most recent shared documents [%END%] [% END %] [% IF for %] [%|loc(for)%] for %1 days [%END%] [% END %]


[% FOREACH d = documents %] [% END %]
[%|loc%]Last update[%END%] [%|loc%]Name[%END%] [%|loc%]Author[%END%] [%|loc%]Directory[%END%]
[% d.last_update %] [% IF d.html %] [% d.escaped_title %] [% d.name %] [% ELSIF d.url %] [% d.escaped_title %] [% d.anchor %] [% ELSE %] [% d.escaped_title %] [% d.name %] [% END %] [% IF d.author %] [% d.author %] [% ELSE %] [%|loc%]Unknown[%END%] [% END %] [% d.content_dir %]
sympa-6.1.24~dfsg/web_tt2/latest_lists.tt2000066400000000000000000000016551246372670100205310ustar00rootroot00000000000000

[% IF count %] [%|loc(count)%]The %1 newest lists[%END%] [% ELSE %] [%|loc%]New lists[%END%] [% END %] [% IF for %] [%|loc(for)%] for %1 days [%END%] [% END %]


[% FOREACH l = latest_lists %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
[%|loc%]Listname[%END%] [%|loc%]Creation date[%END%] [%|loc%]Subject[%END%]
[% hidden_head %][% l.name %][% hidden_at %][% l.host %][% hidden_end %] [% l.date %] [% l.subject %]
sympa-6.1.24~dfsg/web_tt2/lca.tt2000066400000000000000000000002101246372670100165400ustar00rootroot00000000000000[% TRY %] [% PROCESS "${custom_action}.tt2" %] [% CATCH file %] File Error! [% error.info %] [% CATCH %] [% error %] [% END %] sympa-6.1.24~dfsg/web_tt2/list_button_footer.tt2000066400000000000000000000002571246372670100217400ustar00rootroot00000000000000
sympa-6.1.24~dfsg/web_tt2/list_button_header.tt2000066400000000000000000000005401246372670100216650ustar00rootroot00000000000000
sympa-6.1.24~dfsg/web_tt2/list_menu.tt2000066400000000000000000000113161246372670100200110ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/list_panel.tt2000066400000000000000000000035531246372670100201500ustar00rootroot00000000000000
    [% IF list_status == 'closed' || list_status == 'family_closed' %] [% ELSIF list_status == 'pending' %] [% ELSIF list_status == 'error_config' %] [% END %] [% FOREACH o = owner %] [% IF o.value.visibility != 'conceal' %] [% IF o.value.gecos %][% o.value.gecos %][% ELSE %][% o.value.local %][% END %]
    [% END %] [% END %] [% IF editor %][% END %] [% FOREACH e = editor %] [% IF e.value.visibility != 'conceal' %] [% IF e.value.gecos %][% e.value.gecos %][% ELSE %][% e.value.local %][% END %]
    [% END %] [% END %] [% IF list_as_x509_cert %]
    [%|loc%]Load certificate[%END%]
    [% END %]
    [% IF protection_type == 'javascript' %] [% ELSE %] [% END %]
sympa-6.1.24~dfsg/web_tt2/lists.tt2000066400000000000000000000044041246372670100171500ustar00rootroot00000000000000 [% letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','others' ] %] [% IF action == 'search_list' %] [%|loc(occurrence)%]%1 occurrence(s) found[%END%]

[% ELSIF action == 'search_user' %] [%|loc(email)%]%1 is subscribed to the following mailing lists[%END%] [% END %] [% IF which %]
[% FOREACH letter IN letters %] [% IF orderedlist.$letter.0 %] [% END %] [% END %]
[% IF action == 'which' %] [% IF ! which %]   [%|loc(user.email)%]No subscriptions with address %1![%END%]
[% END %] [% END %] [% ELSE %]

[%|loc%]No mailing list available.[%END%]

[% END %]
sympa-6.1.24~dfsg/web_tt2/login.tt2000066400000000000000000000002441246372670100171200ustar00rootroot00000000000000 [%|loc(user.email)%]You have logged in with email address %1[%END%].
sympa-6.1.24~dfsg/web_tt2/login_menu.tt2000066400000000000000000000027771246372670100201610ustar00rootroot00000000000000
[% IF user.email %]
[% user.email %]  [% IF restore_email %]
[%END%] [% IF is_listmaster %][[%|loc%]Listmaster[%END%]] [% ELSIF is_privileged_owner %][[%|loc%]Privileged owner[%END%]] [% ELSIF is_owner %][[%|loc%]Owner[%END%]] [% ELSIF is_editor %][[%|loc%]Editor[%END%]] [% ELSIF is_subscriber %][[%|loc%]Subscriber[%END%]] [% END %]
[% IF last_login_date %] [%|loc(last_login_host)%]last login from %1[%END%]    ([% last_login_date %]) [%END%]
[% IF auth_method == 'md5' %] [% END %] [% ELSE %] [% PROCESS 'loginbanner.tt2' %] [% END %]
sympa-6.1.24~dfsg/web_tt2/loginbanner.tt2000066400000000000000000000054331246372670100203130ustar00rootroot00000000000000
[% IF use_sso %]
[% IF sso_number == 1 %] [% FOREACH server = sso %] [% END %] [% ELSE %] [% END %]
[% END %] [% IF use_passwd == '1' %]
     
[% END %] [% IF use_passwd == '1' %] [% IF authentication_info_url %] [%|loc%]Authentication help[% END %]
[% ELSE %] [% END %] [% END %]
sympa-6.1.24~dfsg/web_tt2/loginrequest.tt2000066400000000000000000000004271246372670100205340ustar00rootroot00000000000000
[%|loc%]In order to perform a privileged operation (one that requires your email address), you need to login.[%END%] [% PROCESS 'loginbanner.tt2' %]
sympa-6.1.24~dfsg/web_tt2/ls_templates.tt2000066400000000000000000000165661246372670100205220ustar00rootroot00000000000000

[%|loc%]Template edition system[%END%]

[%|loc%]This page is suggested in order to edit or create mail or web tt2 templates.[%END%] [%|loc%]Priority rules for template selection:
  • IF list context defined, apply list template if exist
  • ELSE apply robot defined template if exist
  • ELSE apply locally defined template if exist
  • ELSE apply template included in the distribution (these must not be edited because Sympa's update will over write these files)
[%END%]
[%|loc%]First you may list a category of templates; the list should include a default template and may include any templates designated for a particular list. You will then have an oportunity to copy and edit templates for the current robot or the named list.[%END%]
  • [%|loc%]Select templates type:[%END%]

[% IF templates %]

[% SET dark = 1 %] [% END %] [% FOREACH lang = lang_per_level.distrib %] [%- IF lang.key == 'default' -%] [%- ELSE -%] [%- END %] [% END %] [% FOREACH lang = lang_per_level.site %] [%- IF lang.key == 'default' -%] [%- ELSE -%] [%- END %] [% END %] [% FOREACH lang = lang_per_level.robot %] [%- IF lang.key == 'default' -%] [%- ELSE -%] [%- END %] [% END %] [% FOREACH lang = lang_per_level.list %] [%- IF lang.key == 'default' -%] [%- ELSE -%] [%- END %] [% END %] [% FOREACH file = templates %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark=1 %] [% END %] [% FOREACH lang = file.value.distrib %] [% END %] [% FOREACH lang = file.value.site %] [% END %] [% IF default_robot %] [% ELSE %] [% FOREACH lang = file.value.robot %] [% END %] [%END%] [% IF list %] [% FOREACH lang = file.value.list %] [%END%] [% ELSE %] [% END %] [% END %] [% IF templates %]
[%|loc%]template name[%END%] [%|loc%]default[%END%] [%|loc%]site[%END%] [%|loc%]robot[%END%] [%|loc(list)%]list %1[%END%]
   [% lang.value.title %] [% lang.value.title %] [% lang.value.title %] [% lang.value.title %]
[% file.key %] [% IF lang.value %]
[% END %]
[% IF lang.value %]
[% END %]
[% IF lang.value %]
[% END %]
[% IF lang.value %]
[% END %]
[% END %]

sympa-6.1.24~dfsg/web_tt2/main.tt2000066400000000000000000000114441246372670100167400ustar00rootroot00000000000000 [% main_title %] - [% action %] [% IF custom_css %] [% ELSE %] [% END %] [% PROCESS css_ie.tt2 %] [% IF locale_css %] [% END %] [% IF list -%] [% IF arc_public_access %][% END -%] [% IF shared_public_access %][% END -%] [% ELSE -%] [% IF (action == 'lists') && topic -%] [% ELSE -%] [% END -%] [% END -%] [% PROCESS javascript.tt2 %] [% IF base %] [% END %]
[% IF nomenu %] [% PROCESS error.tt2 IF errors %]
[% PROCESS notice.tt2 IF notices %] [% PROCESS "${action}.tt2" IF action %]
[% ELSE %] [% PROCESS error.tt2 IF errors %] [% PROCESS login_menu.tt2 %]
 
 
[% IF list %]
[% IF list_title %] [% list_title %] [% END %]

[% END %] [% PROCESS nav.tt2 %]
[% PROCESS notice.tt2 IF notices %] [% IF action == 'edit_list_request' %]

[% END %] [% PROCESS "${action}.tt2" IF action %]
Top of page
[% PROCESS dumpvars.tt2 IF dumpvars == 'true'%]
[% PROCESS footer.tt2 %] [% END %] sympa-6.1.24~dfsg/web_tt2/maintenance.tt2000066400000000000000000000003251246372670100202720ustar00rootroot00000000000000 [%|loc%]The mailing list server is in maintenance mode, no operation can be performed during this period.[%END%] sympa-6.1.24~dfsg/web_tt2/manage_template.tt2000066400000000000000000000065121246372670100211370ustar00rootroot00000000000000

[%|loc%]Modify or delete existing rejection messages[%END%]

[% IF available_files %]
[% SET dark = 1 %] [% FOREACH file = available_files %] [% IF dark == '1' %] [% dark = '0' %]
[% ELSE %] [% dark = '1' %] [% END %] [% END %] [% ELSE %]
[%|loc%]Listing rejection messages[%END%]
[%|loc%]Use as default[%END%][%|loc%]Message name[%END%][%|loc%]Operation[%END%]
[% file %] [%|loc%]Edit[%END%] [%|loc%]Delete[%END%]
[%|loc%]There are currently no Rejection Messages available[%END%] [% END %]

[%IF content %]

[%|loc%]Edit[%END%] [% message_template %]

[% ELSE %]

[%|loc%]Create new rejection messages[%END%]

[% END %] sympa-6.1.24~dfsg/web_tt2/menu.tt2000066400000000000000000000057051246372670100167630ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/menu_search.tt2000066400000000000000000000010041246372670100202740ustar00rootroot00000000000000 sympa-6.1.24~dfsg/web_tt2/modindex.tt2000066400000000000000000000200131246372670100176130ustar00rootroot00000000000000
[% IF mod_total %]

[%|loc%]Listing messages to moderate[%END%] [%|loc%]Help[%END%]

[%- IF topic_required -%]

[%- ELSE -%]

[%- END -%]
[%|loc %]Reject without notification [%END%]
[%|loc %]Add to blacklist[%END%]

[%- IF request_topic -%]
[%|loc%]This list is configured to require topic(s).[%END%]
[%|loc%]Please select one or more topic(s) that correspond to the messages you wish to distribute:[%END%]
[% FOREACH t = available_topics %]
[%- END- %]
[%- END -%] [%- IF conf.antispam_feature == 'on' -%] [%- ELSE -%] [%- END -%] [% FOREACH msg = spool %] [% IF dark == '1' %] [% dark = '0' %] [% ELSE %] [% dark = '1' %] [% END %] [%- IF conf.antispam_feature == 'on' -%] [% END %]
«»X[%|loc%]Date[%END%] [%|loc%]Author[%END%] [%|loc%]Subject[%END%] [%|loc%]Size[%END%]
[%- IF msg.value.spam_status == 'spam' -%][% idname = 'idspam' %][% therissomespam = '1' %][% ELSE %][% idname = 'id' %][% therissomeham = '1' %][% END %] [%- IF msg.value.spam_status == 'spam' -%]junk [%- ELSE -%]  [%- END -%] [%- END -%] [% IF msg.value.date %] [% msg.value.date %] [% ELSE %]   [% END %] [% msg.value.from %] [% IF msg.value.subject == 'no_subject' %] [%|loc%]No subject[%END%] [% ELSE %] [% msg.value.subject %] [% END %] [% msg.value.size %] kb
[% IF mod_message >= 10 %] [%- IF topic_required -%] [%- ELSE -%] [% END %] [% IF conf.use_blacklist != 'none' %] [% END %]
[%- END- %] [%- IF therissomespam -%] [%- END -%]

[%END%]
[% IF mod_total_shared %]

[%|loc%]Listing of shared documents to moderate[%END%]


[% FOREACH f = info_doc_mod %] [% END %]
X [%|loc%]Date[%END%] [%|loc%]Author[%END%] [%|loc%]Path[%END%] [%|loc%]Size[%END%]
[% IF f.date %] [% f.date %] [% ELSE %]   [% END %] [% f.author %] [% f.visible_path %][% f.visible_fname %] [% f.size %] kb

[% IF conf.use_blacklist != 'none' %] [%- END- %]
[% END %]

[%|loc%]Moderation management[%END%] [%|loc%]Help[%END%]

[% IF conf.use_blacklist != 'none' %] [% END %]

sympa-6.1.24~dfsg/web_tt2/nav.tt2000066400000000000000000000210201246372670100165670ustar00rootroot00000000000000 [% IF action == 'create_list_request' %] [% END %] [% IF action == 'serveradmin' or action == 'skinsedit' or action == 'edit_config' or action == 'get_pending_lists' or action == 'get_closed_lists' or action == 'get_latest_lists' or action == 'get_inactive_lists' %] [% END %] [% IF action == 'lists' %] [% letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','others' ] %] [% END %] [% IF action == 'admin' || action_type == 'admin' || action == 'editfile' %] [% IF action == 'edit_list_request' %] [% END %] [% END %] [% IF action == 'compose_mail' %] [% END %] sympa-6.1.24~dfsg/web_tt2/notice.tt2000066400000000000000000000057511246372670100173010ustar00rootroot00000000000000
[% IF last_login_date %] [%|loc(last_login_host,last_login_date)%]last login from %1 (%2)[%END%]
[%END%] [% FOREACH notice = notices %] [% IF notice.msg == 'sent_to_owner' %] [%|loc%]Your request has been forwarded to the list owner[%END%] [% ELSIF notice.msg == 'add_performed' %] [%|loc(notice.total)%]%1 subscribers added[%END%] [% ELSIF notice.msg == 'del_performed' %] [%|loc(notice.total)%]%1 addresses have been removed[%END%] [% ELSIF notice.msg == 'performed' %] [%|loc(notice.action)%]%1: action completed[%END%] [% ELSIF notice.msg == 'performed_soon' %] [%|loc(notice.action)%]%1: action will complete soon[%END%] [% ELSIF notice.msg == 'list_config_updated' %] [%|loc%]Configuration file has been updated[%END%] [% ELSIF notice.msg == 'list_purged' %] [%|loc%]List has been purged[%END%] [% ELSIF notice.msg == 'list_closed' %] [%|loc%]List has been closed[%END%] [% ELSIF notice.msg == 'list_restored' %] [%|loc%]List has been restored[%END%] [% ELSIF notice.msg == 'upload_success' %] [%|loc(notice.path)%]File %1 successfully uploaded![%END%] [% ELSIF notice.msg == 'unzip_success' %] [%|loc(notice.path)%]File %1 unziped![%END%] [% ELSIF notice.msg == 'file_erased' %] [%|loc(notice.path)%]Old file %1 has been erased[%END%] [% ELSIF notice.msg == to_moderate %] [%|loc(notice.path)%]File %1 waiting for moderation[%END%] [% ELSIF notice.msg == 'save_success' %] [%|loc(notice.path)%]File %1 saved[%END%] [% ELSIF notice.msg == 'password_sent' %] [%|loc%]Your password has been emailed to you[%END%] [% ELSIF notice.msg == 'you_should_choose_a_password' %] [%|loc%]To choose your password go to 'preferences', from the upper menu.[%END%] [% ELSIF notice.msg == 'no_msg_document' %] [%|loc(notice.list)%]No message and no document to moderate for list %1[%END%] [% ELSIF notice.msg == 'subscribers_updated' %] [%|loc%]The list of list members have been built/updated.[%END%] [% ELSIF notice.msg == 'subscribers_updated_soon' %] [%|loc%]The list of list members will be built/updated soon (a few minutes).[%END%] [% ELSIF notice.msg == 'subscribers_noticed_deleted_topics' %] [%|loc%]Concerned subscribers have been notified about deleted topics.[%END%] [% ELSIF notice.msg == 'add_performed' %] [%|loc(notice.total)%]%1 addresses have been subscribed[%END%] [% ELSIF notice.msg == 'you_have_been_authenticated' %] [%|loc%]You have been authenticated[%END%] [% ELSIF notice.msg == 'file_renamed' %] [%|loc(notice.orig_file,notice.new_file)%]File %1 has been renamed to %2[%END%] [% ELSIF notice.msg == 'pending_list' %] [%|loc%]List has been set to the pending status; listmaster need to validate it[%END%] [% ELSIF notice.msg == 'listname_lowercased' %] [%|loc%]List name has been lowercased[%END%] [% ELSIF notice.msg == 'user_notified' %] [%|loc(notice.notified_user)%]User %1 has been notified[%END%] [% ELSE %] [% notice.msg.replace('\n','
') %] [% END %] [% END %]
sympa-6.1.24~dfsg/web_tt2/picture_upload.tt2000066400000000000000000000026771246372670100210430ustar00rootroot00000000000000 [% IF pictures_display %]

[%|loc%]Setting your picture for this list[%END%]

[%|loc%]You can upload your picture below; it will be available in the list review page. The picture should use a standard format (gif, jpp, jpeg or png) and its size should not exceed 100 Kb.[%END%]




[% IF pictures_url %]
[%|loc%]Your picture[%END%]
[%|loc%]Your picture[%END%]
[%|loc%]Your picture in the subscribers list[%END%]
[%|loc%]In the members page[%END%]

[%END%]
[% END %] sympa-6.1.24~dfsg/web_tt2/pref.tt2000066400000000000000000000052031246372670100167440ustar00rootroot00000000000000

[%|loc%]User preferences[%END%] [%|loc%]Help[%END%]


[% user.email %]




[%|loc%]Changing your email address[%END%]

[%|loc%]You can update your email address for all your list memberships at once. If you are also list owner or list moderator your email address for these roles will also be updated.[%END%]

[%|loc%]Changing your password[%END%]




sympa-6.1.24~dfsg/web_tt2/remove_arc.tt2000066400000000000000000000012671246372670100201400ustar00rootroot00000000000000 [% IF status == 'done' %] [%|loc%]Operation successful. The message will be deleted as soon as possible. This task may be performed in a few minutes.[%END%] [% ELSIF status == 'no_msgid' %] [%|loc%]Unable to find the message to delete, probably this message was received without Message-Id. Please refer to listmaster with complete URL of the message concerned[%END%] [% ELSIF status == 'not_found' %] [%|loc%]Unable to find the message to delete[%END%] [% ELSE %] [%|loc%]Error while deleting this message, please refer to listmaster with complete URL of the message concerned.[%END%] [% END %] sympa-6.1.24~dfsg/web_tt2/rename_list_request.tt2000066400000000000000000000024351246372670100220660ustar00rootroot00000000000000

[%|loc%]Renaming the list[%END%] [%|loc%]Help[%END%]


@ [% IF robots %] [% ELSE %] [% robot %] [% END %]
sympa-6.1.24~dfsg/web_tt2/renewpasswd.tt2000066400000000000000000000036151246372670100203570ustar00rootroot00000000000000

[% IF account_creation %] [%|loc%]You requested an account creation on this list server.[%END%] [% ELSIF login_error == 'wrong_password' %] [%|loc%]The password you submitted is wrong. Maybe you forgot it?[%END%] [% ELSIF login_error == 'password_reset' %] [%|loc%]Too many wrong password were sumitted for this account. Your account as been blocked in order to protect you agains attacks. You must renew your password in order to login.[%END%] [% ELSIF login_error == 'missing_password' %] [%|loc%]You missed the password, please submit again. Maybe you forgot it?[%END%] [% ELSIF login_error == 'ticket_sent' %] [%|loc%]You will receive an email that will allow you to choose your password.[%END%] [% ELSE %] [% IF requestpasswd_context == 'firstpasswd' %] [%|loc%]In order to use Sympa, you will need to define a password.[%END%] [% ELSE %] [%|loc%]You forgot your password, so you have to renew it.[%END%] [% END %] [% END %] [%|loc%]Please submit your email address, you will receive a message that gives you access to the appropriate form in order to choose your password.[%END%]

    [% ELSE %] value="[%|loc%]Request new password[%END%]" /> [% END %]
sympa-6.1.24~dfsg/web_tt2/request_topic.tt2000066400000000000000000000024631246372670100207030ustar00rootroot00000000000000
[%|loc%]This list is configured to require topic(s) foreach message.[%END%]


[% FOREACH t = available_topics %]
[% END %]
[%- IF topic_required -%] [%- ELSE -%] [%- END -%]


[%|loc(from)%]From: %1[%END%]
[%|loc(mailto)%]To: %1[%END%]
[%|loc(date)%]Date: %1[%END%]
[%|loc(subject)%]Subject: %1[%END%]

[%body%]
sympa-6.1.24~dfsg/web_tt2/requestpasswd.tt2000066400000000000000000000013401246372670100207200ustar00rootroot00000000000000 [% IF login_error == 'ticket_sent' %]

[%|loc%]Message sent[%END%]

[%|loc%]We've sent you an email that will allow you choose your password. Please check your mailbox.[%END%]

[% ELSIF login_error == 'unable_to_send_ticket' %]

[%|loc%]We were not able to send you a validation message.[%END%]

[% ELSIF login_error == 'unable_to_create_ticket' %]

[%|loc%]Internal error: could not build a validation link, please contact the service administrator[%END%]

[% ELSE %]

[%|loc%]Unkown error.[%END%]

[% END %] sympa-6.1.24~dfsg/web_tt2/review.tt2000066400000000000000000000173771246372670100173300ustar00rootroot00000000000000
[% IF is_listmaster || is_owner || ( is_editor && may_review ) %]

[%|loc%]Manage list members[%END%] [%|loc%]Help[%END%]

[% END %] [% IF action != 'search' %]

[% IF prev_page %] [%|loc%]Previous page[%END%] [% END %] [% IF page %] [%|loc%]page[%END%] [% page %] / [% total_page %] [% END %] [% IF next_page %] [%|loc%]Next page[%END%] [% END %]
[% END %] [% IF is_listmaster || is_owner || ( is_editor && may_review ) %]
[% IF is_listmaster || is_owner || may_add %] [%|loc%]Pending subscriptions[%END%] [% END %] [% IF conf.use_blacklist != 'none' %] [%|loc%]Blacklist[%END%] [% END %] [%|loc%]Bounces[%END%] [% IF action == 'search' %] [%|loc%]Dump[%END%] [% ELSE %] [%|loc%]Dump[%END%] [% END %] [%|loc%]Exclude[%END%]
[% IF may_sync %]
[% END %]


[% IF may_add %]
  [%|loc%]Multiple add[%END%]
[% END %] [% END %]

[% IF action == 'search' %] [% IF too_many_select %] [%|loc%]Selection too wide, can not show selection[%END%] [% END %] [% END %]


[% letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' ] %]
[% IF may_del %] [% END %] [% IF action == 'search' %]

[%|loc(occurrence)%]%1 occurrence(s) found[%END%]

[% END %] [% SET thosesubscribers = members %] [% PROCESS subscriber_table.tt2 %] [% IF action == 'search' %] [% IF similar_subscribers_occurrence != 0 %]

[%|loc(similar_subscribers_occurrence)%] Other similar subscriber's email(s) (%1)[%END%]

[% SET thosesubscribers = similar_subscribers %] [% PROCESS subscriber_table.tt2 %] [% END %] [% END %] [% IF action != 'search' %]
[% IF prev_page %] [%|loc%]Previous page[%END%] [% END %] [% IF page %] [%|loc%]page[%END%] [% page %] / [% total_page %] [% END %] [% IF next_page %] [%|loc%]Next page[%END%] [% END %]
[% END %] [% IF may_del %]
[% END %]

[% IF action == 'search' %]

[%|loc%]Excluded users[%END%]

[% PROCESS exclusion_table.tt2 %] [% END %]
[% PROCESS picture_upload.tt2 %] sympa-6.1.24~dfsg/web_tt2/review_family.tt2000066400000000000000000000011471246372670100206550ustar00rootroot00000000000000 [% FOREACH list = family_lists %] [% END %]
[%|loc%]Status[%END%] [%|loc%]Listname[%END%] [%|loc%]Instantiation date[%END%] [%|loc%]Subject[%END%]
[%|optdesc('status')%][% list.status %][%END%] [% list.name %] [% list.instantiation_date %] [% list.subject %]
sympa-6.1.24~dfsg/web_tt2/reviewbouncing.tt2000066400000000000000000000125611246372670100210430ustar00rootroot00000000000000

[%|loc%]Manage bouncing list members[%END%] [%|loc%]Help[%END%]


[%|loc%]Dump[%END%]
 


[% IF prev_page %] [%|loc%]Previous page[%END%] [% END %] [% IF page %] [%|loc(page,total_page)%]page %1 / %2[%END%] [% END %] [% IF next_page %] [%|loc%]Next page[%END%] [% END %]
[% FOREACH u = members %] [% IF dark == '1' %] [% ELSE %] [% END %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
«» [%|loc%]Email[%END%] [%|loc%]Bounce score[%END%] [%|loc%]Details[%END%]
[%|loc%]# of bounces[%END%] [%|loc%]First bounce[%END%] [%|loc%]Last bounce[%END%]
[% u.email %] [% IF ! u.bounce_score %] [%|loc%]no score[%END%] [% ELSE %] [% u.bounce_score %] [% END %] [% u.bounce_count %] [% u.first_bounce %] [% u.last_bounce %]
[% IF prev_page %] [%|loc%]Previous page[%END%] [% END %] [% IF page %] [%|loc(page,total_page)%]page %1 / %2[%END%] [% END %] [% IF next_page %] [%|loc%]Next page[%END%] [% END %]
[% IF is_owner %]


[% END %]
sympa-6.1.24~dfsg/web_tt2/rss.tt2000066400000000000000000000067131246372670100166260ustar00rootroot00000000000000 [% FILTER escape_xml %][% title_clear_txt %][%END%][%|loc%]:[%END%] [%- IF errors %] [%|loc%]Server error[%END%] [% END -%] [%- IF action == 'latest_lists' -%][%|loc%] new lists [%END%] [%- ELSIF action == 'active_lists' -%][%|loc%] active lists [%END%] [%- ELSIF action == 'latest_arc' -%][%|loc%] most recent messages [%END%] [%- ELSIF action == 'latest_d_read' -%][%|loc%] most recent documents [%END%] [%- END -%] [%- IF action == 'latest_arc' -%][% base_url %][% path_cgi %]/arc/[% list %]/ [%- ELSIF action == 'latest_d_read' -%] [% base_url %][% path_cgi %]/d_read/[% list %]/ [%- ELSE -%][% base_url %][% path_cgi %] [%- END -%] [% IF errors %] [%|loc%]Server error[%END%] [% END -%] [%- IF action == 'latest_lists' -%] [%- IF count -%] [%|loc(count)%]The %1 new lists[%END%] [%- ELSE -%] [%|loc%]New lists[%END%] [%- END -%] [%- IF for -%] [%|loc(for)%]for %1 days[%END -%] [%- END -%] [%- IF subtitle %] - [% subtitle %] [% END -%] [%- ELSIF action == 'active_lists' -%] [%- IF count -%] [%|loc(count)%]The %1 most active lists[%END%] [%- ELSE -%] [%|loc%]The most active lists[%END%] [%- END -%] [%- IF for -%] [%|loc(for)%]for %1 days[%END%] [%- END -%] [%- IF subtitle %] - [% subtitle %] [% END -%] [%- ELSIF action == 'latest_arc' -%] [%- IF count -%] [%|loc(count)%]The %1 most recent messages[%END%] [%- ELSE -%] [%|loc%]Most recent messages[%END%] [%- END -%] [%- IF for -%] [%|loc(for)%]for %1 days [%END%] [%- END -%] [%- ELSIF action == 'latest_d_read' -%] [%- IF count -%] [%|loc(count)%]The %1 most recent shared documents[%END%] [%- ELSE -%] [%|loc%]Most recent shared documents[%END%] [%- END -%] [%- IF for -%] [%|loc(for)%]for %1 days [%END%] [%- END -%] [%- END -%] [% lang %] [% date %] Sympa[% version %] listmaster@[% conf.host %] http://web.resource.org/rss/1.0/spec [% IF action == 'latest_lists' -%] [% FOREACH l = latest_lists -%] [% l.name %]@[% l.host %] - [% FILTER escape_xml %][% l.subject %][%END%] [% base_url %][% path_cgi %]/info/[% l.name %] [% END -%] [% ELSIF action == 'active_lists' -%] [% FOREACH l = active_lists -%] [%|loc(l.name,l.host,l.subject,l.msg_count)%]%1@%2 - %3: %4 messages[%END%] [% IF l.average -%] - [%|loc(l.average)%]%1 by day [%END%][%END%] [% base_url %][% path_cgi %]/info/[% l.name %] [% END -%] [% ELSIF action == 'latest_arc' -%] [% FOREACH a = archives -%] [% FILTER escape_xml %][% a.subject %] - [% a.from %][%END%] [% conf.wwsympa_url %]/arcsearch_id/[% list %]/[% a.year_month %]/[% a.message_id %] [% a.date_smtp %] [% END -%] [% ELSIF action == 'latest_d_read' -%] [% FOREACH d = documents -%] [% IF d.url -%] [%|loc(d.anchor)%]Bookmark %1[%END%] [% d.url %] [% ELSE -%] [% d.name %] [% FILTER escape_xml %][% base_url %][% path_cgi %]/d_read/[% list %][% d.escaped_content_dir %]/[% d.escaped_name %][%END%] [% END -%] [% END -%] [% END -%] sympa-6.1.24~dfsg/web_tt2/rss_request.tt2000066400000000000000000000044311246372670100203710ustar00rootroot00000000000000
[% USE String %]

[%|loc%]RSS channel[%END%]


[%|loc%]This server provides various news via RSS. Choose parameters and pickup the RSS url[%END%]
[% IF list %] [% END %]


[% IF output %]
    [% IF latest_arc_url %] [% s = String.new(latest_arc_url) %]
  • [% END %] [% IF latest_d_read_url %] [% s = String.new(latest_d_read_url) %]
  • [% END %] [% IF active_lists_url %] [% s = String.new(active_lists_url) %]
  • [% END %] [% IF latest_lists_url %] [% s = String.new(latest_lists_url) %]
  • [% END %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/scenario_test.tt2000066400000000000000000000042661246372670100206620ustar00rootroot00000000000000

[%|loc%]Scenario test module[%END%]









[% IF scenario_action %] [% scenario_condition %], [% scenario_auth_method %] -> [% scenario_action %] [% ELSE %] [% END %]
sympa-6.1.24~dfsg/web_tt2/search_user.tt2000066400000000000000000000047361246372670100203250ustar00rootroot00000000000000

[%|loc%]User search result:[%END%]


[%|loc(email)%]%1 is subscribed to the following mailing lists:[%END%] [% IF which %] [% FOREACH l = which %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% IF l.value.subscribed %] [% ELSE %] [% END %] [% END %]
[%|loc%]list[%END%] [%|loc%]role[%END%] [%|loc%]reception[%END%] [%|loc%]topics[%END%] [%|loc%]bounce[%END%]
[% hidden_head %][% l.key %][% hidden_at %][% l.value.host %][% hidden_end %] [% IF l.value.is_member %][%|loc%]member[%END%] [% END %] [% IF l.value.is_owner %][%|loc%]owner[%END%] [% END %] [% IF l.value.is_editor %][%|loc%]editor[%END%] [% END %] [%|optdesc('reception')%][% l.value.reception %][% END %] [% l.value.topic %] [% l.value.bounce %]
[% ELSE %]

[%|loc%]No mailing list available.[%END%]

[% END %]
sympa-6.1.24~dfsg/web_tt2/serveradmin.tt2000066400000000000000000000243731246372670100203400ustar00rootroot00000000000000
[% IF subaction == '' or subaction == 'm_lists' %]

[%|loc%]Lists[%END%]

[% END %] [% IF main_robot %] [% IF subaction == 'vhosts' %]

[%|loc%]Virtual Robots[%END%]


[% IF robots %] [%|loc%]The following virtual robots are running on this server:[%END%]
    [% FOREACH vr = robots %]
  • [% vr.key %] - [%|loc%]Sympa URL:[%END%] [% vr.value.wwsympa_url %] - [%|loc%]Mail domain:[%END%] [% vr.value.host %] - [%|loc%]Listmaster(s):[%END%] [% FOREACH admin = vr.value.listmaster %][% admin %] [% END %]
  • [% END %]
[% ELSE %] [%|loc%]No Virtual Robot defined on this server[%END%] [% END %]
[% END %] [% END %] [% IF families -%] [% IF subaction == 'families' %]

[%|loc%]Families[%END%]


[% END %] [% END %] [% IF subaction == 'users' %]

[%|loc%]Users[%END%]




[%|loc%]You can change your users email address here:[%END%]


[%|loc%]Changing a user's email address will apply the change his memberships but it will also apply to his ownerships if he is owner of some lists.[%END%]

[%|loc%]Change identity[%END%]


[%|loc%]As listmaster, you can change your identity and act as someone else. This is may be usefull when providing assistance or to test users privileges. The new identity is attached to the current session[%END%]
[% END %] [% IF subaction == 'archives' %]

[%|loc%]Archives[%END%]


[%|loc%]Rebuild HTML archives using "arctxt" directories as input.[%END%]
([%|loc%] May take a lot of CPU time, be careful![%END%])
[% END %] [% IF subaction == 'logs' %]

[%|loc%]System log[%END%]


[%|loc%]You can increase the log level specified by configuration only for your current session. On a production server, this allow to keep logs rather small and to test some features with the maximum log level. [%END%]
[%|loc%]Log level:[%END%] | | | |
[% END %] [% IF subaction == 'templates' %]

[%|loc%]Templates[%END%]






[%|loc%]view translations[%END%] [%|loc%]Customize templates[%END%]
[%|loc%]When customizing Sympa web templates, it is needed to known which variables can be processed by the tt2 parser.This button is for controlling tt2 vars dump feature:[%END%]
[% IF dumpvars == 'true' %] [% ELSE %] [% END %]
[% END %] [% IF subaction == 'skins' %]

[%|loc%]Skins, CSS and colors[%END%]


[% END %] [% IF subaction == 'edit_config' %]

[%|loc%]View main config[%END%]


[% SET close_table = '' %] [% FOREACH confparam IN editable_params %] [% IF confparam.title %] [% close_table %]

[% confparam.title %]

[% ELSE %] [% IF dark == '1' %] [% SET dark='0'%] [% ELSE %] [% SET dark='1'%] [% END %] [% SET close_table = '
parameter namevaluedefaultsemantic
[% confparam.name %] [% IF confparam.edit == '1' %]
[% ELSE %] [% confparam.current_value %] [% END %]
[% confparam.default %][% confparam.query %]
' %] [% END %] [% END %] [% close_table %]
[% END %] [% IF subaction == 'translation' %]

[%|loc%]Translating Sympa[%END%]


[%|loc%]Sympa is designed to allow easy internationalization of its user interface (service mail messages and web interface). All translations for one language are gathered in a single PO file that can be manipulated by standard GNU gettext tools.[%END%]
[%|loc%]Check our translation FAQ if you wish to translate Sympa GUI in your native language: [%END%] http://www.sympa.org/translating_sympa
[% END %] [% IF subaction == 'tracker' %]

[%|loc%]Submitting a bug, a feature request[%END%]


[%|loc%]You can submit a problem or request a new feature: [%END%]http://www.sympa.org/tracking
[% END %]


[% IF loop_count %] [%|loc(process_id,loop_count,start_time)%]This FastCGI process (%1) has served %2 pages since %3.[%END%] [% END %]
sympa-6.1.24~dfsg/web_tt2/set_pending_list_request.tt2000066400000000000000000000033361246372670100231170ustar00rootroot00000000000000
[%|loc%]Listname:[%END%] [% list %]
[%|loc%]Subject:[%END%] [% list_subject %]
[%|loc%]List requested by[%END%] [% list_request_by %] [%|loc(list_request_date)%] on %1[%END%]


[% IF is_listmaster %] [% IF list_status == 'pending' %]



[% END %] [% END %]
[%|loc%]Information file:[%END%]
[% IF is_listmaster %] ([% list_info %]) [% END %]
   [% IF list_info_file_exists %]
       [% INSERT 'info' %]
   [% ELSE %]
	No info file provided.
   [% END %]




[%|loc%]Configuration file[%END%] [% IF is_listmaster %] ([% list_config %]) [% END %]

        [% INSERT 'config' %]
sympa-6.1.24~dfsg/web_tt2/setlang.tt2000066400000000000000000000020501246372670100174420ustar00rootroot00000000000000
[% IF languages.size > 1 %] [% END %]
sympa-6.1.24~dfsg/web_tt2/show_cert.tt2000066400000000000000000000010211246372670100177770ustar00rootroot00000000000000
    [%|loc%]HTTPS authentication information[%END%]
  • [%|loc%]User certificate belong to[%END%][% ssl_client_s_dn %]

  • [%|loc%]Certificate expiration date[%END%][% ssl_client_v_end %]

  • [%|loc%]Certificate issuer[%END%][% ssl_client_i_dn %]

  • [%|loc%]Cipher key size used[%END%][% ssl_cipher_usekeysize %]

sympa-6.1.24~dfsg/web_tt2/show_exclude.tt2000066400000000000000000000012761246372670100205070ustar00rootroot00000000000000

[%|loc%]Exclusion table[%END%]

[%|loc%]Users listed below are excluded from the list. Beware, that this feature is different from the blacklist. Exclusion makes sense when a list is based on external data sources ; thanks to exclusion, a user can unsubscribe (or being removed by list owner) even though he should be included via an external data source. Users get into the exclusion table, through the standard unsubscribe/delete functions. They get off the exclusion table with the standard subscribe/add functions.[%END%]
[% PROCESS exclusion_table.tt2 %] sympa-6.1.24~dfsg/web_tt2/show_sessions.tt2000066400000000000000000000014441246372670100207210ustar00rootroot00000000000000 [% FOREACH session = sessions %] [% IF dark == '1' %][% SET dark = '0' %][% ELSE %][% SET dark = '1' %][% END %] [% END %]
[%|loc%]last hit date[%END%][%|loc%]start date[%END%][%|loc%]hit count[%END%][%|loc%]remote address[%END%][%|loc%]robot[%END%][%|loc%]User email[%END%]
[% session.formated_date %][% session.formated_start_date %][% session.hit_session %][% session.remote_addr_session %][% session.robot_session %][% session.email_session %]
sympa-6.1.24~dfsg/web_tt2/sigrequest.tt2000066400000000000000000000037721246372670100202140ustar00rootroot00000000000000 [% IF status == 'user.email' %] [%|loc(list)%]You asked to be unsubscribed from list %1[%END%].
[%|loc%]To confirm your request, please click the button below:[%END%]

[% ELSIF not_subscriber %] [%|loc(list,email)%]You are not subscribed to list %1 with e-mail address %2[%END%].

[%|loc%]You might have subscribed with another address. Please contact the list owner to help you unsubscribe:[%END%] [% list %]-request@[% conf.host %] [% ELSIF ! email %] [%|loc(list)%]We need to check your identity before validating your unsubscription request for list %1. If you have a user account you should login or create an account.
Otherwise we can send you an email with a validation link inside; please provide your email address below to receive this validation link.[%END%]

[% ELSE %] [%|loc%]We've sent you an email including a validation link. You should check your mailbox to read this email and use this validation link.[%END%]

[% END %] sympa-6.1.24~dfsg/web_tt2/skinsedit.tt2000066400000000000000000000367211246372670100200160ustar00rootroot00000000000000

[%|loc%]Cascading Style Sheet[%END%]

[%|loc%]When not using css_url parameters, sympa deliver a dynamic CSS which is created using a template name css.tt2. Usually this template is comming from Sympa distribution tar. Using this CSS is not a good solution because for each clic, Sympa fcgi server is requested twice. If you use css_url parameters the style sheet are delivered by your http server. When you install a new Sympa version and start it at the first time, the different CSS files are installed in the directory specified by css_path parameter. So if you want to preserve some site customization from being overwriten when starting a new sympa version, css_path and css_url should not point to the same directory .[%END%] [% IF css_result %] [%|loc%]static css installation succeed. Reload the current page and/or check sympa logs to be sure that static css a really in use.[%END%]

[% END %] [% IF css_path %]

  • [%|loc%]The css_path parameter is defined, value is[%END%] [% css_path %]
  • [%|loc%]the current definition for css location (css_url parameter) is[%END%] [% css_url %] [% css_warning %].

[% ELSE %] [%|loc(cssurl)%]Currently you have not defined the css_path parameter. You should edit the robot.conf configuration file (or if not using virtual robot, the sympa.conf file). Setting this parameter allows you to use this page to install static CSS and make sympa faster. Don't forget to set parameter css_url, it must be the URL for the directory where css are stored (current value is %1).[%END%] [% END %]

[%|loc%]Colors[%END%]

[%|loc%]If you are not using css_path and css_url parameters, colors are defined in the robot.conf configuration file. Otherwise, colors are defined in the static CSS. Colors can be changed on your current session using the following color editor. When finished, you may copy the result a new CCS static file. The target is specified by css_path parameter.[%END%] [% IF css_path %]([% css_path %])[% END %] [%|loc%] Be careful: the CSS file is overwritten using css.tt2 template, usually this file is comming from Sympa distribution so your CSS customization may be erased when doing this.[% END %]

[%|loc%]Use the color editor in order to change defined colors. First select the color you want to change and pick a color,then apply it using the test button. The new color is not really installed but it is used only for your own session. When happy with the different colors you choosen, you may save them in a new static CSS. [%END%]

  1. [%|loc%] pick the color you want to test. [%END%]
  2. [%|loc%]Select the parameter you want to change: [%END%]

[%|loc%]Color chart[%END%]

[%|loc%]Please note that these indications don't cover the exact usage of each color parameter, as it would be far too long to describe. What lies in this table should however give you a correct snapshot of what the color parameters are used for. For more details on rendering, feel free to try changing the colors in your session to see how well all that works.[%END%]

[% IF custom_css -%] [%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%] [% IF custom_css %][%END%]
[%|loc%]parameter[%END%] [%|loc%]parameter value[%END%] [%|loc%]color lookup[%END%][%|loc%]default[%END%] [%|loc%]color lookup[%END%][%|loc%]parameter usage[%END%]
color_0 [% color_0 %]  [% conf.color_0 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]one out of two lines in tables, alternated with color_14.[%END%]
color_1 [% color_1 %]  [% conf.color_1 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]main navigation tabs;[%END%]
  • [%|loc%]most of the buttons;[%END%]
  • [%|loc%]border colors in the online help.[%END%]
color_2 [% color_2 %]  [% conf.color_2 %]  [%|loc%]font and border color of:[%END%]
  • [%|loc%]almost everything. Exceptions for borders are <TD/> borders; exceptions for fonts are links, buttons and some titles.[%END%]
color_3 [% color_3 %]  [% conf.color_3 %]  [%|loc%]Background color for the news area of the home page.[%END%]
color_4 [% color_4 %]  [% conf.color_4 %]  [%|loc%]font color of:[%END%]
  • [%|loc%]titles;[%END%]
  • [%|loc%]hovered links.[%END%]
color_5 [% color_5 %]  [% conf.color_5 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]all the main interface boxes.[%END%]
color_6 [% color_6 %]  [% conf.color_6 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]buttons;[%END%]
  • [%|loc%]navigation tabs;[%END%]
  • [%|loc%]active option, if you have several buttons representing an alternative.[%END%]
color_7 [% color_7 %]  [% conf.color_7 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]error messages.[%END%]
color_8 [% color_8 %]  [% conf.color_8 %]  [%|loc%]font and border color of:[%END%]
  • [%|loc%]hovered links;[%END%]
  • [%|loc%]navigation tabs;[%END%]
  • [%|loc%]buttons;[%END%]
[%|loc%]background color of hovered buttons.[%END%]
color_9 [% color_9 %]  [% conf.color_9 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]main page;[%END%]
  • [%|loc%]table headers;[%END%]
  • [%|loc%]buttons.[%END%]
color_10 [% color_10 %]  [% conf.color_10 %]  [%|loc%]border color for forms.[%END%]
color_11 [% color_11 %]  [% conf.color_11 %]  [%|loc%]border color that can be used to isolate some forms.[%END%]
color_12 [% color_12 %]  [% conf.color_12 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]top box;[%END%]
  • [%|loc%]footer box;[%END%]
  • [%|loc%]online help buttons;[%END%]
[%|loc%]border color of:[%END%]
  • [%|loc%]tables;[%END%]
  • [%|loc%]side menu areas.[%END%]
color_13 [% color_13 %]  [% conf.color_13 %]  [%|loc%]background color of:[%END%]
  • [%|loc%]editable form areas (text areas, selection lists, etc.).[%END%]
color_14 [% color_14 %]  [% conf.color_14 %]  [%|loc%]Default background color of the tables.[%END%]
color_15 [% color_15 %]  [% conf.color_15 %]  [%|loc%]unused[%END%]
dark_color [% dark_color %]   [%|loc%]deprecated[%END%]
light_color [% light_color %]   [%|loc%]deprecated[%END%]
text_color [% text_color %]   [%|loc%]deprecated[%END%]
bg_color [% bg_color %]   [%|loc%]deprecated[%END%]
selected_color [% selected_color %]   [%|loc%]deprecated[%END%]
shaded_color [% shaded_color %]   [%|loc%]deprecated[%END%]


[%|loc%]W3C CSS validation tools[%END%]

sympa-6.1.24~dfsg/web_tt2/sso_login.tt2000066400000000000000000000042261246372670100200100ustar00rootroot00000000000000 [% IF subaction == 'requestemail' %] [%|loc%]The mailing list server requires a working email address. Please provide your email address below.

Please note: the email field may be pre-populated. If it is, then your authentication server has supplied an email address it associates with you. Whether you enter an address manually or accept the one provided, you will still need to confirm that this is a working email address by entering a confirmation password that will be sent to you. Please press the "Send me a confirmation password" to continue to the confirmation step.[%END%]

[%|loc%]A confirmation password will be sent to the address you supply, after pressing the button. This will take you to the next screen to confirm that your address is operational.[%END%]

     
[% ELSIF subaction == 'validateemail' %]
[%|loc(init_email)%]A confirmation password has been sent to your email address %1.
Please check your e-mail box for the message containing the confirmation link.[%END%]

[% END %] sympa-6.1.24~dfsg/web_tt2/stats.tt2000066400000000000000000000003731246372670100171510ustar00rootroot00000000000000 [%|loc%]Shared document directory size:[%END%] [% shared_size %]K
[%|loc%]Web archives size:[%END%] [% arc_size %]K
sympa-6.1.24~dfsg/web_tt2/subindex.tt2000066400000000000000000000044321246372670100176340ustar00rootroot00000000000000

[%|loc%]Listing subscription to moderate[%END%]

[% IF subscriptions %] [% FOREACH sub = subscriptions %] [% IF dark == '1' %] [% ELSE %] [% END %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %] [% ELSE %] [% END %]
«» [%|loc%]Email[%END%] [%|loc%]Name[%END%] [%|loc%]Date[%END%] [%|loc%]Additional information[%END%]
[% sub.key %] [% sub.value.gecos %]  [% sub.value.date %] [% FOREACH ca_k IN list_conf.custom_attribute %][% ca_k.name %][%|loc%]:[%END%] [% IF sub.value.custom_attribute.item(ca_k.id).value %][% sub.value.custom_attribute.item(ca_k.id).value %][% ELSE %]-[% END %]
[% END %]
[%|loc%]No subscription requests[%END%]
sympa-6.1.24~dfsg/web_tt2/suboptions.tt2000066400000000000000000000064071246372670100202240ustar00rootroot00000000000000

[%|loc%]Your list options[%END%]




[% PROCESS edit_attributes.tt2 %] [%|loc%]You are subscribed since[%END%] [% subscriber.date %]

[%|loc%]Last update:[%END%] [% subscriber.update_date %]

[%|loc%]Receiving mode:[%END%] [% SET single_option = 0 %][% FOREACH r = reception %] [% IF loop.size == 1 %][% SET single_option = 1 %][% END %][% LAST %] [% END %] [% IF single_option %] [% FOREACH r = reception %] [% r.value.description %] [% END %] [% ELSE %] [% END %] [%|loc%]Help[%END%] [%IF available_topics %]

[%|loc%]Topic subscription:[%END%] [%IF possible_topic %]
[% FOREACH t = available_topics %] [%IF topic_checked.${t.name} %] [%ELSE%] [%END%]
[% END %] [%IF topic_checked.other %] [%ELSE%] [%END%]
[%IF sub_user_topic %]

[%END%] [%ELSE%]
[%|loc%]Only possible for direct reception modes.[%END%]
[%END%] [%END%]
[%|loc%]Visibility:[%END%]



[% PROCESS picture_upload.tt2 %]
sympa-6.1.24~dfsg/web_tt2/subrequest.tt2000066400000000000000000000070421246372670100202150ustar00rootroot00000000000000 [% IF status == 'auth' %] [% IF listconf.custom_attribute.size == 0 %] [%|loc(list)%]You requested a subscription to list %1[%END%]. [%|loc%]To confirm your request, please click the button below:[%END%]
[% ELSE %] [%|loc(list)%]You want to subscribe to list %1[%END%]. [%|loc%]Please fill in the form below and then click the validation button[%END%]

[% END %]

[% PROCESS edit_attributes.tt2 %]
[% ELSIF status == 'notauth_passwordsent' %] [%|loc(list)%]You requested a subscription to list %1[%END%].

[%|loc%]To confirm your identity and prevent anyone from subscribing you to this list against your will, a message containing an validation link was sent to the e-mail address you provided.[%END%]

[%|loc(list)%]Check your mailbox for new messages and click this link. This will confirm your subscription to list %1.[%END%] [% ELSIF status == 'notauth_noemail' %]

[% ELSIF status == 'notauth' %] [%|loc(list)%]To confirm your subscription to list %1, please enter your password below[%END%] [% IF listconf.custom_attribute.size == 0 %] [%|loc%], in addition to the personnal informations requested.[%END%] [% END %]
[% email %]

[% PROCESS edit_attributes.tt2 %]      
[% ELSIF status == 'notauth_subscriber' %] [%|loc(list)%]You are already subscribed to list %1[%END%].

[% PROCESS 'loginbanner.tt2' %] [% END %] sympa-6.1.24~dfsg/web_tt2/subscriber_table.tt2000066400000000000000000000104501246372670100213220ustar00rootroot00000000000000 [% IF is_listmaster || is_owner || may_del %] [% END %] [% IF sortby == 'email' %] [% IF sortby == 'domain' %] [% IF pictures_display %] [% END %] [% IF sortby == 'name' %] [% IF is_listmaster || is_owner || is_editor %] [% IF sortby == 'sources' %] [% IF sortby == 'date' %] [% IF additional_fields %] [% END %] [% FOREACH ca_k IN list_conf.custom_attribute %][% END %] [% END %] [% FOREACH u = thosesubscribers %] [% IF dark == '1' %] [% ELSE %] [% END %] [% IF is_listmaster || is_owner || may_del %] [% END %] [% IF u.bounce %] [% IF is_listmaster || is_owner || is_editor %] [% END %] [% ELSE %] [% END %] [% IF pictures_display %] [% IF u.pictures_url %] [% ELSE %] [% END %] [% END %] [% IF is_listmaster || is_owner || is_editor %] [% IF additional_fields %] [% END %] [% FOREACH ca_k IN list_conf.custom_attribute %][% END %] [% END %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
«» [%|loc%]Email[%END%] [% ELSE %] [%|loc%]Email[%END%] [% END %] [%|loc%]Domain[%END%] [% ELSE %] [%|loc%]Domain[%END%] [% END %] [%|loc%]Picture[%END%] [%|loc%]Name[%END%] [% ELSE %] [%|loc%]Name[%END%] [% END %] [%|loc%]Reception[%END%] [%|loc%]Sources[%END%] [% ELSE %] [%|loc%]Sources[%END%] [% END %] [%|loc%]Sub date[%END%] [% ELSE %] [%|loc%]Sub date[%END%] [% END %] [%|loc%]Last update[%END%][% additional_fields %][% ca_k.name %]
[% IF is_listmaster || is_owner %] [% u.email %] [% ELSE %] [% u.email %] [% END %] [%|loc%]bouncing[%END%] [% IF is_listmaster || is_owner %] [% u.email %] [% ELSE %] [% u.email %] [% END %] [%|loc(u.email)%]%1's picture[%END%] [% u.gecos %]  [%|optdesc('reception')%][% u.reception %][% END %] [% IF u.subscribed %] [% IF u.included %] subscribed
[% u.sources %] [% ELSE %] subscribed [% END %] [% ELSE %] [% u.sources %] [% END %]
[% u.date %] [% u.update_date %] [% u.additional %] [% SET id = ca_k.id %][% u.custom_attribute.$id.value %]
sympa-6.1.24~dfsg/web_tt2/suspend_request.tt2000066400000000000000000000055221246372670100212450ustar00rootroot00000000000000

[%|loc%]Manage your subscriptions[%END%][%|loc%]Help[%END%]


[% IF user.email %] [% IF suspend_list %] [% FOREACH sub = suspend_list %] [% IF sub.listsuspend %] [% ELSE %] [% END %] [% END %] [% ELSE %]

[%|loc%]No subscription.[% END %]

[% END %] [% END %]
[%|loc%]Lists[%END%] [%|loc%]Reception[%END%] [%|loc%]Suspended[%END%]
 [%|loc%]Suspended from[% END %] [% sub.liststartdate %] [%|loc%]to[% END %] [% IF sub.listenddate %][% sub.listenddate %][% ELSE %][%|loc%]indefinite end date[% END %][% END %] [%|optdesc('reception')%][% sub.listreception %][% END %]


[%|loc%]Suspend / Resume the selected lists[%END%]

[%|loc%]From:[%END%] [%|loc%]To:[%END%]
[% IF sub.display == 1 %] [% END %]

[%|loc%]Unsubscribe selected lists[%END%]


sympa-6.1.24~dfsg/web_tt2/ticket.tt2000066400000000000000000000032431246372670100172750ustar00rootroot00000000000000 [% IF user.email %]

[%|loc%]The link you used is unvalid[%END%]

[% ELSE %]

[%|loc%]Sorry, this operation can't be performed[%END%]

[%END%] [% IF ticket_context.date %] [%|loc(ticket_context.printable_date) %]A validation link was sent to you on %1. [%END%] [% IF ticket_context.remote_addr %] [%|loc(ticket_context.remote_addr) %](the validation link was requested from host %1) [%END%] [%END%] [%END%]

[%|loc%]reason[%END%]

[% IF ticket_context.result == 'closed' -%] [%|loc(ticket_context.status)%]The validation link has already been validated from host %1 .If you did not perform this validation, please report this confidentiality issue to your mail services administrator.[%END%] [% IF user.email %]
[%|loc%]However, as you are logged in already, you can probably perform the action you requested anyway.[%END%] [%END%] [%END%] [% IF ticket_context.result == 'expired' -%] [%|loc(ticket_context.printable_date)%]The validation link has expired[%END%]. [% IF user.email %]
[%|loc%]However, as you are logged in already, you can probably perform the action you requested anyway.[%END%] [% ELSE %] [%|loc%]Please login or request a new validation link[%END%] [%END%] [%END%] [% IF ticket_context.result == 'not_found' -%] [%|loc(ticket_context.printable_date)%]The validation link has an unknow format or has expired[%END%]. [% IF user.email %]
[%|loc%]However, as you are logged in already, you can probably perform the action you requested anyway.[%END%] [% ELSE %] [%|loc%]Please login or request a new validation link[%END%] [%END%] [%END%] sympa-6.1.24~dfsg/web_tt2/title.tt2000066400000000000000000000006531246372670100171350ustar00rootroot00000000000000 [% IF conf.logo_html_definition %] [% ELSE %] [% END %]
[% robot_title %]
sympa-6.1.24~dfsg/web_tt2/tt2_error.tt2000066400000000000000000000021141246372670100177300ustar00rootroot00000000000000 [%|loc%]Sympa error - could not display web page[%END%]

[%|loc%]Sympa could not deliver the requested page for the following reason: [%END%]



[% IF tt2_error %] [% tt2_error %]

[% ELSIF errors %] [% PROCESS error.tt2 %] [% END %]

[%|loc%]Please contact the listmaster.[%END%]

[%|loc%]Powered by[%END%] Sympa sympa-6.1.24~dfsg/web_tt2/view_template.tt2000066400000000000000000000031531246372670100206570ustar00rootroot00000000000000

[%|loc%]Template edition system[%END%]


  • [%|loc%]Template name: [%END%] [% template_name %]
  • [%|loc%]Type: [%END%] [% SWITCH webormail -%] [% CASE 'web' %][%|loc%]web[%END -%] [% CASE 'mail' %][%|loc%]mail[%END -%] [% CASE %][% webormail -%] [%END%]
  • [%|loc%]Path: [%END%] [% template_path %]
  • [%|loc%]Scope: [%END%] [%- SWITCH scope -%] [% CASE 'distrib' %] [%|loc%]default[%END%] [%|loc%](this template is the default included in the distribution)[%END%] [% CASE 'site' %] [%|loc%]site[%END%] [%|loc%](this template is the default used by all robots unless redefined for a specific robot)[%END%] [% CASE 'robot' %] [%|loc%]robot[%END%] [%|loc(robot)%](this template is the default for all lists of robot %1 unless it is redefined for a specific list)[%END%] [% CASE 'list' %] [%|loc%]list[%END -%] [%|loc(list,robot)%](this template is defined for list %1@%2)[%END%] [% CASE %] [% scope %] [% END %]
  • [%|loc%]Language: [%END%] [%- IF tpl_lang == 'default' -%] [%|loc%]default[%END%] [%|loc%](This template is the default for all languages unless it is redefined for a specific language)[%END%] [%- ELSE -%] [%tpl_lang_title%] [%- END %]


[% template_content %]
sympa-6.1.24~dfsg/web_tt2/viewbounce.tt2000066400000000000000000000003031246372670100201520ustar00rootroot00000000000000
[% FILTER html_entity %]
[% INSERT $lastbounce_path IF lastbounce_path %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/viewlogs.tt2000066400000000000000000000165261246372670100176610ustar00rootroot00000000000000

[%|loc%]Logs view[%END%]

[% IF action != 'search' %]
[%|loc%]Page size[%END%]

[% IF prev_page %] [%|loc%]Previous page[%END%] [% END %] [% IF page %] [%|loc%]page[%END%] [% page %] / [% total_page %] [% END %] [% IF next_page %] [%|loc%]Next page[%END%] [% END %]
[% END %]



[% IF is_listmaster %]
[% END %]   
[%|loc%]Search period: [%END%][%|locdt(date_from_formated)%]%d %b %Y %H:%M:%S[%END%] [%|loc%]to[%END%] [%|locdt(date_to_formated)%]%d %b %Y %H:%M:%S[%END%]
[% IF total_results %] [%|loc(list)%]Research was carried out in list %1.[%END%]

[%|loc(total_results)%]%1 results[%END%].
[% IF sortby == 'date' %] [% IF sortby == 'action' %] [% IF sortby == 'email' %] [% IF is_listmaster || is_owner %] [% END %] [% FOREACH l = log_entries %] [% IF dark == '1' %] [% ELSE %] [% END %] [% IF l.status == 'error' %] [% END %] [% IF is_listmaster || is_owner %] [% END %] [% IF is_listmaster || is_owner %] [% END %] [% IF dark == '1' %] [% SET dark = 0 %] [% ELSE %] [% SET dark = 1 %] [% END %] [% END %]
[%|loc%]Sub date[%END%] [% ELSE %] [%|loc%]Date[%END%] [% END %] [%|loc%]List[%END%] [%|loc%]Action[%END%] [% ELSE %] [%|loc%]Action[%END%] [% END %] [%|loc%]Parameters[%END%] [%|loc%]Target Email[%END%] [%|loc%]Message ID[%END%] [%|loc%]Status[%END%] [%|loc%]Error type[%END%] [%|loc%]User Email[%END%] [% ELSE %] [%|loc%]User Email[%END%] [% END %] [%|loc%]User IP[%END%] [%|loc%]Service[%END%]
[% l.date %] [% l.list %] [% l.action %] [% l.parameters %] [% l.target_email %] [% IF l.msg_id %] [%|loc%]Other events[%END%] [% END %] [% l.status %] [% l.error_type %] [% l.user_email %] [% l.client %] [% l.daemon %]
[% END %]
sympa-6.1.24~dfsg/web_tt2/viewmod.tt2000066400000000000000000000032331246372670100174630ustar00rootroot00000000000000
[%- IF topic_required -%] [%- ELSE -%] [%- END -%]
[%- IF request_topic -%]


[%|loc%]This list is configured to require topic(s).[%END%]
[%- END -%] [% PROCESS 'msg00000.html' %] sympa-6.1.24~dfsg/web_tt2/your_lists.tt2000066400000000000000000000013661246372670100202320ustar00rootroot00000000000000 [% IF user.email %] [% END %] sympa-6.1.24~dfsg/web_tt2/zh_CN/000077500000000000000000000000001246372670100163565ustar00rootroot00000000000000sympa-6.1.24~dfsg/web_tt2/zh_CN/css.tt2000066400000000000000000000016231246372670100176030ustar00rootroot00000000000000[%# $Id: $ -%] [%# This style sheet will override any portion of main CSS, not fully replacing it. So this may be used for locale-specific customization. -%] /* begin zh_CN/css.tt2 */ @media screen { } [%# Enlarge fonts. %] body { font-size: 69%; line-height: 1.5; } [%# Overriding default font family. %] * { font-family:"儷黑 Pro", "LiHei Pro Medium", "华文黑体", STHeiti, Hei, "微软雅黑", "Microsoft YaHei", "MS Hei", "黑体", SimHei, "文泉驿微米黑", "WenQuanYi Micro Hei", sans-serif; } [%# Overriding serif !important. %] #bandeau_top #Identity, #bandeau_top #Identity_not_connect, #bandeau_top #Identity_not_connect label, #bandeau_top .prefs, #bandeau_top a, #bandeau_top .login, #bandeau_top .remember { font-family:"儷宋 Pro", "LiSong Pro Light", "华文仿宋", STFangsong, Song, "仿宋_GB2312", FangSong, "宋体", SimSun, serif !important; } /* end zh_CN/css.tt2 */ sympa-6.1.24~dfsg/web_tt2/zh_TW/000077500000000000000000000000001246372670100164105ustar00rootroot00000000000000sympa-6.1.24~dfsg/web_tt2/zh_TW/css.tt2000066400000000000000000000014311246372670100176320ustar00rootroot00000000000000[%# $Id: $ -%] [%# This style sheet will override any portion of main CSS, not fully replacing it. So this may be used for locale-specific customization. -%] /* begin zh_TW/css.tt2 */ @media screen { } [%# Enlarge fonts. %] body { font-size: 69%; line-height: 1.5; } [%# Overriding default font family. %] * { font-family:"儷黑 Pro", "LiHei Pro", "Apple LiGothic", "微軟正黑體", "Microsoft JhengHei", sans-serif; } [%# Overriding "serif!important" %] #bandeau_top #Identity, #bandeau_top #Identity_not_connect, #bandeau_top #Identity_not_connect label, #bandeau_top .prefs, #bandeau_top a, #bandeau_top .login, #bandeau_top .remember { font-family:"儷宋 Pro", "LiSong Pro", "Apple LiSung Light", PMingLiu, "PMingLiu-ExtB", serif !important; } /* end zh_TW/css.tt2 */ sympa-6.1.24~dfsg/wwsympa/000077500000000000000000000000001246372670100155165ustar00rootroot00000000000000sympa-6.1.24~dfsg/wwsympa/Auth.pm000066400000000000000000000417641246372670100167710ustar00rootroot00000000000000# Auth.pm - This module provides web authentication functions # RCS Identication ; $Revision: 10083 $ ; $Date: 2014-01-01 01:12:12 +0100 (mer. 01 janv. 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package Auth; use Digest::MD5; use Language; use Log; use Conf; use List; use report; ## return the password finger print (this proc allow futur replacement of md5 by sha1 or ....) sub password_fingerprint{ do_log('debug', 'Auth::password_fingerprint'); my $pwd = shift; if(&Conf::get_robot_conf('*','password_case') eq 'insensitive') { return &tools::md5_fingerprint(lc($pwd)); }else{ return &tools::md5_fingerprint($pwd); } } ## authentication : via email or uid sub check_auth{ my $robot = shift; my $auth = shift; ## User email or UID my $pwd = shift; ## Password &do_log('debug', 'Auth::check_auth(%s)', $auth); my ($canonic, $user); if( &tools::valid_email($auth)) { return &authentication($robot, $auth,$pwd); }else{ ## This is an UID foreach my $ldap (@{$Conf{'auth_services'}{$robot}}){ # only ldap service are to be applied here next unless ($ldap->{'auth_type'} eq 'ldap'); $canonic = &ldap_authentication($robot, $ldap, $auth,$pwd,'uid_filter'); last if ($canonic); ## Stop at first match } if ($canonic){ unless($user = &List::get_user_db($canonic)){ $user = {'email' => $canonic}; } return {'user' => $user, 'auth' => 'ldap', 'alt_emails' => {$canonic => 'ldap'} }; }else{ &report::reject_report_web('user','incorrect_passwd',{}) unless ($ENV{'SYMPA_SOAP'}); &do_log('err', "Incorrect Ldap password"); return undef; } } } ## This subroutine if Sympa may use its native authentication for a given user ## It might not if no user_table paragraph is found in auth.conf or if the regexp or ## negative_regexp exclude this user ## IN : robot, user email ## OUT : boolean sub may_use_sympa_native_auth { my ($robot, $user_email) = @_; my $ok = 0; ## check each auth.conf paragrpah foreach my $auth_service (@{$Conf{'auth_services'}{$robot}}){ next unless ($auth_service->{'auth_type'} eq 'user_table'); next if ($auth_service->{'regexp'} && ($user_email !~ /$auth_service->{'regexp'}/i)); next if ($auth_service->{'negative_regexp'} && ($user_email =~ /$auth_service->{'negative_regexp'}/i)); $ok = 1; last; } return $ok; } sub authentication { my ($robot, $email,$pwd) = @_; my ($user,$canonic); &do_log('debug', 'Auth::authentication(%s)', $email); unless ($user = &List::get_user_db($email)) { $user = {'email' => $email }; } unless ($user->{'password'}) { $user->{'password'} = ''; } if ($user->{'wrong_login_count'} > &Conf::get_robot_conf($robot, 'max_wrong_password')){ # too many wrong login attemp &List::update_user_db($email,{wrong_login_count => $user->{'wrong_login_count'}+1}) ; &report::reject_report_web('user','too_many_wrong_login',{}) unless ($ENV{'SYMPA_SOAP'}); &do_log('err','login is blocked : too many wrong password submission for %s', $email); return undef; } foreach my $auth_service (@{$Conf{'auth_services'}{$robot}}){ next if ($auth_service->{'auth_type'} eq 'authentication_info_url'); next if ($email !~ /$auth_service->{'regexp'}/i); next if (($email =~ /$auth_service->{'negative_regexp'}/i)&&($auth_service->{'negative_regexp'})); ## Only 'user_table' and 'ldap' backends will need that Sympa collects the user passwords ## Other backends are Single Sign-On solutions if ($auth_service->{'auth_type'} eq 'user_table') { my $fingerprint = &password_fingerprint ($pwd); if ($fingerprint eq $user->{'password'}) { &List::update_user_db($email,{wrong_login_count => 0}) ; return {'user' => $user, 'auth' => 'classic', 'alt_emails' => {$email => 'classic'} }; } }elsif($auth_service->{'auth_type'} eq 'ldap') { if ($canonic = &ldap_authentication($robot, $auth_service, $email,$pwd,'email_filter')){ unless($user = &List::get_user_db($canonic)){ $user = {'email' => $canonic}; } &List::update_user_db($canonic,{wrong_login_count => 0}) ; return {'user' => $user, 'auth' => 'ldap', 'alt_emails' => {$email => 'ldap'} }; } } } # increment wrong login count. &List::update_user_db($email,{wrong_login_count =>$user->{'wrong_login_count'}+1}) ; &report::reject_report_web('user','incorrect_passwd',{}) unless ($ENV{'SYMPA_SOAP'}); &do_log('err','authentication: incorrect password for user %s', $email); $param->{'init_email'} = $email; $param->{'escaped_init_email'} = &tools::escape_chars($email); return undef; } sub ldap_authentication { my ($robot, $ldap, $auth, $pwd, $whichfilter) = @_; my ($mesg, $host,$ldap_passwd,$ldap_anonymous); &do_log('debug2','Auth::ldap_authentication(%s,%s,%s)', $auth,'****',$whichfilter); &do_log('debug3','Password used: %s',$pwd); unless (&tools::get_filename('etc',{},'auth.conf', $robot)) { return undef; } ## No LDAP entry is defined in auth.conf if ($#{$Conf{'auth_services'}{$robot}} < 0) { &do_log('notice', 'Skipping empty auth.conf'); return undef; } # only ldap service are to be applied here return undef unless ($ldap->{'auth_type'} eq 'ldap'); # skip ldap auth service if the an email address was provided # and this email address does not match the corresponding regexp return undef if ($auth =~ /@/ && $auth !~ /$ldap->{'regexp'}/i); my @alternative_conf = split(/,/,$ldap->{'alternative_email_attribute'}); my $attrs = $ldap->{'email_attribute'}; my $filter = $ldap->{'get_dn_by_uid_filter'} if($whichfilter eq 'uid_filter'); $filter = $ldap->{'get_dn_by_email_filter'} if($whichfilter eq 'email_filter'); $filter =~ s/\[sender\]/$auth/ig; ## bind in order to have the user's DN my $param = &tools::dup_var($ldap); my $ds = new Datasource('LDAP', $param); unless (defined $ds && ($ldap_anonymous = $ds->connect())) { &do_log('err',"Unable to connect to the LDAP server '%s'", $ldap->{'host'}); return undef; } $mesg = $ldap_anonymous->search(base => $ldap->{'suffix'}, filter => "$filter", scope => $ldap->{'scope'} , timeout => $ldap->{'timeout'}); if ($mesg->count() == 0) { do_log('notice','No entry in the Ldap Directory Tree of %s for %s',$ldap->{'host'},$auth); $ds->disconnect(); return undef; } my $refhash=$mesg->as_struct(); my (@DN) = keys(%$refhash); $ds->disconnect(); ## bind with the DN and the pwd ## Duplicate structure first ## Then set the bind_dn and password according to the current user $param = &tools::dup_var($ldap); $param->{'ldap_bind_dn'} = $DN[0]; $param->{'ldap_bind_password'} = $pwd; $ds = new Datasource('LDAP', $param); unless (defined $ds && ($ldap_passwd = $ds->connect())) { do_log('err',"Unable to connect to the LDAP server '%s'", $param->{'host'}); return undef; } $mesg= $ldap_passwd->search ( base => $ldap->{'suffix'}, filter => "$filter", scope => $ldap->{'scope'}, timeout => $ldap->{'timeout'} ); if ($mesg->count() == 0 || $mesg->code() != 0) { do_log('notice',"No entry in the Ldap Directory Tree of %s", $ldap->{'host'}); $ds->disconnect(); return undef; } ## To get the value of the canonic email and the alternative email my (@canonic_email, @alternative); ## Keep previous alt emails not from LDAP source my $previous = {}; foreach my $alt (keys %{$param->{'alt_emails'}}) { $previous->{$alt} = $param->{'alt_emails'}{$alt} if ($param->{'alt_emails'}{$alt} ne 'ldap'); } $param->{'alt_emails'} = {}; my $entry = $mesg->entry(0); @canonic_email = $entry->get_value($attrs,alloptions); foreach my $email (@canonic_email){ my $e = lc($email); $param->{'alt_emails'}{$e} = 'ldap' if ($e); } foreach my $attribute_value (@alternative_conf){ @alternative = $entry->get_value($attribute_value,alloptions); foreach my $alter (@alternative){ my $a = lc($alter); $param->{'alt_emails'}{$a} = 'ldap' if($a) ; } } ## Restore previous emails foreach my $alt (keys %{$previous}) { $param->{'alt_emails'}{$alt} = $previous->{$alt}; } $ds->disconnect() or &do_log('notice', "unable to unbind"); &do_log('debug3',"canonic: $canonic_email[0]"); ## If the identifier provided was a valid email, return the provided email. ## Otherwise, return the canonical email guessed after the login. if( &tools::valid_email($auth) && !$Conf::Conf{'robots'}{$robot}{'ldap_force_canonical_email'}) { return ($auth); }else{ return lc($canonic_email[0]); } } # fetch user email using his cas net_id and the paragrapah number in auth.conf sub get_email_by_net_id { my $robot = shift; my $auth_id = shift; my $attributes = shift; do_log ('debug',"Auth::get_email_by_net_id($auth_id,$attributes->{'uid'})"); if (defined $Conf{'auth_services'}{$robot}[$auth_id]{'internal_email_by_netid'}) { my $sso_config = @{$Conf{'auth_services'}{$robot}}[$auth_id]; my $netid_cookie = $sso_config->{'netid_http_header'} ; $netid_cookie =~ s/(\w+)/$attributes->{$1}/ig; $email = &List::get_netidtoemail_db($robot, $netid_cookie, $Conf{'auth_services'}{$robot}[$auth_id]{'service_id'}); return $email; } my $ldap = @{$Conf{'auth_services'}{$robot}}[$auth_id]; my $param = &tools::dup_var($ldap); my $ds = new Datasource('LDAP', $param); my $ldap_anonymous; unless (defined $ds && ($ldap_anonymous = $ds->connect())) { &do_log('err',"Unable to connect to the LDAP server '%s'", $ldap->{'ldap_host'}); return undef; } my $filter = $ldap->{'ldap_get_email_by_uid_filter'} ; $filter =~ s/\[([\w-]+)\]/$attributes->{$1}/ig; # my @alternative_conf = split(/,/,$ldap->{'alternative_email_attribute'}); my $emails= $ldap_anonymous->search ( base => $ldap->{'ldap_suffix'}, filter => $filter, scope => $ldap->{'ldap_scope'}, timeout => $ldap->{'ldap_timeout'}, attrs => [$ldap->{'ldap_email_attribute'}], ); my $count = $emails->count(); if ($emails->count() == 0) { do_log('notice',"No entry in the Ldap Directory Tree of %s", $host); $ds->disconnect(); return undef; } $ds->disconnect(); ## return only the first attribute my @results = $emails->entries; foreach my $result (@results){ return (lc($result->get_value($ldap->{'ldap_email_attribute'}))); } } # check trusted_application_name et trusted_application_password : return 1 or undef; sub remote_app_check_password { my ($trusted_application_name,$password,$robot) = @_; do_log('debug','Auth::remote_app_check_password (%s,%s)',$trusted_application_name,$robot); my $md5 = &tools::md5_fingerprint($password); my $vars; # seach entry for trusted_application in Conf my @trusted_apps ; # select trusted_apps from robot context or symap context if ((defined $robot) && (defined $Conf::Conf{'robots'}{$robot}{'trusted_applications'})) { @trusted_apps = @{$Conf::Conf{'robots'}{$robot}{'trusted_applications'}{'trusted_application'}}; }else{ @trusted_apps = @{$Conf::Conf{'trusted_applications'}{'trusted_application'}}; } foreach my $application (@trusted_apps){ if (lc($application->{'name'}) eq lc($trusted_application_name)) { if ($md5 eq $application->{'md5password'}) { # &do_log('debug', 'Auth::remote_app_check_password : authentication succeed for %s',$application->{'name'}); my %proxy_for_vars ; foreach my $varname (@{$application->{'proxy_for_variables'}}) { $proxy_for_vars{$varname}=1; } return (\%proxy_for_vars); }else{ &do_log('info', 'Auth::remote_app_check_password: bad password from %s', $trusted_application_name); return undef; } } } # no matching application found &do_log('info', 'Auth::remote_app-check_password: unknown application name %s', $trusted_application_name); return undef; } # create new entry in one_time_ticket table using a rand as id so later access is authenticated # sub create_one_time_ticket { my $email = shift; my $robot = shift; my $data_string = shift; my $remote_addr = shift; ## Value may be 'mail' if the IP address is not known my $ticket = &SympaSession::get_random(); do_log('info', 'Auth::create_one_time_ticket(%s,%s,%s,%s) value = %s',$email,$robot,$data_string,$remote_addr,$ticket); my $date = time; my $dbh = &List::db_get_handler(); my $sth; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } my $statement = sprintf "INSERT INTO one_time_ticket_table (ticket_one_time_ticket, robot_one_time_ticket, email_one_time_ticket, date_one_time_ticket, data_one_time_ticket, remote_addr_one_time_ticket, status_one_time_ticket) VALUES (%s, %s, %s, %d, %s, %s, %s)",$dbh->quote($ticket),$dbh->quote($robot),$dbh->quote($email),time,$dbh->quote($data_string),$dbh->quote($remote_addr),$dbh->quote('open'); unless ($dbh->do($statement)) { do_log('err','Unable to insert in table one_time_ticket_table while executing SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } return $ticket; } # read one_time_ticket from table and remove it # sub get_one_time_ticket { my $ticket_number = shift; my $addr = shift; do_log('debug2', '(%s)',$ticket_number); my $dbh = &List::db_get_handler(); my $sth; ## Check database connection unless ($dbh and $dbh->ping) { return return {'result'=>'error'} unless &List::db_connect(); } my $statement; $statement = sprintf "SELECT ticket_one_time_ticket AS ticket, robot_one_time_ticket AS robot, email_one_time_ticket AS email, date_one_time_ticket AS \"date\", data_one_time_ticket AS data, remote_addr_one_time_ticket AS remote_addr, status_one_time_ticket as status FROM one_time_ticket_table WHERE ticket_one_time_ticket = %s ", $dbh->quote($ticket_number); unless ($sth = $dbh->prepare($statement)) { do_log('err','Auth::get_one_time_ticket: Unable to prepare SQL statement : %s', $dbh->errstr); return {'result'=>'error'}; } unless ($sth->execute) { do_log('err','Auth::get_one_time_ticket: Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return {'result'=>'error'}; } my $ticket = $sth->fetchrow_hashref('NAME_lc'); $sth->finish(); unless ($ticket) { do_log('info','Auth::get_one_time_ticket: Unable to find one time ticket %s (SQL query %s)%s', $ticket,$statement, $dbh->errstr); return {'result'=>'not_found'}; } my $result; my $printable_date = gettext_strftime "%d %b %Y at %H:%M:%S", localtime($ticket->{'date'}); if ($ticket->{'status'} ne 'open') { $result = 'closed'; do_log('info','Auth::get_one_time_ticket: ticket %s from %s has been used before (%s)',$ticket_number,$ticket->{'email'},$printable_date); } elsif (time - $ticket->{'date'} > 48 * 60 * 60) { do_log('info','Auth::get_one_time_ticket: ticket %s from %s refused because expired (%s)',$ticket_number,$ticket->{'email'},$printable_date); $result = 'expired'; }else{ $result = 'success'; } $statement = sprintf "UPDATE one_time_ticket_table SET status_one_time_ticket = %s WHERE ticket_one_time_ticket = %s", $dbh->quote($addr), $dbh->quote($ticket_number); unless ($dbh->do($statement)) { do_log('err','Auth::get_one_time_ticket Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); } do_log('info', 'xxxx Auth::get_one_time_ticket(%s) : result : %s',$ticket_number,$result); return {'result'=>$result, 'date'=>$ticket->{'date'}, 'email'=>$ticket->{'email'}, 'remote_addr'=>$ticket->{'remote_addr'}, 'robot'=>$ticket->{'robot'}, 'data'=>$ticket->{'data'}, 'status'=>$ticket->{'status'} }; } 1; sympa-6.1.24~dfsg/wwsympa/Bounce.pm000066400000000000000000000517561246372670100173050ustar00rootroot00000000000000# bounce-lib.pl - This module includes functions for analysing non-delivery reports # RCS Identication ; $Revision: 10713 $ ; $Date: 2014-05-23 11:18:40 +0200 (ven. 23 mai 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package Bounce; use strict; use MIME::Parser; use Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(rfc1891 corrige anabounce); ## RFC1891 compliance check sub rfc1891 { my ($fic, $result, $from) = @_; local $/ = "\n"; my $nbrcpt; my $parser = new MIME::Parser; $parser->output_to_core(1); my $entity = $parser->read($fic); unless ($entity) { return undef; } my $head = $entity->head; $$from = $head->get('From', 0); $$from =~ s/^.*<(.*)>.*$/$1/; my @parts = $entity->parts(); foreach my $p (@parts) { my $h = $p->head(); my $content = $h->get('Content-type'); next unless ($content =~ /message\/delivery-status/i); my $body = $p->body(); ## Fork, communicate with child via BODY my $pid = open BODY, "-|"; unless (defined($pid)) { die 'Fork failed'; } if (! $pid) { ## Child process print STDOUT @$body; exit; }else { ## Multiline paragraph separator local $/ = ''; while () { my ($status, $recipient); if (/^Status:\s*(\d+\.\d+\.\d+)(\s|$)/mi) { $status = $1; } if (/^Original-Recipient:\s*rfc822\s*;\s*(.*)$/mi || /^Final-Recipient:\s*rfc822\s*;\s*(.*)$/mi) { $recipient = $1; if ($recipient =~ /\@.+:(.+)$/) { $recipient = $1; } $recipient =~ s/^<(.*)>$/$1/; $recipient =~ y/[A-Z]/[a-z]/; } if ($recipient and $status) { $result->{$recipient} = $status; $nbrcpt++; } } local $/ = "\n"; close BODY; } } return $nbrcpt; } ## Corrige une adresse SMTP sub corrige { my ($adr, $from) = @_; ## X400 address if ($adr =~ /^\//) { my (%x400, $newadr); my @detail = split /\//, $adr; foreach (@detail) { my ($var, $val) = split /=/; $x400{$var} = $val; #print "\t$var <=> $val\n"; } $newadr = $x400{PN} || "$x400{s}"; $newadr = "$x400{g}.".$newadr if $x400{g}; my ($l, $d) = split /\@/, $from; $newadr .= "\@$d"; return $newadr; }elsif ($adr =~ /\@/) { return $adr; }elsif ($adr =~ /\!/) { my ($dom, $loc) = split /\!/, $adr; return "$loc\@$dom"; }else { my ($l, $d) = split /\@/, $from; my $newadr = "$adr\@$d"; return $newadr; } } ## Analyse d'un rapport de non-remise ## Param 1 : descripteur du fichier contenant le bounce ## // 2 : reference d'un hash pour retourner @ en erreur ## // 3 : reference d'un tableau pour retourner des stats ## // 4 : reference d'un tableau pour renvoyer le bounce sub anabounce { my ($fic, $result, $from) = @_; my $entete = 1; my $type; my %info; my ($qmail, $type_9, $type_18, $exchange, $ibm_vm, $lotus, $sendmail_5, $yahoo, $type_21, $exim, $vines, $mercury_143, $altavista, $mercury_131, $type_31, $type_32,$exim_173, $type_38, $type_39, $type_40, $pmdf, $following_recipients, $postfix, $groupwise7); ## Le champ separateur de paragraphe est un ensemble ## de lignes vides local $/ = ''; ## Parcour du bounce, paragraphe par paragraphe foreach (<$fic>) { # push @$bounce, $_; if ($entete) { undef $entete; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; my ($champ_courant, %champ); foreach (@paragraphe) { if (/^(\S+):\s*(.*)$/) { $champ_courant = $1; $champ_courant =~ y/[A-Z]/[a-z]/; $champ{$champ_courant} = $2; }elsif (/^\s+(.*)$/) { $champ{$champ_courant} .= " $1"; } ## Le champ From: if($champ{from} =~ /([^\s<]+@[^\s>]+)/){ $$from = $1; } } local $/ = ''; $champ{from} =~ s/^.*<(.+)[\>]$/$1/; $champ{from} =~ y/[A-Z]/[a-z]/; my ($local, $domaine) = split /\@/, $champ{from}; if ($champ{subject} =~ /^Returned mail: (Quota exceeded for user (\S+))$/) { $info{$2}{error} = $1; $type = 27; }elsif ($champ{subject} =~ /^Returned mail: (message not deliverable): \<(\S+)\>$/) { $info{$2}{error} = $1; $type = 34; } if ($champ{'x-failed-recipients'} =~ /^\s*(\S+)$/) { $info{$1}{error} = ""; } elsif ($champ{'x-failed-recipients'} =~ /^\s*(\S+),/) { for my $xfr (split (/\s*,\s*/, $champ{'x-failed-recipients'})) { $info{$xfr}{error} = ""; } } }elsif (/^\s*-+ The following addresses (had permanent fatal errors|had transient non-fatal errors|have delivery notifications) -+/m) { my $adr; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^(\S[^\(]*)/) { $adr = $1; my $error = $2; $adr =~ s/^[\"\<](.+)[\"\>]\s*$/$1/; #print "\tADR : #$adr#\n"; $info{$adr}{error} = $error; $type = 1; }elsif (/^\s+\(expanded from: (.+)\)/) { #print "\tEXPANDED $adr : $1\n"; $info{$adr}{expanded} = $1; $info{$adr}{expanded} =~ s/^[\"\<](.+)[\"\>]$/$1/; } } local $/ = ''; }elsif (/^\s+-+\sTranscript of session follows\s-+/m) { my $adr; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^(\d{3}\s)?(\S+|\".*\")\.{3}\s(.+)$/) { $adr = $2; my $cause = $3; $cause =~ s/^(.*) [\(\:].*$/$1/; foreach $a(split /,/, $adr) { $a =~ s/^[\"\<]([^\"\>]+)[\"\>]$/$1/; $info{$a}{error} = $cause; $type = 2; } }elsif (/^\d{3}\s(too many hops).*to\s(.*)$/i) { $adr = $2; my $cause = $1; foreach $a (split /,/, $adr) { $a =~ s/^[\"\<](.+)[\"\>]$/$1/; $info{$a}{error} = $cause; $type = 2; } }elsif (/^\d{3}\s.*\s([^\s\)]+)\.{3}\s(.+)$/) { $adr = $1; my $cause = $2; $cause =~ s/^(.*) [\(\:].*$/$1/; foreach $a(split /,/, $adr) { $a =~ s/^[\"\<](.+)[\"\>]$/$1/; $info{$a}{error} = $cause; $type = 2; } } } local $/ = ''; ## Rapport Compuserve }elsif (/^Receiver not found:/m) { ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { $info{$2}{error} = $1 if /^(.*): (\S+)/; $type = 3; } local $/ = ''; }elsif (/^\s*-+ Special condition follows -+/m) { my ($cause,$adr); ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^(Unknown QuickMail recipient\(s\)):/) { $cause = $1; $type = 4; }elsif (/^\s+(.*)$/ and $cause) { $adr = $1; $adr =~ s/^[\"\<](.+)[\"\>]$/$1/; $info{$adr}{error} = $cause; $type = 4; } } local $/ = ''; }elsif (/^Your message add?ressed to .* couldn\'t be delivered/m) { my $adr; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^Your message add?ressed to (.*) couldn\'t be delivered, for the following reason :/) { $adr = $1; $adr =~ s/^[\"\<](.+)[\"\>]$/$1/; $type = 5; }else { /^(.*)$/; $info{$adr}{error} = $1; $type = 5; } } local $/ = ''; ## Rapport X400 }elsif (/^Your message was not delivered to:\s+(\S+)\s+for the following reason:\s+(.+)$/m) { my ($adr, $error) = ($1, $2); $error =~ s/Your message.*$//; $info{$adr}{error} = $error; $type = 6; ## Rapport X400 }elsif (/^Your message was not delivered to\s+(\S+)\s+for the following reason:\s+(.+)$/m) { my ($adr, $error) = ($1, $2); $error =~ s/\(.*$//; $info{$adr}{error} = $error; $type = 6; ## Rapport X400 }elsif (/^Original-Recipient: rfc822; (\S+)\s+Action: (.*)$/m) { $info{$1}{error} = $2; $type = 16; ## Rapport NTMail }elsif (/^The requested destination was:\s+(.*)$/m) { $type = 7; }elsif (($type == 7) && (/^\s+(\S+)/)) { undef $type; my $adr =$1; $adr =~ s/^[\"\<](.+)[\"\>]$/$1/; next unless $adr; $info{$adr}{'error'} = ''; ## Rapport Qmail dans prochain paragraphe }elsif (/^Hi\. This is the qmail-send program/m) { $qmail = 1; ## Rapport Qmail }elsif ($qmail) { undef $qmail if /^[^<]/; if (/^<(\S+)>:\n(.*)/m) { $info{$1}{error} = $2; $type = 8; } local $/ = ''; ## Sendmail }elsif (/^Your message was not delivered to the following recipients:/m) { $type_9 = 1; }elsif ($type_9) { undef $type_9; if (/^\s*(\S+):\s+(.*)$/m) { $info{$1}{error} = $2; $type = 9; } ## Rapport Exchange dans prochain paragraphe }elsif (/^The following recipient\(s\) could not be reached:/m or /^did not reach the following recipient\(s\):/m) { $exchange = 1; ## Rapport Exchange }elsif ($exchange) { undef $exchange; if (/^\s*(\S+).*\n\s+(.*)$/m) { $info{$1}{error} = $2; $type = 10; } ## IBM VM dans prochain paragraphe }elsif (/^Your mail item could not be delivered to the following users/m) { $ibm_vm = 1; ## Rapport IBM VM }elsif ($ibm_vm) { undef $ibm_vm; if (/^(.*)\s+\---->\s(\S+)$/m) { $info{$2}{error} = $1; $type = 12; } ## Rapport Lotus SMTP dans prochain paragraphe }elsif (/^-+\s+Failure Reasons\s+-+/m) { $lotus = 1; ## Rapport Lotus SMTP }elsif ($lotus) { undef $lotus; if (/^(.*)\n(\S+)$/m) { $info{$2}{error} = $1; $type = 13; } ## Rapport Sendmail 5 dans prochain paragraphe }elsif (/^\-+\sTranscript of session follows\s\-+/m) { $sendmail_5 = 1; ## Rapport Sendmail 5 }elsif ($sendmail_5) { undef $sendmail_5; if (/<(\S+)>\n\S+, (.*)$/m) { $info{$1}{error} = $2; $type = 14; } ## Rapport Smap }elsif (/^\s+-+ Transcript of Report follows -+/) { my $adr; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^Rejected-For: (\S+),/) { $adr = $1; $info{$adr}{error} = ""; $type = 17; }elsif (/^\s+explanation (.*)$/) { $info{$adr}{error} = $1; } } local $/ = ''; }elsif (/^\s*-+Message not delivered to the following:/) { $type_18 = 1; }elsif ($type_18) { undef $type_18; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^\s*(\S+)\s+(.*)$/) { $info{$1}{error} = $2; $type = 18; } } local $/ = ''; }elsif (/unable to deliver following mail to recipient\(s\):/m) { ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^\d+ <(\S+)>\.{3} (.+)$/) { $info{$1}{error} = $2; $type = 19; } } local $/ = ''; ## Rapport de Yahoo dans paragraphe suivant }elsif (/^Unable to deliver message to the following address\(es\)/m) { $yahoo = 1; ## Rapport Yahoo }elsif ($yahoo) { undef $yahoo; if (/^<(\S+)>:\s(.+)$/m) { $info{$1}{error} = $2; $type = 20; } }elsif (/^Content-Description: Session Transcript/m) { $type_21 = 1; }elsif ($type_21) { undef $type_21; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/<(\S+)>\.{3} (.*)$/) { $info{$1}{error} = $2; $type = 21; } } local $/ = ''; }elsif (/^Your message has encountered delivery problems\s+to local user \S+\.\s+\(Originally addressed to (\S+)\)/m or /^Your message has encountered delivery problems\s+to (\S+)\.$/m or /^Your message has encountered delivery problems\s+to the following recipient\(s\):\s+(\S+)$/m) { my $adr = $2 || $1; $info{$adr}{error} = ""; $type = 22; }elsif (/^(The user return_address (\S+) does not exist)/) { $info{$2}{error} = $1; $type = 23; ## Rapport Exim paragraphe suivant }elsif (/^A message that you sent could not be delivered to all of its recipients/m or /^The following address\(es\) failed:/m) { $exim = 1; ## Rapport Exim }elsif ($exim) { undef $exim; if (/^\s*(\S+):\s+(.*)$/m) { $info{$1}{error} = $2; $type = 24; }elsif (/^\s*(\S+)$/m) { $info{$1}{error} = ""; } ## Rapport VINES-ISMTP par. suivant }elsif (/^Message not delivered to recipients below/m) { $vines = 1; ## Rapport VINES-ISMTP }elsif ($vines) { undef $vines; if (/^\s+\S+:.*\s+(\S+)$/m) { $info{$1}{error} = ""; $type = 25; } ## Rapport Mercury 1.43 par. suivant }elsif (/^The local mail transport system has reported the following problems/m) { $mercury_143 = 1; ## Rapport Mercury 1.43 }elsif ($mercury_143) { undef $mercury_143; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/<(\S+)>\s+(.*)$/) { $info{$1}{error} = $2; $type = 26; } } local $/ = ''; ## Rapport de AltaVista Mail dans paragraphe suivant }elsif (/unable to deliver mail to the following recipient\(s\):/m) { $altavista = 1; ## Rapport AltaVista Mail }elsif ($altavista) { undef $altavista; if (/^(\S+):\n.*\n\s*(.*)$/m) { $info{$1}{error} = $2; $type = 27; } ## Rapport SMTP32 }elsif (/^(User mailbox exceeds allowed size): (\S+)$/m) { $info{$2}{error} = $1; $type = 28; }elsif (/^The following recipients did not receive this message:$/m) { $following_recipients = 1; }elsif ($following_recipients) { undef $following_recipients; if (/^\s+<(\S+)>/) { $info{$1}{error} = ""; $type = 29; } ## Rapport Mercury 1.31 par. suivant }elsif (/^One or more addresses in your message have failed with the following/m) { $mercury_131 = 1; ## Rapport Mercury 1.31 }elsif ($mercury_131) { undef $mercury_131; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/<(\S+)>\s+(.*)$/) { $info{$1}{error} = $2; $type = 30; } } local $/ = ''; }elsif (/^The following recipients haven\'t received this message:/m) { $type_31 = 1; }elsif ($type_31) { undef $type_31; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/(\S+)$/) { $info{$1}{error} = ""; $type = 31; } } local $/ = ''; }elsif (/^The following destination addresses were unknown/m) { $type_32 = 1; }elsif ($type_32) { undef $type_32; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/<(\S+)>/) { $info{$1}{error} = ""; $type = 32; } } local $/ = ''; }elsif (/^-+Transcript of session follows\s-+$/m) { ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/^(\S+)$/) { $info{$1}{error} = ""; $type = 33; }elsif (/<(\S+)>\.{3} (.*)$/) { $info{$1}{error} = $2; $type = 33; } } local $/ = ''; ## Rapport Bigfoot }elsif (/^The message you tried to send to <(\S+)>/m) { $info{$1}{error} = "destination mailbox unavailable"; }elsif (/^The destination mailbox (\S+) is unavailable/m) { $info{$1}{error} = "destination mailbox unavailable"; }elsif (/^The following message could not be delivered because the address (\S+) does not exist/m) { $info{$1}{error} = "user unknown"; }elsif (/^Error-For:\s+(\S+)\s/) { $info{$1}{error} = ""; ## Rapport Exim 1.73 dans proc. paragraphe }elsif (/^The address to which the message has not yet been delivered is:/m) { $exim_173 = 1; ## Rapport Exim 1.73 }elsif ($exim_173) { undef $exim_173; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/(\S+)/) { $info{$1}{error} = ""; $type = 37; } } local $/ = ''; }elsif (/^This Message was undeliverable due to the following reason:/m) { $type_38 = 1; }elsif ($type_38) { undef $type_38 if /Recipient:/; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/\s+Recipient:\s+<(\S+)>/) { $info{$1}{error} = ""; $type = 38; }elsif (/\s+Reason:\s+<(\S+)>\.{3} (.*)/) { $info{$1}{error} = $2; $type = 38; } } local $/ = ''; }elsif (/Your message could not be delivered to:/m) { $type_39 = 1; }elsif ($type_39) { undef $type_39; if (/^(\S+)/) { $info{$1}{error} = ""; $type = 39; } }elsif (/Session Transcription follow:/m) { if (/^<+\s+\d+\s+(.*) for \((.*)\)$/m) { $info{$2}{error} = $1; $type = 43; } }elsif (/^This message was returned to you for the following reasons:/m) { $type_40 = 1; }elsif ($type_40) { undef $type_40; if (/^\s+(.*): (\S+)/) { $info{$2}{error} = $1; $type = 40; } ## Rapport PMDF dans proc. paragraphe }elsif (/^Your message cannot be delivered to the following recipients:/m or /^Your message has been enqueued and undeliverable for \d day\s*to the following recipients/m) { $pmdf = 1; ## Rapport PMDF }elsif ($pmdf) { my $adr; undef $pmdf; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if (/\s+Recipient address:\s+(\S+)/) { $adr = $1; $info{$adr}{error} = ""; $type = 41; }elsif (/\s+Reason:\s+(.*)$/) { $info{$adr}{error} = $1; $type = 41; } } local $/ = ''; ## Rapport MDaemon }elsif (/^(\S+) - (no such user here)\.$/m) { $info{$1}{error} = $2; $type = 42; # Postfix dans le prochain paragraphe }elsif (/^This is the Postfix program/m || /^This is the mail system at host/m) { $postfix = 1; ## Rapport Postfix }elsif ($postfix) { undef $postfix if /THIS IS A WARNING/; # Pas la peine de le traiter if (/^<(\S+)>:\s(.*)/m) { my ($addr,$error) = ($1,$2); if ($error =~ /^host\s[^:]*said:\s(\d+)/) { $info{$addr}{error} = $1; } elsif ($error =~ /^([^:]+):/) { $info{$addr}{error} = $1; }else { $info{$addr}{error} = $error; } } local $/ = ''; }elsif ( /^The message that you sent was undeliverable to the following:/ ) { $groupwise7 = 1; }elsif ($groupwise7) { undef $groupwise7; ## Parcour du paragraphe my @paragraphe = split /\n/, $_; local $/ = "\n"; foreach (@paragraphe) { if ( /^\s+(\S*) \((.+)\)/ ) { $info{$1}{error} = $2; } } local $/ = ''; ## Wanadoo }elsif (/^(\S+); Action: Failed; Status: \d.\d.\d \((.*)\)/m) { $info{$1}{error} = $2; } } my $count=0; ## On met les adresses au clair foreach my $a1 (keys %info) { next unless ($a1 and ref ($info{$a1})); $count++; my ($a2, $a3); $a2 = $a1; unless (! $info{$a1}{expanded} or ($a1 =~ /\@/ and $info{$a1}{expanded} !~ /\@/) ) { $a2 = $info{$a1}{expanded}; } $a3 = &corrige($a2, $$from); # print "CORRECTION : $a2 --> $a3\n" if $a2 ne $a3; $a3 =~ y/[A-Z]/[a-z]/; $a3 =~ s/^<(.*)>$/$1/; # $result->{$a3}{error} = $info{$a1}{error}; $result->{$a3} = lc ($info{$a1}{error}); } # print "$b\n" if ! $type; return $count; } 1; sympa-6.1.24~dfsg/wwsympa/INSTALL000066400000000000000000000001111246372670100165400ustar00rootroot00000000000000Refer to the documentation for installation notes : doc/sympa/node5.html sympa-6.1.24~dfsg/wwsympa/Makefile.am000066400000000000000000000042341246372670100175550ustar00rootroot00000000000000# Makefile - This Makefile does the web interface of Sympa installation # RCS Identication ; $Revision: 10083 $ ; $Date: 2014-01-01 01:12:12 +0100 (mer. 01 janv. 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . SUBDIRS = icons sbin_SCRIPTS = bounced.pl archived.pl execcgi_SCRIPTS = wwsympa.fcgi execcgi_PROGRAMS = wwsympa-wrapper.fcgi wwsympa_wrapper_fcgi_SOURCES = wwsympa-wrapper.fcgi.c wwsympa_wrapper_fcgi_CPPFLAGS = -DWWSYMPA=\"$(execcgidir)/wwsympa.fcgi\" nobase_modules_DATA = wwslib.pm cookielib.pm Bounce.pm Marc.pm Auth.pm \ Marc/Search.pm SharedDocument.pm SympaSession.pm default_DATA = mime.types EXTRA_DIST = $(default_DATA) $(nobase_modules_DATA) \ archived.pl.in \ bounced.pl.in \ wwsympa.fcgi.in CLEANFILES = $(sbin_SCRIPTS) $(execcgi_SCRIPTS) install-exec-hook: -chown $(USER) $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi -chgrp $(GROUP) $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi chmod 6755 $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi archived.pl bounced.pl wwsympa.fcgi: Makefile rm -f $@ $(AM_V_GEN)$(SED) \ -e 's|--PERL--|$(PERL)|' \ -e 's|--modulesdir--|$(modulesdir)|' \ < $(srcdir)/$@.in > $@ chmod +x $@ archived.pl: $(srcdir)/archived.pl.in bounced.pl: $(srcdir)/bounced.pl.in wwsympa.fcgi: $(srcdir)/wwsympa.fcgi.in sympa-6.1.24~dfsg/wwsympa/Makefile.in000066400000000000000000000630731246372670100175740ustar00rootroot00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile - This Makefile does the web interface of Sympa installation # RCS Identication ; $Revision: 10083 $ ; $Date: 2014-01-01 01:12:12 +0100 (mer. 01 janv. 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd INSTALL = @INSTALL@ install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : execcgi_PROGRAMS = wwsympa-wrapper.fcgi$(EXEEXT) subdir = wwsympa DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ INSTALL ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = am__installdirs = "$(DESTDIR)$(execcgidir)" "$(DESTDIR)$(execcgidir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(defaultdir)" \ "$(DESTDIR)$(modulesdir)" execcgiPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(execcgi_PROGRAMS) am_wwsympa_wrapper_fcgi_OBJECTS = \ wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.$(OBJEXT) wwsympa_wrapper_fcgi_OBJECTS = $(am_wwsympa_wrapper_fcgi_OBJECTS) wwsympa_wrapper_fcgi_LDADD = $(LDADD) execcgiSCRIPT_INSTALL = $(INSTALL_SCRIPT) sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT) SCRIPTS = $(execcgi_SCRIPTS) $(sbin_SCRIPTS) DEFAULT_INCLUDES = -I. -I$(srcdir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ SOURCES = $(wwsympa_wrapper_fcgi_SOURCES) DIST_SOURCES = $(wwsympa_wrapper_fcgi_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-exec-recursive install-info-recursive \ install-recursive installcheck-recursive installdirs-recursive \ pdf-recursive ps-recursive uninstall-info-recursive \ uninstall-recursive am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; defaultDATA_INSTALL = $(INSTALL_DATA) nobase_modulesDATA_INSTALL = $(install_sh_DATA) DATA = $(default_DATA) $(nobase_modules_DATA) ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CAT = @CAT@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFIG = @CONFIG@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GROUP = @GROUP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MAKEMAP = @MAKEMAP@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ NEWALIASES = @NEWALIASES@ NEWALIASES_ARG = @NEWALIASES_ARG@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ POD2MAN = @POD2MAN@ POSTALIAS = @POSTALIAS@ POSTMAP = @POSTMAP@ POSTMAP_ARG = @POSTMAP_ARG@ SED = @SED@ SENDMAIL_ALIASES = @SENDMAIL_ALIASES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ USER = @USER@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ VIRTUAL_ALIASES = @VIRTUAL_ALIASES@ WWSCONFIG = @WWSCONFIG@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ arcdir = @arcdir@ bindir = @bindir@ bouncedir = @bouncedir@ build_alias = @build_alias@ confdir = @confdir@ datadir = @datadir@ datarootdir = @datarootdir@ defaultdir = @defaultdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ execcgidir = @execcgidir@ expldir = @expldir@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ initdir = @initdir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lockdir = @lockdir@ mailtemplatedir = @mailtemplatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ modulesdir = @modulesdir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ piddir = @piddir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ scriptdir = @scriptdir@ sharedstatedir = @sharedstatedir@ spooldir = @spooldir@ staticdir = @staticdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ webtemplatedir = @webtemplatedir@ SUBDIRS = icons sbin_SCRIPTS = bounced.pl archived.pl execcgi_SCRIPTS = wwsympa.fcgi wwsympa_wrapper_fcgi_SOURCES = wwsympa-wrapper.fcgi.c wwsympa_wrapper_fcgi_CPPFLAGS = -DWWSYMPA=\"$(execcgidir)/wwsympa.fcgi\" nobase_modules_DATA = wwslib.pm cookielib.pm Bounce.pm Marc.pm Auth.pm \ Marc/Search.pm SharedDocument.pm SympaSession.pm default_DATA = mime.types EXTRA_DIST = $(default_DATA) $(nobase_modules_DATA) \ archived.pl.in \ bounced.pl.in \ wwsympa.fcgi.in CLEANFILES = $(sbin_SCRIPTS) $(execcgi_SCRIPTS) all: all-recursive .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign wwsympa/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign wwsympa/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh install-execcgiPROGRAMS: $(execcgi_PROGRAMS) @$(NORMAL_INSTALL) test -z "$(execcgidir)" || $(mkdir_p) "$(DESTDIR)$(execcgidir)" @list='$(execcgi_PROGRAMS)'; for p in $$list; do \ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ if test -f $$p \ ; then \ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ echo " $(INSTALL_PROGRAM_ENV) $(execcgiPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(execcgidir)/$$f'"; \ $(INSTALL_PROGRAM_ENV) $(execcgiPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(execcgidir)/$$f" || exit 1; \ else :; fi; \ done uninstall-execcgiPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(execcgi_PROGRAMS)'; for p in $$list; do \ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ echo " rm -f '$(DESTDIR)$(execcgidir)/$$f'"; \ rm -f "$(DESTDIR)$(execcgidir)/$$f"; \ done clean-execcgiPROGRAMS: -test -z "$(execcgi_PROGRAMS)" || rm -f $(execcgi_PROGRAMS) wwsympa-wrapper.fcgi$(EXEEXT): $(wwsympa_wrapper_fcgi_OBJECTS) $(wwsympa_wrapper_fcgi_DEPENDENCIES) @rm -f wwsympa-wrapper.fcgi$(EXEEXT) $(LINK) $(wwsympa_wrapper_fcgi_LDFLAGS) $(wwsympa_wrapper_fcgi_OBJECTS) $(wwsympa_wrapper_fcgi_LDADD) $(LIBS) install-execcgiSCRIPTS: $(execcgi_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(execcgidir)" || $(mkdir_p) "$(DESTDIR)$(execcgidir)" @list='$(execcgi_SCRIPTS)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f $$d$$p; then \ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ echo " $(execcgiSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(execcgidir)/$$f'"; \ $(execcgiSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(execcgidir)/$$f"; \ else :; fi; \ done uninstall-execcgiSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(execcgi_SCRIPTS)'; for p in $$list; do \ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ echo " rm -f '$(DESTDIR)$(execcgidir)/$$f'"; \ rm -f "$(DESTDIR)$(execcgidir)/$$f"; \ done install-sbinSCRIPTS: $(sbin_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" @list='$(sbin_SCRIPTS)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f $$d$$p; then \ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ $(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \ else :; fi; \ done uninstall-sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(sbin_SCRIPTS)'; for p in $$list; do \ f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ rm -f "$(DESTDIR)$(sbindir)/$$f"; \ done mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.o: wwsympa-wrapper.fcgi.c @am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wwsympa_wrapper_fcgi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.o -MD -MP -MF "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo" -c -o wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.o `test -f 'wwsympa-wrapper.fcgi.c' || echo '$(srcdir)/'`wwsympa-wrapper.fcgi.c; \ @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo" "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Po"; else rm -f "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo"; exit 1; fi @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wwsympa-wrapper.fcgi.c' object='wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wwsympa_wrapper_fcgi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.o `test -f 'wwsympa-wrapper.fcgi.c' || echo '$(srcdir)/'`wwsympa-wrapper.fcgi.c wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.obj: wwsympa-wrapper.fcgi.c @am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wwsympa_wrapper_fcgi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.obj -MD -MP -MF "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo" -c -o wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.obj `if test -f 'wwsympa-wrapper.fcgi.c'; then $(CYGPATH_W) 'wwsympa-wrapper.fcgi.c'; else $(CYGPATH_W) '$(srcdir)/wwsympa-wrapper.fcgi.c'; fi`; \ @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo" "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Po"; else rm -f "$(DEPDIR)/wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.Tpo"; exit 1; fi @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wwsympa-wrapper.fcgi.c' object='wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wwsympa_wrapper_fcgi_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wwsympa_wrapper_fcgi-wwsympa-wrapper.fcgi.obj `if test -f 'wwsympa-wrapper.fcgi.c'; then $(CYGPATH_W) 'wwsympa-wrapper.fcgi.c'; else $(CYGPATH_W) '$(srcdir)/wwsympa-wrapper.fcgi.c'; fi` uninstall-info-am: install-defaultDATA: $(default_DATA) @$(NORMAL_INSTALL) test -z "$(defaultdir)" || $(mkdir_p) "$(DESTDIR)$(defaultdir)" @list='$(default_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(defaultDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(defaultdir)/$$f'"; \ $(defaultDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(defaultdir)/$$f"; \ done uninstall-defaultDATA: @$(NORMAL_UNINSTALL) @list='$(default_DATA)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(defaultdir)/$$f'"; \ rm -f "$(DESTDIR)$(defaultdir)/$$f"; \ done install-nobase_modulesDATA: $(nobase_modules_DATA) @$(NORMAL_INSTALL) test -z "$(modulesdir)" || $(mkdir_p) "$(DESTDIR)$(modulesdir)" @$(am__vpath_adj_setup) \ list='$(nobase_modules_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ $(am__vpath_adj) \ echo " $(nobase_modulesDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(modulesdir)/$$f'"; \ $(nobase_modulesDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(modulesdir)/$$f"; \ done uninstall-nobase_modulesDATA: @$(NORMAL_UNINSTALL) @$(am__vpath_adj_setup) \ list='$(nobase_modules_DATA)'; for p in $$list; do \ $(am__vpath_adj) \ echo " rm -f '$(DESTDIR)$(modulesdir)/$$f'"; \ rm -f "$(DESTDIR)$(modulesdir)/$$f"; \ done # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" mostlyclean-recursive clean-recursive distclean-recursive \ maintainer-clean-recursive: @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$tags $$unique; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ test -z "$(CTAGS_ARGS)$$tags$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$tags $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) $(mkdir_p) $(distdir)/Marc @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(mkdir_p) "$(distdir)/$$subdir" \ || exit 1; \ distdir=`$(am__cd) $(distdir) && pwd`; \ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ (cd $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$top_distdir" \ distdir="$$distdir/$$subdir" \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(execcgidir)" "$(DESTDIR)$(execcgidir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(defaultdir)" "$(DESTDIR)$(modulesdir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-execcgiPROGRAMS clean-generic mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive info: info-recursive info-am: install-data-am: install-defaultDATA install-nobase_modulesDATA install-exec-am: install-execcgiPROGRAMS install-execcgiSCRIPTS \ install-sbinSCRIPTS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-info: install-info-recursive install-man: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-defaultDATA uninstall-execcgiPROGRAMS \ uninstall-execcgiSCRIPTS uninstall-info-am \ uninstall-nobase_modulesDATA uninstall-sbinSCRIPTS uninstall-info: uninstall-info-recursive .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ clean clean-execcgiPROGRAMS clean-generic clean-recursive \ ctags ctags-recursive distclean distclean-compile \ distclean-generic distclean-recursive distclean-tags distdir \ dvi dvi-am html html-am info info-am install install-am \ install-data install-data-am install-defaultDATA install-exec \ install-exec-am install-exec-hook install-execcgiPROGRAMS \ install-execcgiSCRIPTS install-info install-info-am \ install-man install-nobase_modulesDATA install-sbinSCRIPTS \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ maintainer-clean-recursive mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ tags tags-recursive uninstall uninstall-am \ uninstall-defaultDATA uninstall-execcgiPROGRAMS \ uninstall-execcgiSCRIPTS uninstall-info-am \ uninstall-nobase_modulesDATA uninstall-sbinSCRIPTS install-exec-hook: -chown $(USER) $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi -chgrp $(GROUP) $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi chmod 6755 $(DESTDIR)$(execcgidir)/wwsympa-wrapper.fcgi archived.pl bounced.pl wwsympa.fcgi: Makefile rm -f $@ $(AM_V_GEN)$(SED) \ -e 's|--PERL--|$(PERL)|' \ -e 's|--modulesdir--|$(modulesdir)|' \ < $(srcdir)/$@.in > $@ chmod +x $@ archived.pl: $(srcdir)/archived.pl.in bounced.pl: $(srcdir)/bounced.pl.in wwsympa.fcgi: $(srcdir)/wwsympa.fcgi.in # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: sympa-6.1.24~dfsg/wwsympa/Marc.pm000066400000000000000000000022321246372670100167350ustar00rootroot00000000000000package Marc; use strict; use Carp; our $AUTOLOAD; our $VERSION = "4.3"; ##------------------------------------------------------------------------## ## Constructor sub new { my $class = shift; my $fields_ref = shift; my $self = { directory_labels => {}, permitted => $fields_ref, sort_function => 'sub { $a cmp $b }', %$fields_ref, }; $self->{permitted}->{sort_function} = 'sub { $a cmp $b }'; bless $self,$class; return $self; } ##------------------------------------------------------------------------## ## The AUTOLOAD function allows for the dynamic creation of accessor methods sub AUTOLOAD { my $self = shift; my $type = ref($self) or croak "$self is not an object"; my $name = $AUTOLOAD; # DESTROY messages should never be propagated. return if $name =~ /::DESTROY$/; # Remove the package name. $name =~ s/^.*://; unless (exists($self->{permitted}->{$name})) { &message('arcsearch_marc_autoload_no_access'); &wwslog('info','arcsearch_marc: Can not access %s field in object of class %s', $name, $type); return undef; } if (@_) { return $self->{$name} = shift; } else { return $self->{$name}; } } 1; sympa-6.1.24~dfsg/wwsympa/Marc/000077500000000000000000000000001246372670100164005ustar00rootroot00000000000000sympa-6.1.24~dfsg/wwsympa/Marc/Search.pm000066400000000000000000000274411246372670100201530ustar00rootroot00000000000000package Marc::Search; use strict; use File::Find; use HTML::Entities qw(decode_entities encode_entities); use Encode qw(decode_utf8 encode_utf8 is_utf8); use Marc; our @ISA = qw(Marc); our $VERSION = "4.3"; our ($AUTOLOAD, @MSGFILES); ##------------------------------------------------------------------------## ## Constructor my %fields = ( age => 0, archive_name => undef, base_href => undef, body => undef, body_count => 0, case => 0, clean_words => undef, date => undef, date_count => 0, directories => undef, error => undef, file_count => 0, from => undef, from_count => 0, function1 => undef, function2 => undef, how => undef, id => undef, id_count => 0, key_word => undef, limit => 25, match => 0, previous => undef, res => undef, searched => 0, search_base => undef, subj => undef, subj_count => 0, words => undef, ); sub new { my $class = shift; my $self = Marc->new(\%fields); bless $self, $class; return $self; } ##------------------------------------------------------------------------## ## These accessor methods keep a running count of matches in each area ## PUBLIC METHOD sub body_count { my $self = shift; my $count = shift || 0; return $self->{body_count} += $count; } sub id_count { my $self = shift; my $count = shift || 0; return $self->{id_count} += $count; } sub date_count { my $self = shift; my $count = shift || 0; return $self->{date_count} += $count; } sub from_count { my $self = shift; my $count = shift || 0; return $self->{from_count} += $count; } sub subj_count { my $self = shift; my $count = shift || 0; return $self->{subj_count} += $count; } sub key_word { my $self = shift; if (scalar @_) { my $key_word = shift; if (defined $key_word) { $key_word = decode_utf8($key_word) unless is_utf8($key_word); $self->{'key_word'} = $key_word; } else { $self->{'key_word'} = undef; } } return $self->{'key_word'}; } ##------------------------------------------------------------------------## ## Handle Actual Search ## PRIVATE METHOD sub _find_match { my($self,$file,$subj,$from,$date,$id,$body_ref) = @_; my $body_string = ''; my $match = 0; my $res = undef; # Check for a match in subject if (($self->subj) && ($_ = $subj) && (&{$self->{function2}})) { $subj =~ s,($self->{key_word}),\001$1\002,g; # Bold any matches $self->subj_count(1); # Keeping count $match = 1; # We'll be printing this one } # Check for a match in from if (($self->from) && ($_ = $from) && (&{$self->{function2}})) { $from =~ s,($self->{key_word}),\001$1\002,g; $self->from_count(1); $match = 1; } # Check for a match in date if (($self->date) && ($_ = $date) && (&{$self->{function2}})) { $date =~ s,($self->{key_word}),\001$1\002,g; $self->date_count(1); $match = 1; } # Check for a match in id if (($self->id) && ($_ = $id) && (&{$self->{function2}})) { $id =~ s,($self->{key_word}),\001$1\002,g; $self->id_count(1); $match = 1; } # Is this a full? if (defined($body_ref)) { my @body = @$body_ref; # use routine generated by body_match_all if (defined($self->function1)) { my @words = @{$self->words}; my $i; BODY: for $i (0 .. $#body) { my %matches = (); my $hit = ''; $_ = $body[$i]; my @linematches = &{$self->{function1}}; foreach $hit (@linematches) { # key=searchterm; value=line $matches{$hit} = $i; } # all keys = all terms? if (keys %matches == @words) { # Add to the running total $self->body_count(1); my $line; $match = 1; foreach $hit (sort {$matches{$a} <=> $matches{$b}} keys %matches) { # no duplicates please next if ($matches{$hit} + 1 == $line); # arrays start from 0 $line = $matches{$hit} + 1; $body_string .= "line $line: $body[$matches{$hit}]"; } $body_string =~ s,($self->{key_word}),\001$1\002,g; last BODY; } } } # otherwise use routine supplied by match_any or match_this else { my $i; BODY: for $i (0 .. $#body) { if (($_ = $body[$i]) && (&{$self->{function2}})) { ($body_string = $body[($i - 1)] . $body[$i] . $body[($i + 1)]) =~ s,($self->{key_word}),\001$1\002,g; $self->body_count(1); $match = 1; last BODY; } } } } if ($match == 1) { $file =~ s,$self->{'search_base'},$self->{'base_href'},; $res->{'file'} = $file; $res->{'body_string'} = $body_string; $res->{'id'} = $id; $res->{'date'} = $date; $res->{'from'} = $from; $res->{'subj'} = $subj; $res->{'rich'} = {}; foreach my $k (qw(body_string id date from subj)) { my @rich = (); foreach my $s (split /(\n|\001.*?\002)/, $res->{$k}) { next unless length $s; if ($s =~ /\n/) { push @rich, { 'text' => '', 'format' => 'br' }; } elsif ($s =~ /\001(.*)\002/) { push @rich, { 'text' => encode_utf8($1), 'format' => 'b' }; } else { push @rich, { 'text' => encode_utf8($s), 'format' => '' }; } } $res->{'rich'}->{$k} = \@rich; $res->{$k} = encode_entities($res->{$k}, '<>&"'); $res->{$k} =~ s,\001,,g; $res->{$k} =~ s,\002,,g; $res->{$k} =~ s,\n,
,g; $res->{$k} = encode_utf8($res->{$k}); } push @{$self->{'res'}}, $res; } return $match; # 1 if match suceeds; 0 otherwise } ##------------------------------------------------------------------------## ## Build up a list of files to search; read in the relevant portions; ## pass those parts off for checking (and printing if there's a match) ## by the _find_match method ## PUBLIC METHOD sub search { my $self = shift; my $limit = $self->limit; my $previous = $self->previous || 0; my $directories = $self->directories; my $body = $self->body || 0; @MSGFILES = ''; my @directories = split/\0/, $directories; foreach my $dir (@directories) { my $directory = ($self->search_base . '/' . $dir . '/'); find({ wanted => \&_get_file_list, untaint => 1, untaint_pattern => qr|^([-@\w./]+)$| },$directory); } # File::Find returns these in somewhat haphazard order. @MSGFILES = sort @MSGFILES; # Newest files first! @MSGFILES = reverse(@MSGFILES) if $self->age; # The *real* number of files $self->file_count($#MSGFILES); @MSGFILES = splice(@MSGFILES,$previous) if $previous; my $file; my $i = 1; # Arrays are numbered from 0 # Avoid doing a lot of extra math inside the loop $limit += $previous; foreach $file (@MSGFILES) { my ($subj,$from,$date,$id,$body_ref); unless (open FH, '<:encoding(utf8)', $file) { # $self->error("Couldn't open file $file, $!"); } # Need this loop because newer versions of MHonArc put a version # number on the first line of the message. Just in case Earl # decides to change this again, we will loop until the subject # comment tag is found. Thanks to Douglas Gray Stephens for # pointing this out, and more importantly, for suggesting a good # solution (though ultimately not the one in place here). That # DGS was able to contribute to this modest little program is, I # think, a good argument in favor of open source code! while () { ## Next line is appended to the subject if (defined $subj) { $subj .= $1 if (/\s(.*)( -->|$)/); if (/-->$/) { $subj =~ s/ -->$//; last; } } elsif (/^|$)/) { ## No more need to decode header fields # $subj = &MIME::Words::decode_mimewords($1); $subj = $1; last if (/-->/); } } $subj =~ s/ *-->$//; ($from = ) =~ s/^/$1/; ## No more need to decode header fields #$from = &MIME::Words::decode_mimewords($from); $from =~ tr/N-Z[@A-Mn-za-m/@A-Z[a-z/; ($date = ) =~ s/^/$1/; ($id = ) =~ s/^/$1/; if ($body) { my $lines = ''; while () { # Messages are contained between Body-of-Message tags next unless (/^/); $_ = ; while (! eof && ($_ !~ /^/)) { $lines .= $_; $_ = ; } last; } # Remove HTML comments $lines =~ s///g; # Translate newlines $lines =~ s{]*>(.*?)]*>} { my $s = $1; $s =~ s,\r\n|\r|\n,
,g; $s; }egis; $lines =~ s/[\r\n]/ /g; $lines =~ s/<(BR|DIV|P)\b[^>]*>[ \t]*/\n/gi; # Remove other HTML tags $lines =~ s,[ \t]*]*>,,g; $lines =~ s/<[^>]*>[ \t]*//g; $lines =~ s/[<>]/ /g; # Decode entities $lines = decode_entities($lines); $lines =~ s/[\001\002]/ /g; # Split lines $body_ref = [split /(?<=\n)/, $lines]; } unless (close FH) { # $self->error("Couldn't close file $file, $!"); } # Decode entities if ($subj) { $subj = decode_entities($subj); $subj =~ s/[\001\002\r\n]/ /g; } if ($from) { $from = decode_entities($from); $from =~ s/[\001\002\r\n]/ /g; } if ($date) { $date = decode_entities($date); $date =~ s/[\001\002\r\n]/ /g; } if ($id) { $id = decode_entities($id); $id =~ s/[\001\002\r\n]/ /g; } if ($self->_find_match($file,$subj,$from,$date,$id,$body_ref)) { return ($i + $previous) if ( $self->body_count == $limit or $self->subj_count == $limit or $self->from_count == $limit or $self->date_count == $limit or $self->id_count == $limit); } $i++; } return $self->file_count + 1; } ##------------------------------------------------------------------------## ## Function for use with File::Find -- recursive ## PRIVATE METHOD sub _get_file_list { /^msg/ && push @MSGFILES,$File::Find::name; } ##------------------------------------------------------------------------## ## Eval anonymous pattern match functions based on user search terms ## PUBLIC METHOD sub match_any { my $self = shift; my ($tail,$pat); if ($self->case) {$tail = '/i'} else {$tail = '/'}; my $code = < 5; study; EOCODE for $pat (@_) { $code .= <case) {$tail = '/i'} else {$tail = '/'}; my $code = < 5; study; EOCODE my $i; for $i (0 .. $#pat) { $code .= <$code"; # used for debugging my $function = eval $code; die "bad pattern: $@" if $@; return $function; } ## PUBLIC METHOD sub match_all { my $self = shift; my ($sep,$tail); if ($self->case) { $sep = "/i && /"; $tail = "/i }"; } else { $sep = "/ && /"; $tail = "/ }"; } my $code = "sub { use utf8; /" . join ("$sep", @_) . $tail; my $function = eval $code; die "bad pattern: $@" if $@; return $function; } ## PUBLIC METHOD sub match_this { my $self = shift; my $string = join(' ', @_); $string = '(?i)' . $string if ($self->case); my $code = "sub { use utf8; /" . $string . "/ }"; my $function = eval $code; die "bad pattern: $@" if $@; return $function; } 1; __END__ vim:ts=4 sympa-6.1.24~dfsg/wwsympa/README000066400000000000000000000001231246372670100163720ustar00rootroot00000000000000Refer to Sympa documentation http://www.sympa.org/distribution/current/doc/sympa/ sympa-6.1.24~dfsg/wwsympa/SharedDocument.pm000066400000000000000000000326731246372670100207740ustar00rootroot00000000000000# SharedDocument.pm - module to manipulate shared web documents # # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package SharedDocument; use strict; use Carp; use tools; use Language qw(gettext_strftime); use Log; ## Creates a new object sub new { my($pkg, $list, $path, $param) = @_; my $email = $param->{'user'}{'email'}; #$email ||= 'nobody'; my $document = {}; &do_log('debug2', 'SharedDocument::new(%s, %s)', $list->{'name'}, $path); unless (ref($list) =~ /List/i) { &do_log('err', 'SharedDocument::new : incorrect list parameter'); return undef; } $document->{'root_path'} = $list->{'dir'}.'/shared'; $document->{'path'} = &main::no_slash_end($path); $document->{'escaped_path'} = &tools::escape_chars($document->{'path'}, '/'); ### Document isn't a description file if ($document->{'path'} =~ /\.desc/) { &do_log('err',"SharedDocument::new : %s : description file", $document->{'path'}); return undef; } ## absolute path # my $doc; $document->{'absolute_path'} = $document->{'root_path'}; if ($document->{'path'}) { $document->{'absolute_path'} .= '/'.$document->{'path'}; } ## Check access control &check_access_control($document, $param); ############################### ## The path has been checked ## ############################### ### Document exist ? unless (-r $document->{'absolute_path'}) { &do_log('err',"SharedDocument::new : unable to read %s : no such file or directory", $document->{'absolute_path'}); return undef; } ### Document has non-size zero? unless (-s $document->{'absolute_path'}) { &do_log('err',"SharedDocument::new : unable to read %s : empty document", $document->{'absolute_path'}); return undef; } $document->{'visible_path'} = &main::make_visible_path($document->{'path'}); ## Date my @info = stat $document->{'absolute_path'}; $document->{'date'} = gettext_strftime "%d %b %Y", localtime $info[9]; $document->{'date_epoch'} = $info[9]; # Size of the doc $document->{'size'} = (-s $document->{'absolute_path'}) / 1000; ## Filename my @tokens = split /\//, $document->{'path'}; $document->{'filename'} = $document->{'visible_filename'} = $tokens[$#tokens]; ## Moderated document if ($document->{'filename'} =~ /^\.(.*)(\.moderate)$/) { $document->{'moderate'} = 1; $document->{'visible_filename'} = $1; } $document->{'escaped_filename'} = &tools::escape_chars($document->{'filename'}); ## Father dir if ($document->{'path'} =~ /^(([^\/]*\/)*)([^\/]+)$/) { $document->{'father_path'} = $1; }else { $document->{'father_path'} = ''; } $document->{'escaped_father_path'} = &tools::escape_chars($document->{'father_path'}, '/'); ### File, directory or URL ? if (! (-d $document->{'absolute_path'})) { if ($document->{'filename'} =~ /^\..*\.(\w+)\.moderate$/) { $document->{'file_extension'} = $1; }elsif ($document->{'filename'} =~ /^.*\.(\w+)$/) { $document->{'file_extension'} = $1; } if ($document->{'file_extension'} eq 'url') { $document->{'type'} = 'url'; }else { $document->{'type'} = 'file'; } }else { $document->{'type'} = 'directory'; } ## Load .desc file unless root directory my $desc_file; if ($document->{'type'} eq 'directory') { $desc_file = $document->{'absolute_path'}.'/.desc'; }else { if ($document->{'absolute_path'} =~ /^(([^\/]*\/)*)([^\/]+)$/) { $desc_file = $1.'.desc.'.$3; }else { &do_log('err',"SharedDocument::new() : cannot determine desc file for %s", $document->{'absolute_path'}); return undef; } } if ($document->{'path'} && (-e $desc_file)) { my @info = stat $desc_file; $document->{'serial_desc'} = $info[9]; my %desc_hash = &main::get_desc_file($desc_file); $document->{'owner'} = $desc_hash{'email'}; $document->{'title'} = $desc_hash{'title'}; $document->{'escaped_title'} = &tools::escape_html($document->{'title'}); # Author if ($desc_hash{'email'}) { $document->{'author'} = $desc_hash{'email'}; $document->{'author_mailto'} = &main::mailto($list,$desc_hash{'email'}); $document->{'author_known'} = 1; } } ### File, directory or URL ? if ($document->{'type'} eq 'url') { $document->{'icon'} = &main::get_icon('url'); open DOC, $document->{'absolute_path'}; my $url = ; close DOC; chomp $url; $document->{'url'} = $url; if ($document->{'filename'} =~ /^(.+)\.url/) { $document->{'anchor'} = $1; } }elsif ($document->{'type'} eq 'file') { if (my $type = &main::get_mime_type($document->{'file_extension'})) { # type of the file and apache icon if ($type =~ /^([\w\-]+)\/([\w\-]+)$/) { my ($mimet, $subt) = ($1, $2); if ($subt) { if ($subt =~ /^octet-stream$/) { $mimet = 'octet-stream'; $subt = 'binary'; } $type = "$subt file"; } $document->{'icon'} = &main::get_icon($mimet) || &main::get_icon('unknown'); } } else { # unknown file type $document->{'icon'} = &main::get_icon('unknown'); } ## HTML file if ($document->{'file_extension'} =~ /^html?$/i) { $document->{'html'} = 1; $document->{'icon'} = &main::get_icon('text'); } ## Directory }else { $document->{'icon'} = &main::get_icon('folder'); # listing of all the shared documents of the directory unless (opendir DIR, $document->{'absolute_path'}) { &do_log('err',"SharedDocument::new() : cannot open %s : %s", $document->{'absolute_path'}, $!); return undef; } # array of entry of the directory DIR my @tmpdir = readdir DIR; closedir DIR; my $dir = &main::get_directory_content(\@tmpdir, $email, $list, $document->{'absolute_path'}); foreach my $d (@{$dir}) { my $sub_document = new SharedDocument ($list, $document->{'path'}.'/'.$d, $param); push @{$document->{'subdir'}}, $sub_document; } } $document->{'list'} = $list; ## Bless Message object bless $document, $pkg; return $document; } sub dump { my $self = shift; my $fd = shift; &tools::dump_var($self, 0, $fd); } sub dup { my $self = shift; my $copy = {}; foreach my $k (keys %$self ) { $copy->{$k} = $self->{$k}; } return $copy; } ## Regulars # read(/) = default (config list) # edit(/) = default (config list) # control(/) = not defined # read(A/B)= (read(A) && read(B)) || # (author(A) || author(B)) # edit = idem read # control (A/B) : author(A) || author(B) # + (set owner A/B) if (empty directory && # control A) sub check_access_control { # Arguments: # (\%mode,$path) # if mode->{'read'} control access only for read # if mode->{'edit'} control access only for edit # if mode->{'control'} control access only for control # return the hash ( # $result{'may'}{'read'} == $result{'may'}{'edit'} == $result{'may'}{'control'} if is_author else : # $result{'may'}{'read'} = 0 or 1 (right or not) # $result{'may'}{'edit'} = 0(not may edit) or 0.5(may edit with moderation) or 1(may edit ) : it is not a boolean anymore # $result{'may'}{'control'} = 0 or 1 (right or not) # $result{'reason'}{'read'} = string for authorization_reject.tt2 when may_read == 0 # $result{'reason'}{'edit'} = string for authorization_reject.tt2 when may_edit == 0 # $result{'scenario'}{'read'} = scenario name for the document # $result{'scenario'}{'edit'} = scenario name for the document # Result my %result; $result{'reason'} = {}; # Control # Arguments my $self = shift; my $param = shift; my $list = $self->{'list'}; &do_log('debug', "check_access_control(%s)", $self->{'path'}); # Control for editing my $may_read = 1; my $why_not_read = ''; my $may_edit = 1; my $why_not_edit = ''; ## First check privileges on the root shared directory $result{'scenario'}{'read'} = $list->{'admin'}{'shared_doc'}{'d_read'}{'name'}; $result{'scenario'}{'edit'} = $list->{'admin'}{'shared_doc'}{'d_edit'}{'name'}; ## Privileged owner has all privileges if ($param->{'is_privileged_owner'}) { $result{'may'}{'read'} = 1; $result{'may'}{'edit'} = 1; $result{'may'}{'control'} = 1; $self->{'access'} = \%result; return 1; } # if not privileged owner if (1) { my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_read = $result->{'reason'}; } $may_read = ($action =~ /do_it/i); } if (1) { my $result = $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_edit = $result->{'reason'}; } #edit = 0, 0.5 or 1 $may_edit = &main::find_edit_mode($action); $why_not_edit = '' if ($may_edit); } ## Only authenticated users can edit files unless ($param->{'user'}{'email'}) { $may_edit = 0; $why_not_edit = 'not_authenticated'; } my $current_path = $self->{'path'}; my $current_document; my %desc_hash; my $user = $param->{'user'}{'email'} || 'nobody'; while ($current_path ne "") { # no description file found yet my $def_desc_file = 0; my $desc_file; $current_path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $current_document = $3; my $next_path = $1; # opening of the description file appropriated if (-d $self->{'root_path'}.'/'.$current_path) { # case directory # unless ($slash) { $current_path = $current_path.'/'; # } if (-e "$self->{'root_path'}/$current_path.desc"){ $desc_file = $self->{'root_path'}.'/'.$current_path.".desc"; $def_desc_file = 1; } }else { # case file if (-e "$self->{'root_path'}/$next_path.desc.$3"){ $desc_file = $self->{'root_path'}.'/'.$next_path.".desc.".$3; $def_desc_file = 1; } } if ($def_desc_file) { # a description file was found # loading of acces information %desc_hash = &main::get_desc_file($desc_file); ## Author has all privileges if ($user eq $desc_hash{'email'}) { $result{'may'}{'read'} = 1; $result{'may'}{'edit'} = 1; $result{'may'}{'control'} = 1; $self->{'access'} = \%result; return 1; } if (1) { my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario'=> $desc_hash{'read'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_read = $result->{'reason'}; } $may_read = $may_read && ( $action=~ /do_it/i); $why_not_read = '' if ($may_read); } if (1) { my $result = $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario'=> $desc_hash{'edit'}}); my $action_edit; if (ref($result) eq 'HASH') { $action_edit = $result->{'action'}; $why_not_edit = $result->{'reason'}; } # $may_edit = 0, 0.5 or 1 my $may_action_edit = &main::find_edit_mode($action_edit); $may_edit = &main::merge_edit($may_edit,$may_action_edit); $why_not_edit = '' if ($may_edit); } ## Only authenticated users can edit files unless ($param->{'user'}{'email'}) { $may_edit = 0; $why_not_edit = 'not_authenticated'; } unless (defined $result{'scenario'}{'read'}) { $result{'scenario'}{'read'} = $desc_hash{'read'}; $result{'scenario'}{'edit'} = $desc_hash{'edit'}; } } # truncate the path for the while $current_path = $next_path; } if (1) { $result{'may'}{'read'} = $may_read; $result{'reason'}{'read'} = $why_not_read; } if (1) { $result{'may'}{'edit'} = $may_edit; $result{'reason'}{'edit'} = $why_not_edit; } $self->{'access'} = \%result; return 1; } 1; sympa-6.1.24~dfsg/wwsympa/SympaSession.pm000066400000000000000000000625011246372670100205150ustar00rootroot00000000000000# SympaSession.pm - This module includes functions managing HTTP sessions in Sympa # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package SympaSession; use strict ; use Digest::MD5; use POSIX; use CGI::Cookie; use Time::Local; use Log; use Conf; # this structure is used to define which session attributes are stored in a dedicated database col where others are compiled in col 'data_session' my %session_hard_attributes = ('id_session' => 1, 'prev_id' => 1, 'date' => 1, 'refresh_date' => 1, 'remote_addr' => 1, 'robot' => 1, 'email' => 1, 'start_date' => 1, 'hit' => 1, 'new_session' => 1, ); sub new { my $pkg = shift; my $robot = shift; my $context = shift; my $cookie = $context->{'cookie'}; my $action = $context->{'action'}; my $rss = $context->{'rss'}; do_log('debug', 'SympaSession::new(%s,%s,%s)', $robot,$cookie,$action); my $self = {'robot' => $robot}; # set current robot bless $self, $pkg; unless ($robot) { &do_log('err', 'Missing robot parameter, cannot create session object') ; return undef; } # passive_session are session not stored in the database, they are used for crawler bots and action such as css, wsdl and rss if (&tools::is_a_crawler($robot,{'user_agent_string' => $ENV{'HTTP_USER_AGENT'}})) { $self->{'is_a_crawler'} = 1; $self->{'passive_session'} = 1; } $self->{'passive_session'} = 1 if ($rss||$action eq 'wsdl'||$action eq 'css'); # if a session cookie exist, try to restore an existing session, don't store sessions from bots if (($cookie)&&($self->{'passive_session'} != 1)){ my $status ; $status = $self->load($cookie); unless (defined $status) { return undef; } if ($status eq 'not_found') { do_log('info',"SympaSession::new ignoring unknown session cookie '$cookie'" ); # start a new session (may ne a fake cookie) return (new SympaSession ($robot)); } }else{ # create a new session context $self->{'new_session'} = 1; ## Tag this session as new, ie no data in the DB exist $self->{'id_session'} = &get_random(); $self->{'email'} = 'nobody'; $self->{'remote_addr'} = $ENV{'REMOTE_ADDR'}; $self->{'date'} = $self->{'refresh_date'} = $self->{'start_date'} = time; $self->{'hit'} = 1; $self->{'data'} = ''; } return $self; } sub load { my $self = shift; my $cookie = shift; Log::do_log('debug', '(%s)', $cookie); unless ($cookie) { do_log('err', 'SympaSession::load() : internal error, SympaSession::load called with undef id_session'); return undef; } my $dbh = &List::db_get_handler(); my $sth; my $statement; my $id_session; my $is_old_session = 0; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } ## Load existing session. if ($cookie and $cookie =~ /^\d{,16}$/) { ## Compatibility: session by older releases of Sympa. $id_session = $cookie; $is_old_session = 1; ## Session by older releases of Sympa doesn't have refresh_date. $statement = q{SELECT id_session AS id_session, id_session AS prev_id, date_session AS "date", remote_addr_session AS remote_addr, email_session AS email, data_session AS data, hit_session AS hit, start_date_session AS start_date, date_session AS refresh_date FROM session_table WHERE id_session = ? AND refresh_date_session IS NULL}; unless ($sth = $dbh->prepare($statement)) { do_log('err', 'Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute($id_session)) { do_log('err', 'Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } } else { $id_session = decrypt_session_id($cookie); unless ($id_session) { do_log('err', 'SympaSession::load() : internal error, SympaSession::load called with undef id_session'); return 'not_found'; } ## Cookie may contain current or previous session ID. $statement = q{SELECT id_session AS id_session, prev_id_session AS prev_id, date_session AS "date", remote_addr_session AS remote_addr, email_session AS email, data_session AS data, hit_session AS hit, start_date_session AS start_date, refresh_date_session AS refresh_date FROM session_table WHERE id_session = ? AND prev_id_session IS NOT NULL OR prev_id_session = ?}; unless ($sth = $dbh->prepare($statement)) { do_log('err', 'Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute($id_session, $id_session)) { do_log('err', 'Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } } my $session = undef; my $new_session = undef; my $counter = 0; while ($new_session = $sth->fetchrow_hashref('NAME_lc')) { if ( $counter > 0){ &Log::do_log('err',"The SQL statement did return more than one session. Is this a bug coming from dbi or mysql ? "); $session->{'email'} = ''; last; } $session = $new_session; $counter ++; } unless ($session) { return 'not_found'; } ## Compatibility: Upgrade session by older releases of Sympa. if ($is_old_session) { $sth = $dbh->prepare( q{UPDATE session_table SET prev_id_session = id_session WHERE id_session = ? AND prev_id_session IS NULL AND refresh_date_session IS NULL} ) and $sth->execute($id_session); } my %datas= &tools::string_2_hash($session->{'data'}); foreach my $key (keys %datas) {$self->{$key} = $datas{$key};} $self->{'id_session'} = $session->{'id_session'}; $self->{'prev_id'} = $session->{'prev_id'}; $self->{'date'} = $session->{'date'}; $self->{'refresh_date'} = $session->{'refresh_date'}; $self->{'start_date'} = $session->{'start_date'}; $self->{'hit'} = $session->{'hit'} +1 ; $self->{'remote_addr'} = $session->{'remote_addr'}; $self->{'email'} = $session->{'email'}; return ($self); } ## This method will both store the session information in the database sub store { my $self = shift; do_log('debug', ''); return undef unless ($self->{'id_session'}); return if ($self->{'is_a_crawler'}); # do not create a session in session table for crawlers; return if ($self->{'passive_session'}); # do not create a session in session table for action such as RSS or CSS or wsdlthat do not require this sophistication; my %hash ; foreach my $var (keys %$self ) { next if ($session_hard_attributes{$var}); next unless ($var); $hash{$var} = $self->{$var}; } my $data_string = &tools::hash_2_string (\%hash); my $dbh = &List::db_get_handler(); my $sth; my $time = time; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } ## If this is a new session, then perform an INSERT if ($self->{'new_session'}) { ## Store the new session ID in the DB ## Previous session ID is set to be same as new session ID. my $add_statement = sprintf q{INSERT INTO session_table (id_session, prev_id_session, date_session, refresh_date_session, remote_addr_session, robot_session, email_session, start_date_session, hit_session, data_session) VALUES (%s, %s, %d, %d, %s, %s, %s, %d, %d, %s)}, $dbh->quote($self->{'id_session'}), $dbh->quote($self->{'id_session'}), $time, $time, $dbh->quote($ENV{'REMOTE_ADDR'}), $dbh->quote($self->{'robot'}), $dbh->quote($self->{'email'}), $self->{'start_date'}, $self->{'hit'}, $dbh->quote($data_string); unless ($dbh->do($add_statement)) { do_log('err','Unable to update session information in database while execute SQL statement "%s" : %s', $add_statement, $dbh->errstr); return undef; } $self->{'prev_id'} = $self->{'id_session'}; ## If the session already exists in DB, then perform an UPDATE }else { ## Cookie may contain previous session ID. my $statement = sprintf q{SELECT id_session FROM session_table WHERE prev_id_session = %s}, $dbh->quote($self->{'id_session'}); my $sth = $dbh->prepare($statement); unless ($sth) { do_log('err', 'Unable to update session information in database while execute SQL statement "%s" : %s', $statement, $dbh->errstr ); return undef; } $sth->execute; if ($sth->rows) { my $new_id = $sth->fetchrow; $sth->finish; if ($new_id) { $self->{'prev_id'} = $self->{'id_session'}; $self->{'id_session'} = $new_id; } } ## Update the new session in the DB my $update_statement = sprintf q{UPDATE session_table SET date_session = %d, remote_addr_session = %s, robot_session = %s, email_session = %s, start_date_session = %d, hit_session = %d, data_session = %s WHERE id_session = %s AND prev_id_session IS NOT NULL OR prev_id_session = %s}, $time, $dbh->quote($ENV{'REMOTE_ADDR'}), $dbh->quote($self->{'robot'}), $dbh->quote($self->{'email'}), $self->{'start_date'}, $self->{'hit'}, $dbh->quote($data_string), $dbh->quote($self->{'id_session'}), $dbh->quote($self->{'id_session'}); unless ($dbh->do($update_statement)) { do_log('err','Unable to update session information in database while execute SQL statement "%s" : %s', $update_statement, $dbh->errstr); return undef; } } return 1; } ## This method will renew the session ID sub renew { my $self = shift; do_log('debug', 'id_session=(%s)',$self->{'id_session'}); return undef unless ($self->{'id_session'}); return if ($self->{'is_a_crawler'}); # do not create a session in session table for crawlers; return if ($self->{'passive_session'}); # do not create a session in session table for action such as RSS or CSS or wsdlthat do not require this sophistication; my %hash ; foreach my $var (keys %$self ) { next if ($session_hard_attributes{$var}); next unless ($var); $hash{$var} = $self->{$var}; } my $data_string = &tools::hash_2_string (\%hash); my $dbh = &List::db_get_handler(); my $sth; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } ## Cookie may contain previous session ID. my $statement = sprintf q{SELECT id_session FROM session_table WHERE prev_id_session = %s}, $dbh->quote($self->{'id_session'}); $sth = $dbh->prepare($statement); unless ($sth) { do_log('err', 'Unable to update session information in database while execute SQL statement "%s" : %s', $statement, $dbh->errstr ); return undef; } $sth->execute; if ($sth->rows) { my $new_id = $sth->fetchrow; $sth->finish; if ($new_id) { $self->{'prev_id'} = $self->{'id_session'}; $self->{'id_session'} = $new_id; } } ## Renew the session ID in order to prevent session hijacking my $new_id = &get_random(); ## Do refresh the session ID when remote address was changed or refresh ## interval was past. Conditions also are checked by SQL so that ## simultaneous processes will be prevented renewing cookie. my $time = time; my $remote_addr = $ENV{'REMOTE_ADDR'}; my $refresh_term; if ($Conf::Conf{'cookie_refresh'} == 0) { $refresh_term = $time; } else { my $cookie_refresh = $Conf::Conf{'cookie_refresh'}; $refresh_term = int($time - $cookie_refresh * 0.25 - rand($cookie_refresh * 0.5)); } unless ($self->{'remote_addr'} ne $remote_addr or $self->{'refresh_date'} <= $refresh_term) { return 0; } ## First insert DB entry with new session ID, my $add_statement = sprintf q{INSERT INTO session_table (id_session, prev_id_session, start_date_session, date_session, refresh_date_session, remote_addr_session, robot_session, email_session, hit_session, data_session) SELECT %s, id_session, start_date_session, date_session, %d, %s, %s, email_session, hit_session, data_session FROM session_table WHERE (id_session = %s AND prev_id_session IS NOT NULL OR prev_id_session = %s) AND (remote_addr_session <> %s OR refresh_date_session <= %d)}, $dbh->quote($new_id), $time, $dbh->quote($remote_addr), $dbh->quote($self->{'robot'}), $dbh->quote($self->{'id_session'}), $dbh->quote($self->{'id_session'}), $dbh->quote($remote_addr), $refresh_term; ## Keep previous ID to prevent crosstalk, clearing grand-parent ID. my $update_statement = sprintf q{UPDATE session_table SET prev_id_session = NULL WHERE id_session = %s}, $dbh->quote($self->{'id_session'}); ## Remove record of grand-parent ID. my $del_statement = sprintf q{DELETE FROM session_table WHERE id_session = %s AND prev_id_session IS NULL}, $dbh->quote($self->{'prev_id'}); $sth = $dbh->prepare($add_statement); unless ($sth) { do_log('err','Unable to renew session ID for session %s',$self->{'id_session'}); return undef; } $sth->execute; unless ($sth->rows) { return 0; } $dbh->do($update_statement); $dbh->do($del_statement); ## Renew the session ID in order to prevent session hijacking do_log('info', '[robot %s] [session %s] [client %s]%s new session %s', $self->{'robot'}, $self->{'id_session'}, $remote_addr, ($self->{'email'} ? sprintf(' [user %s]', $self->{'email'}) : ''), $new_id ); $self->{'prev_id'} = $self->{'id_session'}; $self->{'id_session'} = $new_id; $self->{'refresh_date'} = $time; $self->{'remote_addr'} = $remote_addr; return 1; } ## remove old sessions from a particular robot or from all robots. delay is a parameter in seconds ## sub purge_old_sessions { my $robot = shift; do_log('info', 'SympaSession::purge_old_sessions(%s)',$robot); my $delay = &tools::duration_conv($Conf{'session_table_ttl'}) ; my $anonymous_delay = &tools::duration_conv($Conf{'anonymous_session_table_ttl'}) ; unless ($delay) { do_log('info', 'SympaSession::purge_old_session(%s) exit with delay null',$robot); return;} unless ($anonymous_delay) { do_log('info', 'SympaSession::purge_old_session(%s) exit with anonymous delay null',$robot); return;} my @sessions ; my $sth; my $dbh = &List::db_get_handler(); my $robot_condition = sprintf "robot_session = %s", $dbh->quote($robot) unless (($robot eq '*')||($robot)); my $delay_condition = time-$delay.' > date_session' if ($delay); my $anonymous_delay_condition = time-$anonymous_delay.' > date_session' if ($anonymous_delay); my $and = ' AND ' if (($delay_condition) && ($robot_condition)); my $anonymous_and = ' AND ' if (($anonymous_delay_condition) && ($robot_condition)); my $count_statement = sprintf q{SELECT count(*) FROM session_table WHERE %s %s %s}, $robot_condition, $and, $delay_condition; my $anonymous_count_statement = sprintf q{SELECT count(*) FROM session_table WHERE %s %s %s AND email_session = 'nobody' AND hit_session = '1'}, $robot_condition, $anonymous_and, $anonymous_delay_condition; my $statement = sprintf q{DELETE FROM session_table WHERE %s %s %s}, $robot_condition, $and, $delay_condition; my $anonymous_statement = sprintf q{DELETE FROM session_table WHERE %s %s %s AND email_session = 'nobody' AND hit_session = '1'}, $robot_condition, $anonymous_and, $anonymous_delay_condition; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } unless ($sth = $dbh->prepare($count_statement)) { do_log('err','Unable to prepare SQL statement %s : %s',$count_statement, $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } my $total = $sth->fetchrow; if ($total == 0) { do_log('debug','SympaSession::purge_old_sessions no sessions to expire'); }else{ unless ($sth = $dbh->prepare($statement)) { do_log('err','Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } } unless ($sth = $dbh->prepare($anonymous_count_statement)) { do_log('err','Unable to prepare SQL statement %s : %s',$anonymous_count_statement, $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $anonymous_statement, $dbh->errstr); return undef; } my $anonymous_total = $sth->fetchrow; if ($anonymous_total == 0) { do_log('debug','SympaSession::purge_old_sessions no anonymous sessions to expire'); return $total ; } unless ($sth = $dbh->prepare($anonymous_statement)) { do_log('err','Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $anonymous_statement, $dbh->errstr); return undef; } return $total+$anonymous_total; } ## remove old one_time_ticket from a particular robot or from all robots. delay is a parameter in seconds ## sub purge_old_tickets { my $robot = shift; do_log('info', 'SympaSession::purge_old_tickets(%s)',$robot); my $delay = &tools::duration_conv($Conf{'one_time_ticket_table_ttl'}) ; unless ($delay) { do_log('info', 'SympaSession::purge_old_tickets(%s) exit with delay null',$robot); return;} my @tickets ; my $sth; my $dbh = &List::db_get_handler(); my $robot_condition = sprintf "robot_one_time_ticket = %s", $dbh->quote($robot) unless (($robot eq '*')||($robot)); my $delay_condition = time-$delay.' > date_one_time_ticket' if ($delay); my $and = ' AND ' if (($delay_condition) && ($robot_condition)); my $count_statement = sprintf "SELECT count(*) FROM one_time_ticket_table WHERE $robot_condition $and $delay_condition"; my $statement = sprintf "DELETE FROM one_time_ticket_table WHERE $robot_condition $and $delay_condition"; ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } unless ($sth = $dbh->prepare($count_statement)) { do_log('err','Unable to prepare SQL statement %s : %s',$count_statement, $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } my $total = $sth->fetchrow; if ($total == 0) { do_log('debug','SympaSession::purge_old_tickets no tickets to expire'); }else{ unless ($sth = $dbh->prepare($statement)) { do_log('err','Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } } return $total; } # list sessions for $robot where last access is newer then $delay. List is limited to connected users if $connected_only sub list_sessions { my $delay = shift; my $robot = shift; my $connected_only = shift; do_log('debug', 'SympaSession::list_session(%s,%s,%s)',$delay,$robot,$connected_only); my @sessions ; my $sth; my $dbh = &List::db_get_handler(); my $condition = sprintf "robot_session = %s", $dbh->quote($robot) unless ($robot eq '*'); my $condition2 = time-$delay.' < date_session ' if ($delay); my $and = ' AND ' if (($condition) && ($condition2)); $condition = $condition.$and.$condition2 ; my $condition3 = " email_session <> 'nobody' " if ($connected_only eq 'on'); my $and2 = ' AND ' if (($condition) && ($condition3)); $condition = $condition.$and2.$condition3 ; $condition .= sprintf '%sprev_id_session IS NOT NULL', ($condition ? ' AND ' : ''); my $statement = sprintf q{SELECT remote_addr_session, email_session, robot_session, date_session, start_date_session, hit_session FROM session_table WHERE %s}, $condition; do_log('debug', 'SympaSession::list_session() : statement = %s',$statement); ## Check database connection unless ($dbh and $dbh->ping) { return undef unless &List::db_connect(); } unless ($sth = $dbh->prepare($statement)) { do_log('err','Unable to prepare SQL statement : %s', $dbh->errstr); return undef; } unless ($sth->execute) { do_log('err','Unable to execute SQL statement "%s" : %s', $statement, $dbh->errstr); return undef; } while (my $session = ($sth->fetchrow_hashref('NAME_lc'))) { $session->{'formated_date'} = Language::gettext_strftime("%d %b %Y at %H:%M:%S", localtime($session->{'date_session'})); $session->{'formated_start_date'} = Language::gettext_strftime("%d %b %Y at %H:%M:%S", localtime($session->{'start_date_session'})); # do_log('debug', 'SympaSession::list_session() DUMP : %s,%s,%s,%s',$session->{'remote_addr_session'}, $session->{'email_session'}, $session->{'robot_session'}, $session->{'formated_date'}); push @sessions, $session; } $sth->finish(); return \@sessions; } ############################### # Subroutines to read cookies # ############################### ## Generic subroutine to get a cookie value sub get_session_cookie { my $http_cookie = shift; if ($http_cookie =~/\S+/g) { my %cookies = parse CGI::Cookie($http_cookie); foreach (keys %cookies) { my $cookie = $cookies{$_}; next unless ($cookie->name eq 'sympa_session'); return ($cookie->value); } } return (undef); } ## Generic subroutine to set a cookie ## Set user $email cookie, ckecksum use $secret, expire=(now|session|#sec) domain=(localhost|) sub set_cookie { my ($self, $http_domain, $expires,$use_ssl) = @_ ; do_log('debug','Session::set_cookie(%s,%s,secure= %s)',$http_domain, $expires,$use_ssl ); my $expiration; if ($expires =~ /now/i) { ## 10 years ago $expiration = '-10y'; }else{ $expiration = '+'.$expires.'m'; } if ($http_domain eq 'localhost') { $http_domain=""; } my $value = encrypt_session_id($self->{'id_session'}); my $cookie; if ($expires =~ /session/i) { $cookie = new CGI::Cookie (-name => 'sympa_session', -value => $value, -domain => $http_domain, -path => '/', -secure => $use_ssl, -httponly => 1 ); }else { $cookie = new CGI::Cookie (-name => 'sympa_session', -value => $value, -expires => $expiration, -domain => $http_domain, -path => '/', -secure => $use_ssl, -httponly => 1 ); } ## Send cookie to the client printf "Set-Cookie: %s\n", $cookie->as_string; return 1; } # Build an HTTP cookie value to be sent to a SOAP client sub soap_cookie2 { my ($session_id, $http_domain, $expire) = @_; my $cookie; my $value; # WARNING : to check the cookie the SOAP services does not gives # all the cookie, only it's value so we need ':' $value = encrypt_session_id($session_id); ## With set-cookie2 max-age of 0 means removing the cookie ## Maximum cookie lifetime is the session $expire ||= 600; ## 10 minutes if ($http_domain eq 'localhost') { $cookie = CGI::Cookie->new( -name => 'sympa_session', -value => $value, -path => '/', ); $cookie->max_age(time + $expire); # needs CGI >= 3.51. } else { $cookie = CGI::Cookie->new( -name => 'sympa_session', -value => $value, -domain => $http_domain, -path => '/', ); $cookie->max_age(time + $expire); # needs CGI >= 3.51. } ## Return the cookie value return $cookie->as_string; } sub get_random { ## Concatenates two integers for a better entropy. return sprintf '%07d%07d', int(rand(10**7)), int(rand(10**7)); } ## Return the session object content, as a hashref sub as_hashref { my $self = shift; my $data; foreach my $key (keys %{$self}) { $data->{$key} = $self->{$key}; } return $data; } ## Generate cookie from session ID. sub encrypt_session_id { my $id_session = shift; my $cipher = tools::ciphersaber_installed(); unless ($cipher) { return "5e55$id_session"; } return unpack 'H*', $cipher->encrypt(pack 'H*', $id_session); } ## Get session ID from cookie. sub decrypt_session_id { my $cookie = shift; return undef unless $cookie and $cookie =~ /\A(?:[0-9a-f]{2})+\z/; my $cipher = tools::ciphersaber_installed(); unless ($cipher) { return undef unless $cookie =~ s/\A5e55//; return $cookie; } return unpack 'H*', $cipher->decrypt(pack 'H*', $cookie); } 1; sympa-6.1.24~dfsg/wwsympa/archived.pl.in000066400000000000000000000731671246372670100202630ustar00rootroot00000000000000#!--PERL-- # archived.pl - This script does the web archives building for Sympa # RCS Identication ; $Revision: 11009 $ ; $Date: 2014-06-18 09:54:30 +0200 (mer. 18 juin 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . ## Options : F -> do not detach TTY ## : d -> debug -d is equiv to -dF ## Now, it is impossible to use -dF but you have to write it -d -F =pod =head1 NAME I - Daemon running the web archive building. =head1 DESCRIPTION This script must be run along with sympa. It regularly checks the 'outgoing' spool and picks the messages it finds in it. It then calls MHonArc to build the HTML version of archives and add an SMTP text version (i.e. an ASCII file including headers and body) to the appropriate directory. =cut use lib '--modulesdir--'; use List; use Message; use Conf; use Log; use Commands; #use Getopt::Std; use Getopt::Long; use Language; use Digest::MD5; use wwslib; use mail; use tt2; use tools; use Sympa::Constants; my $daemon_name = &Log::set_daemon($0); my $ip = $ENV{'REMOTE_HOST'}; #getopts('dF'); ## Check options my %options; unless (&GetOptions(\%main::options, 'debug|d', 'foreground|F')) { &fatal_err("Unknown options."); } if ($main::options{'debug'}) { $main::options{'log_level'} = 2 unless ($main::options{'log_level'}); } $main::options{'foreground'} = 1 if ($main::options{'debug'}); $main::options{'log_to_stderr'} = 1 if ($main::options{'debug'} || $main::options{'foreground'}); my $wwsympa_conf = Sympa::Constants::WWSCONFIG; my $sympa_conf_file = Sympa::Constants::CONFIG; my $wwsconf = {}; my $adrlist = {}; # Load WWSympa configuration unless ($wwsconf = &wwslib::load_config($wwsympa_conf)) { print STDERR 'unable to load config file'; exit -1; } # Load sympa.conf unless (Conf::load($sympa_conf_file)) { &fatal_err("Unable to load sympa configuration, file $sympa_conf_file has errors."); } ## Create arc_path if required if ($wwsconf->{'arc_path'}) { unless (-d $wwsconf->{'arc_path'}) { printf STDERR "Creating missing %s directory\n", $wwsconf->{'arc_path'}; mkdir $wwsconf->{'arc_path'}, 0775; unless (&tools::set_file_rights(file => $wwsconf->{'arc_path'}, user => Sympa::Constants::USER, group => Sympa::Constants::GROUP, mode => 0775, )) { &do_log('err','Unable to set rights on %s',$Conf{'db_name'}); exit -1; } } } ## Check databse connectivity unless (&List::check_db_connect()) { &fatal_err('Database %s defined in sympa.conf has not the right structure or is unreachable.', $Conf{'db_name'}); } ## Put ourselves in background if not in debug mode. unless ($main::options{'debug'} || $main::options{'foreground'}) { open(STDERR, ">> /dev/null"); open(STDOUT, ">> /dev/null"); if (open(TTY, "/dev/tty")) { # ioctl(TTY, &TIOCNOTTY, 0); ioctl(TTY, 0x20007471, 0); # XXX s/b &TIOCNOTTY close(TTY); } setpgrp(0, 0); if ((my $child_pid = fork) != 0) { print STDOUT "Starting archive daemon, pid $_\n"; exit(0); } } ## If process is running in foreground, don't write STDERR to a dedicated file my $options; $options->{'stderr_to_tty'} = 1 if ($main::options{'foreground'}); ## Create and write the PID file tools::write_pid('archived', $$, $options); # setting log_level using conf unless it is set by calling option if ($main::options{'log_level'}) { &Log::set_log_level($main::options{'log_level'}); do_log('info', "Configuration file read, log level set using options : $main::options{'log_level'}"); }else{ &Log::set_log_level($Conf{'log_level'}); do_log('info', "Configuration file read, default log level $Conf{'log_level'}"); } $wwsconf->{'log_facility'}||= $Conf{'syslog'}; do_openlog($wwsconf->{'log_facility'}, $Conf{'log_socket_type'}, 'archived'); ## Set the UserID & GroupID for the process $( = $) = (getgrnam(Sympa::Constants::GROUP))[2]; $< = $> = (getpwnam(Sympa::Constants::USER))[2]; ## Required on FreeBSD to change ALL IDs(effective UID + real UID + saved UID) &POSIX::setuid((getpwnam(Sympa::Constants::USER))[2]); &POSIX::setgid((getgrnam(Sympa::Constants::GROUP))[2]); ## Check if the UID has correctly been set (usefull on OS X) unless (($( == (getgrnam(Sympa::Constants::GROUP))[2]) && ($< == (getpwnam(Sympa::Constants::USER))[2])) { &fatal_err("Failed to change process userID and groupID. Note that on some OS Perl scripts can't change their real UID. In such circumstances Sympa should be run via SUDO."); } ## Sets the UMASK umask(oct($Conf{'umask'})); ## Check access to arc_path unless ((-r $wwsconf->{'arc_path'}) && (-w $wwsconf->{'arc_path'})) { do_log('err', 'Unsufficient access to %s directory', $wwsconf->{'arc_path'}); } ## Change to list root unless (chdir($Conf{'home'})) { &do_log('err','unable to change directory'); exit (-1); } my $pinfo = &List::_apply_defaults(); $Language::default_lang = $Conf{'lang'}; do_log('notice', "archived %s Started", Sympa::Constants::VERSION); ## Catch SIGTERM, in order to exit cleanly, whenever possible. $SIG{'TERM'} = 'sigterm'; my $end = 0; my $queue = $Conf{'queueoutgoing'}; print "queue : $queue\n"; #if (!chdir($queue)) { # fatal_err("Can't chdir to %s: %m", $queue); # ## Function never returns. #} ## infinite loop scanning the queue (unless a sig TERM is received while (!$end) { &List::init_list_cache(); unless (opendir(DIR, $queue)) { fatal_err("Can't open dir %s: %m", $queue); ## No return. } my @files = (grep(/(^[^\.]|^\.(remove|rebuild)\.(.*))/, readdir DIR )); closedir DIR; ## this sleep is important to be raisonably sure that sympa is not currently ## writting the file this deamon is openning. sleep 6; foreach my $file (@files) { last if $end; next unless (-f "$queue/$file"); next if $file =~ /\.(lock|NFSLock)\z/; # Skip lock. if ($file =~ /^\.remove\.((.*)\.(\d\d\d\d\-\d\d))\.\d+$/ ) { my $arclistdir = $1; my $listname = $2; my $yyyymm = $3; my $arcpath = "$wwsconf->{'arc_path'}/$listname/$yyyymm"; do_log('debug',"start remove process :listname :'$listname' arclistdir '$arclistdir' arcpath '$arcpath' yyyymm '$yyyymm'"); my $list; unless ($list = new List ($listname)) { do_log('err',"remove : unknown list $listname"); next; } do_log('debug',"remove found : $file for list $yyyymm"); unless (open REMOVE, "$queue/$file") { do_log ('err',"Ignoring file $queue/$file because couldn't read it, archived.pl must use the same uid as sympa"); next; } my $email_regexp = &tools::get_regexp('email'); foreach my $order () { unless($order =~ /(.*)\|\|($email_regexp)/){ do_log ('err',"Ignoring remove_order $order not recognized format"); next; } my $msgid = $1; my $sender = $2; chomp $msgid ; if ($msgid =~ /NO-ID-FOUND\.mhonarc\.org/) { do_log('err','remove_arc: no message id found'); next; } my $message ; unless ($message = &Archive::search_msgid("$arcpath/arctxt",$msgid)){ do_log('err','No message with message-id %s found in %s/arctxt',$msgid,$arcpath); next; } unless ($list->am_i('privileged_owner',$sender)|| $list->am_i('owner',$sender)||$list->am_i('editor', $sender)||&List::is_listmaster($sender,$list->{'domain'} )){ # if not list owner or list editor or listmaster,n check if sender of remove order is sender of the message to remove my $new_message; unless ($new_message = new Message("$arcpath/arctxt/$message",'noxsympato')) { do_log('err',"unable to load new message $arcpath/arctxt/$message"); next; } my $messagesender = lc($new_message->{'sender'}); unless ($sender eq $messagesender) { &do_log('err', 'remove command by unauthorized sender'); next; } } # this point : requested command is from a authorized personn (message sender or list admin or listmaster &remove($arclistdir,$msgid); my $url_dir = $list->{'dir'}.'/urlized/'.$msgid; &tools::remove_dir ($url_dir); unless (-d "$arcpath/deleted"){ unless (mkdir ("$arcpath/deleted",0777)) { do_log('info',"remove_arc: unable to create $arcpath/deleted : $!"); last; } } unless (rename ("$arcpath/arctxt/$message","$arcpath/deleted/$message")) { do_log('info',"remove_arc: unable to rename message $arcpath/arctxt/$message"); next; } # remove directory if empty arctxt unless (opendir (DIR,"$arcpath/arctxt")) { do_log('info',"remove_arc: unable to open dir $arcpath/arctxt"); next; } my @files = grep(/^\d+$/, readdir( DIR )); closedir (DIR); if ($#files == -1) { &tools::remove_dir ($arcpath); }else{ } } close REMOVE; unless (unlink("$queue/$file")) { do_log ('err',"Ignoring file $queue/$file because couldn't remove it, archived.pl must use the same uid as sympa"); next; } }elsif ($file =~ /^\.rebuild\.(.*)$/ ) { do_log('debug',"rebuild found : $file for list $1"); &rebuild($1); unless (unlink("$queue/$file")) { do_log ('err',"Ignoring file $queue/$file because couldn't remove it, archived.pl must use the same uid as sympa"); next; } }else{ my ($yyyy, $mm, $dd, $hh, $min, $ss, $adrlist); if ($file =~ /^(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(.*)$/) { ($yyyy, $mm, $dd, $hh, $min, $ss, $adrlist) = ($1, $2, $3, $4, $5, $6, $7); }elsif (($file =~ /^(.*)\.(\d+)\.(\d+)\.(\d+)$/) || ($file =~ /^(.*)\.(\d+)\.(\d+)$/)) { $adrlist = $1; my $date = $2; my @now = localtime($date); $yyyy = sprintf '%04d', 1900+$now[5]; $mm = sprintf '%02d', $now[4]+1; $dd = sprintf '%02d', $now[3]; $hh = sprintf '%02d', $now[2]; $min = sprintf '%02d', $now[1]; $ss = sprintf '%02d', $now[0]; }else { do_log ('err',"Ignoring file $queue/$file because not to be rebuid or liste archive"); unlink("$queue/$file"); next; } my ($listname, $hostname); if ($adrlist =~ /^(.*)\@(.*)$/) { $listname = $1; $hostname = $2; }else { &do_log('err',"Match of list address '$adrlist' failed"); return undef; } do_log('notice',"Archiving $file for list $adrlist"); unless (mail2arc ($file, $listname, $hostname, $yyyy, $mm, $dd, $hh, $min, $ss)) { &tools::save_to_bad({ 'file' => $file, 'hostname' => $hostname, 'queue' => $queue, }); unless (&List::send_notify_to_listmaster('archiving_failed',$hostname,{'file' => "$file",'bad' => "$queue/bad"})) { &do_log('notice',"Unable to send notify 'archiving_failed' to listmaster"); } } if (-f "$queue/$file") { unless (unlink("$queue/$file")) { do_log ('err',"Ignoring file $queue/$file because couldn't remove it, archived.pl must use the same uid as sympa"); do_log ('err',"exiting because I don't want to loop until file system is full"); last; } } } } ## Free zombie sendmail processes &mail::reaper; } do_log('notice', 'archived exited normally due to signal'); tools::remove_pid('archived', $$); exit(0); =pod =head1 SUBFUNCTIONS This is the description of the subfunctions contained by archived.pl. =cut =pod =head2 sub sigterm() Switches the loop control variable $end value to 1 when SIGTERM signal is caught. =head3 Arguments =over =item * I =back =head3 Return =over =item * I =back =head3 Calls =over =item * none =back =cut ## When we catch SIGTERM, just change the value of the loop ## variable. sub sigterm { $end = 1; } =pod =head2 sub remove(STRING $adrlist, STRING $msgid) Removes the message having the identifier $msgid from the list named $adrlist. =head3 Arguments =over =item * I<$adrlist>, a character string containing the list name. =item * I<$msgid> , a character string containing the message identifier. =back =head3 Return =over =item * I =back =head3 Calls =over =item * Log::db_log =item * Log::do_log =back =cut sub remove { my $adrlist = shift; my $msgid = shift; my $robot = shift; do_log ('debug2',"remove ($adrlist, $msgid)"); my $arc ; if ($adrlist =~ /^(.*)\.(\d{4}-\d{2})$/) { $adrlist = $1; $arc = $2; } do_log('notice',"Removing $msgid in list $adrlist section $2"); unless(&Log::db_log({'robot' => $robot,'list' => $adrlist,'action' => 'remove','parameters' => $msgid.','.$adrlist,'target_email' => '','msg_id' => $msgid,'status' => 'succes','error_type' => '','user_email' =>'','client' => $ip,'daemon' => $daemon_name})) { &do_log('error','archived::remove: unable to log event'); } my $arcpath = $wwsconf->{'arc_path'}; $arc =~ /^(\d{4})-(\d{2})$/ ; my $yyyy = $1 ; my $mm = $2 ; $msgid =~ s/\$/\\\$/g; system "$wwsconf->{'mhonarc'} -outdir $arcpath/$adrlist/$yyyy-$mm -rmm $msgid"; } =pod =head2 sub rebuild(STRING $adrlist) Rebuilds archives for the list the name of which is given in the argument $adrlist. =head3 Arguments =over =item * I<$adrlist>, a character string containing the name of the list the archives of which we want to rebuild. =back =head3 Return =over =item * I if something goes wrong. =back =head3 Calls =over =item * get_tag =item * set_hidden_mode =item * unset_hidden_mode =item * List::new =item * Log::do_log =item * tools::get_filename =item * tools::remove_dir =back =cut sub rebuild { my $adrlist = shift; my $arc ; do_log ('debug2',"rebuild ($adrlist)"); if ($adrlist =~ /^(.*)\.(\d{4}-\d{2})$/) { $adrlist = $1; $arc = $2; } my ($listname, $hostname); if ($adrlist =~ /^(.*)\@(.*)$/) { $listname = $1; $hostname = $2; }else { &do_log('err',"Match of list address '$adrlist' failed"); return undef; } my $tag = &get_tag($listname); my $list = new List($listname, $hostname); my $robot = $list->{'domain'}; my $arcpath = $wwsconf->{'arc_path'}; Log::do_log('debug3', "Rebuilding %s archive (%s)", $adrlist, $2); my $mhonarc_ressources = &tools::get_filename('etc',{},'mhonarc-ressources.tt2',$list->{'domain'}, $list); if (($list->{'admin'}{'web_archive_spam_protection'} ne 'none') && ($list->{'admin'}{'web_archive_spam_protection'} ne 'cookie')) { &set_hidden_mode($tag); }else { &unset_hidden_mode(); } Log::do_log('notice', "Rebuilding %s with M2H_ADDRESSMODIFYCODE : %s", $arc, $ENV{'M2H_ADDRESSMODIFYCODE'}); if ($arc) { Log::do_log('notice',"Rebuilding %s of %s archive", $arc, $adrlist); $arc =~ /^(\d{4})-(\d{2})$/ ; my $yyyy = $1 ; my $mm = $2 ; # remove empty directory my $arcdir = $arcpath.'/'.$adrlist.'/'.$yyyy.'-'.$mm ; my $arctxt = $arcdir.'/arctxt' ; if (opendir (DIR,$arctxt)) { my @files = (grep(/^\d+$/,(readdir DIR ))); close (DIR); if ($#files == -1) { Log::do_log('notice', "Removing empty directory %s", $arcdir); &tools::remove_dir ($arcdir); next ; } ## index file was removed ; recreate it my $index = $files[$#files]; &save_idx($arcdir.'/index', $index+1); } ## recreate index file if needed unless (-f $arcdir.'/index') { &create_idx($arcdir); } ## Remove .mhonarc.db unlink $arcpath.'/'.$adrlist.'/'.$yyyy.'-'.$mm.'/.mhonarc.db'; ## Remove existing HTML files opendir HTML, "$arcpath/$adrlist/$yyyy-$mm"; ## Skip arctxt/ . and .. foreach my $html_file (grep !/^arctxt$|^index$|\.+$/, readdir(HTML)) { unlink $arcpath.'/'.$adrlist.'/'.$yyyy.'-'.$mm.'/'.$html_file; } closedir HTML; my $arcs_dir = Archive::clean_archive_directory( $robot, "/$adrlist/$arc/arctxt"); my $dir_to_rebuild = $arcs_dir->{'dir_to_rebuild'} if($arcs_dir); my $cmd = "$wwsconf->{'mhonarc'} -modifybodyaddresses -addressmodifycode \'$ENV{'M2H_ADDRESSMODIFYCODE'}\' -rcfile $mhonarc_ressources -outdir $arcpath/$adrlist/$yyyy-$mm -definevars \"listname='$listname' hostname=$hostname yyyy=$yyyy mois=$mm yyyymm=$yyyy-$mm wdir=$arcpath base=" . Conf::get_robot_conf($robot, 'wwsympa_url') . "/arc tag=$tag\" -umask $Conf{'umask'} $dir_to_rebuild"; do_log('debug',"System call : $cmd"); my $exitcode = system($cmd); $exitcode = $exitcode / 256; # Delete temporary directory containing files with escaped HTML. if ($arcs_dir && -d $arcs_dir->{'cleaned_dir'}) { &tools::del_dir($arcs_dir->{'cleaned_dir'}); } ## Remove lock if required if ($exitcode == 75) { &do_log('notice', 'Removing lock directory %s', $arcpath.'/'.$adrlist.'/'.$arc.'/.mhonarc.lck'); rmdir $arcpath.'/'.$adrlist.'/'.$arc.'/.mhonarc.lck'; $exitcode = system($cmd); $exitcode = $exitcode / 256; } if ($exitcode) { do_log('err',"Command $cmd failed with exit code $exitcode"); } }else{ do_log('notice',"Rebuilding $adrlist archive completely"); if (!opendir(DIR, "$arcpath/$adrlist" )) { do_log('err',"unable to open $arcpath/$adrlist to rebuild archive"); return ; } my @archives = (grep (/^\d{4}-\d{2}/, readdir(DIR))); close DIR ; foreach my $arc (@archives) { $arc =~ /^(\d{4})-(\d{2})$/ ; my $yyyy = $1 ; my $mm = $2 ; my $arcdir = $arcpath.'/'.$adrlist.'/'.$yyyy.'-'.$mm; ## Remove .mhonarc.db unlink $arcdir.'/.mhonarc.db'; ## Remove existing HTML files opendir HTML, $arcdir; ## Skip arctxt/ . and .. foreach my $html_file (grep !/^arctxt$|^index$|\.+$/, readdir(HTML)) { unlink $arcdir.'/'.$html_file; } closedir HTML; my $dir_to_rebuild = $arcpath . "/$adrlist/$arc/arctxt"; my $arcs_dir = Archive::clean_archive_directory( $robot, "/$adrlist/$arc/arctxt"); if($arcs_dir) { $dir_to_rebuild = $arcs_dir->{'dir_to_rebuild'}; } ## recreate index file if needed unless (-f $arcdir.'/index') { &create_idx($arcdir); } my $cmd = "$wwsconf->{'mhonarc'} -modifybodyaddresses -addressmodifycode \'$ENV{'M2H_ADDRESSMODIFYCODE'}\' -rcfile $mhonarc_ressources -outdir $arcdir -definevars \"listname=$listname hostname=$hostname yyyy=$yyyy mois=$mm yyyymm=$yyyy-$mm wdir=$arcpath base=" . Conf::get_robot_conf($robot, 'wwsympa_url') . "/arc tag=$tag\" -umask $Conf{'umask'} $dir_to_rebuild"; my $exitcode = system($cmd); $exitcode = $exitcode / 256; # Delete temporary directory containing files with escaped HTML. if ($arcs_dir && -d $arcs_dir->{'cleaned_dir'}) { &tools::del_dir($arcs_dir->{'cleaned_dir'}); } ## Remove lock if required if ($exitcode == 75) { &do_log('notice', 'Removing lock directory %s', $arcdir.'/.mhonarc.lck'); rmdir $arcdir.'/.mhonarc.lck'; $exitcode = system($cmd); $exitcode = $exitcode / 256; } if ($exitcode) { do_log('err',"Command $cmd failed with exit code $exitcode"); } } } } =pod =head2 sub mail2arc(STRING $file,STRING $listname,STRING $robot,STRING $yyyy,STRING $mm,STRING $dd,STRING $hh,STRING $min,STRING $ss) Archives one message into one list archives directory. =head3 Arguments =over =item * I<$file>: a character string containing the message filename. =item * I<$listname>: a character string containing the name of the list in which to archive the message =item * I<$robot>: a character string containing the name of the virtual robot hosting the list. =item * I<$yyyy>: a character string containing the year of the date when the message is archived (i.e. now) =item * I<$mm>: a character string containing the month of the date when the message is archived (i.e. now) =item * I<$dd>: a character string containing the day of the date when the message is archived (i.e. now) =item * I<$hh>: a character string containing the hour of the date when the message is archived (i.e. now) =item * I<$min>: a character string containing the minute of the date when the message is archived (i.e. now) =item * I<$ss>: a character string containing the second of the date when the message is archived (i.e. now) =back =head3 Return =over =item * I if something goes wrong. =back =head3 Calls =over =item * get_tag =item * save_idx =item * set_hidden_mode =item * unset_hidden_mode =item * List::get_arc_size =item * List::get_list_id =item * List::new =item * List::send_notify_to_owner =item * Log::do_log =item * tools::get_filename =item * tools::remove_dir =back =cut sub mail2arc { my ($file, $listname, $robot, $yyyy, $mm, $dd, $hh, $min, $ss) = @_; my $arcpath = $wwsconf->{'arc_path'}; my $newfile; my $list = new List($listname, $robot); unless (defined $list) { &do_log('err', 'Unknown list %s@%s', $listname, $robot); return undef; } my $tag = &get_tag($listname); if (($list->{'admin'}{'web_archive_spam_protection'} ne 'none') && ($list->{'admin'}{'web_archive_spam_protection'} ne 'cookie')) { &set_hidden_mode($tag); }else { &unset_hidden_mode(); } do_log('debug',"mail2arc $file for %s yyyy:$yyyy, mm:$mm dd:$dd hh:$hh min$min ss:$ss", $list->get_list_id()); # chdir($wwsconf->{'arc_path'}); if ($wwsconf->{'custom_archiver'}) { `$wwsconf->{'custom_archiver'} --list=$listname\@$robot --file=$queue/$file`; return 1; }else{ my $basedir = $arcpath.'/'.$list->get_list_id(); if (! -d $basedir) { unless (mkdir $basedir, 0775) { &do_log('err', 'Cannot create directory %s', $basedir); unless (&List::send_notify_to_listmaster('unable_to_create_dir',$robot,{'dir' => "$basedir"})) { &do_log('notice',"Unable to send notify 'unable_to_create_dir' to listmaster"); } } do_log('debug',"mkdir $basedir"); } ## Check quota if ($list->{'admin'}{'web_archive'}{'quota'}) { my $used = $list->get_arc_size("$arcpath") ; if ($used >= $list->{'admin'}{'web_archive'}{'quota'} * 1024){ &do_log('err',"archived::mail2arc : web_arc Quota exceeded for list $list->{'name'}"); unless ($list->send_notify_to_owner('arc_quota_exceeded',{'size' => $used})) { &do_log('notice',"Unable to send notify 'arc_quota_exceeded' to $list->{'name'} owner"); } return undef; } if ($used >= ($list->{'admin'}{'web_archive'}{'quota'} * 1024 * 0.95)){ &do_log('err',"archived::mail2arc : web_arc Quota exceeded for list $list->{'name'}"); unless ($list->send_notify_to_owner('arc_quota_95',{'size' => $used, 'rate' => int($used * 100 / ($list->{'admin'}{'web_archive'}{'quota'} * 1024 ))})) { &do_log('notice',"Unable to send notify 'arc_quota_95' to $list->{'name'} owner"); } } } my $monthdir = $basedir."/$yyyy-$mm"; if (! -d $monthdir) { unless (mkdir ($monthdir, 0775)) { &do_log('err', 'Cannot create directory %s', $monthdir); return undef; } do_log('debug',"mkdir $arcpath/%s/$yyyy-$mm", $list->get_list_id()); if ($list->{'admin'}{'web_archive'}{'max_month'}){ # maybe need to remove some old archive if (opendir DIR,$arcpath.'/'.$list->get_list_id()) { my @archives = (sort {$a cmp $b} grep (/^\d{4}-\d{2}/, readdir(DIR))); closedir DIR; my $nb_month = $#archives + 1 ; my $i = 0 ; while ( $nb_month > $list->{'admin'}{'web_archive'}{'max_month'}) { do_log('info',"removing $arcpath/%s/$archives[$i]", $list->get_list_id()); &tools::remove_dir ($arcpath.'/'.$list->get_list_id().'/'.$archives[$i]); $i ++; $nb_month --; } } } } my $arctxtdir = $monthdir."/arctxt"; if (! -d $arctxtdir) { unless (mkdir ($arctxtdir, 0775)) { &do_log('err', 'Cannot create directory %s', $arctxtdir); return undef; } do_log('debug',"mkdir $arctxtdir"); } ## copy the file in the arctxt and in "mhonarc -add" if( -f $monthdir."/index" ){ open(IDX,"<$monthdir/index") || fatal_err("couldn't read index for $listname"); $newfile = ; chomp($newfile); $newfile++; close IDX; }else{ ## recreate index file if needed and update it $newfile = &create_idx($monthdir) + 1; } # savee arctxt dump of original message. my $output = $Conf::Conf{'tmpdir'}.'/arc'.$newfile; open ORIG, '<', "$queue/$file" or fatal_err("couldn't open file $queue/$file"); open DUMP, '>', "$arctxtdir/$newfile" or fatal_err("couldn't open file $arctxtdir/$newfile"); print DUMP join('', ); close ORIG; close DUMP; # prepare a temporary file with clean message content (htlm parts are cleaned) my $safe = Archive::clean_archived_message( $robot, "$arctxtdir/$newfile", $output); unless ($safe) { do_log('err',"Could not clean message, ignoring message"); next; } my $mhonarc_ressources = &tools::get_filename('etc',{},'mhonarc-ressources.tt2',$list->{'domain'}, $list); do_log ('debug',"calling $wwsconf->{'mhonarc'} for list %s", $list->get_list_id() ) ; # call mhonarc on cleaned message source to make clean htlm view of message my $cmd = "$wwsconf->{'mhonarc'} -add -modifybodyaddresses -addressmodifycode \'$ENV{'M2H_ADDRESSMODIFYCODE'}\' -rcfile $mhonarc_ressources -outdir $monthdir -definevars \"listname='$listname' hostname=$robot yyyy=$yyyy mois=$mm yyyymm=$yyyy-$mm wdir=$arcpath base=" . Conf::get_robot_conf($robot, 'wwsympa_url') . "/arc tag=$tag\" -umask $Conf{'umask'} < $output"; do_log('debug',"System call : %s",$cmd); my $exitcode = system($cmd); $exitcode = $exitcode / 256; # Delete temporary file containing escaped HTML. if ($output && -f $output) { unlink $output; } ## Remove lock if required if ($exitcode == 75) { &do_log('notice', 'Removing lock directory %s', $monthdir.'/.mhonarc.lck'); rmdir $monthdir.'/.mhonarc.lck'; $exitcode = system($cmd); $exitcode = $exitcode / 256; } if ($exitcode) { do_log('err',"Command $cmd failed with exit code $exitcode"); } &save_idx("$monthdir/index",$newfile); } } =pod =head2 sub set_hidden_mode(STRING $tag) Sets the value of $ENV{'M2H_ADDRESSMODIFYCODE'} and $ENV{'M2H_MODIFYBODYADDRESSES'} =head3 Arguments =over =item * I<$tag> a character string (containing the result of get_tag($listname)) =back =head3 Return =over =item * I =back =head3 Calls =over =item * none =back =cut sub set_hidden_mode { my $tag = shift; ## tag is used as variable elements in tags to prevent message contents to be parsed ## $ENV{'M2H_MODIFYBODYADDRESSES'} à positionner si le corps du message est parse $ENV{'M2H_ADDRESSMODIFYCODE'} = "s|^([^\@]+)\@([^\@]+)\$|\($tag\%hidden_head\%$tag\)\$1\($tag\%hidden_at\%$tag\)\$2\($tag\%hidden_end\%$tag\)|g"; $ENV{'M2H_MODIFYBODYADDRESSES'} = 1; } =pod =head2 sub unset_hidden_mode() Empties $ENV{'M2H_ADDRESSMODIFYCODE'}. =head3 Arguments =over =item * I =back =head3 Return =over =item * I =back =head3 Calls =over =item * none =back =cut sub unset_hidden_mode { ## Be carefull, the .mhonarc.db file keeps track of previous M2H_ADDRESSMODIFYCODE setup $ENV{'M2H_ADDRESSMODIFYCODE'} = ''; } =pod =head2 sub save_idx(STRING $index,STRING $lst) Saves the archives index file =head3 Arguments =over =item * I<$index>, a string corresponding to the file name to which save an index. =item * I<$lst>, a character string =back =head3 Return =over =item * I =back =head3 Calls =over =item * none =back =cut sub save_idx { my ($index,$lst) = @_; # &do_log('notice', "save_idx($index,$lst)"); open(INDEXF,">$index") || fatal_err("couldn't overwrite index $index"); print INDEXF "$lst\n"; close INDEXF; # do_log('debug',"last arc entry for $index is $lst"); } ## Create the 'index' file for one archive subdir sub create_idx { my $arc_dir = shift; ## corresponds to the yyyy-mm directory my $arc_txt_dir = $arc_dir.'/arctxt'; unless (opendir (DIR, $arc_txt_dir)) { &do_log('err', "Failed to open directory '$arc_txt_dir'"); return undef; } my @files = (sort { $a <=> $b;} grep(/^\d+$/,(readdir DIR ))) ; my $index = $files[$#files]; &save_idx($arc_dir.'/index', $index); closedir DIR; return $index; } =pod =head2 sub get_tag(STRING $listname) Returns a tag derived from the listname. =head3 Arguments =over =item * I<$listname>, a character string correspondiong to the list name. =back =head3 Return =over =item * I, corresponding to the 10 last characters of a 32 bytes string containing the MD5 digest of the concatenation of the following strings (in this order): =over 4 =item - the cookie config parameter =item - a slash: "/" =item - the I<$listname> argument =back =head3 Calls =over =item * Digest::MD5::md5_hex =back =cut sub get_tag { my $listname = shift; return (substr(Digest::MD5::md5_hex(join('/', $Conf{'cookie'}, $listname)), -10)) ; } =pod =head1 AUTHORS =over =item * Serge Aumont =item * Olivier Salaun =back =cut sympa-6.1.24~dfsg/wwsympa/bounced.pl.in000066400000000000000000000547541246372670100201160ustar00rootroot00000000000000#!--PERL-- # bounced.pl - This script runs as a daemon ; it does the incoming # non-delivery reports analysis and storage # RCS Identication ; $Revision: 11009 $ ; $Date: 2014-06-18 09:54:30 +0200 (mer. 18 juin 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . ## Worl Wide Sympa is a front-end to Sympa Mailing Lists Manager ## Copyright Comite Reseau des Universites ## Patch 2001.07.24 by nablaphi ## Change the Getopt::Std to Getopt::Long ## Options : F -> do not detach TTY ## : d -> debug -d is equiv to -dF ## Now, it is impossible to use -dF but you have to write it -d -F use lib '--modulesdir--'; use strict; use FileHandle; use List; use Conf; use Log; use mail; #use Getopt::Std; use Getopt::Long; use POSIX; use tt2; use tools; use Sympa::Constants; ## Equivalents relative to RFC 1893 my %equiv = ( "user unknown" => '5.1.1', "receiver not found" => '5.1.1', "the recipient name is not recognized" => '5.1.1', "sorry, no mailbox here by that name" => '5.1.1', "utilisateur non recens\xE9 dans le carnet d'adresses public" => '5.1.1', "unknown address" => '5.1.1', "unknown user" => '5.1.1', "550" => '5.1.1', "le nom du destinataire n'est pas reconnu" => '5.1.1', "user not listed in public name & address book" => '5.1.1', "no such address" => '5.1.1', "not known at this site." => '5.1.1', "user not known" => '5.1.1', "user is over the quota. you can try again later." => '4.2.2', "quota exceeded" => '4.2.2', "write error to mailbox, disk quota exceeded" => '4.2.2', "user mailbox exceeds allowed size" => '4.2.2', "insufficient system storage" => '4.2.2', "User's Disk Quota Exceeded:" => '4.2.2'); use Bounce; use wwslib; #getopts('dF'); ## Check options my %options; unless (&GetOptions(\%main::options, 'debug|d','log_level=s','foreground|F')) { &fatal_err("Unknown options."); } # $main::options{'debug2'} = 1 if ($main::options{'debug'}); if ($main::options{'debug'}) { $main::options{'log_level'} = 2 unless ($main::options{'log_level'}); } $main::options{'foreground'} = 1 if ($main::options{'debug'}); $main::options{'log_to_stderr'} = 1 if ($main::options{'debug'} || $main::options{'foreground'}); my $wwsympa_conf = Sympa::Constants::WWSCONFIG; my $sympa_conf_file = Sympa::Constants::CONFIG; my $daemon_name = &Log::set_daemon($0); my $ip = $ENV{'REMOTE_HOST'}; my $wwsconf = {}; # Load WWSympa configuration unless ($wwsconf = &wwslib::load_config($wwsympa_conf)) { print STDERR 'unable to load config file'; exit; } # Load sympa.conf unless (Conf::load($sympa_conf_file)) { &fatal_err("Unable to load sympa configuration, file $sympa_conf_file has errors."); } unshift @INC, $wwsconf->{'wws_path'}; ## Check databse connectivity unless (&List::check_db_connect()) { &fatal_err('Database %s defined in sympa.conf has not the right structure or is unreachable.', $Conf{'db_name'}); } ## Put ourselves in background if not in debug mode. unless ($main::options{'debug'} || $main::options{'foreground'}) { open(STDERR, ">> /dev/null"); open(STDOUT, ">> /dev/null"); if (open(TTY, "/dev/tty")) { ioctl(TTY, 0x20007471, 0); # XXX s/b &TIOCNOTTY # ioctl(TTY, &TIOCNOTTY, 0); close(TTY); } setpgrp(0, 0); if ((my $child_pid = fork) != 0) { print STDOUT "Starting bounce daemon, pid $_\n"; exit(0); } } ## If process is running in foreground, don't write STDERR to a dedicated file my $options; $options->{'stderr_to_tty'} = 1 if ($main::options{'foreground'}); ## Create and write the PID file tools::write_pid('bounced', $$, $options); if ($main::options{'log_level'}) { &Log::set_log_level($main::options{'log_level'}); do_log('info', "Configuration file read, log level set using options : $main::options{'log_level'}"); }else{ &Log::set_log_level($Conf{'log_level'}); do_log('info', "Configuration file read, default log level $Conf{'log_level'}"); } $wwsconf->{'log_facility'}||= $Conf{'syslog'}; do_openlog($wwsconf->{'log_facility'}, $Conf{'log_socket_type'}, 'bounced'); ## Set the UserID & GroupID for the process $( = $) = (getgrnam(Sympa::Constants::GROUP))[2]; $< = $> = (getpwnam(Sympa::Constants::USER))[2]; ## Required on FreeBSD to change ALL IDs(effective UID + real UID + saved UID) &POSIX::setuid((getpwnam(Sympa::Constants::USER))[2]); &POSIX::setgid((getgrnam(Sympa::Constants::GROUP))[2]); ## Check if the UID has correctly been set (usefull on OS X) unless (($( == (getgrnam(Sympa::Constants::GROUP))[2]) && ($< == (getpwnam(Sympa::Constants::USER))[2])) { &fatal_err("Failed to change process userID and groupID. Note that on some OS Perl scripts can't change their real UID. In such circumstances Sympa should be run via SUDO."); } ## Sets the UMASK umask(oct($Conf{'umask'})); ## Change to list root unless (chdir($Conf{'home'})) { &do_log('info','Unable to change directory'); exit (-1); } my $pinfo = &List::_apply_defaults(); do_log('notice', "bounced Started"); ## Catch SIGTERM, in order to exit cleanly, whenever possible. $SIG{'TERM'} = 'sigterm'; my $end = 0; my $queue = $Conf{'queuebounce'}; do_log('debug','starting infinite loop'); ## infinite loop scanning the queue (unless a sig TERM is received while (!$end) { ## this sleep is important to be raisonably sure that sympa is not currently ## writting the file this deamon is openning. sleep $Conf{'sleep'}; &List::init_list_cache(); unless (opendir(DIR, $queue)) { fatal_err("Can't open dir %s: %m", $queue); ## No return. } my @files = (sort grep(!/^(\.|T\.|BAD\-)/, readdir DIR )); closedir DIR; foreach my $file (@files) { last if $end; next unless (-f "$queue/$file"); next if $file =~ /\.(lock|NFSLock)\z/; # Skip lock. unless ($file =~ /^(\S+)\.\d+\.\d+$/) { my @s = stat("$queue/$file"); if (POSIX::S_ISREG($s[2])) { do_log ('notice',"Ignoring file $queue/$file because unknown format"); &ignore_bounce({'file' => $file, 'queue' => $queue, }); unlink("$queue/$file"); } next; } if ($file =~ /^(\S+)\.\d+\.\d+$/) { my ($listname, $robot) = split(/\@/,$1); $robot ||= &List::search_list_among_robots($listname); } my ($listname, $robot) = split(/\@/,$1); $robot ||= &List::search_list_among_robots($listname); if (-z "$queue/$file") { do_log ('notice',"Ignoring file $queue/$file because empty file"); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); unlink("$queue/$file"); } unless (open BOUNCE, "$queue/$file") { &do_log('notice', 'Could not open %s/%s: %s', $queue, $file, $!); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); next; } my $parser = new MIME::Parser; $parser->output_to_core(1); my $entity = $parser->read(\*BOUNCE); my $head = $entity->head; ## Some MTAs decorate To: field of DSN as "mailbox
". ## Pick address only. my $to; my $to_header = $head->get('to', 0); if ($to_header) { my @to = Mail::Address->parse($to_header); if (@to and $to[0] and $to[0]->address) { $to = $to[0]->address; } } close BOUNCE ; my $who; &do_log('debug', 'bounce for :%s: Conf{bounce_email_prefix}=%sxx',$to,$Conf{'bounce_email_prefix'}); if ($to and $to =~ /^$Conf{'bounce_email_prefix'}\+(.*)\@(.*)$/) { #VERP in use my $local_part = $1; my $robot = $2; my $unique ; if ($local_part =~ /^(.*)(\=\=([wr]))$/) { $local_part = $1; $unique = $2; } $local_part =~ s/\=\=a\=\=/\@/ ; $local_part =~ /^(.*)\=\=(.*)$/ ; $who = $1; $listname = $2 ; &do_log('debug', 'VERP in use : bounce related to %s for list %s@%s',$who,$listname,$robot); if ($unique =~ /[wr]/) { # in this case the bounce result from a remind or a welcome message ;so try to remove the subscriber &do_log('debug', "VERP for a service message, try to remove the subscriber"); my $list = new List ($listname, $robot); unless($list) { do_log('notice','Skipping bouncefile %s for unknown list %s@%s',$file,$listname,$robot); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); unlink("$queue/$file"); next; } my $result =$list->check_list_authz('del','smtp', {'sender' => $Conf{'listmasters'}[0], 'email' => $who}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); if ($action =~ /do_it/i) { if ($list->is_user($who)) { my $u = $list->delete_user('users' => [$who], 'exclude' =>' 1'); do_log ('notice',"$who has been removed from $listname because welcome message bounced"); &Log::db_log({'robot' => $list->{'domain'},'list' => $list->{'name'},'action' => 'del', 'target_email' => $who,'status' => 'error','error_type' => 'welcome_bounced', 'daemon' => 'bounced'}); if ($action =~ /notify/) { unless ($list->send_notify_to_owner('automatic_del', {'who' => $who, 'by' => 'bounce manager', 'reason' => 'welcome'})) { &wwslog('err',"Unable to send notify 'automatic_del' to $list->{'name'} list owner"); } } } }else { do_log ('notice',"Unable to remove $who from $listname (welcome message bounced but del is closed)"); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); } unlink("$queue/$file"); next; } } if(($head->get('Content-type') =~ /multipart\/report/) && ($head->get('Content-type') =~ /report\-type\=feedback-report/)) { # this case a report Email Feedback Reports http://www.shaftek.org/publications/drafts/abuse-report/draft-shafranovich-feedback-report-01.txt mainly use by AOL do_log ('notice',"processing Email Feedback Report"); my @parts = $entity->parts(); my $original_rcpt; my $user_agent; my $version; my $feedback_type = ''; my $listname; my $robot; foreach my $p (@parts) { my $h = $p->head(); my $content = $h->get('Content-type'); next if ($content =~ /text\/plain/i); if ($content =~ /message\/feedback-report/) { my @report = split(/\n/, $p->bodyhandle->as_string()); foreach my $line (@report) { $feedback_type = 'abuse' if ($line =~ /Feedback\-Type\:\s*abuse/i); if ($line =~ /Feedback\-Type\:\s*(.*)/i) { $feedback_type = $1; } if ($line =~ /User\-Agent\:\s*(.*)/i) { $user_agent = $1; } if ($line =~ /Version\:\s*(.*)/i) { $version = $1; } my $email_regexp = &tools::get_regexp('email'); if ($line =~ /Original\-Rcpt\-To\:\s*($email_regexp)\s*$/i) { $original_rcpt = $1; chomp $original_rcpt; } } }elsif ($content =~ /message\/rfc822/) { # do_log ('notice',"xxx processing Email Feedback Report : message/rfc822 part"); my @subparts = $p->parts(); foreach my $subp (@subparts) { # do_log ('notice',"xxx subparts : subps"); my $subph = $subp->head; $listname = $subph->get('X-Loop'); } # do_log ('notice',"xxx processing Email Feedback Report : extract listname $listname"); } } my $forward ; ## RFC compliance remark: We do something if there is an abuse or an unsubscribe request. ## We don't throw an error if we find another kind of feedback (fraud, miscategorized, not-spam, virus or other) ## but we don't take action if we meet them yet. This is to be done, if relevant. if (($feedback_type =~ /(abuse|opt-out|opt-out-list|fraud|miscategorized|not-spam|virus|other)/i) && (defined $version) && (defined $user_agent)) { do_log ('debug',"Email Feedback Report: $listname feedback-type: $feedback_type"); if (defined $original_rcpt) { do_log ('debug',"Recognized user : $original_rcpt list"); my @lists; if (( $feedback_type =~ /(opt-out-list|abuse)/i ) && (defined $listname)) { $listname = lc($listname); chomp $listname ; $listname =~ /(.*)\@(.*)/; $listname = $1; $robot = $2; my $list = new List ($listname, $robot); unless($list) { do_log('err','Skipping Feedback Report for unknown list %s@%s',$file,$listname,$robot); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); unlink("$queue/$file"); next; } push @lists, $list; }elsif( $feedback_type =~ /opt-out/ && (defined $original_rcpt)){ @lists = &List::get_which($original_rcpt,$robot,'member'); }else { &do_log('notice','Ignoring Feedback Report %s : Nothing to do for this feedback type.(feedback_type:%s, original_rcpt:%s, listname:%s)',$file, $feedback_type, $original_rcpt, $listname ); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); } foreach my $list (@lists){ my $result =$list->check_list_authz('unsubscribe','smtp',{'sender' => $original_rcpt}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); if ($action =~ /do_it/i) { if ($list->is_user($original_rcpt)) { my $u = $list->delete_user('users' => [$original_rcpt], 'exclude' =>' 1'); do_log ('notice',"$original_rcpt has been removed from %s because abuse feedback report",$list->name); unless ($list->send_notify_to_owner('automatic_del',{'who' => $original_rcpt, 'by' => 'listmaster'})) { &do_log('notice',"Unable to send notify 'automatic_del' to $list->{'name'} list owner"); } }else{ do_log('err','Ignore Feedback Report %s for list %s@%s : user %s not subscribed',$file,$list->name,$robot,$original_rcpt); unless ($list->send_notify_to_owner('warn-signoff',{'who' => $original_rcpt})) { &do_log('notice',"Unable to send notify 'warn-signoff' to $list->{'name'} list owner"); } } }else{ $forward = 'request'; do_log('err','Ignore Feedback Report %s for list %s@%s : user %s is not allowed to unsubscribe',$file,$list->name,$robot,$original_rcpt); } } }else{ do_log ('err','Ignoring Feedback Report %s : Unknown Original-Rcpt-To field. Can\'t do anything. (feedback_type:%s, listname:%s)',$file, $feedback_type, $listname ); &ignore_bounce({'file' => $file, 'notify' => 1, 'robot' => $robot, 'queue' => $queue, 'error' => "Unknown Original-Rcpt-To field (feedback_type:$feedback_type, listname:$listname)", }); } }else{ do_log ('err','Ignoring Feedback Report %s : Unknown format (feedback_type:%s, original_rcpt:%s, listname:%s)',$file, $feedback_type, $original_rcpt, $listname ); &ignore_bounce({'file' => $file, 'notify' => 1, 'robot' => $robot, 'queue' => $queue, 'error' => "Unknown format (feedback_type:$feedback_type, original_rcpt:$original_rcpt, listname:$listname)", }); } if (-f "$queue/$file") { unlink("$queue/$file"); } next; } # else (not welcome or remind) my $list = new List ($listname, $robot); if (! $list) { &do_log('err','Skipping bouncefile %s for unknown list %s@%s',$file,$listname,$robot); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); unlink("$queue/$file"); next; }else{ &do_log('debug',"Processing bouncefile $file for list $listname"); unless (open BOUNCE, "$queue/$file") { &do_log('notice', 'Could not open %s/%s: %s', $queue, $file, $!); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); next; } my (%hash, $from); my $bounce_dir = $list->get_bounce_dir(); ## RFC1891 compliance check my $bounce_count = &rfc1891(\*BOUNCE, \%hash, \$from); unless ($bounce_count) { close BOUNCE; unless (open BOUNCE, "$queue/$file") { &do_log('notice', 'Could not open %s/%s: %s', $queue, $file, $!); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); next; } ## Analysis of bounced message &anabounce(\*BOUNCE, \%hash, \$from); } close BOUNCE; ## Bounce directory if (! -d $bounce_dir) { unless (mkdir $bounce_dir, 0777) { &List::send_notify_to_listmaster('bounce_intern_error',$Conf{'domain'},{'error' => "Failed to list create bounce directory $bounce_dir"}); &do_log('err', 'Could not create %s: %s bounced dir, check bounce_path in wwsympa.conf', $bounce_dir, $!); exit; } } my $adr_count; ## Bouncing addresses while (my ($rcpt, $status) = each %hash) { $adr_count++; my $bouncefor = $who; $bouncefor ||= $rcpt; next unless (&store_bounce ($bounce_dir,$file,$bouncefor)); next unless (&update_subscriber_bounce_history($list, $rcpt, $bouncefor, &canonicalize_status ($status))); } ## No address found in the bounce itself unless ($adr_count) { if ( $who ) { # rcpt not recognized in the bounce but VERP was used &store_bounce($bounce_dir, $file, $who); &update_subscriber_bounce_history($list, 'unknown', $who); # status is undefined }else{ # no VERP and no rcpt recognized my $escaped_from = &tools::escape_chars($from); &do_log('info', 'error: no address found in message from %s for list %s',$from, $list->{'name'}); &ignore_bounce({'file' => $file, 'robot' => $robot, 'queue' => $queue, }); next; } } } unless (unlink("$queue/$file")) { do_log ('err',"Could not remove $queue/$file ; $0 might NOT be running with the right UID or file was not created with the right UID"); &ignore_bounce({'file' => $file, 'notify' => 1, 'robot' => $robot, 'queue' => $queue, 'error' => "Could not remove $queue/$file ; $0 might NOT be running with the right UID or file was not created with the right UID", }); last; } } ## Free zombie sendmail processes &mail::reaper; } do_log('notice', 'bounced exited normally due to signal'); tools::remove_pid('bounced', $$); exit(0); ## When we catch SIGTERM, just change the value of the loop ## variable. sub sigterm { $end = 1; } ## copy the bounce to the appropriate filename sub store_bounce { my $bounce_dir = shift; my $file= shift; my $rcpt=shift; do_log('debug', 'store_bounce(%s,%s,%s)', $bounce_dir,$file,$rcpt); my $queue = $Conf{'queuebounce'}; #Store bounce unless (open BOUNCE, "$queue/$file") { &do_log('notice', 'Could not open %s/%s: %s', $queue, $file, $!); &ignore_bounce({'file' => $file, 'queue' => $queue, }); return undef; } my $filename = &tools::escape_chars($rcpt); unless (open ARC, ">$bounce_dir/$filename") { &do_log('notice', "Unable to write $bounce_dir/$filename"); &ignore_bounce({'file' => $file, 'queue' => $queue, }); return undef; } print ARC ; close ARC; close BOUNCE; } ## Set error message to a status RFC1893 compliant sub canonicalize_status { my $status =shift; if ($status !~ /^\d+\.\d+\.\d+$/) { if ($equiv{$status}) { $status = $equiv{$status}; }else { return undef; } } return $status; } ## update subscriber information # $bouncefor : the email address the bounce is related for (may be extracted using verp) # $rcpt : the email address recognized in the bounce itself. In most case $rcpt eq $bouncefor sub update_subscriber_bounce_history { my $list = shift; my $rcpt = shift; my $bouncefor = shift; my $status = shift; &do_log ('debug','&update_subscriber_bounce_history (%s,%s,%s,%s)',$list->{'name'},$rcpt,$bouncefor,$status); my $first = my $last = time; my $count = 0; my $user = $list->get_subscriber($bouncefor); unless ($user) { &do_log ('notice', 'Subscriber not found in list %s : %s', $list->{'name'}, $bouncefor); return undef; } if ($user->{'bounce'} =~ /^(\d+)\s\d+\s+(\d+)/) { ($first, $count) = ($1, $2); } $count++; if ($rcpt ne $bouncefor) { &do_log('notice','Bouncing address identified with VERP : %s / %s', $rcpt, $bouncefor); &do_log ('debug','&update_subscribe (%s, bounce-> %s %s %s %s,bounce_address->%s)',$bouncefor,$first,$last,$count,$status,$rcpt); $list->update_user($bouncefor,{'bounce' => "$first $last $count $status", 'bounce_address' => $rcpt}); &Log::db_log({'robot' => $list->{'domain'},'list' => $list->{'name'},'action' => 'get_bounce','parameters' => "address=$rcpt", 'target_email' => $bouncefor,'msg_id' => '','status' => 'error','error_type' => $status, 'daemon' => 'bounced'}); }else{ $list->update_user($bouncefor,{'bounce' => "$first $last $count $status"}); &do_log('notice','Received bounce for email address %s, list %s', $bouncefor, $list->{'name'}); &Log::db_log({'robot' => $list->{'domain'},'list' => $list->{'name'},'action' => 'get_bounce', 'target_email' => $bouncefor,'msg_id' => '','status' => 'error','error_type' => $status, 'daemon' => 'bounced'}); } } ## If bounce can't be handled correctly, saves it to the "bad" subdirectory of the bounce spool and notifies listmaster. sub ignore_bounce { my $param = shift; my $file = $param -> {'file'}; my $notify = $param -> {'notify'}; my $error = $param -> {'error'}; my $queue = $param -> {'queue'}; my $robot = $param -> {'robot'}; $notify |= 0; &tools::save_to_bad({ 'file' => $file, 'hostname' => $robot, 'queue' => $queue, }); if ($notify) { unless (&List::send_notify_to_listmaster('bounce_management_failed',$robot,{'file' => $file, 'bad' => "$queue/bad", 'error' => $error, })) { &do_log('notice',"Unable to send notify 'bounce_management_failed' to listmaster"); } } } sympa-6.1.24~dfsg/wwsympa/cookielib.pm000066400000000000000000000122731246372670100200210ustar00rootroot00000000000000# cookielib.pm - This module includes functions managing HTTP cookies in Sympa # RCS Identication ; $Revision: 10713 $ ; $Date: 2014-05-23 11:18:40 +0200 (ven. 23 mai 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package cookielib; use strict "vars"; use Digest::MD5; use POSIX; use CGI::Cookie; use Log; ## Generic subroutine to set a cookie sub generic_set_cookie { my %param = @_; my %cookie_param; foreach my $p ('name','value','expires','domain','path') { $cookie_param{'-'.$p} = $param{$p}; ## CGI::Cookie expects -param => value } if ($cookie_param{'-domain'} eq 'localhost') { $cookie_param{'-domain'} = ''; } my $cookie = new CGI::Cookie(%cookie_param); ## Send cookie to the client printf "Set-Cookie: %s\n", $cookie->as_string; return 1; } # Sets an HTTP cookie to be sent to a SOAP client # OBSOLETED: Use SympaSession::soap_cookie2(). sub set_cookie_soap { my ($session_id,$http_domain,$expire) = @_ ; my $cookie; my $value; # WARNING : to check the cookie the SOAP services does not gives # all the cookie, only it's value so we need ':' $value = $session_id; ## With set-cookie2 max-age of 0 means removing the cookie ## Maximum cookie lifetime is the session $expire ||= 600; ## 10 minutes if ($http_domain eq 'localhost') { $cookie = sprintf "%s=%s; Path=/; Max-Age=%s", 'sympa_session', $value, $expire; }else { $cookie = sprintf "%s=%s; Domain=%s; Path=/; Max-Age=%s", 'sympa_session', $value, $http_domain, $expire;; } ## Return the cookie value return $cookie; } ## returns Message Authentication Check code sub get_mac { my $email = shift ; my $secret = shift ; &do_log('debug3', "get_mac($email, $secret)"); unless ($secret) { &do_log('err', 'get_mac : failure missing server secret for cookie MD5 digest'); return undef; } unless ($email) { &do_log('err', 'get_mac : failure missing email address or cookie MD5 digest'); return undef; } my $md5 = new Digest::MD5; $md5->reset; $md5->add($email.$secret); return substr( unpack("H*", $md5->digest) , -8 ); } sub set_cookie_extern { my ($secret,$http_domain,%alt_emails) = @_ ; my $expiration; my $cookie; my $value; my @mails ; foreach my $mail (keys %alt_emails) { my $string = $mail.':'.$alt_emails{$mail}; push(@mails,$string); } my $emails = join(',',@mails); $value = sprintf '%s&%s',$emails,&get_mac($emails,$secret); if ($http_domain eq 'localhost') { $http_domain=""; } $cookie = new CGI::Cookie (-name => 'sympa_altemails', -value => $value, -expires => '+1y', -domain => $http_domain, -path => '/' ); ## Send cookie to the client printf "Set-Cookie: %s\n", $cookie->as_string; #do_log('notice',"set_cookie_extern : %s",$cookie->as_string); return 1; } ############################### # Subroutines to read cookies # ############################### ## Generic subroutine to get a cookie value sub generic_get_cookie { my $http_cookie = shift; my $cookie_name = shift; if ($http_cookie =~/\S+/g) { my %cookies = parse CGI::Cookie($http_cookie); foreach (keys %cookies) { my $cookie = $cookies{$_}; next unless ($cookie->name eq $cookie_name); return ($cookie->value); } } return (undef); } ## Returns user information extracted from the cookie sub check_cookie { my $http_cookie = shift; my $secret = shift; my $user = &generic_get_cookie($http_cookie, 'sympauser'); my @values = split /:/, $user; if ($#values >= 1) { my ($email, $mac, $auth) = @values; $auth ||= 'classic'; ## Check the MAC if (&get_mac($email,$secret) eq $mac) { return ($email, $auth); } } return undef; } sub check_cookie_extern { my ($http_cookie,$secret,$user_email) = @_; my $extern_value = &generic_get_cookie($http_cookie, 'sympa_altemails'); if ($extern_value =~ /^(\S+)&(\w+)$/) { return undef unless (&get_mac($1,$secret) eq $2) ; my %alt_emails ; foreach my $element (split(/,/,$1)){ my @array = split(/:/,$element); $alt_emails{$array[0]} = $array[1]; } my $e = lc($user_email); unless ($alt_emails{$e}) { return undef; } return (\%alt_emails); } return undef } 1; sympa-6.1.24~dfsg/wwsympa/icons/000077500000000000000000000000001246372670100166315ustar00rootroot00000000000000sympa-6.1.24~dfsg/wwsympa/icons/Makefile.am000066400000000000000000000027461246372670100206760ustar00rootroot00000000000000# $Id$ # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . iconsdir = $(staticdir)/icons icons_DATA = back.png \ begin.png \ binary.png \ crosshairs.png \ down.png \ end.png \ favicon_sympa.png \ folder.open.png \ folder.png \ h.png \ image2.png \ junk.png \ left.png \ link.png \ locked.png \ logo-s-lock.png \ logo-s.png \ logo_sympa.png \ movie.png \ position.png \ right.png \ sound1.png \ sv.png \ text.png \ top.png \ unknown.png EXTRA_DIST = $(icons_DATA) sympa-6.1.24~dfsg/wwsympa/icons/Makefile.in000066400000000000000000000260161246372670100207030ustar00rootroot00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # $Id$ # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = ../.. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd INSTALL = @INSTALL@ install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = wwsympa/icons DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; am__installdirs = "$(DESTDIR)$(iconsdir)" iconsDATA_INSTALL = $(INSTALL_DATA) DATA = $(icons_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CAT = @CAT@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFIG = @CONFIG@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EXEEXT = @EXEEXT@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GROUP = @GROUP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MAKEMAP = @MAKEMAP@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ NEWALIASES = @NEWALIASES@ NEWALIASES_ARG = @NEWALIASES_ARG@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ POD2MAN = @POD2MAN@ POSTALIAS = @POSTALIAS@ POSTMAP = @POSTMAP@ POSTMAP_ARG = @POSTMAP_ARG@ SED = @SED@ SENDMAIL_ALIASES = @SENDMAIL_ALIASES@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ USER = @USER@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ VIRTUAL_ALIASES = @VIRTUAL_ALIASES@ WWSCONFIG = @WWSCONFIG@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ arcdir = @arcdir@ bindir = @bindir@ bouncedir = @bouncedir@ build_alias = @build_alias@ confdir = @confdir@ datadir = @datadir@ datarootdir = @datarootdir@ defaultdir = @defaultdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ execcgidir = @execcgidir@ expldir = @expldir@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ initdir = @initdir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lockdir = @lockdir@ mailtemplatedir = @mailtemplatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ modulesdir = @modulesdir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ piddir = @piddir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ scriptdir = @scriptdir@ sharedstatedir = @sharedstatedir@ spooldir = @spooldir@ staticdir = @staticdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ webtemplatedir = @webtemplatedir@ iconsdir = $(staticdir)/icons icons_DATA = back.png \ begin.png \ binary.png \ crosshairs.png \ down.png \ end.png \ favicon_sympa.png \ folder.open.png \ folder.png \ h.png \ image2.png \ junk.png \ left.png \ link.png \ locked.png \ logo-s-lock.png \ logo-s.png \ logo_sympa.png \ movie.png \ position.png \ right.png \ sound1.png \ sv.png \ text.png \ top.png \ unknown.png EXTRA_DIST = $(icons_DATA) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign wwsympa/icons/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign wwsympa/icons/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh uninstall-info-am: install-iconsDATA: $(icons_DATA) @$(NORMAL_INSTALL) test -z "$(iconsdir)" || $(mkdir_p) "$(DESTDIR)$(iconsdir)" @list='$(icons_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(iconsDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(iconsdir)/$$f'"; \ $(iconsDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(iconsdir)/$$f"; \ done uninstall-iconsDATA: @$(NORMAL_UNINSTALL) @list='$(icons_DATA)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(iconsdir)/$$f'"; \ rm -f "$(DESTDIR)$(iconsdir)/$$f"; \ done tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(iconsdir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-iconsDATA install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-iconsDATA uninstall-info-am .PHONY: all all-am check check-am clean clean-generic distclean \ distclean-generic distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-exec \ install-exec-am install-iconsDATA install-info install-info-am \ install-man install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ uninstall-am uninstall-iconsDATA uninstall-info-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: sympa-6.1.24~dfsg/wwsympa/icons/back.png000066400000000000000000000004071246372670100202400ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTEfff333C&o4tRNSN=PTbKGDH pHYs  ~tIME ܓy[IDATxe 0 ЀyG(ȹ4`񾎗p"t6`Pٗɡ(rclW43&effEN=$eT~&4æ?IENDB`sympa-6.1.24~dfsg/wwsympa/icons/begin.png000066400000000000000000000003121246372670100204170ustar00rootroot00000000000000PNG  IHDR2o5tIME  *ސne pHYs B4gAMA aPLTEU~tRNS@f:IDATxmλ 0 CAkheC H s?KҡJ*t3B[]مkGIENDB`sympa-6.1.24~dfsg/wwsympa/icons/binary.png000066400000000000000000000004161246372670100206240ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE̙333L>tRNSN=PTbKGDH pHYs  ~tIME #Z%bIDATx} 0  =h J #RhN〡ʅ cZn" Xs1ݹp]˿ut]1({ٝ gؓ@,2^|IENDB`sympa-6.1.24~dfsg/wwsympa/icons/crosshairs.png000066400000000000000000000003321246372670100215150ustar00rootroot00000000000000PNG  IHDRyM PLTEtRNS@fbKGDH pHYs  tIME0EtEXtCommentCreated with The GIMPd%n!IDATcd`PH. 5PƣL?V87IENDB`sympa-6.1.24~dfsg/wwsympa/icons/down.png000066400000000000000000000003251246372670100203060ustar00rootroot00000000000000PNG  IHDR/TgAMA a PLTEw]btRNSN=PTbKGDH pHYs  ~tIME  )2IDATxc` ? $00I fJaC s'3IENDB`sympa-6.1.24~dfsg/wwsympa/icons/end.png000066400000000000000000000003601246372670100201040ustar00rootroot00000000000000PNG  IHDR2o5tIME  '1.L] pHYs B4gAMA a0PLTE/ݠ{tRNS@f6IDATxc`QDb HLS)` "12jjV tL{.X+IENDB`sympa-6.1.24~dfsg/wwsympa/icons/favicon_sympa.png000066400000000000000000000124071246372670100222010ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs{EntEXtSoftwarewww.inkscape.org<IDATx[yxUյ3!fHrAR'%( O|S_V+U_S_-vBkB2X+ 2+L!@$dI;q?Cr3p@k^k0"V0F{?/ / DDk48::^@ccO,nxYFZlРDY?Bm ܤX-pдW-Ec~>dG[!=WP"N:P$ RYJiѻw ex#.<? X@g'cC4琚Zk+IШUG ppQ(</-fXD>3 sO2b!]B!^؆?岟Kyϙ"e>d#@d( sAp&|J)1' Vp D"@,&" v{ F\}NDY[qr@8f`#yqn]BO&b D() .WPe9M`Wt 0`8^J,gxL2ySw3Ĝa_DE W")8Ǝ]OM϶Ijt¥*Ȩiy™ G3l @.DWӂ1Җk\Oy_:!9F1H~V}e"[bZgRSAJPz%8S.W*pROQ!06` qu#E2KVxL{b (**DJF\Vaq+r UMbyft@6`ejfk74 ?;|$f3֭@z:6pma6j&& *gfGfR͕@1J XlӜs^&#G{ ƙ&f6)@M^g>(+]%vN-/f&(~dj+'p |f"=9;6 X1 yyg))bT̷7#~%ݗ,4}i}%3aS\p@ #9<:U @DJ xQ࣏kƌ1M0jO 3i ~j`fOTࢋrXVM&O"C;~F>AD+:VE ׬1]"27Ƥ+W{*+>M+оKqw[4ui=CoO5ችO/P wD˖2p2"" J׸y%.@7G'yCfƷƧvp;oP -q,ChNAX՝8~܋="g5L+QÝH Ź7S 8Ain4!"rGޤxcWf~D01!"C_>qm[1_*[(u>|Nx~p5FSh)Fxr)נѣoj=GT3%?2XWAL4w ؀ v~װx:_eval\ľ1YuWޱiZG6<}o]VUH9 ߞW\ukh,:/ҿPU!MꙙxѰOnM_ϼߞ<ӫ{ mA^1>R >hz9cz r|k窻vWUHGz.sgz'R>BQK>OCԛ*$ǚ <>ts o?# I3^3}˱>U8oex.9J]pzK; ʸn oO-RroMe,p|ZT@ ){=Μꅮ#s7]ye(J_/f^>uY.H62ݞUx޴ԑ⪗ws ]g Yt^pr9lIyxneڿD.`⹕"rTF1v`U͒x3>G P^Fșt-oykboe?^iC?CP1; t4؅ rEo@m i^Z۬u~<5[h b!D`Q ԬXw|@ P^j(ph;1n@ Ȳ` q V8DIEHΆS <;ie+9ҿ % `c ZwE( K]~5. CWТ!|4@BGY-49 ^@P^Aw}# ǃ# z't#&cuoXTA,1M,aHB461麾7`0 XJ t;Qo3Hj]w `҃<5[:au'7ޏS!4Dړ$08ne~kvAoj>5C-JxW >D??1v+h"]Ot"DD5-A z_ͫwس Xg~| UƟU'2tw~ܩsWtCVʽ5w<MwhFx21Ɛ%U%/eTUT15P3=r-,$뗼h1xJ uk~}Vc=[fxaK`2݈)-YDÙl9$mkIYFum<]Ug;WQUMl7WB^kw"ZH@<"%ď1 ##yن(Xw}7Օh/Vph Nl{X GA!DA'$WA>(DZ[JҷO+˨7i85*92O[ M Ҁ&"|u1|ne`K@0<,CkԜ=MDqTT;t% 2^0xzNB xfز]v+(_)~D:A3B}mIp3bqUI׳26,U>=ao`H,u5-==l;@Vwg*c\x`MB-v]~rDWT-N!)\ bЌvUPHV`L_P}jYF~pN#7t ^!)g&؜8('j]F@2uX?QV8+a Y9V8@4#6?g%gӌ+v0-9~JW\Q%k3ae MA'ӏ俽,zyqpeI^B> 8^ zWQ%wyJr-1&xHy,).mh/($IENDB`sympa-6.1.24~dfsg/wwsympa/icons/folder.open.png000066400000000000000000000004321246372670100215510ustar00rootroot00000000000000PNG  IHDRdgAMA aPLTE̙f3333T PtRNSN=PTbKGDH pHYs  ~tIME  9kIDATx 0Eu4.b t6 P}&WAҪWkIԱ`ʾ eѧ6h8mF9Α8DIB9Ǹ:$Z4z\TIENDB`sympa-6.1.24~dfsg/wwsympa/icons/folder.png000066400000000000000000000003711246372670100206130ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE̙f3333{tRNSN=PTbKGDH pHYs  ~tIME  /JwMIDATxc` Ŧ `f䥀N@P< p 3U¨,a l. @A@H$KIENDB`sympa-6.1.24~dfsg/wwsympa/icons/h.png000066400000000000000000000005741246372670100175740ustar00rootroot00000000000000PNG  IHDR#8ObKGD pHYs  tIME15W IDATxQ@ D r>ԌyLJg~T &rɞDpb #G󴥊i]k99V if=R{t>-PF ~DN49dֽ<=?]h2F8:NIyjttH;ld ?)}R!'wi{8Wu]ҍ[$cC젯yTٿm.)HSΗ԰t7q~WGc5am_ބKrbAPMQh_89? MUijWeqDh(7<4& #L@"^~d3!̀N a Deg3lB0/)*$nl#r{S ntp$ ͉t|\JUAtTԠo9*doFya* aG/\e{9qrk7/BlnTPhg"s'Zfq~vחo#=m4( ~ 8 8lnUa|a גF^U+x گ׽`yсQ2tʃިU&&TE( ʴ .\-7QhCQ m]TDvy`Nq/_D(R5/TK֪8zgϡ\m@!ˊh4{Ҏ(c<5 dtُgh@mx~~|3۳HgGFZaw.__?HPcDf@oU 8iy;uo H 18| L!WZ-G۴\ݡro>|gp+{Riz,%&ytФ!9V vѐ-Q=Z6mxچݡy,#=D#"Xnp0볟 }r*J LZ2UUJȻ.B@ JW #/?֭rhIENDB`sympa-6.1.24~dfsg/wwsympa/icons/left.png000066400000000000000000000003261246372670100202720ustar00rootroot00000000000000PNG  IHDR/TgAMA a PLTEw]btRNSN=PTbKGDH pHYs  ~tIME  ߐ^3IDATxc`@```L`L#HP]HALJXZcIENDB`sympa-6.1.24~dfsg/wwsympa/icons/link.png000066400000000000000000000004161246372670100202750ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE33f333TEtRNSN=PTbKGDH pHYs  ~tIME  J_IDATxM1 @\!Y-tE7G !FsF,Fg{W$Vʰ>AYr!Z^v,/J̖I|5$fIENDB`sympa-6.1.24~dfsg/wwsympa/icons/locked.png000066400000000000000000000012611246372670100206000ustar00rootroot00000000000000PNG  IHDRl"gAMA aSPLTEEDCBA@??0!}}{|z y y x w v u u tqqponlkjkkjjhihhggffeddccbba``)a']#_okWoge{ms{wmsq u   u syws w}{ywusqokgec_ tRNSN=PTbKGDH pHYs  ~tIME  '9$IDATx]1 @ oV ;dzXD.U(Adrji}[>qnqK51"%P3wk3G&&Lg5b#H "ο6[o0*cXCƸ|a TRYQ^6ƹ3lN*[+(q #Jͧaj;tRNSN=PTbKGDH pHYs  ~tIME  k2IDATx͖}0)oӚ 'cT<nnPN`fө *?BG;A?F_7cs'xHm?`s?L5}^h0+-1QrOm *ʟ>B^hڠnC'0 kGM>渞(d0Zv='6bQ|#,`JŞвڽLr)0aBHi'Q82Dpzgv S:!I6.$ǫiZש<5^'ӰK$DrA 2d!P|;Jvv'!Hz 8 !6@!A#ߟϋ:r,kF SDa1+%h;_S'cZvvro{4*2입 EWe$,|=1ov{39߷0.g#3pC6G 81xIENDB`sympa-6.1.24~dfsg/wwsympa/icons/logo-s.png000066400000000000000000000011201246372670100205310ustar00rootroot00000000000000PNG  IHDRP!A1gAMA aPLTEZZk{{!!)3tRNSN=PTbKGDH pHYs  ~tIME  +)jIDATxO0p}ј]?anlGhJyƎ To4G(rt/!%DbO 5'@PrׄG1(r $v -Իci s C*{̢고9{vpg%/w1Iz<ZX\ahZc8`IKIENDB`sympa-6.1.24~dfsg/wwsympa/icons/logo_sympa.png000066400000000000000000000567111246372670100215220ustar00rootroot00000000000000PNG  IHDRyصsBIT|d pHYsmotEXtSoftwarewww.inkscape.org< IDATxyxU;'77# f(`qF[ZOZZmUTV٪ujk+ҊS*bEP$$d_+? thd<}l]V81Ӕ ZGh5 U VCLNRb)e $yy2ۆ*iڦc==zjLmOJ`b۶mZi*%<ZO;д%&P:`(DVDD>xk/= %m)e MK Rqa)#ʌ,{q&s NV\Ni %rW^RФLۦ`Y)Kʌ&DFSiVgg]\\l;#=?o}O(!JTC P-(ѡD5^|ß_` eZ M3&bY1eeHS8hn5k7jkZ>ߛ$m'$0m;鲬дiM)d,#/ϊD"V]]M=$JԀ'_0Xy t_r%Dq HDLKp^!x% ]}jhJGXI}sw?x#=?U_D$"5_w{<+MHBĕRQ)eˊK]OXJYL%DFi&Vm}f_y _G,o<֯_qq579.2%D_L8JuTk l5[j_@\([v߶/$ [L`8 \(2 %qѭiK]eof KێBD-N(HH\)#6t`[*..njc/^Fa IM\zg;t_R%DPD xt*Ԡ\| ^0A}Ɔ7UMJ/JJJTj۶Gp%Q3vltgӃjZᙩԉ09O\V* ]h5LwIؐvdM4mVuvZJ+-uvv_{}R8>V#BLL#4 nG%==j]sW DZVsB|SnfڳiXM>rvfZ.K(R-H*Lٚ۶Q`F_ ׁU!WRb 2=n닋=RD WDn/GuHho'a_Rhޞ߰3+#¶mm1>H0lu@*g"RfsV{5,DP*b l;jv" $ɤ!-˰I3Yubݺ~*$ؗJL{om3^r޹{Svl;ئBlHZȤ=//23Y$PJٻ|E+KX6{XlsIwb !RJeٳE{{ 1&v犀t˾>@rHUQx%)N ' >M?L)W\yZlVTR*AP*g2i])#]P ) mK^\k)< 1K(gk8 35vـ'#{ u +Ju|(RPS_UJaj[$DBD"b+3{-+n)BSxeE[gΛ/^mυX;x'O.mƣNȟ0$# `Lov9 џ18'S_UY0{EW/kV!eD9j2L3*K+aARYVxҪ4EE~&b%>¤dz%gw׼,b('jN=E҆_;W ܲ=˓Xч`r]_sϷZBDR1۶t*yymfWEu2D5PBԈN9F_#]G80ݳ"a l+/̐-N?^}UYї`Bد{|e[hV:dXhZİ&e"cYqm'ɤ!,(7>u5 |#.7 y#+&{uWbȮ ^\~ckKr(Rk2?[u]w]ocYaJE5NH]O #ajZZO3Ҳ #rll^\C&VT@|RS#;;˦U\?@,ܽҪܲ$M:}{b^óQ8`G|>ΰw{6z#ݣj5+)F:yߕ_Si@yy6?%t#t'4f5pF[߲8< ]¶#~!;J!غWz ¶Q".\$zFa$ kĈeHXOf4ZϸCE+%C/ChNhLk{uD j۾SH ]%UHO`S.},v4˚IY)Ԅ.W^,+d TJ[4ݷmt~YiM"?pL *!z/o;1] .lk7^KcvA02á[9k ~i¶J{4ۖM3״tc:mYA%B\͜_/<ܙeW0<_eR >swr~IۗBZZ BTV2F/:Ȳf)+e WB)(є*LfKJܲ<@@5 )L#AaD: cR7g`_RV pt}rx<3WQ]G>ClnmW_Y 8L;utfp07 F) )|Hƶ=ʲ\BJ0 hP}Bp({rlی~*T (Pr+ooLHs gP:bO>p {Ü9D4JDJ@H)w MKZXFrH4g]T\LRQJSBhD)M\BmzL$gB+~ #1pn! kcL ,Kb}VfL*L}ry&{?m*XrnIq!;DSWe}3(K(B6{9a3f8jm4ihl}qXB{<݀-mǥRmPޔZ:`-;KC pFqNSd:nflzw8mHMLJ%ݎZ^ יuݔpmӷ7rfCR q0w.JD0;?ME" %Q}csM'>`Rjl߇;Ub2J BR)i+e ]]nc !-to8~.g^:d3a~zeKRP$[pAH}69?X \3 >a.%bN7i?N: NE9$J0tu!zz!;`@L&*+O_ҧ5(<sr5/?fMVJu!DRnVـ΄eE6v  4Zo45D^fͶ(H)QBҩ$mMY6qbJ 4BMr %Pxp,4gn˲rmTRIKR `FxAhjr4M&B ňbDQ|;FgXg۔_sw4 )eMv;Q(VJuT B4۰&T{FL#uu%.hOCH젙eYXO0PJi. ۍє"Jarp\d5"[id22 J)\.Z=m_:4MlF4t]vMrt˲H3W45F6麞#rJ)LD)E:&JaYB\_ !XH۷iNsef֓%x<\=0|SE.hWТlцfaY(ՙvc0b#^5y5NVhkk# gtJ)hmm )++ݻ]RNu@ @ ڡ>4iiiC^^~ߏrm&s"HA:}T-]zLw^/'Gd2I dm ܺBE<Pn}/"dT*G,zzzr7a<4!zp6$ihVdCдea#!S__on`VF&}-^ߋ/Abvj׳d֮]K&sΡh4J]]pnZ/_η-***F|RVVFaa!RJ|IjkkIR 6O>c6ׯ' 1f{._+W2w\&NH]]Vߧqq衇RUUk-[Xz57n3c M|R6m`0HAAAA\.xG(//gܹ\.VZŪUhkkcʔ)L>1cPXXHCC7n '//׋u]G)ECC.k׮% ~1c ,⩧b͚5xwq\.^}Un7ș\t)?z" t+:mw֤f2i*eYάqG@j 1ǥS۶s O~B*b̘17ߛ5ZNb444 XlsᡇsrqFF?# .$|>.]ʜ9sXt)B:("z(֭][PP@8ޡm̟?C=;_5 ˲زeKF<)//5k )..& x\{aY֠/ȑ#Yh~8===XEII o+VfӦM;i^x<;wrwGꫯR^^Nee%B!^_}8%O>Z:mvӲڥm$Rf*6o^{Чl;|8 ;Y`??{&!t]tmmm z{,\O>gyc=uֱuV4Mc,'x :&I&;~?\B$q`T81IO4|3RP/N-T8{x<lۦ[oE]]]?o͏SD3ollm۶uXN9A?+,XUW]%KmP(DggntpEpn9iȚ5k9ru-?x/Kw[vdR&w4>& ą1!Dii -u;XJ)uhפ prYtvv2sj_tttpa1zhBv̜9枌gT⑃K/Q__y_N$/nm0獞|>g0a&N0t ;F%Q^ #xW`ee9WK/'D׷FH HTbG\fҶt:42)(l`*+ZƎǏcnVN9!Dx'#t]g֭=Yf_{\v1e)JA5Dw{՚)J HXJ%,Hl;ef2ief<7K{'seI׎t};PĨY"H0as7xh6b Rl~^|jI)[K?EvYt)fbѢEL6O>.tW(--xh夫xk+iDP.1il;4)ӆ˕0̤ct]-MJ3Q8 ?G9r$W_}5'po8#5kk׮EJ3{11m4?yfd^{ &__ض_W*++wZnaa!?X|9J)9x\4h qM ]Je2iuV+ʅ Eؖ!~vرc[wzRF^|EnF<@:C.'v-n4dBc~%KHRz꩜s9~=]euOKKK0{&MT4k,V\#IJ,n:S)%Ǐg֬Yzp q\plٲG}<(++v,X@QQ]v=^{-d2;<K& q7裏|rjjj8ihh Q^^Nw/&nl(<[ZaYnt݌XnŶm$*<eWk\b1>`^6oܹswYiL4+ oKOO%%%bҥtI\wu̝;+UVv"73(տn\w}GGy?\nN8"?wlܸG@dժUf*o~ÝwIUU7t?| z6̙3g-UW]ƍ袋4iojb <455G?E1zh֬YM7݄磸rNyy9!89ꨣH4E4,BJɔ)S{ケ/K.)+8 EC!Jgc)maYJV|=$i<=HS Y wMZB#Ji>۶wX pXp|Sz+6m" QQQAuu5^;H$oM:f}SXXȚ5k+[os9nmc9'3<9H$+Vt}ߨTtF{˂`(Msϵ{+.iժU_w}rQ-jG O-fɒ%;toMMMiORJy|B!5jՎ#L"`ܸqnRl޼5k }m,{ŋ dĉz@sAXSSCmm-RJ^/tlB2>qU[ SozW: )B9 ߟ73N'y晖_W[4 ~>Ѓ̨7Qo|% )cL!N63gKb l›osPRRBEE#G<^.V\I vSRR'm455zjZZZ(//'??zV\֭[6lEEETTTPRRҳfp\hBJB*!4wkDׇ[bVy`8U onnF:::#`)HQRR# RTT o~f[d?ޛYfG}t>,Ǐyf'2$T /&~Sָ~Ҵ!lBt~G#fJa(UrEE?. yʊmneDalB< 6lw | H `#F  BP^^NYY%%%Ehkkv$pX,MBJiTWWr*//h8xvŪEҥrg,•2M-hFڽ!ҟ}]ϾlNU" L&s s鲁vz r욦 6Ƕm8?x~RfΜ/~ N7:^UV7ԬDdRrkÒ&״Zzv. aaD&c)JBt+a;x,T$+]z'Jg'DJ" vXd2$ PaÆQZZJ( 7. @ KFdbR%|> (..{!bK9,sڗxG۴m7ϲdg8,kC"Vja!" lmI9D_UPPGIIICمԝI>/ a!fbժUeO-u!|^inC\* "U7vN,b]~91WYN6](+#Vs6|O \)끥">vx=nĮMy) 977c9"VXi)BQQΖ-[rʪ#GRRRB ȥ}ͷR7r)((7mO_BfvqݹB!ߣ+Gnj J>ݶ7;Ǎٴ`ª|*JƄ<(;R&dMHud@yrN[DQLBSSG桇bժU^ 60ff̘ӹkxwqݬZ*fLUU&Mbʔ)5P(*'J)ԻF_>˥5QJ"\t/{_W{o6oLmm-6mB)E 3䡇+eee=I&1qD*++ \~?f sco܍v K!A Ϊ7Xv-ZRyQ]]_~a5jT.iĈS{ٺvu ϢoŇOu:?[eӌ- ڤmdL3* #i;u= JªBne=^Ne;l /D1b۶ms_i݇v> cƌ᭷#//Ç3n8&Lѣ)--#QxHa'wm*C.Bqj?`qf1b1t]' ITTTPUUEUUz=\~w_~˶m4H$OFOOO.=-;,//ϵ3$UhM?X !QPRm+n55om5K)lVx%G\ueMP1n6.˷5nUS4ЀR ȑ# B.t7W">lKv~KF&XL&[!W_=1C2cಋE!,B,KY~(E{5Þ}L6 bmҖ3jN!XUeQ'G[2*چj߀jۀݶ1VKqt^2:=QZ7v&ţRv:(**" si|nR̐yu+qt;ڴKDT*qik~'qyH^Rxhcjjhp|{E%%q;<*[r?/BX"0u۲ai۶7Y5X 3r;kb@P&DQ6Y:Q6Q6(qHo[" twݶ{ *K*ۧd確l"8x~aiJYє f; ~î#TOO$SiՄͶm*Risl-[)'OR *Y5>wW]Cuc7b7jnDsHS:Y6;'i{I4XD{.%S6H=.\%9/~6+T"Hm2~ĕ;U}]Yߒiw9mwGb)kJ"D_ F8?>m/}-C4'nIJe upR-[-@F}i]Wu˲,YȲ,˖16qR2`LI+ ,2uM:ۄN/Mc02$,,RI%T{w}OT-rW:{j}uD2{ g3tÐ[@ V/YuP{ɛja e'Y= kZ8'>[Y:nK^ Z woWu {,|\#u wQźMkEØ,h&Z[ݓ߆? 7M]O#;TTy~v ?XD?KK/W~ɫN$aȞ{]WppA"*=ѓs<߫J4C\nF\>4TqZ]~p/MH0bmltIcaxi`~O*2EWV ÿ9i3s.V"sIT@Bә4I@S<ʬ"XyXΕstC" } oC) C< SZ?߉- F\>nӓg &xI: uNt{QNjFbE!gZ>?LߢU2gy$Ϲs4/W׸\J*ULp?[I$d*͕= d t3 xg~ q~#`icR#dv8*P7 s7wAUPw%v1l ]pUGWynЩ]ʮH~^u]Ep*Ӭd&۫?5UvƠ '~ykqf?jiGsw}T~/Fݬ]jBƐNW8]T~YA8ƒP 9"OZP5FHyYj[2+\{g&> -{}Q͕a-cK Χ`v4n_*)?ތ Du԰2@vA 0y2Sl?wh?TB21*b9cUش%}lFoub{82]Su/t:T3cpO|}#&v=5+:Aӻ `!{m(TiU·?x7$2jj3!*5]P. C=keW/4?ӧ(7_xXxBഋ== mUBƞ{o&Ej]"ԹfQ!{~u0r1LPIsZeKl|Nβ ܏7w}vq KK2>R#Jؽ;n1ӏ܏.J@ T*%fTu QQ6nddDI#"Pf7< ]_ӟByϐ/+A c9aO׳A ݈on-\9=0t巒I1}ͧ u')ylu(QU5#T#%T~gs+_' Eڵ R͕2҉bU(+t&iuڎ2ƹ4 9g w #1Њ+K^hH h1e]~/.oZRwf^(v'—ο$yO]oe _e  ہ.t{PY }O=AWL1R T Չ/jDD$U.u!2@AGHع4"s;::z츪YKA\ Wʜ3V۶7)p~C O7k x5x?qNɳ1+uR&.nVcij#TdT^.%T5RՐsWĘ`H٘"ϦpIcosx8`^\z^ 0sETdPߺν|%MKn~qFAP@=)OoBٷ»}~]p6}#,Rijt+'ZZQD"J(j@L=QW"!!Db"J|6|?$" K[eMɚ&֯Mc ں51MRmզ3"?)s&Aj@Cx'N݈w»^Sz_¢p{)k(3gTZm{/~~J Ɉ9EoEP !P{҉9t.gդfL^2YvIeŋeVF޻h2WZCCC}#~YՈfX զ200 Do_)g]K_S ;Q&Pc<}ʶJ0 Q]X~Wrl{/|vmÄRP r T RЯ@#y; 2ߘ•ee箝$ou 8Hl\{uVvnhh̶\N5UUyѝ;v޸]WdoZԳ&nA,%O {NCn/6o{&rdJ@5HjRjPKQ!3QD2W/LMva\6'ĺ'Ntm*`Fig䤓4hx48X0̽Z-Ws.v"s3GxƏ<3+]_–q|߂-8*o}R3WW-_.QDu|nQO䞧(%()ĨYV7QC\h#b%J= )=vU h8<[HZUDQ8ZkU-r͔(u U78=At׭(.VYS/^;.u[r.Ÿ ~L TJًNK6a6n:22a C˖YR 26DTv.4 ޻t ˵^a5xa. ǿ z _QI2t { ߓm`Q3Ϲ*e] UC"?=-T*Ae?T`蜋("TUSԕeYVy.k׺7('ճ&T5rq~Pl4АF93Qֹ \to`۫k]~څn=A~pzbbEO[qte?vo>UA&sOޠS6*߃}Sh@S1jE7 Pz3\*D :ԹeZ;8l&aftTn 5N{'YdYJĄvEP0"B$cO\•H-0}^䗼Uϗ`;8=E`z7>6XR~\u.8|Vi܄ [ۻ@vʩt>p7EwBS!`Ry9T3%JI$ jՐ BŤs %lmEQ z[k%L-ccc:6oN3"upxX&{CYa.!{z^*@"l߽U^ݸ~>x+xE0]JE&\vۆlۦcapS/.(MP1n 2Df#MDwS(PjiPihwdH$!TTBGs QyX[_-t\^ccnجGwy;2`UxjhQdyjϫ!@T[Z %v{7^X!(N:<%; GR݃cMRI2*:Ү9my.|^ڋ >NtQ=H,ʿ,o{mfz 2X짊L̹d\DEsnʍ Y5[(vҚ"'*vQ,YDz&!lQu4f@"q2sg%:ZV< /rv.j,ɠFkƗ^֪>9iR"{+0x{#[W̍SB՞hN>,[>}5s|^ӞtܱqACp,Esu&Cґ\r&J% 2عP gCV$)n"vffƵ.ږ-rؘ(r&pVQ*7 der*7鸨ղjADR91D&ϼt[Z2hR o9L,-@tdc 8VJf$_4mt<8_< x>E*P8AFpㅀzzYwnəIKD/ ʡSUU"B0G*RO1L]V]ݖ5k%pԜol^0ck֬v^cR5k5 Pky-QmyDz͒d 0Q[NZBUM~/^m? [:oêOB![-|Y' ك N6>ԝBbO6`ʆ"Jz.gQH΅9*GgD-,0&c4v(lǮn˒#YPG'le0u:n[-cAT7}LI7u ݊_]x֐zP"`fjB L}1 X J̐!%:ĤӿwK3Du@ ax1Tsdn"nNu+Ota pڟZJTj Mv9ȉD sRdom3j'.cme˒%Y T8^N4 V#sH쀹3↞W_|U[W3Qc_=R5sDmՈPVC94{}2#Y$Q?p7O^BDLD"* sNu1C0@=dLVd1yAt\ݖ-xA 9I>`f "oD+R1u&6 hBu@Uu`YK\P|mx:?e}^>T=# s")"kSdLk{-λv^nLi3&Dq"")U+qtOx(2 T˜sPEqXBgC͇sXq8l`N^p;IlTں2sDhHSFDN8\D]R`R0]T>6g絜jۈ,bAuu"RuE2핪HHcJ;"sI(,˲t} u6/ȩr ;]i6yq(L"'"Z jЂj ]^K"H=ͨ cBA "J%*۬A0DDz 5H9ajYƆ() BR]sSMc\$ɋ$)Z[Z86ǝx.EI>sg\y߯I P sODFU, c%mY֘n,-Km\#UE j윯ĀPu,JT$cJUeY8mY69N5fomذ;wC%/iZ-x^ eE`9,JJW!*Jd+[Ä-b"X_U,2[xjjiycZe13{ՔD:<[E@Zyn%]'4MVyycj AK\m+\uGI(E(f 쪢|awh 0YQZ$Z-xE!eQH^ζۮH=3IEͦ j5PƬ̬yi]^4ua pbYz|h%VsVP,hѢE99-rXuVA GY|gVx"vkaIjښq֮]^ "Np9IENDB`sympa-6.1.24~dfsg/wwsympa/icons/movie.png000066400000000000000000000003501246372670100204540ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE̙fff333"tRNSN=PTbKGDH pHYs  ~tIME Ii9IDATxc(bs0˸ q_m4RIENDB`sympa-6.1.24~dfsg/wwsympa/icons/right.png000066400000000000000000000003231246372670100204520ustar00rootroot00000000000000PNG  IHDR/TgAMA a PLTEw]btRNSN=PTbKGDH pHYs  ~tIME 4\0IDATxc`@<`B0B$,H!قGIENDB`sympa-6.1.24~dfsg/wwsympa/icons/sound1.png000066400000000000000000000004341246372670100205510ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE33̙f333tRNSN=PTbKGDH pHYs  ~tIME ojIDATxc`  ,%%I"a 6(0Uptt 6ZCCL6ScB sP^R*qY‘c IENDB`sympa-6.1.24~dfsg/wwsympa/icons/sv.png000066400000000000000000000261551246372670100200000ustar00rootroot00000000000000PNG  IHDRQfsBITUFtEXtSoftwarewww.inkscape.org<,IDATx|r$9vAVUwK3ծF/k/k}MfsHV6֤"~;`=}߲ ȟ ȟ ^ ӟYn,)tU{}~?>o~x}){e/Wp]9`@>!8~=>m}?>~~/_j^~.Q|g %CO)‰ҿOfƌmm_y,tEN䷗w߱Xz#kEA k9r\5@z2>f|/O@~}}  @OK>y GnR.M}ʼnH:?eɐx#(6|b>h,C$U8d o'C~y)W!LSV. # fA1P?VsS`ˑ|7! Iꄥw3>y_=_> ɀ`"V' KSd1]&(ƾ@]91~*̠$5>7O) X=2 ߟ m Hd.xd˩.gHJTb]ABa@EH5x` !VoO~lO=o' @>9RwLE2OR^pMӔ'Gʸ€nq|FȮt PJRpd8{qG_Brx+;YM}a3ƤE`Ҭ,Vܴ}c=akd(g=>,9 %e{S3nҦ"׳20ݎ1|hms%:@,U#WaƌwĐy!Yȯ'GJ!7RRR-؃T.͙ qWĩw]kTi- 0C>~eHI6Qpa9f FƣR;M<^d tyGiU ȧO|{Ͼ Xw e)c{ $YŸF e"b$RiHRPӰ}'|JO|_# 00b!,=QktCZ EL=t=@Aa?!,qz: 8{H3u<uH]hJ ~w|mRPwӢKbg+ -| G+H43oqĐϯux9\<iTŪ F1>3"_ ^U 0TJP j%VJY8ϛMYq62p4>-,)FՔyfB443AaQ!4 Ȝ q@.lCJ)k)YCo' ?" 6%iB1LWc}r*ͼIi`?w[ʊ= 1B2 ˚kn`"伮҅,R 8<2Ɯ 2co%9.7S ?M g_?7"x/gUv^TE60!l#m9uUBݝ$ex?y7 m ޑ&>b^a-&sY uN7 xɢc>Cq (@ y6vns:,Jz8x2Ի3 Jy` Qm@}O:RCl&N`طczq Y-]{_mc)YN@ZɉZ3u`"Y}!\0apzጰyȸ1!_>3חQ3Vr {wڢL))]IkC?ڋ1*HKV =݉C0RvƀljpEgqDZ+Y=#^0K9jui!oM9%[!0d  E4"Q5)fvtu5T>RGLߨVcȏ5 jVn;[z|NlC@`ۇ%"MzQw;qLBk4@w@eaݔnjGQÏ p0)nH"ۀ JDCzӋp"6:ٞi]!;ቻ!H<&0[~N:czx 9?搬7#P+mߚ/6 ?"UmmLϿ:r0%_$jJrt7%@fXq"?Z*!GQ F'o^ϐ; 096-oȔi&Rɶ5 1UqirTcς!C*_II` H_[ad4ѰA`l[2ӂb5 REIxS$0d*Y!g҆C2 ~ "R(Czʂ+@'E&rBbHX5,P7# w! @h}b7QlK~D%0eM=$3$T@=D=-e3eK4/ĔG`σCU:t8")CpCHztaہsخM@ _їQ8KBISQAz:r:3-trj%SG+pB*?,t7/40$~ge8jBɭ2$ X%qDC.U0ݜV'77N>ykQyJUHYw/i i>d̲d{5B!1d!iHB$Hy* G$SFKShMaΗ~d@@'[`&K3qaqa,Iհ{P8/*j)\}gY)' 3: oO<S Or$ 0 IjXZPN0u %aԻ=MϲH /Φ,~JV!1W"Er2<0RVbHE!ډ! yЋ;!$w^tKsLGK&CƳtcʚ:usfZωVbHIO[l2*)EbDt `bo4<,;z"e0m-Đc|Y Fʤ,bHK!C@"'S{;H[# 9GMᡣ(q7"l5^"\R̔/MM[!',c<$ռeA\l{URR5g !4BJ6u91D+ܣ9D@sR[HcD&n<5\I"UJVqN^Ҋ!b5<%޹+ֱ,yd]v4u׻ S.O{!|Bزh::2B@H5 2N>R 76a$WIo 桃v @Աv` ȱޑ0L^bak5U1LsX7NhbcVRܩw@Mq2dk@&,pWs 3pB8eeF,Y}(<A;q~HL`]6]2 i&=70Ii1xn:L6`q+^bv)n5 (@s!YzqENJ0S+s:P22_ 0)k̲NNB51z!\4;9u|7tL X x zg3!3:{Hl1bPa-g4ݹSzz _. ]Gb*aGM!<5v@&[c{%_]h>7I3C{AsGC6biS>~5G h"U =$竺g(Ig‚isn3*ŶkS?{mvL{g&;Q"LטB僌CbjϪ IY|0*Կ&ȉ!LSoA9o!+@x23_d%Y 3$3uf$/ JVC-FC7 ,pr4Br2 bH* ECy0$kS#+>A2u̐2 ::Rx%ÛCЯ%^0.Q)C, GZ +0K% 902DG!P3/C9%~$0DIW!"Y?%2%`2VD|BG%0=c/Cw|khy^O" H:GE$ '2gxF〲 OYĦwn%]IzIEҥ  - ѕ(3DY"0r!0rzio D 93E@fQǗ;5cCKHKXɥd *,$8SL"آ 5YH{H.L}*Y278Qc/Shz^:8Lwb !\,R#_UF M6SR3Sʒ!y:_m ̲i3D: )Os^,!ͣCwuu[JSv4KO\31>ˢݏ$Gm`9%x:p"<^*S $5 sɲ.q!t5$9 Ove{C&M8.F >m^ O*ށ )+LeD`0Va*xG?1 |"`iFtj&׊ a"m!A|C2m8^\t.g&^2Ᵽ ayŤ,'YUXTOrt?6/x)I9U@XqhLP XQʦ>bHM* Mbj5D!qޤ4:@t2AhK^r?Pd~'d/e]*h!V=(u@-(TSM̜͞OMŎӖzG ū%0HN)8bm`)OAdIf"%^z t{:wJzHzDQQEPN<D%k߸~ԻHYQ#' wk' H%/ B*HxB(,Me81Cs8Mcy:{HIFT8W"<24{i 0d&[f(<=@r1lz3+YY%ח.?#ÔEJcKͱ7hc :2a{ <25{ " ɩ6 Fň vQ9 3@ $^s郷8@F wH;OY2:ٲpx8貇 N3`䭿" )W!U!\r t yw *d:@)/R: SvD$s#,yUu/} 9<:Nap>H5)k` dY{E G,R"yL_\WA3ĂV "K` C30@BЍg(WYxduB f6M=z :SAUkQۀގe HV6./ bSVl!xԘV ƱB )8\vBɚ?d$,Cbb.dO1Qb&T|!R4~Z0*G )h bu\Iњ<&< <xG#'&&Δ~P:)C* % 78@?}kw!]E*G`Z ll}FJP4} [k"ǒ{G~#R=eb5?e-3$@]*RQƿ %\ReѲ g&g ^Nj ٷ2rU<bw88bno30D`c? ɒPx%OHhS9W㦰wS>'7rN"~8r ©deYT&C՛a *?e4+#S![1$^zN5RG/lxp6ԓ紫0b_u~_]! K`H2 a^^4$H R:Qy^`1>3sHy93na*=%QiCB[Y-5t'G^=$q')Dv}%0r"J|Xj-sP@-ZGXdɎ?T0 zS3dS=$exS/" 6^T˴n07 0yיJRFg`|$FZAa'c0] `+(s4'ʗH!pj /8!aҵ,éj^A@Zl H,v3p6ka,&tcr!q lS:V/!vqΖO , 4si iԴ *K0 kN80 񭋅L0N7Av +G$]ni۟v; ^`3n3C~j$w{ޟ%ȼ9)37Ħz dK/Oe(E3X*Nn/BF^}fz{UmݬkLr9ԽdRgH]쒕J;Ϩ38-x͟AH1Ƃsj GE!]FX;r3]1S יwO]Z_zq0Eܑ&,zceb6n.~: EBγzȥ4,!],~m `$]E{enЗiPy@VDAK^qwe@&~i $Ǣ?u[|7¿s7I}& sY}q! iNҵ_9>'o|T4}c i> 1oJ@W^qR ֿ qGW\r '|U_k ;|3||Եr?ⷼBzu9X~J} /n&Y{ ?Ck|?[h$5IENDB`sympa-6.1.24~dfsg/wwsympa/icons/text.png000066400000000000000000000003701246372670100203230ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE333~#tRNSN=PTbKGDH pHYs  ~tIME /HOIDATx +f$<_8rU2MHQMEKԝ{q̢x8GMu>)1 )EIENDB`sympa-6.1.24~dfsg/wwsympa/icons/top.png000066400000000000000000000011741246372670100201440ustar00rootroot00000000000000PNG  IHDRasBIT|dtEXtSoftwarewww.inkscape.org<IDAT8OHTQygnl(* PrQ9DhQjm@A۴ M \ bI2L38q1Jd^ù{ipR1~1tupz_?\.\3VffT"/ ̺n@CÇJ VJF} L p==89z?>B f{hu=m p=3xYYR) 6>bdhE)kt;vL!"ЖD92J gS6çӝ=IW $AjB5*eX,V!AS y왚QB WX(5HIȞ bYX*R$L<k8)s7^ڕ`FԠ-rO׍~ Id&9F9Ci@\.]lP`Ǝq˨fEf Q(69q$!+vxY3 mʽ쫄nIENDB`sympa-6.1.24~dfsg/wwsympa/icons/unknown.png000066400000000000000000000004131246372670100210340ustar00rootroot00000000000000PNG  IHDR2o5gAMA aPLTE333~#tRNSN=PTbKGDH pHYs  ~tIME bIDATxm Ѐ;$0*墉Obje K?l:Fք}Obpz΃ 2h}.8Gft38#ECIENDB`sympa-6.1.24~dfsg/wwsympa/mime.types000066400000000000000000000163161246372670100175420ustar00rootroot00000000000000# This is the default mime.types file from the Apache web server distribution # This file controls what Internet media types are sent to the client for # given file extension(s). Sending the correct media type to the client # is important so they know how to handle the content of the file. # Extra types can either be added here or by using an AddType directive # in your config files. For more information about Internet media types, # please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type # registry is at . # MIME type Extension application/EDI-Consent application/EDI-X12 application/EDIFACT application/activemessage application/andrew-inset ez application/applefile application/atomicmail application/cals-1840 application/commonground application/cybercash application/dca-rft application/dec-dx application/eshop application/hyperstudio application/iges application/mac-binhex40 hqx application/mac-compactpro cpt application/macwriteii application/marc application/mathematica application/msword doc application/news-message-id application/news-transmission application/octet-stream bin dms lha lzh exe class application/oda oda application/pdf pdf application/pgp-encrypted application/pgp-keys application/pgp-signature application/pkcs10 application/pkcs7-mime application/pkcs7-signature application/postscript ai eps ps application/prs.alvestrand.titrax-sheet application/prs.cww application/prs.nprend application/remote-printing application/riscos application/rtf rtf application/set-payment application/set-payment-initiation application/set-registration application/set-registration-initiation application/sgml application/sgml-open-catalog application/slate application/smil smi smil application/vemmi application/vnd.3M.Post-it-Notes application/vnd.FloGraphIt application/vnd.acucobol application/vnd.anser-web-certificate-issue-initiation application/vnd.anser-web-funds-transfer-initiation application/vnd.audiograph application/vnd.businessobjects application/vnd.claymore application/vnd.comsocaller application/vnd.dna application/vnd.dxr application/vnd.ecdis-update application/vnd.ecowin.chart application/vnd.ecowin.filerequest application/vnd.ecowin.fileupdate application/vnd.ecowin.series application/vnd.ecowin.seriesrequest application/vnd.ecowin.seriesupdate application/vnd.enliven application/vnd.epson.salt application/vnd.fdf application/vnd.ffsns application/vnd.framemaker application/vnd.fujitsu.oasys application/vnd.fujitsu.oasys2 application/vnd.fujitsu.oasys3 application/vnd.fujitsu.oasysgp application/vnd.fujitsu.oasysprs application/vnd.fujixerox.docuworks application/vnd.hp-HPGL application/vnd.hp-PCL application/vnd.hp-PCLXL application/vnd.hp-hps application/vnd.ibm.MiniPay application/vnd.ibm.modcap application/vnd.intercon.formnet application/vnd.intertrust.digibox application/vnd.intertrust.nncp application/vnd.is-xpr application/vnd.japannet-directory-service application/vnd.japannet-jpnstore-wakeup application/vnd.japannet-payment-wakeup application/vnd.japannet-registration application/vnd.japannet-registration-wakeup application/vnd.japannet-setstore-wakeup application/vnd.japannet-verification application/vnd.japannet-verification-wakeup application/vnd.koan application/vnd.lotus-1-2-3 application/vnd.lotus-approach application/vnd.lotus-freelance application/vnd.lotus-organizer application/vnd.lotus-screencam application/vnd.lotus-wordpro application/vnd.meridian-slingshot application/vnd.mif mif application/vnd.minisoft-hp3000-save application/vnd.mitsubishi.misty-guard.trustweb application/vnd.ms-artgalry application/vnd.ms-asf application/vnd.ms-excel application/vnd.ms-powerpoint ppt application/vnd.ms-project application/vnd.ms-tnef application/vnd.ms-works application/vnd.music-niff application/vnd.musician application/vnd.netfpx application/vnd.noblenet-directory application/vnd.noblenet-sealer application/vnd.noblenet-web application/vnd.novadigm.EDM application/vnd.novadigm.EDX application/vnd.novadigm.EXT application/vnd.osa.netdeploy application/vnd.powerbuilder6 application/vnd.powerbuilder6-s application/vnd.rapid application/vnd.seemail application/vnd.shana.informed.formtemplate application/vnd.shana.informed.interchange application/vnd.shana.informed.package application/vnd.street-stream application/vnd.svd application/vnd.swiftview-ics application/vnd.truedoc application/vnd.visio application/vnd.webturbo application/vnd.wrq-hp3000-labelled application/vnd.wt.stf application/vnd.xara application/vnd.yellowriver-custom-menu application/wita application/wordperfect5.1 application/x-bcpio bcpio application/x-cdlink vcd application/x-chess-pgn pgn application/x-compress application/x-cpio cpio application/x-csh csh application/x-director dcr dir dxr application/x-dvi dvi application/x-futuresplash spl application/x-gtar gtar application/x-gzip application/x-hdf hdf application/x-javascript js application/x-koan skp skd skt skm application/x-latex latex application/x-netcdf nc cdf application/x-sh sh application/x-shar shar application/x-shockwave-flash swf application/x-stuffit sit application/x-sv4cpio sv4cpio application/x-sv4crc sv4crc application/x-tar tar application/x-tcl tcl application/x-tex tex application/x-texinfo texinfo texi application/x-troff t tr roff application/x-troff-man man application/x-troff-me me application/x-troff-ms ms application/x-ustar ustar application/x-wais-source src application/x400-bp application/xml application/zip zip audio/32kadpcm audio/basic au snd audio/midi mid midi kar audio/mpeg mpga mp2 mp3 audio/vnd.qcelp audio/x-aiff aif aiff aifc audio/x-pn-realaudio ram rm audio/x-pn-realaudio-plugin rpm audio/x-realaudio ra audio/x-wav wav chemical/x-pdb pdb xyz image/cgm image/g3fax image/gif gif image/ief ief image/jpeg jpeg jpg jpe image/naplps image/png png image/prs.btif image/tiff tiff tif image/vnd.dwg image/vnd.dxf image/vnd.fpx image/vnd.net-fpx image/vnd.svf image/vnd.xiff image/x-cmu-raster ras image/x-portable-anymap pnm image/x-portable-bitmap pbm image/x-portable-graymap pgm image/x-portable-pixmap ppm image/x-rgb rgb image/x-xbitmap xbm image/x-xpixmap xpm image/x-xwindowdump xwd message/delivery-status message/disposition-notification message/external-body message/http message/news message/partial message/rfc822 model/iges igs iges model/mesh msh mesh silo model/vnd.dwf model/vrml wrl vrml multipart/alternative multipart/appledouble multipart/byteranges multipart/digest multipart/encrypted multipart/form-data multipart/header-set multipart/mixed multipart/parallel multipart/related multipart/report multipart/signed multipart/voice-message text/css css text/directory text/enriched text/plain asc txt text/prs.lines.tag text/rfc822-headers text/richtext rtx text/rtf rtf text/sgml sgml sgm text/tab-separated-values tsv text/uri-list text/vnd.abc text/vnd.flatland.3dml text/vnd.fmi.flexstor text/vnd.in3d.3dml text/vnd.in3d.spot text/vnd.latex-z text/x-setext etx text/xml xml video/mpeg mpeg mpg mpe video/quicktime qt mov video/vnd.motorola.video video/vnd.motorola.videop video/vnd.vivo video/x-msvideo avi video/x-sgi-movie movie x-conference/x-cooltalk ice text/html html htm sympa-6.1.24~dfsg/wwsympa/wwslib.pm000066400000000000000000000262721246372670100173740ustar00rootroot00000000000000# wwslib.pm - This module includes functions used by wwsympa.fcgi # RCS Identication ; $Revision: 10725 $ ; $Date: 2014-05-24 02:49:31 +0200 (sam. 24 mai 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . package wwslib; use Log; use Conf; use Sympa::Constants; ## No longer used: Use List->get_option_title(). %reception_mode = ('mail' => {'gettext_id' => 'standard (direct reception)'}, 'digest' => {'gettext_id' => 'digest MIME format'}, 'digestplain' => {'gettext_id' => 'digest plain text format'}, 'summary' => {'gettext_id' => 'summary mode'}, 'notice' => {'gettext_id' => 'notice mode'}, 'txt' => {'gettext_id' => 'text-only mode'}, 'html'=> {'gettext_id' => 'html-only mode'}, 'urlize' => {'gettext_id' => 'urlize mode'}, 'nomail' => {'gettext_id' => 'no mail (useful for vacations)'}, 'not_me' => {'gettext_id' => 'you do not receive your own posts'} ); ## Cookie expiration periods with corresponding entry in NLS %cookie_period = (0 => {'gettext_id' => "session"}, 10 => {'gettext_id' => "10 minutes"}, 30 => {'gettext_id' => "30 minutes"}, 60 => {'gettext_id' => "1 hour"}, 360 => {'gettext_id' => "6 hours"}, 1440 => {'gettext_id' => "1 day"}, 10800 => {'gettext_id' => "1 week"}, 43200 => {'gettext_id' => "30 days"}); ## No longer used: Use List->get_option_title(). %visibility_mode = ('noconceal' => {'gettext_id' => "listed in the list review page"}, 'conceal' => {'gettext_id' => "concealed"} ); ## Filenames with corresponding entry in NLS set 15 %filenames = ('welcome.tt2' => {'gettext_id' => "welcome message"}, 'bye.tt2' => {'gettext_id' => "unsubscribe message"}, 'removed.tt2' => {'gettext_id' => "deletion message"}, 'message.footer' => {'gettext_id' => "message footer"}, 'message.header' => {'gettext_id' => "message header"}, 'remind.tt2' => {'gettext_id' => "remind message"}, 'reject.tt2' => {'gettext_id' => "editor rejection message"}, 'invite.tt2' => {'gettext_id' => "subscribing invitation message"}, 'helpfile.tt2' => {'gettext_id' => "help file"}, 'lists.tt2' => {'gettext_id' => "directory of lists"}, 'global_remind.tt2' => {'gettext_id' => "global remind message"}, 'summary.tt2' => {'gettext_id' => "summary message"}, 'info' => {'gettext_id' => "list description"}, 'homepage' => {'gettext_id' => "list homepage"}, 'create_list_request.tt2' => {'gettext_id' => "list creation request message"}, 'list_created.tt2' => {'gettext_id' => "list creation notification message"}, 'your_infected_msg.tt2' => {'gettext_id' => "virus infection message"}, 'list_aliases.tt2' => {'gettext_id' => "list aliases template"} ); ## Defined in RFC 1893 %bounce_status = ('1.0' => 'Other address status', '1.1' => 'Bad destination mailbox address', '1.2' => 'Bad destination system address', '1.3' => 'Bad destination mailbox address syntax', '1.4' => 'Destination mailbox address ambiguous', '1.5' => 'Destination mailbox address valid', '1.6' => 'Mailbox has moved', '1.7' => 'Bad sender\'s mailbox address syntax', '1.8' => 'Bad sender\'s system address', '2.0' => 'Other or undefined mailbox status', '2.1' => 'Mailbox disabled, not accepting messages', '2.2' => 'Mailbox full', '2.3' => 'Message length exceeds administrative limit', '2.4' => 'Mailing list expansion problem', '3.0' => 'Other or undefined mail system status', '3.1' => 'Mail system full', '3.2' => 'System not accepting network messages', '3.3' => 'System not capable of selected features', '3.4' => 'Message too big for system', '4.0' => 'Other or undefined network or routing status', '4.1' => 'No answer from host', '4.2' => 'Bad connection', '4.3' => 'Routing server failure', '4.4' => 'Unable to route', '4.5' => 'Network congestion', '4.6' => 'Routing loop detected', '4.7' => 'Delivery time expired', '5.0' => 'Other or undefined protocol status', '5.1' => 'Invalid command', '5.2' => 'Syntax error', '5.3' => 'Too many recipients', '5.4' => 'Invalid command arguments', '5.5' => 'Wrong protocol version', '6.0' => 'Other or undefined media error', '6.1' => 'Media not supported', '6.2' => 'Conversion required and prohibited', '6.3' => 'Conversion required but not supported', '6.4' => 'Conversion with loss performed', '6.5' => 'Conversion failed', '7.0' => 'Other or undefined security status', '7.1' => 'Delivery not authorized, message refused', '7.2' => 'Mailing list expansion prohibited', '7.3' => 'Security conversion required but not possible', '7.4' => 'Security features not supported', '7.5' => 'Cryptographic failure', '7.6' => 'Cryptographic algorithm not supported', '7.7' => 'Message integrity failure'); ## if Crypt::CipherSaber installed store the cipher object my $cipher; ## Load WWSympa configuration file sub load_config { my $file = pop; ## Old params my %old_param = ('alias_manager' => 'No more used, using '.$Conf{'alias_manager'}, 'wws_path' => 'No more used', 'icons_url' => 'No more used. Using static_content/icons instead.', 'robots' => 'Not used anymore. Robots are fully described in their respective robot.conf file.', 'task_manager_pidfile' => 'No more used', 'bounced_pidfile' => 'No more used', 'archived_pidfile' => 'No more used', ); ## Valid params my %default_conf = map { $_->{'name'} => $_->{'default'} } grep { exists $_->{'file'} and $_->{'file'} eq 'wwsympa.conf' } @confdef::params; my $conf = \%default_conf; unless (open (FILE, $file)) { &Log::do_log('err',"load_config: unable to open $file"); return undef; } while () { next if /^\s*\#/; if (/^\s*(\S+)\s+(.+)$/i) { my ($k, $v) = ($1, $2); $v =~ s/\s*$//; if (exists $conf->{$k}) { $conf->{$k} = $v; }elsif (defined $old_param{$k}) { &Log::do_log('err',"Parameter %s in %s no more supported : %s", $k, $file, $old_param{$k}); }else { &Log::do_log('err',"Unknown parameter %s in %s", $k, $file); } } next; } close FILE; ## Check binaries and directories if ($conf->{'arc_path'} && (! -d $conf->{'arc_path'})) { &Log::do_log('err',"No web archives directory: %s\n", $conf->{'arc_path'}); } if ($conf->{'bounce_path'} && (! -d $conf->{'bounce_path'})) { &Log::do_log('err',"Missing directory '%s' (defined by 'bounce_path' parameter)", $conf->{'bounce_path'}); } if ($conf->{'mhonarc'} && (! -x $conf->{'mhonarc'})) { &Log::do_log('err',"MHonArc is not installed or %s is not executable.", $conf->{'mhonarc'}); } return $conf; } ## Load HTTPD MIME Types sub load_mime_types { my $types = {}; @localisation = ('/etc/mime.types', '/usr/local/apache/conf/mime.types', '/etc/httpd/conf/mime.types',$Conf{'etc'}.'/mime.types'); foreach my $loc (@localisation) { next unless (-r $loc); unless(open (CONF, $loc)) { &Log::do_log('err',"load_mime_types: unable to open $loc"); return undef; } } while () { next if /^\s*\#/; if (/^(\S+)\s+(.+)\s*$/i) { my ($k, $v) = ($1, $2); my @extensions = split / /, $v; ## provides file extention, given the content-type if ($#extensions >= 0) { $types->{$k} = $extensions[0]; } foreach my $ext (@extensions) { $types->{$ext} = $k; } next; } } close FILE; return $types; } ## Returns user information extracted from the cookie sub get_email_from_cookie { # &Log::do_log('debug', 'get_email_from_cookie'); my $cookie = shift; my $secret = shift; my ($email, $auth) ; # &Log::do_log('info', "get_email_from_cookie($cookie,$secret)"); unless (defined $secret) { &report::reject_report_web('intern','cookie_error',{},'','','',$robot); &Log::do_log('info', 'parameter cookie undefined, authentication failure'); } unless ($cookie) { &report::reject_report_web('intern','cookie_error',$cookie,'get_email_from_cookie','','',$robot); &Log::do_log('info', ' cookie undefined, authentication failure'); } ($email, $auth) = &cookielib::check_cookie ($cookie, $secret); unless ( $email) { &report::reject_report_web('user','auth_failed',{},''); &Log::do_log('info', 'get_email_from_cookie: auth failed for user %s', $email); return undef; } return ($email, $auth); } sub new_passwd { my $passwd; my $nbchar = int(rand 5) + 6; foreach my $i (0..$nbchar) { $passwd .= chr(int(rand 26) + ord('a')); } return 'init'.$passwd; } ## Basic check of an email address sub valid_email { my $email = shift; $email =~ /^([\w\-\_\.\/\+\=]+|\".*\")\@[\w\-]+(\.[\w\-]+)+$/; } sub init_passwd { my ($robot, $email, $data) = @_; my ($passwd, $user); if (&List::is_user_db($email)) { $user = &List::get_user_db($email); $passwd = $user->{'password'}; unless ($passwd) { $passwd = &new_passwd(); unless ( &List::update_user_db( $email, {'password' => $passwd} )) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$email},'','',$email,$robot); &Log::do_log('info','init_passwd: update failed'); return undef; } } }else { $passwd = &new_passwd(); unless ( &List::add_user_db({'email' => $email, 'password' => $passwd, 'lang' => $data->{'lang'}, 'gecos' => $data->{'gecos'}})) { &report::reject_report_web('intern','add_user_db_failed',{'user'=>$email},'','',$email,$robot); &Log::do_log('info','init_passwd: add failed'); return undef; } } return 1; } sub get_my_url { my $return_url; ## Mod_ssl sets SSL_PROTOCOL ; apache-ssl sets SSL_PROTOCOL_VERSION if ($ENV{'HTTPS'} eq 'on') { $return_url = 'https'; }else{ $return_url = 'http'; } $return_url .= '://'.&main::get_header_field('HTTP_HOST'); $return_url .= ':'.$ENV{'SERVER_PORT'} unless (($ENV{'SERVER_PORT'} eq '80')||($ENV{'SERVER_PORT'} eq '443')); $return_url .= $ENV{'REQUEST_URI'}; return ($return_url); } 1; sympa-6.1.24~dfsg/wwsympa/wwsympa-wrapper.fcgi.c000066400000000000000000000004031246372670100217530ustar00rootroot00000000000000#include int main(int argn, char **argv, char **envp) { setreuid(geteuid(),geteuid()); // Added to fix the segfault setregid(getegid(),getegid()); // Added to fix the segfault argv[0] = WWSYMPA; return execve(WWSYMPA,argv,envp); } sympa-6.1.24~dfsg/wwsympa/wwsympa.fcgi.in000066400000000000000000023127441246372670100205010ustar00rootroot00000000000000#!--PERL-- # wwsympa.fcgi - This script provides the web interface to Sympa # RCS Identication ; $Revision: 11778 $ ; $Date: 2014-12-17 11:41:05 +0100 (mer. 17 déc. 2014) $ # # Sympa - SYsteme de Multi-Postage Automatique # # Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel # Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites # Copyright (c) 2011, 2012, 2013, 2014 GIP RENATER # # 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, see . ## Copyright 1999 Comité Réseaux des Universités ## web interface to Sympa mailing lists manager ## Sympa: http://www.sympa.org/ ## Authors : ## Serge Aumont ## Olivier Salaün =encoding utf-8 =head1 NAME I - Sympa web interface =head1 DESCRIPTION This fcgi script completely handles all aspects of the Sympa web interface =cut use lib '--modulesdir--'; use Getopt::Long; use Archive::Zip; use Data::Dumper; use strict 'vars'; use Time::Local; use MIME::Lite::HTML; ## Sympa API use List; use mail; use Conf; use confdef; use Commands; use Language; use Log; use Auth; use admin; use SharedDocument; use report; use SympaSession; use Log; use tools; use time_utils; use Sympa::Constants; use wwslib; use cookielib; use Robot; use tt2; #use DateTime; #use open ':utf8'; ## Default is to consider files utf8 use Mail::Header; use Mail::Address; my $crypt_openssl_x509_ok; BEGIN { if (eval "require Crypt::OpenSSL::X509") { require Crypt::OpenSSL::X509; $crypt_openssl_x509_ok = 1; } else { $crypt_openssl_x509_ok = 0; } }; ## WWSympa librairies my %options; ## Configuration my $wwsconf = {}; ## Change to your wwsympa.conf location my $conf_file = Sympa::Constants::WWSCONFIG; my $sympa_conf_file = Sympa::Constants::CONFIG; my $loop = 0; my $list; my $param = {}; my ($robot, $robot_object); my $ip ; my $rss ; my $session; my $allow_absolute_path; #FIXME: to be removed in the future. ## Load config unless ($wwsconf = &wwslib::load_config($conf_file)) { &fatal_err('Unable to load config file %s', $conf_file); } ## Load sympa config unless (&Conf::load( $sympa_conf_file )) { &fatal_err('Unable to load sympa config file %s', $sympa_conf_file); } &Log::set_log_level($Conf{'log_level'}) if ($Conf{'log_level'}); &mail::set_send_spool($Conf{'queue'}); if ($wwsconf->{'use_fast_cgi'}) { require CGI::Fast; }else { require CGI; } my $daemon_name = &Log::set_daemon($0); my $mime_types = &wwslib::load_mime_types(); # hash of all the description files already loaded # format : # $desc_files{pathfile}{'date'} : date of the last load # $desc_files{pathfile}{'desc_hash'} : hash which describes # the description file #%desc_files_map; NOT USED ANYMORE # hash of the icons linked with a type of file my %icon_table; # application file $icon_table{'unknown'} = $Conf{'static_content_url'}.'/icons/unknown.png'; $icon_table{'folder'} = $Conf{'static_content_url'}.'/icons/folder.png'; $icon_table{'current_folder'} = $Conf{'static_content_url'}.'/icons/folder.open.png'; $icon_table{'application'} = $Conf{'static_content_url'}.'/icons/unknown.png'; $icon_table{'octet-stream'} = $Conf{'static_content_url'}.'/icons/binary.png'; $icon_table{'audio'} = $Conf{'static_content_url'}.'/icons/sound1.png'; $icon_table{'image'} = $Conf{'static_content_url'}.'/icons/image2.png'; $icon_table{'text'} = $Conf{'static_content_url'}.'/icons/text.png'; $icon_table{'video'} = $Conf{'static_content_url'}.'/icons/movie.png'; $icon_table{'father'} = $Conf{'static_content_url'}.'/icons/back.png'; $icon_table{'sort'} = $Conf{'static_content_url'}.'/icons/down.png'; $icon_table{'url'} = $Conf{'static_content_url'}.'/icons/link.png'; $icon_table{'left'} = $Conf{'static_content_url'}.'/icons/left.png'; $icon_table{'right'} = $Conf{'static_content_url'}.'/icons/right.png'; ## Shared directory and description file #$shared = 'shared'; #$desc = '.desc'; ## subroutines my %comm = ('home' => 'do_home', 'logout' => 'do_logout', 'loginrequest' => 'do_loginrequest', 'login' => 'do_login', 'sso_login' => 'do_sso_login', 'sso_login_succeeded' => 'do_sso_login_succeeded', 'subscribe' => 'do_subscribe', 'multiple_subscribe' => 'do_multiple_subscribe', 'subrequest' => 'do_subrequest', 'subindex' => 'do_subindex', 'suboptions' => 'do_suboptions', 'signoff' => 'do_signoff', 'auto_signoff' => 'do_auto_signoff', 'family_signoff' => 'do_family_signoff', 'family_signoff_request' => 'do_family_signoff_request', #XXX'multiple_signoff' => 'do_multiple_signoff', 'sigrequest' => 'do_sigrequest', 'ignoresub' => 'do_ignoresub', 'which' => 'do_which', 'lists' => 'do_lists', 'latest_lists' => 'do_latest_lists', 'active_lists' => 'do_active_lists', 'info' => 'do_info', 'subscriber_count' => 'do_subscriber_count', 'review' => 'do_review', 'search' => 'do_search', 'pref', => 'do_pref', 'setpref' => 'do_setpref', 'setpasswd' => 'do_setpasswd', 'renewpasswd' => 'do_renewpasswd', 'firstpasswd' => 'do_firstpasswd', 'requestpasswd' => 'do_requestpasswd', 'choosepasswd' => 'do_choosepasswd', 'viewfile' => 'do_viewfile', 'set' => 'do_set', 'admin' => 'do_admin', 'add_request' => 'do_add_request', 'add' => 'do_add', 'del' => 'do_del', 'modindex' => 'do_modindex', 'reject' => 'do_reject', #XXX'reject_notify' => 'do_reject_notify', 'distribute' => 'do_distribute', 'viewmod' => 'do_viewmod', 'd_reject_shared' => 'do_d_reject_shared', #XXX'reject_notify_shared' => 'do_reject_notify_shared', 'd_install_shared' => 'do_d_install_shared', 'editfile' => 'do_editfile', 'savefile' => 'do_savefile', 'arc' => 'do_arc', 'latest_arc' => 'do_latest_arc', 'latest_d_read' => 'do_latest_d_read', 'arc_manage' => 'do_arc_manage', 'remove_arc' => 'do_remove_arc', 'send_me' => 'do_send_me', 'view_source' => 'do_view_source', 'arcsearch_form' => 'do_arcsearch_form', 'arcsearch_id' => 'do_arcsearch_id', 'arcsearch' => 'do_arcsearch', 'rebuildarc' => 'do_rebuildarc', 'rebuildallarc' => 'do_rebuildallarc', 'arc_download' => 'do_arc_download', 'arc_delete' => 'do_arc_delete', 'serveradmin' => 'do_serveradmin', 'set_loglevel' => 'do_set_loglevel', 'set_dumpvars' => 'do_set_dumpvars', 'show_sessions' => 'do_show_sessions', 'unset_dumpvars' => 'do_unset_dumpvars', 'set_session_email' => 'do_set_session_email', 'restore_email' => 'do_restore_email', 'skinsedit' => 'do_skinsedit', 'css' => 'do_css', 'help' => 'do_help', 'edit_list_request' => 'do_edit_list_request', 'edit_list' => 'do_edit_list', 'create_list_request' => 'do_create_list_request', 'create_list' => 'do_create_list', 'get_pending_lists' => 'do_get_pending_lists', 'get_closed_lists' => 'do_get_closed_lists', 'get_latest_lists' => 'do_get_latest_lists', 'get_inactive_lists' => 'do_get_inactive_lists', 'set_pending_list_request' => 'do_set_pending_list_request', 'install_pending_list' => 'do_install_pending_list', 'edit_config' => 'do_edit_config', #XXX'submit_list' => 'do_submit_list', 'editsubscriber' => 'do_editsubscriber', 'viewbounce' => 'do_viewbounce', 'redirect' => 'do_redirect', 'rename_list_request' => 'do_rename_list_request', 'rename_list' => 'do_rename_list', 'copy_list' => 'do_copy_list', 'reviewbouncing' => 'do_reviewbouncing', 'resetbounce' => 'do_resetbounce', 'scenario_test' => 'do_scenario_test', 'search_list' => 'do_search_list', 'show_cert' => 'do_show_cert', 'close_list_request' => 'do_close_list_request', 'close_list' => 'do_close_list', 'purge_list' => 'do_purge_list', 'restore_list' => 'do_restore_list', 'upload_pictures' => 'do_upload_pictures', 'delete_pictures' => 'do_delete_pictures', 'd_read' => 'do_d_read', 'd_create_dir' => 'do_d_create_dir', 'd_upload' => 'do_d_upload', 'd_unzip' => 'do_d_unzip', 'd_editfile' => 'do_d_editfile', 'd_properties' => 'do_d_properties', 'd_overwrite' => 'do_d_overwrite', 'd_savefile' => 'do_d_savefile', 'd_describe' => 'do_d_describe', 'd_delete' => 'do_d_delete', 'd_rename' => 'do_d_rename', 'd_control' => 'do_d_control', 'd_change_access' => 'do_d_change_access', 'd_set_owner' => 'do_d_set_owner', 'd_admin' => 'do_d_admin', 'dump_scenario' => 'do_dump_scenario', 'dump' => 'do_dump', 'arc_protect' => 'do_arc_protect', 'remind' => 'do_remind', 'change_email' => 'do_change_email', 'change_email_request' => 'do_change_email_request', 'load_cert' => 'do_load_cert', 'compose_mail' => 'do_compose_mail', 'send_mail' => 'do_send_mail', 'request_topic' => 'do_request_topic', 'tag_topic_by_sender' =>'do_tag_topic_by_sender', 'search_user' => 'do_search_user', 'set_lang' => 'do_set_lang', 'attach' => 'do_attach', 'stats' => 'do_stats', 'viewlogs'=> 'do_viewlogs', 'wsdl'=> 'do_wsdl', 'sync_include' => 'do_sync_include', 'review_family' => 'do_review_family', 'ls_templates' => 'do_ls_templates', 'remove_template' => 'do_remove_template', 'copy_template' => 'do_copy_template', 'view_template' => 'do_view_template', 'edit_template' => 'do_edit_template', #'rss' => 'do_rss', #FIXME:Currently processed in differenct way. 'rss_request' => 'do_rss_request', 'maintenance' => 'do_maintenance', 'blacklist' => 'do_blacklist', 'edit_attributes' => 'do_edit_attributes', 'ticket' => 'do_ticket', 'manage_template' => 'do_manage_template', #XXX'send_newsletter' => 'do_send_newsletter', 'suspend_request' => 'do_suspend_request', 'suspend_request_action' => 'do_suspend_request_action', 'show_exclude' => 'do_show_exclude', 'ca' => 'do_ca', # 'ca' stands for 'custom_action'. I used a short name to make it discrete in a URL. 'lca' => 'do_lca', # 'lca' stands for 'list_custom_action'. I used a short name to make it discrete in a URL. 'automatic_lists_management_request' => 'do_automatic_lists_management_request', 'automatic_lists_management' => 'do_automatic_lists_management', 'automatic_lists_request' => 'do_automatic_lists_request', 'automatic_lists' => 'do_automatic_lists', ); my %auth_action = ('logout' => 1, 'loginrequest' => 1, 'login' => 1, 'sso_login' => 1, 'sso_login_succeeded' => 1, 'renewpasswd' => 1, 'firstpasswd' => 1, 'choosepasswd' => 1, 'sendssopasswd' => 1, #FIXME: currently not used 'ticket' => 1, ); ## Arguments awaited in the PATH_INFO, depending on the action my %action_args = ('default' => ['list'], 'editfile' => ['list','file'], 'requestpasswd' => ['email'], 'choosepasswd' => ['email','passwd'], 'lists' => ['topic','subtopic'], 'latest_lists' => ['topic','subtopic'], 'active_lists' => ['topic','subtopic'], 'login' => ['email','passwd','previous_action','previous_list'], 'sso_login' => ['auth_service_name','subaction','email', 'ticket'], 'sso_login_succeeded' => ['auth_service_name','previous_action','previous_list'], 'loginrequest' => ['previous_action','previous_list'], 'logout' => ['previous_action','previous_list'], 'renewpasswd' => ['previous_action','previous_list'], 'firstpasswd' => ['previous_action','previous_list'], 'css' => ['file'], 'pref' => ['previous_action','previous_list'], 'reject' => ['list','id'], 'distribute' => ['list','id'], 'dump_scenario' => ['list','pname'], 'd_reject_shared' => ['list','id'], 'd_install_shared' => ['list','id'], 'modindex' => ['list'], 'viewmod' => ['list','id','@file'], 'viewfile' => ['list','file'], 'add' => ['list','email'], 'add_request' => ['list'], 'del' => ['list','email'], 'editsubscriber' => ['list','email','previous_action','custom_attribute'], # 'editsubscriber' => ['list','email','previous_action'], 'viewbounce' => ['list','email'], 'resetbounce' => ['list','email'], 'review' => ['list','page','size','sortby'], 'reviewbouncing' => ['list','page','size'], 'arc' => ['list','month','@arc_file'], 'latest_arc' => ['list'], 'arc_manage' => ['list'], 'arcsearch_form' => ['list','archive_name'], 'arcsearch_id' => ['list','archive_name','msgid'], 'rebuildarc' => ['list','month'], 'rebuildallarc' => [], 'arc_download' => ['list'], 'arc_delete' => ['list','zip'], 'home' => [], 'help' => ['help_topic'], 'show_cert' => [], 'subscribe' => ['list','email','passwd'], #'subrequest' => ['list','email'], 'subrequest' => ['list'], 'subindex' => ['list'], 'ignoresub' => ['list','@email','@gecos'], 'signoff' => ['list','email','passwd'], 'auto_signoff' => ['list','email'], 'family_signoff' => ['family','email'], 'family_signoff_request' => ['family','email'], 'sigrequest' => ['list','email'], 'set' => ['list','email','reception','gecos'], 'serveradmin' => ['subaction'], 'set_session_email' => ['email'], 'skinsedit' => [], 'get_pending_lists' => [], 'get_closed_lists' => [], 'get_latest_lists' => [], 'get_inactive_lists' => [], 'search_list' => ['filter'], 'shared' => ['list','@path'], #FIXME: no such function. 'd_read' => ['list','@path'], 'latest_d_read' => ['list'], 'd_admin' => ['list','d_admin'], 'd_delete' => ['list','@path'], 'd_rename' => ['list','@path'], 'd_create_dir' => ['list','@path'], 'd_overwrite' => ['list','@path'], 'd_savefile' => ['list','@path'], 'd_describe' => ['list','@path'], 'd_editfile' => ['list','@path'], 'd_properties' => ['list','@path'], 'd_control' => ['list','@path'], 'd_change_access' => ['list','@path'], 'd_set_owner' => ['list','@path'], 'dump' => ['list','format'], 'search' => ['list','filter'], 'search_user' => ['email'], 'set_lang' => ['lang'], 'attach' => ['list','dir','file'], 'edit_list_request' => ['list','group'], 'rename_list' => ['list','new_list','new_robot'], 'copy_list' => ['list','new_list','new_robot'], 'redirect' => [], 'viewlogs' => ['list','page','size','sortby'], 'wsdl' => [], 'sync_include' => ['list'], 'review_family' => ['family_name'], 'ls_templates' => ['list'], 'view_template' => [], 'remove_template' => [], 'copy_template' => ['list'], 'edit_template' => ['list'], 'rss_request' => ['list'], 'request_topic' => ['list','authkey'], 'tag_topic_by_sender' => ['list'], 'multiple_subscribe' => ['lists'], 'multiple_signoff' => ['lists'], 'ticket' => ['ticket'], 'change_email' => ['email'], 'manage_template' => ['subaction','list','message_template'], 'send_newsletter' => [], 'compose_mail' => ['list','subaction'], 'suspend_request' => ['subaction'], 'show_exclude' => ['list'], 'ca' => ['custom_action','@cap'], 'lca' => ['custom_action','list','@cap'], 'automatic_lists_management_request' => [], 'automatic_lists_management' => [], 'automatic_lists_request' => ['family'], 'automatic_lists' => [], ); ## Define the required parameters for each action ## Parameter names refer to the %in structure of to $param if mentionned as 'param.x' ## This structure is used to determine if any parameter is missing ## The list of parameters is not ordered ## Some keywords are reserved: param.list and param.user.email ## Alternate parameters can be defined with the '|' character ## Limits of this structure: it does not define optional parameters (a or b) ## Limit: it does not allow to have a specific error message and redirect to a given page if the parameter is missing my %required_args = ('active_lists' => ['for|count'], 'admin' => ['param.list','param.user.email'], 'add' => ['param.list','param.user.email'], 'add_request' => ['param.list','param.user.email'], 'arc' => ['param.list'], 'arc_delete' => ['param.user.email','param.list'], 'arc_download' => ['param.user.email','param.list'], 'arc_manage' => ['param.list'], 'arc_protect' => ['param.list'], 'arcsearch' => ['param.list'], 'arcsearch_form' => ['param.list'], 'arcsearch_id' => ['param.list'], 'automatic_lists_request' => ['family'], 'automatic_lists' => ['family'], 'attach' => ['param.list'], 'blacklist' => ['param.list'], 'change_email' => ['param.user.email'], 'change_email_request' => ['param.user.email','new_email'], 'close_list' => ['param.user.email','param.list'], 'close_list_request' => ['param.user.email','param.list'], 'compose_mail' => ['param.user.email','param.list'], 'copy_template' => ['webormail'], 'create_list' => ['param.user.email'], ## other required parameters are checked in the subroutine 'create_list_request' => ['param.user.email'], 'css' => [], 'd_admin' => ['param.list','param.user.email'], 'd_change_access' => ['param.list','param.user.email'], 'd_control' => ['param.list','param.user.email'], 'd_create_dir' => ['param.list','param.user.email','name_doc'], 'd_delete' => ['param.list','param.user.email'], 'd_describe' => ['param.list','param.user.email','content'], 'd_editfile' => ['param.list','param.user.email'], 'd_install_shared' => ['param.list','param.user.email','id'], 'd_overwrite' => ['param.list','param.user.email'], 'd_properties' => ['param.list','param.user.email'], 'd_read' => ['param.list'], 'd_reject_shared' => ['param.list','param.user.email','id'], 'd_rename' => ['param.list','param.user.email','new_name'], 'd_savefile' => ['param.list','param.user.email','content|url'], 'd_set_owner' => ['param.list','param.user.email'], 'd_unzip' => ['param.list','param.user.email'], 'd_upload' => ['param.list','param.user.email'], 'del' => ['param.list','param.user.email','email'], 'delete_pictures' => ['param.list','param.user.email'], 'distribute' => ['param.list','param.user.email','id|idspam'], 'dump' => ['param.list'], 'dump_scenario' => ['param.list','pname'], 'edit_list' => ['param.user.email','param.list'], 'edit_list_request' => ['param.user.email','param.list'], 'edit_template' => ['webormail'], 'editfile' => ['param.user.email'], 'editsubscriber' => ['param.list','param.user.email','email'], 'get_closed_lists' => ['param.user.email'], 'get_inactive_lists' => ['param.user.email'], 'get_latest_lists' => ['param.user.email'], 'get_pending_lists' => ['param.user.email'], 'ignoresub' => ['param.list','param.user.email'], 'info' => ['param.list'], 'install_pending_list' => ['param.user.email'], 'edit_config' => ['param.user.email'], 'latest_arc' => ['param.list','for|count'], 'latest_d_read' => ['param.list','for','count'], 'latest_lists' => ['for|count'], 'load_cert' => ['param.list'], 'logout' => ['param.user.email'], 'manage_template' => ['param.list','param.user.email'], 'modindex' => ['param.list','param.user.email'], 'multiple_subscribe' => ['param.list'], 'pref' => ['param.user.email'], 'purge_list' => ['param.user.email','selected_lists'], 'rebuildallarc' => ['param.user.email'], 'rebuildarc' => ['param.user.email','param.list'], 'reject' => ['param.list','param.user.email','id|idspam'], 'remind' => ['param.list','param.user.email'], 'remove_arc' => ['param.list'], 'remove_template' => ['webormail'], 'rename_list' => ['param.user.email','param.list','new_listname','new_robot'], 'copy_list' => ['param.user.email','param.list','new_listname','new_robot'], 'rename_list_request' => ['param.user.email','param.list'], 'request_topic' => ['param.list','authkey'], 'resetbounce' => ['param.list','param.user.email','email'], 'restore_list' => ['param.user.email','param.list'], 'review' => ['param.list'], 'review_family' => ['param.user.email','family_name'], 'reviewbouncing' => ['param.list'], 'rss_request' => [], 'savefile' => ['param.user.email','file'], 'search' => ['param.list','filter'], 'search_user' => ['param.user.email','email'], 'send_mail' => ['param.user.email'], 'send_newsletter' => ['param.list','param.user.email', 'url'], 'send_me' => ['param.list'], 'view_source' => ['param.list'], 'requestpasswd' => ['email'], 'serveradmin' => ['param.user.email'], 'set' => ['param.list','reception|visibility'], 'set_lang' => [], 'set_pending_list_request' => ['param.user.email'], 'setpasswd' => ['param.user.email','newpasswd1','newpasswd2'], 'setpref' => ['param.user.email'], 'signoff' => ['param.list'], 'sigrequest' => ['param.list'], 'skinsedit' => ['param.user.email'], 'sso_login' => ['auth_service_name'], 'stats' => ['param.user.email','param.list'], 'subindex' => ['param.list','param.user.email'], 'suboptions' => ['param.list','param.user.email'], 'subrequest' => ['param.list'], 'subscribe' => ['param.list'], 'subscriber_count' => ['param.list'], 'suspend_request' => [], 'suspend_request_action' => [], 'show_exclude' => ['param.list'], 'sync_include' => ['param.list','param.user.email'], 'tag_topic_by_sender' => ['param.list'], 'upload_pictures' => ['param.user.email','param.list'], 'view_template' => ['webormail'], 'viewbounce' => ['param.list','email'], 'viewfile' => ['file','param.list'], 'viewlogs' => ['param.list'], 'viewmod' => ['param.list','param.user.email','id|idspam'], 'wsdl' => [], 'which' => ['param.user.email'], ); ## Defines the required privileges to access privileged actions ## You can define a set ofequiivalent privileges in the ARRAYREF my %required_privileges = ('admin' => ['owner','editor'], 'arc_delete' => ['owner'], 'arc_download' => ['owner'], 'arc_manage' => ['owner'], 'blacklist' => ['owner','editor'], 'close_list' => ['privileged_owner'], 'close_list_request' => ['privileged_owner'], 'copy_template' => ['listmaster'], 'd_install_shared' => ['editor','owner'], 'd_reject_shared' => ['editor','owner'], 'distribute' => ['editor','owner','listmaster'], 'dump_scenario' => ['listmaster'], 'edit_list' => ['owner'], 'edit_list_request' => ['owner'], 'edit_template' => ['listmaster'], 'editsubscriber' => ['owner','editor'], 'get_closed_lists' => ['listmaster'], 'get_inactive_lists' => ['listmaster'], 'get_latest_lists' => ['listmaster'], 'get_pending_lists' => ['listmaster'], 'ignoresub' => ['owner','editor'], 'install_pending_list' => ['listmaster'], 'edit_config' => ['listmaster'], 'ls_templates' => ['listmaster'], 'manage_template' => ['owner'], 'modindex' => ['editor','owner','listmaster'], 'purge_list' => ['privileged_owner','listmaster'], 'rebuildallarc' => ['listmaster'], 'rebuildarc' => ['listmaster'], 'reject' => ['editor','owner','listmaster'], 'remove_template' => ['listmaster'], 'rename_list' => ['privileged_owner'], 'copy_list' => ['owner','listmaster'], 'rename_list_request' => ['privileged_owner'], 'resetbounce' => ['owner','editor'], 'restore_list' => ['listmaster'], 'review_family' => ['listmaster'], 'reviewbouncing' => ['owner','editor'], 'search_user' => ['listmaster'], 'serveradmin' => ['listmaster'], 'set_dumpvars' => ['listmaster'], 'set_loglevel' => ['listmaster'], 'set_pending_list_request' => ['listmaster'], 'set_session_email' => ['listmaster'], 'show_sessions' => ['listmaster'], 'stats' => ['owner'], 'subindex' => ['owner','editor'], 'sync_include' => ['owner','editor'], 'skinsedit' => ['listmaster'], 'view_template' => ['listmaster'], 'viewbounce' => ['owner','editor'], 'viewlogs' => ['owner','editor'], 'viewmod' => ['editor','owner','listmaster'], 'automatic_lists_management_request' => ['listmaster'], 'automatic_lists_management' => ['listmaster'], ); # this definition is used to choose the left side menu type (admin -> listowner admin menu | serveradmin -> server_admin menu | none list or your_list menu) my %action_type = ( 'review' => 'admin', 'search' => 'admin', 'viewfile' => 'admin', 'admin' => 'admin', 'add_request' =>'admin', 'add' =>'admin', 'del' =>'admin', # 'modindex' =>'admin', 'reject' =>'admin', 'reject_notify' =>'admin', 'distribute' =>'admin', 'viewmod' =>'admin', 'savefile' =>'admin', 'rebuildallarc' =>'admin', #FIXME: serveradmin? 'reviewbouncing' =>'admin', 'edit_list_request' =>'admin', 'edit_list' =>'admin', 'editsubscriber' =>'admin', 'viewbounce' =>'admin', 'resetbounce' =>'admin', 'scenario_test' =>'admin', 'close_list_request' =>'admin', 'close_list' =>'admin', 'restore_list' => 'admin', 'd_admin' => 'admin', 'd_reject_shared' =>'admin', 'd_install_shared' =>'admin', 'dump_scenario' => 'admin', 'dump' => 'admin', 'remind' => 'admin', # 'subindex' => 'admin', 'stats' => 'admin', 'ignoresub' => 'admin', 'rename_list' => 'admin', 'copy_list' => 'admin', 'rename_list_request' => 'admin', 'arc_manage' => 'admin', 'sync_include' => 'admin', 'view_template' => 'admin', 'remove_template' => 'admin', 'copy_template' => 'admin', 'edit_template' => 'admin', 'blacklist' => 'admin', 'viewlogs' => 'admin', 'serveradmin' => 'serveradmin', 'get_pending_lists' => 'serveradmin', 'get_closed_lists' => 'serveradmin', 'get_inactive_lists' => 'serveradmin', 'get_latest_lists' => 'serveradmin', 'ls_templates' => 'serveradmin', 'skinsedit' => 'serveradmin', 'review_family' => 'serveradmin', 'search_user' => 'serveradmin', 'show_sessions' => 'serveradmin', 'show_exclude' => 'admin', 'rebuildarc' => 'serveradmin', 'set_session_email' => 'serveradmin', 'set_loglevel' => 'serveradmin', 'editfile' => 'serveradmin', #FIXME: admin? 'unset_dumpvars' => 'serveradmin', 'set_dumpvars' => 'serveradmin', 'automatic_lists_management_request' => 'serveradmin', 'automatic_lists_management' => 'serveradmin', ); ## actions tthat are not used in return of login, my %temporary_actions = ( 'logout' => 1, 'loginrequest' => 1, 'login' => 1, 'sso_login' => 1, 'sso_login_succeeded' => 1, 'ticket' => 1, 'css' => 1, 'rss' => 1, # FIXME:currently not used. 'wsdl' => 1, 'redirect' => 1, ); ## Regexp applied on incoming parameters (%in) ## The aim is not a strict definition of parameter format ## but rather a security check my %in_regexp = ( ## Default regexp '*' => '[\w\-\.]+', ## List config parameters 'single_param' => '.+', 'multiple_param' => '.+', ## Textarea content 'template_content' => '.+', 'content' => '.+', 'body' => '.+', 'info' => '.+', 'new_scenario_content' => '.+', 'blacklist' => '.*', ## Integer 'page' => '\d+', 'size' => '\d+', ## Free data 'subject' => '.*', 'gecos' => '[^<>\\\*\$\n]+', 'additional_field' => '[^<>\\\*\$\n]+', 'dump' => '[^<>\\\*\$]+', # contents email + gecos ## Search 'filter' => '[^<>\\\[\]\(\)\$\n]+', # search list 'key_word' => '.*', 'format' => '[^<>\\\$\n]+', # dump format/filter string ## File names 'file' => '[^<>\*\$\n]+', 'template_path' => '[\w\-\.\/_]+', 'arc_file' => '[^<>\\\*\$\n]+', 'path' => '[^<>\\\*\$\n]+', 'uploaded_file' => '(.*[\/\\\\])?[^<>\*\$\n]+', # Could be precised (use of "'") 'unzipped_file' => '(.*[\/\\\\])?[^<>\*\$\n]+', 'dir' => '[^<>\\\*\$\n]+', 'name_doc' => '[^<>\\\*\$\[\]\/\n]+', 'shortname' => '[^<>\\\*\$\n]+', 'new_name' => '[^<>\\\*\$\n]+', 'id' => '[^<>\\\*\$\n]+', 'template_name' => &tools::get_regexp('template_name'), 'new_template_name' => &tools::get_regexp('template_name'), 'message_template' => &tools::get_regexp('template_name'), 'new_default' => &tools::get_regexp('template_name'), ## Archives 'month' => '\d{2}|\d{4}\-\d{2}', ## format is yyyy-mm for 'arc' and mm for 'send_me' ## URL 'referer' => '[^\\\$\*\"\'\`\^\|\<\>\n]+', 'failure_referer' => '[^\\\$\*\"\'\`\^\|\<\>\n]+', 'url' => '[^\\\$\*\"\'\`\^\|\<\>\n]+', ## Msg ID 'msgid' => '[^\\\*\"\'\`\^\|\n]+', 'in_reply_to' => '[^\\\*\"\'\`\^\|\n]+', 'message_id' => '[^\\\*\"\'\`\^\|\n]+', ## Password 'passwd' => '.+', 'password' => '.+', 'newpasswd1' => '.+', 'newpasswd2' => '.+', 'new_password' => '.+', ## Topics 'topic' => '[\-\w\/]+', 'topics' => '[\-\w\/]+', 'subtopic' => '[\-\w\/]+', ## List names 'list' => '[\w\-\.\+]*', ## &tools::get_regexp('listname') + uppercase 'previous_list' => '[\w\-\.\+]*', 'new_list' => '[\w\-\.\+]*', 'listname' => '[\w\-\.\+]*', 'new_listname' => '[\w\-\.\+]*', 'selected_lists' => '[\w\-\.\+]*', ## Family names 'family_name' => &tools::get_regexp('family_name'), 'family' => &tools::get_regexp('family_name'), ## Email addresses 'email' => &tools::get_regexp('email').'|'.&tools::get_regexp('uid'), 'init_email' => &tools::get_regexp('email'), 'old_email' => &tools::get_regexp('email'), 'new_email' => &tools::get_regexp('email'), 'pending_email' => &tools::get_regexp('email').',.*', # Email address is followed by ',' + gecos data 'sender' => &tools::get_regexp('email'), 'to' => '(([\w\-\_\.\/\+\=\']+|\".*\")\s[\w\-]+(\.[\w\-]+)+(,?))*', 'automatic_list_part_*' => '[\w\-\.\+]*', ## Host 'new_robot' => &tools::get_regexp('host'), 'remote_host' => &tools::get_regexp('host'), 'remote_addr' => &tools::get_regexp('host'), ## Scenario name 'scenario' => &tools::get_regexp('scenario'), 'read_access' => &tools::get_regexp('scenario'), 'edit_access' => &tools::get_regexp('scenario'), ## RSS URL or blank 'active_lists' => '.*', 'latest_lists' => '.*', 'latest_arc' => '.*', 'latest_d_read' => '.*', ##Logs 'target_type' => '[\w\-\.\:]*', 'target' => &tools::get_regexp('email'), 'date_from' => '[\d\/-]+', 'date_to' => '[\d\/-]+', 'ip' => &tools::get_regexp('host'), ## colors 'subaction_test' => '.*', 'subaction_reset' => '.*', 'subaction_install' => '.*', 'custom_color_value' => '\#[0-9a-fA-F]+', 'custom_color_number' => 'color_\w+', ## Custom attribute 'custom_attribute' => '.*', ## Templates 'scope' => 'distrib|robot|family|list|site', ## Custom Inputs from create_list_request.tt2 'custom_input' => '.*', ## conf parameters 'conf_new_value' => '.*', ## custom actions 'cap' => '.*', 'lcap' => '.*', ); ## Regexp applied on incoming parameters (%in) ## This regular expression defines forbidden expressions applied on all incoming parameters ## Note that you can use the ^ and $ expressions to match beginning and ending of expressions my %in_negative_regexp = ( 'arc_file' => '^(arctxt|\.)' ); ## List some required filtering of incoming parameters, depending on current action ## Paramater can be '*' or 'param*' ## Like Q-encoding my %filtering = ('d_reject_shared' => {'id' => 'qencode'}, 'd_install_shared' => {'id' => 'qencode'}, 'd_read' => {'path' => 'qencode'}, 'd_create_dir' => {'name_doc' => 'qencode', 'path' => 'qencode'}, 'd_upload' => {'path' => 'qencode'}, 'd_unzip' => {'path' => 'qencode'}, 'd_editfile' => {'path' => 'qencode'}, 'd_properties' => {'path' => 'qencode'}, 'd_overwrite' => {'path' => 'qencode'}, 'd_savefile' => {'path' => 'qencode', 'name_doc' => 'qencode'}, 'd_describe' => {'path' => 'qencode'}, 'd_delete' => {'path' => 'qencode'}, 'd_rename' => {'path' => 'qencode','new_name' => 'qencode'}, 'd_control' => {'path' => 'qencode'}, 'd_change_access' => {'path' => 'qencode'}, 'd_set_owner' => {'path' => 'qencode'}, 'requestpasswd' => {'email' => 'fix_escape_uri'}, 'viewbounce' => {'email' => 'fix_escape_uri'}, 'editsubscriber' => {'email' => 'fix_escape_uri'}, 'edit_list' => {'*param*' => 'unescape_html'}, ## Required because outgoing parameters have been html-escaped in edit_list_request 'change_email' => {'*email' => 'normalize'}, ## Remove leading/trailing white spaces and lowercase ); ## Open log $wwsconf->{'log_facility'}||= $Conf{'syslog'}; &Log::do_openlog($wwsconf->{'log_facility'}, $Conf{'log_socket_type'}, 'wwsympa'); &do_log('info', 'WWSympa started'); ## Set locale configuration $Language::default_lang = $Conf{'lang'}; #XXX## Important to leave this there because it defined defaults for user_data_source #XXX&List::check_db_connect(); my $pinfo = &List::_apply_defaults(); ## Check that the data structure is uptodate ## If not, set the web interface to maintenance mode my $maintenance_mode; unless (&Upgrade::data_structure_uptodate()) { $maintenance_mode = 1; &do_log('err',"Web interface set to maintenance mode ; you should run sympa.pl --upgrade"); } &tools::ciphersaber_installed(); %::changed_params; my (%in, $query); my $birthday = time ; # Now internal encoding is same as input/output. #XXX## Set output encoding #XXX## All outgoing strings will be recoded transparently using this charset #XXXbinmode STDOUT, ":utf8"; #XXX## Incoming data is utf8-encoded #XXXbinmode STDIN, ":utf8"; ## Main loop my $loop_count; my $start_time = time; while ($query = &new_loop()) { undef %::changed_params; undef $param; undef $list; undef $robot; undef $robot_object; undef $ip; undef $rss; undef $session; &Log::set_log_level($Conf{'log_level'}); &Language::SetLang($Language::default_lang); ## Empty cache of the List.pm module &List::init_list_cache(); ## Check effective ID unless ($> eq (getpwnam(Sympa::Constants::USER))[2]) { $maintenance_mode = 1; &report::reject_report_web('intern_quiet','incorrect_server_config',{},'',''); &wwslog('err','Config error: wwsympa should run with UID %s (instead of %s). *** Switching to maintenance mode. ***', (getpwnam(Sympa::Constants::USER))[2], $>); } unless (&List::check_db_connect()) { &report::reject_report_web('system_quiet','no_database',{},'',''); &do_log('info','WWSympa requires a RDBMS to run'); } ## If in maintenance mode, check if the data structure is now uptodate if ($maintenance_mode && (&Upgrade::data_structure_uptodate() && ($> eq (getpwnam(Sympa::Constants::USER))[2]))) { $maintenance_mode = undef; &do_log('notice',"Data structure seem updated, setting OFF maintenance mode"); } ## Get params in a hash # foreach ($query->param) { # $in{$_} = $query->param($_); # } %in = $query->Vars; foreach my $k (keys %::changed_params) { &do_log('debug3', 'Changed Param: %s', $k); } ## Free terminated sendmail processes # &smtp::reaper; ## Parse CGI parameters # &CGI::ReadParse(); if (defined $Conf{'robot_by_http_host'}{&get_header_field('SERVER_NAME')}) { my ($selected_robot, $selected_path); my ($k,$v); while (($k, $v) = each %{$Conf{'robot_by_http_host'}{&get_header_field('SERVER_NAME')}}) { if ($ENV{'REQUEST_URI'} =~ /^$k/) { ## Longer path wins if (length($k) > length($selected_path)) { ($selected_robot, $selected_path) = ($v, $k); } } } $robot = $selected_robot; } $robot = $Conf{'host'} unless $robot; ## Create Robot object $robot_object = new Robot $robot; ## Default robot if ($robot eq $Conf{'host'}) { $param->{'default_robot'} = 1; } $param->{'cookie_domain'} = $Conf{'robots'}{$robot}{'cookie_domain'} if $Conf{'robots'}{$robot}; $param->{'cookie_domain'} ||= $wwsconf->{'cookie_domain'}; $ip = $ENV{'REMOTE_HOST'}; $ip = $ENV{'REMOTE_ADDR'} unless ($ip); $ip = 'undef' unless ($ip); ## In case HTTP_HOST does not match cookie_domain my $http_host = &get_header_field('HTTP_HOST'); $http_host =~ s/:\d+$//; ## suppress port unless (($http_host =~ /$param->{'cookie_domain'}$/) || ($param->{'cookie_domain'} eq 'localhost')) { &wwslog('notice', 'Cookie_domain(%s) does NOT match HTTP_HOST; setting cookie_domain to %s', $param->{'cookie_domain'}, $http_host); $param->{'cookie_domain'} = $http_host; } &Log::set_log_level($Conf{'robots'}{$robot}{'log_level'}); ## Sympa parameters in $param->{'conf'} $param->{'conf'} = {}; foreach my $p ('email','host','sympa','request','soap_url','wwsympa_url','listmaster_email','logo_html_definition', 'main_menu_custom_button_1_url','main_menu_custom_button_1_title','main_menu_custom_button_1_target', 'main_menu_custom_button_2_url','main_menu_custom_button_2_title','main_menu_custom_button_2_target', 'main_menu_custom_button_3_url','main_menu_custom_button_3_title','main_menu_custom_button_3_target', 'dark_color','light_color','text_color','bg_color','error_color','use_blacklist','antispam_feature','custom_robot_parameter', 'selected_color','shaded_color','color_0','color_1','color_2','color_3','color_4','color_5','color_6','color_7','color_8','color_9','color_10','color_11','color_12','color_13','color_14','color_15','automatic_list_families') { $param->{'conf'}{$p} = &Conf::get_robot_conf($robot, $p); $param->{$p} = &Conf::get_robot_conf($robot, $p) if (($p =~ /_color$/)|| ($p =~ /color_/)); } foreach my $auth (keys %{$Conf{'cas_id'}{$robot}}) { &do_log('debug2', "cas authentication service $auth"); $param->{'sso'}{$auth} = $auth; } foreach my $auth (keys %{$Conf{'generic_sso_id'}{$robot}}) { &do_log('debug', "Generic SSO authentication service $auth"); $param->{'sso'}{$auth} = $Conf{'auth_services'}{$robot}[$Conf{'generic_sso_id'}{$robot}{$auth}]{'service_name'}; } $param->{'sso_number'} = $Conf{'cas_number'}{$robot} + $Conf{'generic_sso_number'}{$robot}; $param->{'use_passwd'} = $Conf{'use_passwd'}{$robot}; $param->{'use_sso'} = 1 if ($param->{'sso_number'}); $param->{'authentication_info_url'} = $Conf{'authentication_info_url'}{$robot}; $param->{'wwsconf'} = $wwsconf; $param->{'path_cgi'} = $ENV{'SCRIPT_NAME'}; $param->{'path_cgi'} =~ s/\/\//\//g; ## Replace '//' with '/' because it would break navigation $param->{'version'} = Sympa::Constants::VERSION; $param->{'date'} = gettext_strftime "%d %b %Y at %H:%M:%S", localtime(time); $param->{'time'} = gettext_strftime "%H:%M:%S", localtime(time); ## Hash defining the parameters where no control is performed (because they are supposed to contain html and/or javascript). $param->{'htmlAllowedParam'} = { 'title' => 1, 'hidden_head' => 1, 'hidden_end' => 1, 'hidden_at' => 1, 'list_protected_email' => 1, 'selected' => 1, 'author_mailto' =>1, 'mailto' =>1, 'logo_html_definition' => 1, 'template_content' => 1, 'html_dumpvars' => 1, }; ## Hash defining the parameters where HTML must be filtered. $param->{'htmlToFilter'} = { 'homepage_content' => 1, 'info_content' => 1, }; ## Change to list root unless (chdir($Conf{'home'})) { &report::reject_report_web('intern','chdir_error',{},'','','',$robot); &wwslog('info','unable to change directory'); exit (-1); } ## Sets the UMASK umask(oct($Conf{'umask'})); ## Authentication ## use https client certificat information if define. ## Default auth method (for scenarios) $param->{'auth_method'} = 'md5'; &report::init_report_web(); ## Get PATH_INFO parameters &get_parameters(); ## CSS related $param->{'css_path'} = &Conf::get_robot_conf($robot, 'css_path'); $param->{'css_url'} = &Conf::get_robot_conf($robot, 'css_url'); ## If CSS file not found, let Sympa do the job... unless (-f $param->{'css_path'}.'/style.css') { &wwslog('err','Could not find CSS file %s, using default CSS', $param->{'css_path'}.'/style.css') if ($param->{'css_path'}); ## Notice only if path was defined $param->{'css_url'} = $param->{'base_url'}.$param->{'path_cgi'}.'/css'; } &wwslog('info', "parameter css_url '%s' seems strange, it must be the url of a directory not a css file", $param->{'css_url'}) if ($param->{'css_url'} =~ /\.css$/); $session = new SympaSession ($robot,{'cookie'=>&SympaSession::get_session_cookie($ENV{'HTTP_COOKIE'}), 'action'=>$in{'action'}, 'rss'=>$rss}); undef $ENV{'HTTP_COOKIE'}; # Getting rid of the environment variable to make sure it won't be affected to another anonymous session. unless (defined $session) { &List::send_notify_to_listmaster('failed_to_create_web_session', $robot); &wwslog('info','Failed to create session'); $session = new SympaSession($robot, {}); } $session->{'is_family_owner'} = undef; my $automatic_list_families = &Conf::get_robot_conf($robot,'automatic_list_families'); if (defined $automatic_list_families) { foreach my $key (keys %{$automatic_list_families}){ my $family; if ($family = new Family($key,$robot)) { if ($family->is_allowed_to_create_automatic_lists(('auth_level' => 'md5', 'sender' => $session->{'email'}, 'message' => undef, 'listname' => ''))){ $session->{'is_family_owner'}{$key} = 1; }else{ $session->{'is_family_owner'}{$key} = undef; } } } } $param->{'session'} = $session->as_hashref(); &Log::set_log_level($session->{'log_level'}) if ($session->{'log_level'}); $param->{'restore_email'} = $session->{'restore_email'}; $param->{'dumpvars'} = $session->{'dumpvars'}; $param->{'unauthenticated_email'} = $session->{'unauthenticated_email'}; ## testing custom CSS if ($session->{'custom_css'}) { foreach my $i (0 .. 15) { $param->{'color_' . $i} = $session->{'color_' . $i} if $session->{'color_' . $i}; } } ## RSS does not require user authentication unless ($rss) { if (($ENV{'SSL_CLIENT_VERIFY'} eq 'SUCCESS') && ($in{'action'} ne 'sso_login')) { ## Do not check client certificate automatically if in sso_login &do_log('debug2', "SSL verified, S_EMAIL = %s,"." S_DN_Email = %s", $ENV{'SSL_CLIENT_S_EMAIL'}, $ENV{'SSL_CLIENT_S_DN_Email'}); if (($ENV{'SSL_CLIENT_S_EMAIL'})) { ## this is the X509v3 SubjectAlternativeName, and requires ## a patch to mod_ssl -- cm@coretec.at $param->{'user'}{'email'} = lc($ENV{'SSL_CLIENT_S_EMAIL'}); }elsif ($ENV{SSL_CLIENT_S_DN_Email}) { $param->{'user'}{'email'} = lc($ENV{'SSL_CLIENT_S_DN_Email'}); }elsif ($ENV{'SSL_CLIENT_S_DN'} =~ /\+MAIL=([^\+\/]+)$/) { ## Compatibility issue with old a-sign.at certs $param->{'user'}{'email'} = lc($1); }elsif ($crypt_openssl_x509_ok and exists($ENV{SSL_CLIENT_CERT})) { ## this is the X509v3 SubjectAlternativeName, and does only ## require "SSLOptions +ExportCertData" without patching ## mod_ssl -- massar@unix-ag.uni-kl.de $param->{'user'}{'email'} = lc(Crypt::OpenSSL::X509->new_from_string($ENV{SSL_CLIENT_CERT})->email()); } if($param->{user}{email}) { $session->{'email'}= $param->{user}{email} ; $param->{'auth_method'} = 'smime'; $session->{'auth'} = 'x509' ; $param->{'ssl_client_s_dn'} = $ENV{'SSL_CLIENT_S_DN'}; $param->{'ssl_client_v_end'} = $ENV{'SSL_CLIENT_V_END'}; $param->{'ssl_client_i_dn'} = $ENV{'SSL_CLIENT_I_DN'}; $param->{'ssl_cipher_usekeysize'} = $ENV{'SSL_CIPHER_USEKEYSIZE'}; } }elsif (($session->{'email'}) && ($session->{'email'} ne 'nobody')) { $param->{'user'}{'email'} = $session->{'email'}; }elsif($in{'ticket'}=~/(S|P)T\-/){ # the request contain a CAS named ticket that use CAS ticket format delete $session->{'do_not_use_cas'}; #reset do_not_use_cas because this client probably use CAS # select the cas server that redirect the user to sympa and check the ticket do_log ('notice',"CAS ticket is detected. in{'ticket'}=$in{'ticket'} checked_cas=$session->{'checked_cas'}"); my $cas_id = ''; if ($in{'checked_cas'} =~ /^(\d+)\,?/) { $cas_id = $1; } elsif ($session->{'checked_cas'} =~ /^(\d+)\,?/) { $cas_id = $1; } if ($cas_id ne '') { my $ticket = $in{'ticket'}; my $cas_server = $Conf{'auth_services'}{$robot}[$cas_id]{'cas_server'}; my $service_url = &wwslib::get_my_url(); $service_url =~ s/(\?|&)ticket\=.+$//; my $net_id = $cas_server->validateST($service_url, $ticket); if(defined $net_id) { # the ticket is valid net-id do_log('notice',"login CAS OK server netid=$net_id" ); $param->{'user'}{'email'} = lc(&Auth::get_email_by_net_id($robot, $cas_id, {'uid' => $net_id})); $session->{'auth'} = 'cas'; $session->{'email'}= $param->{user}{email} ; $session->{'cas_server'} = $cas_id; }else{ do_log('err',"CAS ticket validation failed : %s", &AuthCAS::get_errors()); } }else{ do_log ('notice',"Internal error while receiving a CAS ticket $session->{'checked_cas'} "); } }elsif(($Conf{'cas_number'}{$robot} > 0) && ($in{'action'} !~ /^(login|sso_login|wsdl)$/)) { # some cas server are defined but no CAS ticket detected unless ($session->{'do_not_use_cas'}) { # user not taggued as not using cas foreach my $auth_service (@{$Conf{'auth_services'}{$robot}}){ next unless ($auth_service->{'auth_type'} eq 'cas'); # skip auth services not related to cas next unless ($auth_service->{'non_blocking_redirection'} eq 'on'); ## skip cas server where client as been already redirect to the list of cas servers already checked is stored in the session ## the check below works fine as long as we don't have more then 10 CAS servers (because we don't properly split the list of values) &do_log ('debug',"check_cas checker_cas : $session->{'checked_cas'} current cas_id $Conf{'cas_id'}{$robot}{$auth_service->{'auth_service_name'}}"); next if ($session->{'checked_cas'} =~ /$Conf{'cas_id'}{$robot}{$auth_service->{'auth_service_name'}}/) ; # before redirect update the list of already checked cas server to prevent loop my $cas_server = $auth_service->{'cas_server'}; my $return_url = &wwslib::get_my_url(); ## Append the current CAS server ID to the list of checked CAS servers $session->{'checked_cas'} .= $Conf{'cas_id'}{$robot}{$auth_service->{'auth_service_name'}}; my $redirect_url = $cas_server->getServerLoginGatewayURL($return_url); if ($redirect_url =~ /http(s)+\:\//i) { $in{'action'} = 'redirect'; $param->{'redirect_to'} = $redirect_url; last }elsif($redirect_url == -1) { # CAS server auth error do_log('notice',"CAS server auth error $auth_service->{'auth_service_name'}" ); }else{ do_log('notice',"Strange CAS ticket detected and validated check sympa code !" ); } } $session->{'do_not_use_cas'} = 1 unless ($param->{'redirect_to'} =~ /http(s)+\:\//i) ; #set do_not_use_cas because all cas servers have been checked without success } } ##Cookie extern : sympa_altemails ## !! $param->{'alt_emails'} = &cookielib::check_cookie_extern($ENV{'HTTP_COOKIE'},$Conf{'cookie'},$param->{'user'}{'email'}); if ($param->{'user'}{'email'}) { # $param->{'auth'} = $param->{'alt_emails'}{$param->{'user'}{'email'}} || 'classic'; if (&List::is_user_db($param->{'user'}{'email'})) { $param->{'user'} = &List::get_user_db($param->{'user'}{'email'}); } ## For the parser to display an empty field instead of [xxx] $param->{'user'}{'gecos'} ||= ''; unless (defined $param->{'user'}{'cookie_delay'}) { $param->{'user'}{'cookie_delay'} = $wwsconf->{'cookie_expire'}; } ## Skip get_which if either in a list context or accessing the CSS unless ($in{'action'} eq 'css' || defined $in{'list'}) { @{$param->{'get_which'}} = &List::get_which($param->{'user'}{'email'},$robot,'member') ; @{$param->{'get_which_owner'}} = &List::get_which($param->{'user'}{'email'},$robot,'owner') ; @{$param->{'get_which_editor'}} = &List::get_which($param->{'user'}{'email'},$robot,'editor') ; } # } } } ## END if RSS ## Action my $action = $in{'action'}; ## Store current action in the session in order to redirect after a logi or other temporary actions. ## We should not memorize ULRs that are transitory actions ## POST is not handled ## A lot of other methods where used in the past (before session was introduced in Sympa). We must clean all. unless ($temporary_actions{$action} || $ENV{'REQUEST_METHOD'} ne 'GET') { $session->{'redirect_url'} = $param->{'base_url'}.$param->{'path_cgi'}.$ENV{'PATH_INFO'}; } $action ||= &Conf::get_robot_conf($robot, 'default_home'); $param->{'remote_addr'} = $ENV{'REMOTE_ADDR'} ; $param->{'remote_host'} = $ENV{'REMOTE_HOST'}; $param->{'http_user_agent'} = $ENV{'HTTP_USER_AGENT'}; $param->{'htmlarea_url'} = $wwsconf->{'htmlarea_url'} ; # if ($wwsconf->{'export_topics'} =~ /all/i); if ($in{'action'} eq 'css') { &do_css(); $param->{'action'} = 'css'; }elsif ($maintenance_mode) { &do_maintenance(); $param->{'action'} = 'maintenance'; }else { ## Session loop while ($action) { unless (&check_param_in()) { &report::reject_report_web('user','wrong_param',{},$action,$list); &wwslog('info','Wrong parameters'); last; } $param->{'host'} = $list->{'admin'}{'host'} if (ref($list) eq 'List'); $param->{'host'} ||= $robot; $param->{'domain'} = $list->{'domain'} if (ref($list) eq 'List'); ## language ( $ENV{'HTTP_ACCEPT_LANGUAGE'} not used !) $param->{'list_lang'} = $list->{'admin'}{'lang'} if (ref($list) eq 'List'); $param->{'user_lang'} = $param->{'user'}{'lang'} if (defined $param->{'user'}); $param->{'lang'} = $session->{'lang'} || $param->{'user_lang'} || $param->{'list_lang'} || &Conf::get_robot_conf($robot, 'lang'); $param->{'locale'} = &Language::SetLang($param->{'lang'}); $param->{'lang_tag'} = &Language::LanguageTag($param->{'lang'}); &export_topics ($robot); unless ($comm{$action}) { if (new List ($action, $robot)){ &do_redirect ($param->{'base_url'}.$param->{'path_cgi'}.'/info/'.$action); last; } &report::reject_report_web('user','unknown_action',{},$action,$list); &wwslog('info','unknown action %s', $action); unless ($action = prevent_visibility_bypass()) { last; } } $param->{'action'} = $action; my $old_action = $action; my $old_subaction = $in{'subaction'}; ## Check required action parameters my $check_output = &check_action_parameters($action); if (! defined $check_output ) { &wwslog('err', "missing required parameters for action '$action'"); delete($param->{'action'}); last; }elsif ($check_output != 1) { ## The output of the check may indicate another action to run first ## Example : running loginrequest if user is not authenticated $action = $param->{'action'} = $check_output; } ## Execute the action ## if (defined $action) { $action = &{$comm{$action}}(); } unless (defined $action) { unless($action = prevent_visibility_bypass()) { delete($param->{'action'}); last; }else{ &report::reject_report_web('user','authorization_reject',{},$param->{'action'},''); } } last if ($action =~ /redirect/) ; # after redirect do not send anything, it will crash fcgi lib if ($action eq $old_action) { # if a subaction is define and change, then it is not a loop if (! defined ($in{'subaction'})||($in{'subaction'} eq $old_subaction)){ &wwslog('info','Stopping loop with %s action', $action); #undef $action; $action = 'home'; } } undef $action if ($action == 1); } } ## Prepare outgoing params &check_param_out(); ## Params $param->{'refparam'} = ref($param); $param->{'action_type'} = $action_type{$param->{'action'}}; $param->{'action_type'} = 'none' unless (($param->{'is_priv'})||($param->{'action_type'} eq 'serveradmin')); $param->{'lang'} ||= $param->{'user'}{'lang'} if (defined $param->{'user'}); $param->{'lang'} ||= &Conf::get_robot_conf($robot, 'lang'); $param->{'lang_tag'} = Language::LanguageTag($param->{'lang'}); if ($param->{'list'}) { $param->{'list_title'} = $list->{'admin'}{'subject'}; $param->{'list_protected_email'} = &get_protected_email_address($param->{'list'}, $list->{'admin'}{'host'}); $param->{'title'} = &get_protected_email_address($param->{'list'}, $list->{'admin'}{'host'}); $param->{'title_clear_txt'} = "$param->{'list'}"; if ($param->{'subtitle'}) { $param->{'main_title'} = "$param->{'list'} - $param->{'subtitle'}"; } }else { $param->{'main_title'} = $param->{'title'} = &Conf::get_robot_conf($robot,'title'); $param->{'title_clear_txt'} = $param->{'title'}; } $param->{'robot_title'} = &Conf::get_robot_conf($robot,'title'); ## store in session table this session contexte $session->store(); ## Do not manage cookies at this level if content was already sent unless ($param->{'bypass'} eq 'extreme' || $param->{'action'} eq 'css' || $maintenance_mode || $rss) { my $delay = $param->{'user'}{'cookie_delay'}; unless (defined $delay) { $delay = $wwsconf->{'cookie_expire'}; } if ($delay == 0) { $delay = 'session'; } $session->renew() unless $param->{'use_ssl'}; unless ($session->set_cookie($param->{'cookie_domain'},$delay,$param->{'use_ssl'})) { &wwslog('notice', 'Could not set HTTP cookie'); } ## Set cookies "your_subscribtions" unless in one list page if ($param->{'user'}{'email'} && ref($list) ne 'List') { ## In case get_which was not set @{$param->{'get_which'}} = &List::get_which($param->{'user'}{'email'},$robot,'member') unless (defined $param->{'get_which'}); @{$param->{'get_which_owner'}} = &List::get_which($param->{'user'}{'email'},$robot,'owner') unless (defined $param->{'get_which_owner'}); @{$param->{'get_which_editor'}} = &List::get_which($param->{'user'}{'email'},$robot,'editor') unless (defined $param->{'get_which_editor'}); ## Add lists information to 'which_info' foreach my $list (@{$param->{'get_which'}}) { ## Evaluate AuthZ scenario first my $result = $list->check_list_authz('visibility', $param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'} , 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); next unless (ref($result) eq 'HASH' && $result->{'action'} eq 'do_it'); my $l = $list->{'name'}; $param->{'which_info'}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{'which_info'}{$l}{'host'} = $list->{'admin'}{'host'}; $param->{'which_info'}{$l}{'info'} = 1; } foreach my $list (@{$param->{'get_which_owner'}}) { my $l = $list->{'name'}; $param->{'which_info'}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{'which_info'}{$l}{'host'} = $list->{'admin'}{'host'}; $param->{'which_info'}{$l}{'info'} = 1; $param->{'which_info'}{$l}{'admin'} = 1; } foreach my $list (@{$param->{'get_which_editor'}}) { my $l = $list->{'name'}; $param->{'which_info'}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{'which_info'}{$l}{'host'} = $list->{'admin'}{'host'}; $param->{'which_info'}{$l}{'info'} = 1; $param->{'which_info'}{$l}{'admin'} = 1; } } ## Set cookies unless client use https authentication if ($param->{'user'}{'email'}) { if ($param->{'user'}{'email'} ne 'x509') { $session->{'auth'} ||= 'classic'; $param->{'cookie_set'} = 1; ###Cookie extern : sympa_altemails my $number = 0; foreach my $element (keys %{$param->{'alt_emails'}}){ $number ++ if ($element); } unless ($number == 0) { unless(&cookielib::set_cookie_extern($Conf{'cookie'},$param->{'cookie_domain'},%{$param->{'alt_emails'}})){ &wwslog('notice', 'Could not set HTTP cookie for external_auth'); } } } } #elsif ($ENV{'HTTP_COOKIE'} =~ /sympauser\=/){ # &cookielib::set_cookie('unknown', $Conf{'cookie'}, $param->{'cookie_domain'}, 'now'); #} } ## Available languages my $saved_lang = &Language::GetLang(); foreach my $l (@{&Language::GetSupportedLanguages($robot)}) { &Language::SetLang($l) || next; if (gettext("_language_")) { $param->{'languages'}{$l}{'complete'} = gettext("_language_"); }else { $param->{'languages'}{$l}{'complete'} = $l; } if ($param->{'locale'} eq $l) { $param->{'languages'}{$l}{'selected'} = 'selected="selected"'; }else { $param->{'languages'}{$l}{'selected'} = ''; } $param->{'languages'}{$l}{'lang_tag'} = Language::LanguageTag($l); } &Language::SetLang($saved_lang); $param->{'html_dumpvars'} = &tools::dump_html_var($param) if ($session->{'dumpvars'}); # if bypass is defined select the content-type from various vars if ($param->{'bypass'}) { ## if bypass = 'extreme' leave the action send the content-type and the content itself unless ($param->{'bypass'} eq 'extreme') { ## if bypass = 'asis', file content-type is in the file itself as is define by the action in $param->{'content_type'}; unless ($param->{'bypass'} eq 'asis') { my $type = $param->{'content_type'} || $mime_types->{$param->{'file_extension'}} || 'application/octet-stream'; printf "Content-Type: %s\n\n", $type; } # $param->{'file'} or $param->{'error'} must be define in this case. if (open (FILE, $param->{'file'})){ print ; close FILE; }elsif(&report::is_there_any_reject_report_web()){ ## for compatibility : it could be better my $intern = &report::get_intern_error_web(); my $system = &report::get_system_error_web(); my $user = &report::get_user_error_web(); my $auth = &report::get_auth_reject_web(); if (ref($intern) eq 'ARRAY'){ print "INTERNAL SERVER ERROR\n"; }; if (ref($system) eq 'ARRAY'){ print "SYSTEM ERROR\n"; }; if (ref($user) eq 'ARRAY'){ foreach my $err (@$user){ printf "ERROR : %s\n", $err; } }; if (ref($auth) eq 'ARRAY'){ foreach my $err (@$auth){ printf "AUTHORISATION FAILED : %s\n", $err; } }; }else{ print "Internal error content-type nor file defined\n"; &do_log('err', 'Internal error content-type nor file defined'); } } }elsif ($rss) { ## Send RSS print "Cache-control: no-cache\n"; print "Content-Type: application/rss+xml; charset=utf-8\n\n"; ## Icons $param->{'icons_url'} = &Conf::get_robot_conf($robot, 'static_content_url').'/icons'; ## Retro compatibility concerns $param->{'active'} = 1; if (defined $list) { $param->{'list_conf'} = $list->{'admin'}; } my $locale = Language::Lang2Locale($param->{'lang'}); my $tt2_include_path = tools::make_tt2_include_path($robot, 'web_tt2', $locale, $list); unless (&tt2::parse_tt2( $param, 'rss.tt2', \*STDOUT, $tt2_include_path, {} )) { my $error = &tt2::get_error(); $param->{'tt2_error'} = $error; unless (&List::send_notify_to_listmaster('web_tt2_error', $robot, [$error])) { &wwslog('notice','Unable to send notify "web_tt2_error" to listmaster'); } } # close FILE; }elsif ($param->{'redirect_to'}) { do_log ('notice',"Redirecting to $param->{'redirect_to'}"); print "Location: $param->{'redirect_to'}\n\n"; }else { &prepare_report_user(); &send_html('main.tt2'); } # exit if wwsympa.fcgi itself has changed if ((stat($ENV{'SCRIPT_FILENAME'}))[9] > $birthday ) { do_log('notice',"Exiting because $ENV{'SCRIPT_FILENAME'} has changed since fastcgi server started"); exit(0); } } ############################################################## #-#\#|#/#-#\#|#/#-#\#|#/#-#\#|#/#-#\#|#/#-#\#|#/#-#\#|#/#-#\#|#/ ############################################################## ## Write to log sub wwslog { my $facility = shift; # do not log if log level if too high regarding the log requested by user return if ($Log::levels{$facility} > $Log::log_level); my $msg = shift; my $remote = $ENV{'REMOTE_HOST'} || $ENV{'REMOTE_ADDR'}; ## Determine calling function and parameters my @call = caller(1); $msg = $call[3].'() ' . $msg if ($call[3]); $msg = "[list $param->{'list'}] " . $msg if $param->{'list'}; if ($param->{'alt_emails'}) { my @alts; foreach my $alt (keys %{$param->{'alt_emails'}}) { push @alts, $alt unless ($alt eq $param->{'user'}{'email'}); } if ($#alts >= 0) { my $alt_list = join ',', @alts; $msg = "[alt $alt_list] " . $msg; } } $msg = "[user $param->{'user'}{'email'}] " . $msg if $param->{'user'}{'email'}; $msg = "[rss] ".$msg if $rss; $msg = "[client $remote] ".$msg if $remote; $msg = "[session $session->{'id_session'}] ".$msg if $session; $msg = "[robot $robot] ".$msg; return &Log::do_log($facility, $msg, @_); } sub web_db_log { my $data = shift; $data->{'client'} = $param->{'remote_addr'}; $data->{'daemon'} = 'wwsympa'; $data->{'robot'} ||= $robot; $data->{'list'} ||= $list->{'name'} if (defined $list); $data->{'action'} ||= $param->{'action'}; $data->{'user_email'} ||= $param->{'user'}{'email'} if (defined $param->{'user'}); $data->{'target_email'} ||= $data->{'user_email'}; ## Default email is the user email unless (&Log::db_log($data)) { &wwslog('err','web_db_log: failed to log in database'); return undef; } return 1; } sub new_loop { $loop++; my $query; if ($wwsconf->{'use_fast_cgi'}) { $query = new CGI::Fast; $loop_count++; }else { return undef if ($loop > 1); $query = new CGI; } return $query; } sub get_header_field { my $field = shift; ## HTTP_X_ header fields set when using a proxy if ($field eq 'SERVER_NAME') { return $ENV{'HTTP_X_FORWARDED_SERVER'} || $ENV{'SERVER_NAME'}; }elsif ($field eq 'HTTP_HOST') { return $ENV{'HTTP_X_FORWARDED_HOST'} || $ENV{'HTTP_HOST'}; }else { return $ENV{$field}; } } # _split_params is used by get_parameters to split path info in the appropriate parameters list. # It is used also by action ticket to prepare the context stored in the one_time_ticket table in string like path_info # input ENV{'PATH_INFO'} like string, output in the global $param hash sub _split_params { my $args_string = shift; &do_log('debug', "PATH_INFO: %s",$ENV{'PATH_INFO'}); $args_string =~ s+^/++; my $ending_slash = 0; if ($args_string =~ /\/$/) { $ending_slash = 1; } my @params = split /\//, $args_string; if ($params[0] eq 'nomenu') { $param->{'nomenu'} = 1; $param->{'path_cgi'} .= '/nomenu'; ## other links should keep the nomenu attribute shift @params; } ## debug mode if ($params[0] =~ /debug(\d)?/) { shift @params; if ($1) { $main::options{'debug_level'} = $1 if ($1); }else{ $main::options{'debug_level'} = 1 ; } }else{ $main::options{'debug_level'} = 0 ; } do_log ('debug2', "debug level $main::options{'debug_level'}"); ## rss mode if ($params[0] eq 'rss') { shift @params; $rss = 1; } if ($#params >= 0) { $in{'action'} = $params[0]; my $args; if (defined $action_args{$in{'action'}}) { $args = $action_args{$in{'action'}}; }else { $args = $action_args{'default'}; } my $i = 1; foreach my $p (@$args) { my $pname; ## More than 1 param if ($p =~ /^\@(\w+)$/) { $pname = $1; $in{$pname} = join '/', @params[$i..$#params]; $in{$pname} .= '/' if $ending_slash; last; } else { $pname = $p; $in{$pname} = $params[$i]; } &wwslog('debug',"Incoming parameter: $pname=$in{$pname}"); $i++; } } } sub get_parameters { # &wwslog('debug3', 'get_parameters'); ## CGI URL if ($ENV{'HTTPS'} eq 'on') { $param->{'base_url'} = sprintf 'https://%s', &get_header_field('HTTP_HOST'); $param->{'use_ssl'} = 1; }else { $param->{'base_url'} = sprintf 'http://%s', &get_header_field('HTTP_HOST'); $param->{'use_ssl'} = 0; } $param->{'path_info'} = $ENV{'PATH_INFO'}; $param->{'http_method'} = $ENV{'REQUEST_METHOD'}; ## Usefull to skip previous_action when using POST $param->{'robot_domain'} = $wwsconf->{'robot_domain'}{&get_header_field('SERVER_NAME')}; if ($ENV{'REQUEST_METHOD'} eq 'GET') { &_split_params ($ENV{'PATH_INFO'}); }elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { ## POST if ($in{'javascript_action'}) { ## because of incompatibility javascript $in{'action'} = $in{'javascript_action'}; } foreach my $p (keys %in) { do_log('debug2',"POST key $p value $in{$p}") unless ($p =~ /passwd/); if ($p =~ /^((\w*)action)_(\w+)((\.\w+)*)$/) { $in{$1} = $3; if ($4) { foreach my $v (split /\./, $4) { $v =~ s/^\.?(\w+)\.?/$1/; $in{$v} = 1; } } undef $in{$p}; } } $param->{'nomenu'} = $in{'nomenu'}; } ## Lowercase email addresses $in{'email'} = lc ($in{'email'}); ## Don't get multiple listnames if ($in{'list'}) { my @lists = split /\0/, $in{'list'}; $in{'list'} = $lists[0]; } my $custom_attribute ; my $custom_input; ## Check parameters format foreach my $p (keys %in) { ## Skip empty parameters next if ($in{$p} =~ /^$/); ## Remove DOS linefeeds (^M) that cause problems with Outlook 98, AOL, and EIMS: $in{$p} =~ s/\r\n|\r/\n/g; #XXX## Convert from the web encoding to unicode string #XXX$in{$p} = Encode::decode('utf8', $in{$p}); my @tokens = split (/\./, $p); my $pname = $tokens[0]; ## Regular expressions applied on parameters my $regexp; if ($pname =~ /^additional_field/) { $regexp = $in_regexp{'additional_field'}; }elsif ($pname =~ /^custom_attribute(.*)$/) { my $key = $tokens[1] ; $regexp = $in_regexp{'custom_attribute'}; do_log ('debug2', "get_parameters (custom_attribute) : ($p)($key) $pname $in{$p} $Conf{$key}{type}"); $custom_attribute->{$key} = {value=>$in{$p}} ; undef $in{$p} ; }elsif ($pname =~ /^custom_input(.*)$/) { my $key = $tokens[1]; $regexp = $in_regexp{'custom_input'}; do_log ('debug2', "get_parameters (custom_input) : ($p)($key) $pname $in{$p} $Conf{$key}{type}"); $custom_input->{$key} = $in{$p}; undef $in{$p}; }elsif ($in_regexp{$pname}) { $regexp = $in_regexp{$pname}; }else { $regexp = $in_regexp{'*'}; } my $negative_regexp; if ($pname =~ /^additional_field/) { $negative_regexp = $in_negative_regexp{'additional_field'}; }elsif ($in_negative_regexp{$pname}) { $negative_regexp = $in_negative_regexp{$pname}; } # If we are editing an HTML file in the shared, allow HTML but prevent XSS. if ($pname eq 'content' && $in{'action'} eq 'd_savefile' && $in{'path'} =~ $list->{'dir'}.'/shared' && lc($in{'path'}) =~ /\.html?/) { my $tmpparam = $in{$p}; $tmpparam = &tools::sanitize_html('robot' => $robot, 'string' => $in{$p}); if (defined $tmpparam) { $in{$p} = $tmpparam; } else { &do_log('err','Unable to sanitize parameter %s',$pname); } } foreach my $one_p (split /\0/, $in{$p}) { if ($one_p !~ /^$regexp$/s || (defined $negative_regexp && $one_p =~ /$negative_regexp/s) ) { ## Dump parameters in a tmp file for later analysis my $dump_file = &Conf::get_robot_conf($robot, 'tmpdir').'/sympa_dump.'.time.'.'.$$; unless (open DUMP, ">$dump_file") { &wwslog('err','get_parameters: failed to create %s : %s', $dump_file, $!); } &tools::dump_var(\%in, 0, \*DUMP); close DUMP; &report::reject_report_web('user','syntax_errors',{'params' => $p},'',''); &wwslog('err','get_parameters: syntax error for parameter %s value \'%s\' not conform to regexp:%s ; dumped vars in %s', $pname, $one_p, $regexp, $dump_file); $in{$p} = ''; next; } } } $in{custom_attribute} = $custom_attribute ; $in{custom_input} = $custom_input; ## For shared-related actions, Q-encode filenames ## This required for filenames that include non ascii characters if (defined $filtering{$in{'action'}}) { my %apply_to; ## Build list of parameters filters apply to foreach my $p (keys %{$filtering{$in{'action'}}}) { if ($p =~ /\*/) { ## use of wildcar my $p_regexp = $p; $p_regexp =~ s/\*/\.\*/g; ## Turn wildcar into a regexp foreach my $in_key (keys %in) { if ($in_key =~ /^$p_regexp$/) { $apply_to{$in_key} = $filtering{$in{'action'}}{$p}; } } }else { $apply_to{$p} = $filtering{$in{'action'}}{$p}; } } foreach my $p (keys %apply_to) { my $filtering_action = $apply_to{$p}; if ($filtering_action eq 'qencode') { ## Q-encode file path my @tokens = split /\//, $in{$p}; foreach my $i (0..$#tokens) { $tokens[$i] = &tools::qencode_filename($tokens[$i]); } $in{$p} = join '/', @tokens; ## Sympa's URI escaping subroutine (tools::escape_chars()) replaces '/' with %A5 ('¥' character) ## This should be transformed into a '/' again }elsif ($filtering_action eq 'unescape_html') { $in{$p} = &tools::unescape_chars($in{$p}); }elsif ($filtering_action eq 'fix_escape_uri') { $in{$p} =~ s/\xa5/\//g; }elsif ($filtering_action eq 'normalize') { $in{$p} =~ s/^\$+//; ## remove leading \s $in{$p} =~ s/\$+$//; ## remove trailing \s $in{$p} = lc($in{$p}); ## lowercase } } } return 1; } sub get_parameters_old { # &wwslog('debug3', 'get_parameters'); ## CGI URL if ($ENV{'HTTPS'} eq 'on') { $param->{'base_url'} = sprintf 'https://%s', &get_header_field('HTTP_HOST'); $param->{'use_ssl'} = 1; }else { $param->{'base_url'} = sprintf 'http://%s', &get_header_field('HTTP_HOST'); $param->{'use_ssl'} = 0; } $param->{'path_info'} = $ENV{'PATH_INFO'}; $param->{'robot_domain'} = $wwsconf->{'robot_domain'}{&get_header_field('SERVER_NAME')}; if ($ENV{'REQUEST_METHOD'} eq 'GET') { my $path_info = $ENV{'PATH_INFO'}; &do_log('debug', "PATH_INFO: %s",$ENV{'PATH_INFO'}); $path_info =~ s+^/++; my $ending_slash = 0; if ($path_info =~ /\/$/) { $ending_slash = 1; } my @params = split /\//, $path_info; # foreach my $i(0..$#params) { # $params[$i] = &tools::unescape_chars($params[$i]); # } if ($params[0] eq 'nomenu') { $param->{'nomenu'} = 1; $param->{'path_cgi'} .= '/nomenu'; ## other links should keep the nomenu attribute shift @params; } ## debug mode if ($params[0] =~ /debug(\d)?/) { shift @params; if ($1) { $main::options{'debug_level'} = $1 if ($1); }else{ $main::options{'debug_level'} = 1 ; } }else{ $main::options{'debug_level'} = 0 ; } do_log ('debug2', "debug level $main::options{'debug_level'}"); ## rss mode ########### /^rss$/ ??? if ($params[0] eq 'rss') { shift @params; $rss = 1; } if ($#params >= 0) { $in{'action'} = $params[0]; my $args; if (defined $action_args{$in{'action'}}) { $args = $action_args{$in{'action'}}; }else { $args = $action_args{'default'}; } my $i = 1; foreach my $p (@$args) { my $pname; ## More than 1 param if ($p =~ /^\@(\w+)$/) { $pname = $1; $in{$pname} = join '/', @params[$i..$#params]; $in{$pname} .= '/' if $ending_slash; last; } else { $pname = $p; $in{$pname} = $params[$i]; } $i++; } } }elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { ## POST if ($in{'javascript_action'}) { ## because of incompatibility javascript $in{'action'} = $in{'javascript_action'}; } foreach my $p (keys %in) { do_log('debug2',"POST key $p value $in{$p}"); if ($p =~ /^((\w*)action)_(\w+)((\.\w+)*)$/) { $in{$1} = $3; if ($4) { foreach my $v (split /\./, $4) { $v =~ s/^\.?(\w+)\.?/$1/; $in{$v} = 1; } } undef $in{$p}; } } $param->{'nomenu'} = $in{'nomenu'}; } ## Lowercase email addresses $in{'email'} = lc ($in{'email'}); ## Don't get multiple listnames if ($in{'list'}) { my @lists = split /\0/, $in{'list'}; $in{'list'} = $lists[0]; } my $custom_attribute ; ## Check parameters format foreach my $p (keys %in) { ## Skip empty parameters next if ($in{$p} =~ /^$/); ## Remove DOS linefeeds (^M) that cause problems with Outlook 98, AOL, and EIMS: $in{$p} =~ s/\r\n|\r/\n/g; #XXX## Convert from the web encoding to unicode string #XXX$in{$p} = Encode::decode('utf8', $in{$p}); my @tokens = split (/\./, $p); my $pname = $tokens[0]; ## Regular expressions applied on parameters my $regexp; if ($pname =~ /^additional_field/) { $regexp = $in_regexp{'additional_field'}; }elsif ($pname =~ /^custom_attribute(.*)$/) { my $key = $tokens[1] ; $regexp = $in_regexp{'custom_attribute'}; do_log ('debug2', "get_parameters (custom_attribute) : ($p)($key) $pname $in{$p} $Conf{$key}{type}"); $custom_attribute->{$key} = {value=>$in{$p}} ; undef $in{$p} ; }elsif ($in_regexp{$pname}) { $regexp = $in_regexp{$pname}; }else { $regexp = $in_regexp{'*'}; } my $negative_regexp; if ($pname =~ /^additional_field/) { $negative_regexp = $in_negative_regexp{'additional_field'}; }elsif ($in_negative_regexp{$pname}) { $negative_regexp = $in_negative_regexp{$pname}; } # If we are editing an HTML file in the shared, allow HTML but prevent XSS. if ($pname eq 'content' && $in{'action'} eq 'd_savefile' && $in{'path'} =~ $list->{'dir'}.'/shared' && lc($in{'path'}) =~ /\.html?/) { my $tmpparam = $in{$p}; $tmpparam = &tools::sanitize_html('robot' => $robot, 'string' => $in{$p}); if (defined $tmpparam) { $in{$p} = $tmpparam; } else { &do_log('err','Unable to sanitize parameter %s',$pname); } } foreach my $one_p (split /\0/, $in{$p}) { if ($one_p !~ /^$regexp$/s || (defined $negative_regexp && $one_p =~ /$negative_regexp/s) ) { ## Dump parameters in a tmp file for later analysis my $dump_file = &Conf::get_robot_conf($robot, 'tmpdir').'/sympa_dump.'.time.'.'.$$; unless (open DUMP, ">$dump_file") { &wwslog('err','get_parameters: failed to create %s : %s', $dump_file, $!); } &tools::dump_var(\%in, 0, \*DUMP); close DUMP; &report::reject_report_web('user','syntax_errors',{'params' => $p},'',''); &wwslog('err','get_parameters: syntax error for parameter %s value \'%s\' not conform to regexp:%s ; dumped vars in %s', $pname, $one_p, $regexp, $dump_file); $in{$p} = ''; next; } } } $in{custom_attribute} = $custom_attribute ; ## For shared-related actions, Q-encode filenames ## This required for filenames that include non ascii characters if (defined $filtering{$in{'action'}}) { foreach my $p (keys %{$filtering{$in{'action'}}}) { if ($filtering{$in{'action'}}{$p} eq 'qencode') { ## Q-encode file path my @tokens = split /\//, $in{$p}; foreach my $i (0..$#tokens) { $tokens[$i] = &tools::qencode_filename($tokens[$i]); } $in{$p} = join '/', @tokens; ## Sympa's URI escaping subroutine (tools::escape_chars()) replaces '/' with %A5 ('¥' character) ## This should be transformed into a '/' again }elsif ($filtering{$in{'action'}}{$p} eq 'fix_escape_uri') { $in{$p} =~ s/\xa5/\//g; } } } return 1; } ## Check required parameters for an action ## It compares incoming parameter to those declared as required in %required_args ## Also check required privileges to perform each action sub check_action_parameters { my $action = shift; if (defined $required_args{$action}) { foreach my $arg_name (@{$required_args{$action}}) { ## Missing list parameter if ($arg_name eq 'param.list') { unless (defined $list) { &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$action); &wwslog('info','missing list parameter'); &web_db_log({'status' => 'error', 'error_type' => 'no_list'}); return undef; } ## User is not authenticated }elsif ($arg_name eq 'param.user.email') { unless (defined $param->{'user'} && $param->{'user'}{'email'}) { if(prevent_visibility_bypass()) { &report::reject_report_web('user','authorization_reject',{},$param->{'action'},''); } &report::reject_report_web('user','no_user',{},$action); &wwslog('err','user not logged in'); &web_db_log({'status' => 'error', 'error_type' => "not_logged_in"}); ## User is redirected to the login request form $param->{'previous_action'} = $action; $param->{'previous_list'} = $param->{'list'} if (defined $param->{'list'}); return 'loginrequest'; } ## Other incoming parameters }else { ## There may be alternate parameters ## Then at least one of them MUST be set my @req_parameters = split(/\|/, $arg_name); my $ok = 0; foreach my $req_param (@req_parameters) { $ok =1 if ($in{$req_param}); } unless ($ok) { ## Replace \0 and '|' with ',' before logging $in{$arg_name} =~ s/\0/,/g; $in{$arg_name} =~ s/\|/,/g; if(prevent_visibility_bypass()) { &report::reject_report_web('user','authorization_reject',{'list' => $in{'list'}},$param->{'action'},''); } &report::reject_report_web('user','missing_arg',{'argument' => $arg_name},$action); &wwslog('info',"missing parameter '$arg_name'"); &web_db_log({'status' => 'error', 'error_type' => 'missing_parameter'}); delete $param->{'list'}; return undef; } } } } ## Check required privileges if (defined $required_privileges{$action}) { ## There may be alternate privileges ## Then at least one of them MUST verified my $ok = 0; my $missing_priv; foreach my $req_priv (@{$required_privileges{$action}}) { $ok =1 if ($param->{'is_'.$req_priv}); $missing_priv = $req_priv; } unless ($ok) { &report::reject_report_web('auth','action_'.$missing_priv,{},$param->{'action'},$list); &wwslog('info','authorization failed, insufficient privileges'); &web_db_log({'status' => 'error', 'error_type' => 'authorization'}); delete $param->{'list'}; return undef; } } return 1; } ## Send HTML output sub send_html { my $tt2_file = shift; ## Send HTML headers if ($param->{'date'}) { Language::PushLang("en"); printf "Date: %s\n", &POSIX::strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime(time)); Language::PopLang(); } ## If we set the header indicating the last time the file to send was modified, add an HTTP header (limitate web harvesting). if ($param->{'header_date'}) { Language::PushLang("en"); printf "Last-Modified: %s\n", &POSIX::strftime('%a, %d %b %Y %H:%M:%S GMT', gmtime($param->{'header_date'})); Language::PopLang(); } print "Cache-control: max-age=0\n" unless $param->{'action'} eq 'arc'; print "Content-Type: text/html; charset=utf-8\n"; ## Workaround for Internet Explorer 8 or later. print "X-UA-Compatible: IE=100\n"; ## Notify crash to client. if ($param->{'action'} eq 'crash') { print "Status: 503 Service Unavailable\n"; print "Retry-After: 300\n"; } ## Icons $param->{'icons_url'} = &Conf::get_robot_conf($robot, 'static_content_url').'/icons'; ## Retro compatibility concerns $param->{'active'} = 1; if (defined $list) { $param->{'list_conf'} = $list->{'admin'}; } ## Trying to use custom_vars if (defined $list->{'admin'}{'custom_vars'}) { foreach my $var (@{$list->{'admin'}{'custom_vars'}}) { $param->{'custom_vars'}{$var->{'name'}} = $var->{'value'}; } } # XSS escaping applied to all outgoing parameters. my $param_copy = &tools::dup_var($param); ## Escape parameters on a copy to avoid altering usefull data. if(defined $param_copy) { unless(&tools::sanitize_var('var' => $param_copy, 'level' => 0, 'robot' => $robot, 'htmlAllowedParam' => $param_copy->{'htmlAllowedParam'} , 'htmlToFilter' => $param_copy->{'htmlToFilter'} , ) ) { &do_log('err','Failed to sanitize $param in host %s', $robot); } } ## Save special paths. my @added_include_path = tt2::get_include_path(); tt2::clear_include_path(); ## Custom CSSes. They are stored to $param_copy so that they won't be ## escaped. # Do not include locale paths. # The css.tt2 by each locales will override styles in main CSS. my $tt2_include_path = tools::make_tt2_include_path($robot, 'web_tt2', '', $list); # testing custom CSS. if ($session->{'custom_css'}) { my $custom_css; unless (tt2::parse_tt2($param, 'css.tt2', \$custom_css, $tt2_include_path )) { wwslog('info', 'error while parsing custom CSS'); delete $param_copy->{'custom_css'}; } else { $param_copy->{'custom_css'} = $custom_css; } } my $locale = Language::Lang2Locale($param->{'lang'}); # per-locale CSS. my $locale_css; if (tools::get_filename('etc', {}, 'web_tt2/' . $locale . '/css.tt2', $robot, $list )) { unless (tt2::parse_tt2($param, $locale . '/css.tt2', \$locale_css, $tt2_include_path )) { wwslog('info', 'error while parsing locale CSS %s', $locale . '/css.tt2'); } else { $param_copy->{'locale_css'} = $locale_css; } } ## Now include locale paths. $tt2_include_path = tools::make_tt2_include_path($robot, 'web_tt2', $locale, $list); ## Restore special paths and enable absolute path if neccesary. foreach my $dir (@added_include_path) { tt2::add_include_path($dir); } tt2::allow_absolute_path() if $allow_absolute_path; undef $allow_absolute_path; ## Output the content. unless (&tt2::parse_tt2($param_copy,$tt2_file , \*STDOUT, $tt2_include_path, { 'has_header' => 1 })) { my $error = &tt2::get_error(); $param->{'tt2_error'} = $error; $param_copy->{'tt2_error'} = $error; if ($param->{'action'} eq 'help' and ref $error and $error->type eq 'file') { # "Not Found" response for random help page. print "Status: 404 Not Found\n"; } else { &List::send_notify_to_listmaster('web_tt2_error', $robot, [$error]); } &tt2::parse_tt2($param_copy,'tt2_error.tt2' , \*STDOUT, $tt2_include_path, { 'has_header' => 1 }); } } sub prepare_report_user { $param->{'intern_errors'} = &report::get_intern_error_web(); $param->{'system_errors'} = &report::get_system_error_web(); $param->{'user_errors'} = &report::get_user_error_web(); $param->{'auth_rejects'} = &report::get_auth_reject_web(); $param->{'notices'} = &report::get_notice_web(); $param->{'errors'} = &report::is_there_any_reject_report_web(); } =pod =head2 sub check_param_in Checks parameters contained in the global variable $in. It is the process used to analyze the incoming parameters. Use it to create a List object and initialize output parameters. =head3 Arguments =over =item * I =back =head3 Return =over =item * I if the process encounters problems. =item * I<1> if everything goes well =back =head3 Calls =over =item * d_access_control =item * make_pictures_url =item * wwslog =item * Language::SetLang =item * List::am_i =item * List::check_list_authz =item * List::get_mod_spool_size =item * List::get_shared_moderated =item * List::get_subscriber =item * List::get_subscription_request_count =item * List::get_total =item * List::get_total_bouncing =item * List::is_listmaster =item * List::is_moderated =item * List::is_user =item * List::new =item * List::request_action =item * report::reject_report_web =back =cut ## Analysis of incoming parameters sub check_param_in { &wwslog('debug2', 'check_param_in'); ## Lowercase list name $in{'list'} =~ tr/A-Z/a-z/; ## In case the variable was multiple if ($in{'list'} =~ /^(\S+)\0/) { $in{'list'} = $1; ## Create a new List instance. unless ($list = new List ($in{'list'}, $robot)) { &report::reject_report_web('user','unknown_list',{'list' => $in{'list'}},$param->{'action'},''); &wwslog('info','check_param_in: unknown list %s', $in{'list'}); return undef; } ## Set lang to list lang &Language::SetLang($list->{'admin'}{'lang'}); } ## listmaster has owner and editor privileges for the list if (&List::is_listmaster($param->{'user'}{'email'},$robot)) { $param->{'is_listmaster'} = 1; } if ($in{'list'}) { ## Create a new List instance. unless ($list = new List ($in{'list'}, $robot, {})) { &report::reject_report_web('user','unknown_list',{'list' => $in{'list'}},$param->{'action'},''); &wwslog('info','check_param_in: unknown list %s', $in{'list'}); return undef; } ## Gather list configuration informations for further output. $param->{'list'} = $in{'list'}; $param->{'subtitle'} = $list->{'admin'}{'subject'}; $param->{'subscribe'} = $list->{'admin'}{'subscribe'}{'name'}; $param->{'send'} = $list->{'admin'}{'send'}{'title'}{$param->{'lang'}}; # Pictures are not available unless it is configured for the list and the robot if ($list->{'admin'}{'pictures_feature'} eq 'off') { $param->{'pictures_display'} = undef; } else { $param->{'pictures_display'} = 'on'; } ## Get the total number of subscribers to the list. if (defined $param->{'total'}) { $param->{'total'} = $list->get_total(); }else { $param->{'total'} = $list->get_total('nocache'); } ## Check if the current list has a public key X.509 certificate. $param->{'list_as_x509_cert'} = $list->{'as_x509_cert'}; ## Stores to output the whole list's admin configuration. $param->{'listconf'} = $list->{'admin'}; ## If an user is logged in, checks this user's privileges. if ($param->{'user'}{'email'}) { $param->{'is_subscriber'} = $list->is_user($param->{'user'}{'email'}); $param->{'subscriber'} = $list->get_subscriber($param->{'user'}{'email'}) if $param->{'is_subscriber'}; $param->{'is_privileged_owner'} = $param->{'is_listmaster'} || $list->am_i('privileged_owner', $param->{'user'}{'email'}); $param->{'is_owner'} = $param->{'is_privileged_owner'} || $list->am_i('owner', $param->{'user'}{'email'}); $param->{'is_editor'} = $list->am_i('editor', $param->{'user'}{'email'}); $param->{'is_priv'} = $param->{'is_owner'} || $param->{'is_editor'}; $param->{'pictures_url'} = &tools::make_pictures_url('email' => $param->{'user'}{'email'}, 'list' => $list); ## Checks if the user can post in this list. my $result = $list->check_list_authz('send',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_post'} = 1 if ($r_action !~ /reject/); ## If no user logged in, the output can ask for authentification. }else { $param->{'user'}{'email'} = undef; $param->{'need_login'} = 1; } ## Check if this list's messages must be moderated. $param->{'is_moderated'} = $list->is_moderated(); ## If the user logged in is a privileged user, gather informations relative to administration tasks if ($param->{'is_priv'}) { $param->{'mod_message'} = $list->get_mod_spool_size(); $param->{'mod_subscription'} = $list->get_subscription_request_count(); $param->{'doc_mod_list'} = $list->get_shared_moderated(); $param->{'mod_total_shared'} = $#{$param->{'doc_mod_list'}} + 1; if ($param->{'total'} > 0) { $param->{'bounce_total'} = $list->get_total_bouncing(); $param->{'bounce_rate'} = $param->{'bounce_total'} * 100 / $param->{'total'}; $param->{'bounce_rate'} = int ($param->{'bounce_rate'} * 10) / 10; }else { $param->{'bounce_rate'} = 0; } $param->{'mod_total'} = $param->{'mod_total_shared'}+$param->{'mod_message'}+$param->{'mod_subscription'}; } ## Check unsubscription authorization for the current user and list. my $result = $list->check_list_authz('unsubscribe',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); if (! $param->{'user'}{'email'}) { $param->{'may_signoff'} = 1 if ($main::action =~ /do_it|owner|request_auth/); }elsif ($param->{'is_subscriber'}) { $param->{'may_signoff'} = 1 if ($main::action =~ /do_it|owner|request_auth/); $param->{'may_suboptions'} = 1; } ## Check subscription authorization for the current user and list. my $result = $list->check_list_authz('subscribe',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_subscribe'} = 1 if ($main::action =~ /do_it|owner|request_auth/); ## Check if the current user can read the shared documents. my %mode; $mode{'read'} = 1; my %access = &d_access_control(\%mode,""); $param->{'may_d_read'} = $access{'may'}{'read'}; ## Check the status (exists, deleted, doesn't exist) of the shared directory $param->{'shared'} = $list->get_shared_status(); } ## Check if the current user can create a list. my $result = &Scenario::request_action ('create_list',$param->{'auth_method'},$robot, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } $param->{'create_list_reason'} = $reason; if ($param->{'user'}{'email'} && (($param->{'create_list'} = $r_action ) =~ /do_it|listmaster/)) { $param->{'may_create_list'} = 1; }else{ undef ($param->{'may_create_list'}); } return 1; } ## Prepare outgoing params sub check_param_out { &wwslog('debug2', 'check_param_out'); $param->{'loop_count'} = $loop_count; $param->{'start_time'} = gettext_strftime "%d %b %Y at %H:%M:%S", localtime($start_time); $param->{'process_id'} = $$; ## listmaster has owner and editor privileges for the list if (&List::is_listmaster($param->{'user'}{'email'},$robot)) { $param->{'is_listmaster'} = 1; }else { undef $param->{'is_listmaster'}; } ## Reset $list variable if it is not expected for the current action ## To prevent the list panel from being printed in a non list context ## Only check if the corresponding entry exists in %action_args if (defined $param->{'action'} && defined $action_args{$param->{'action'}}) { unless (grep /^list$/, @{$action_args{$param->{'action'}}}) { $param->{'list'} = undef; $list = undef; } } ## Email addresses protection if (defined $list) { if ($list->{'admin'}{'spam_protection'} eq 'at') { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = ' AT '; $param->{'hidden_end'} = ''; }elsif($list->{'admin'}{'spam_protection'} eq 'javascript') { $param->{'protection_type'} = 'javascript'; $param->{'hidden_head'} = ' '; }else { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = '@'; $param->{'hidden_end'} = ''; } }else { if (&Conf::get_robot_conf($robot,'spam_protection') eq 'at') { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = ' AT '; $param->{'hidden_end'} = ''; }elsif(&Conf::get_robot_conf($robot,'spam_protection') eq 'javascript') { $param->{'protection_type'} = 'javascript'; $param->{'hidden_head'} = ' '; }else { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = '@'; $param->{'hidden_end'} = ''; } } if ($list->{'name'}) { &wwslog('debug2', "list-name $list->{'name'}"); ## Email addresses protection if ($in{'action'} eq 'arc') { $param->{'protection_type'} = undef; if ($list->{'admin'}{'web_archive_spam_protection'} eq 'at') { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = ' AT '; $param->{'hidden_end'} = ''; }elsif($list->{'admin'}{'web_archive_spam_protection'} eq 'javascript') { $param->{'protection_type'} = 'javascript'; $param->{'hidden_head'} = ' '; }else { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = '@'; $param->{'hidden_end'} = ''; } }else { if ($list->{'admin'}{'spam_protection'} eq 'at') { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = ' AT '; $param->{'hidden_end'} = ''; }elsif($list->{'admin'}{'spam_protection'} eq 'javascript') { $param->{'hidden_head'} = ' '; }else { $param->{'hidden_head'} = ''; $param->{'hidden_at'} = '@'; $param->{'hidden_end'} = ''; } } ## Owners my $owners = $list->get_owners(); foreach my $o (@{$owners}) { next unless $o->{'email'}; $param->{'owner'}{$o->{'email'}}{'gecos'} = $o->{'gecos'}; $param->{'owner'}{$o->{'email'}}{'visibility'} = $o->{'visibility'}; $param->{'owner'}{$o->{'email'}}{'mailto'} = &mailto($list,$o->{'email'},$o->{'gecos'}); ($param->{'owner'}{$o->{'email'}}{'local'},$param->{'owner'}{$o->{'email'}}{'domain'}) = split ('@',$o->{'email'}); my $masked_email = $o->{'email'}; $masked_email =~ s/\@/ AT /; $param->{'owner'}{$o->{'email'}}{'masked_email'} = $masked_email; } ## Editors if (defined $list->{'admin'}{'editor'}) { my $editors = $list->get_editors(); foreach my $e (@{$editors}) { next unless $e->{'email'}; $param->{'editor'}{$e->{'email'}}{'gecos'} = $e->{'gecos'}; $param->{'editor'}{$e->{'email'}}{'visibility'} = $e->{'visibility'}; $param->{'editor'}{$e->{'email'}}{'mailto'} = &mailto($list,$e->{'email'},$e->{'gecos'}); ($param->{'editor'}{$e->{'email'}}{'local'},$param->{'editor'}{$e->{'email'}}{'domain'}) = split ('@',$e->{'email'}); my $masked_email = $e->{'email'}; $masked_email =~ s/\@/ AT /; $param->{'editor'}{$e->{'email'}}{'masked_email'} = $masked_email; } } ## Environment variables foreach my $k (keys %ENV) { $param->{'env'}{$k} = $ENV{$k}; } ## privileges if ($param->{'user'}{'email'}) { $param->{'is_subscriber'} = $list->is_user($param->{'user'}{'email'}); $param->{'subscriber'} = $list->get_subscriber($param->{'user'}{'email'}) if $param->{'is_subscriber'}; $param->{'is_privileged_owner'} = $param->{'is_listmaster'} || $list->am_i('privileged_owner', $param->{'user'}{'email'}); $param->{'is_owner'} = $param->{'is_privileged_owner'} || $list->am_i('owner', $param->{'user'}{'email'}); $param->{'is_editor'} = $list->am_i('editor', $param->{'user'}{'email'}); $param->{'is_priv'} = $param->{'is_owner'} || $param->{'is_editor'}; #May post: my $result = $list->check_list_authz('send',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } if ($r_action =~ /do_it/) { $param->{'may_post'} = 1 ; }else { $param->{'may_post_reason'} = $reason; } if ($list->has_include_data_sources() && $param->{'is_owner'}) { $param->{'may_sync'} = 1; } }else { ## If user not logged in && GET method && not an authN-related action ## Keep track of the 'referer' parameter if ($ENV{'REQUEST_METHOD'} eq 'GET' && ! $auth_action{$in{'action'}} ) { $param->{'referer'} = &tools::escape_chars(&wwslib::get_my_url()); }else { ## Keep the previous value of the referer $param->{'referer'} = $in{'referer'}; } } ## Should Not be used anymore ## $param->{'may_subunsub'} = 1 if ($param->{'may_signoff'} || $param->{'may_subscribe'}); ## May review my $result = $list->check_list_authz('review',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_suboptions'} = 1; $param->{'total'} = $list->get_total(); $param->{'may_review'} = 1 if ($r_action =~ /do_it/); $param->{'list_status'} = $list->{'admin'}{'status'}; ## May signoff my $result = $list->check_list_authz('unsubscribe',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); if (! $param->{'user'}{'email'}) { $param->{'may_signoff'} = 1 if ($main::action =~ /do_it|owner|request_auth/); }elsif ($param->{'is_subscriber'} && ($param->{'subscriber'}{'subscribed'} == 1)) { $param->{'may_signoff'} = 1 if ($main::action =~ /do_it|owner|request_auth/); $param->{'may_suboptions'} = 1; } ## May Subscribe my $result = $list->check_list_authz('subscribe',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_subscribe'} = 1 if ($main::action =~ /do_it|owner|request_auth/); # SJS START ## May Add or del subscribers my $result = $list->check_list_authz('add',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_add'} = 1 if ($main::action =~ /do_it/); my $result = $list->check_list_authz('del',$param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); $main::action = $result->{'action'} if (ref($result) eq 'HASH'); $param->{'may_del'} = 1 if ($main::action =~ /do_it/); # SJS END ## Archives Access control if (defined $list->{'admin'}{'web_archive'}) { $param->{'is_archived'} = 1; ## Check if the current user may access web archives my $result = $list->check_list_authz('web_archive.access',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); if ($r_action =~ /do_it/i) { $param->{'arc_access'} = 1; }else{ undef ($param->{'arc_access'}); } ## Check if web archive is publically accessible (useful information for RSS) my $result = $list->check_list_authz('web_archive.access',$param->{'auth_method'}, {'sender' => 'nobody'}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); if ($r_action =~ /do_it/i) { $param->{'arc_public_access'} = 1; } } ## Shared documents access control if ($list->get_shared_status() eq 'exist') { ## Check if shared is publically accessible (useful information for RSS) my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => 'nobody'}); my $r_action; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; } if ($r_action =~ /do_it/i) { $param->{'shared_public_access'} = 1; } } } $param->{'robot'} = $robot; ## If parameter has the Unicode Perl flag, then switch to utf-8 ## this switch is applied recursively &tools::recursive_transformation($param, \&tools::unicode_to_utf8); } ## ticket : this action is used if someone submits a one time ticket sub do_ticket { &wwslog('info', 'do_ticket(%s)', $in{'ticket'}); $param->{'ticket_context'} = &Auth::get_one_time_ticket($in{'ticket'}, $ip ); $param->{'ticket_context'}{'printable_date'} = gettext_strftime "%d %b %Y at %H:%M:%S", localtime($param->{'ticket_context'}{'date'}); return 1 unless ($param->{'ticket_context'}{'result'} eq 'success' or $param->{'ticket_context'}{'result'} eq 'closed'); # if the ticket is related to someone which is not logged in, the system performs the same operation as for a login my $email_regexp = &tools::get_regexp('email'); if ($param->{'ticket_context'}{'result'} eq 'success') { $session->{'email'} = lc($param->{'ticket_context'}{'email'}); $param->{'user'} = &List::get_user_db($session->{'email'}); $param->{'user'}{'email'} = $session->{'email'} ; $param->{'last_login _host'} = $param->{'user'}{'last_login_host'}; $param->{'last_login_date'} = gettext_strftime "%d %b %Y at %H:%M:%S", localtime($param->{'user'}{'last_login_date'}) if ($param->{'user'}{'last_login_date'}); &List::update_user_db($param->{'user'}{'email'},{last_login_date =>time(),last_login_host=>$ip }) ; }elsif($param->{'ticket_context'}{'result'} eq 'closed'){ &wwslog('info', 'do_ticket(%s) : Refusing to perform login because the ticket has been used before', $in{'ticket'}); return 1; }else{ &wwslog('err', 'do_ticket(%s) : Unable to evaluate the ticket validity (status: %s)', $in{'ticket'}, $param->{'ticket_context'}{'result'}); return 1; } &_split_params($param->{'ticket_context'}{'data'}); return $in{'action'} ; } ## Login WWSympa sub do_login { &wwslog('info', 'do_login(%s)', $in{'email'}); my $user; my $next_action; if ($in{'referer'}) { $param->{'redirect_to'} = &tools::unescape_chars($in{'referer'}); }elsif ($in{'previous_action'} && $in{'previous_action'} !~ /^(login|logout|loginrequest)$/) { $next_action = $in{'previous_action'}; $in{'list'} = $in{'previous_list'}; }else { $next_action = &Conf::get_robot_conf($robot, 'default_home'); } # never return to login or logout when login. $next_action = &Conf::get_robot_conf($robot, 'default_home') if $in{'next_action'} =~ /^(login|logout)$/; if ($param->{'user'}{'email'}) { &report::reject_report_web('user','already_login',{'email' => $param->{'user'}{'email'}},$param->{'action'},''); &wwslog('info','do_login: user %s already logged in', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'already_login'}); if ($param->{'nomenu'}) { $param->{'back_to_mom'} = 1; return 1; }else { return $next_action; } } unless ($in{'email'}) { &report::reject_report_web('user','no_email',{},$param->{'action'},''); &wwslog('info','do_login: no email'); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => "no_email"}); return $in{'previous_action'} || &Conf::get_robot_conf($robot, 'default_home'); } $session->{'unauthenticated_email'} = $param->{'unauthenticated_email'} = $in{'email'}; unless ($in{'passwd'}) { my $url_redirect; #Does the email belongs to an ldap directory? if($url_redirect = &is_ldap_user($in{'email'})){ $param->{'redirect_to'} = $url_redirect if ($url_redirect && ($url_redirect != 1)); }elsif ($in{'failure_referer'}) { $param->{'redirect_to'} = $in{'failure_referer'}; }else{ $in{'init_email'} = $in{'email'}; $param->{'init_email'} = $in{'email'}; $param->{'escaped_init_email'} = &tools::escape_chars($in{'email'}); &report::reject_report_web('user','missing_arg',{'argument' => 'passwd'},$param->{'action'},''); &wwslog('info','do_login: missing parameter passwd'); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => "missing_parameter"}); $param->{'login_error'} = 'missing_password'; return $in{'previous_action'} || 'renewpasswd'; } } my $data; unless ($ENV{'REQUEST_METHOD'} eq 'POST') { &do_log('notice', "Authentication failed, because do not use HTTP method POST but %s",$ENV{'REQUEST_METHOD'} ); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'not_using_post'}); return 'loginrequest'; } unless($data = &Auth::check_auth($robot, $in{'email'},$in{'passwd'})){ &do_log('notice', "Authentication failed\n"); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'authentication'}); my $unauthenticated_user = &List::get_user_db($in{'email'}); if ($unauthenticated_user->{'wrong_login_count'} > &Conf::get_robot_conf($robot, 'max_wrong_password')){ $param->{'login_error'} = 'password_reset'; }else{ $param->{'login_error'} = 'wrong_password'; } if ($in{'previous_action'}) { delete $in{'passwd'}; $in{'list'} = $in{'previous_list'}; return $in{'previous_action'}; }elsif ($in{'failure_referer'}) { $param->{'redirect_to'} = $in{'failure_referer'}; }else { return 'renewpasswd'; } } $param->{'user'} = $data->{'user'}; $param->{'last_login_host'} = $data->{'user'}{'last_login_host'}; $param->{'last_login_date'} = gettext_strftime "%d %b %Y at %H:%M:%S", localtime($data->{'user'}{'last_login_date'}) if ($data->{'user'}{'last_login_date'}); $session->{'auth'} = $data->{'auth'}; my $email = lc($param->{'user'}{'email'}); $session->{'email'} = $email; $session->{'unauthenticated_email'} = ''; &List::update_user_db($param->{'user'}{'email'},{last_login_date =>time(),last_login_host=>$ip, wrong_login_count =>0}) ; ## Set alt_email if ($data->{'alt_emails'}) { foreach my $k (keys %{$data->{'alt_emails'}}) { $param->{'alt_emails'}{$k} = $data->{'alt_emails'}{$k}; } } unless($param->{'alt_emails'}{$email}){ unless(&cookielib::set_cookie_extern($Conf{'cookie'},$param->{'cookie_domain'},%{$param->{'alt_emails'}})){ &wwslog('notice', 'Could not set HTTP cookie for external_auth'); web_db_log( { 'parameters' => $param->{'cookie_domain'}.','. join(',', map { "$_ => $param->{'alt_emails'}{$_}" } keys %{$param->{'alt_emails'} || {}}), 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'cookie' } ); return undef; } } ## Current authentication mode #$param->{'auth'} = $param->{'alt_emails'}{$param->{'user'}{'email'}} || 'classic'; if ($session->{'lang'}) { # user did choose a specific language before being logged. Apply it as a user pref. &List::update_user_db($param->{'user'}{'email'},{lang=>$session->{'lang'}}) ; $param->{'lang'} = $session->{'lang'}; }else{ # user did not choose a specific language, apply user pref for this session. $param->{'lang'} = $user->{'lang'} || $list->{'admin'}{'lang'} || &Conf::get_robot_conf($robot, 'lang'); $session->{'lang'} = $param->{'lang'} ; } $param->{'lang_tag'} = Language::LanguageTag($param->{'lang'}); if ($session->{'review_page_size'}) { # user did choose a specific page size upgrade prefs &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } if ($session->{'shared_mode'}) { # user did choose a shared expert/standard mode &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } if ($in{'newpasswd1'} && $in{'newpasswd2'}) { my $old_action = $param->{'action'}; $param->{'action'} = 'setpasswd'; &do_setpasswd(); $param->{'action'} = $old_action; } if ($param->{'nomenu'}) { $param->{'back_to_mom'} = 1; return 1; } &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'success'}); &do_redirect ($session->{'redirect_url'}); return ; } ## Login WWSympa ## The sso_login action is made of 4 subactions that make a complete workflow. ## Note that this comlexe workflow is only used if the SSO server does not provide ## the user email address or if this email address is not trusted and therefore ## needs to be checked. ## The workflow: ## 1) init: determine if email address needs to be collected/checked ## 2) requestemail: collect the user email address in a web form. Note that form may be initialized with ## one email address provided by the SSO server ## 3) validateemail: a challenge is sent to the email address to validate it ## 4) confirmemail: user confirms his email address with the challenge sub do_sso_login { &wwslog('info', 'do_sso_login(%s)', $in{'auth_service_name'}); delete $session->{'do_not_use_cas'}; #when user require CAS login, reset do_not_use_cas cookie my $next_action; if ($param->{'user'}{'email'}) { &report::reject_report_web('user','already_login',{'email' => $param->{'user'}{'email'}},$param->{'action'},''); &wwslog('err','do_login: user %s already logged in', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'status' => 'error', 'error_type' => "already_login"}); return &Conf::get_robot_conf($robot, 'default_home'); } ## This is a CAS service if (defined (my $cas_id = $Conf{'cas_id'}{$robot}{$in{'auth_service_name'}})) { my $cas_server = $Conf{'auth_services'}{$robot}[$cas_id]{'cas_server'}; my $path = ''; if ($param->{'nomenu'}) { $path = "/nomenu"; } $path .= "/sso_login_succeeded/$in{'auth_service_name'}"; $session->{'checked_cas'} = $cas_id; my $service = "$param->{'base_url'}$param->{'path_cgi'}".$path; my $redirect_url = $cas_server->getServerLoginURL($service); &wwslog('info', 'do_sso_login: redirect_url(%s)', $redirect_url); if ($redirect_url =~ /http(s)+\:\//i) { $in{'action'} = 'redirect'; $param->{'redirect_to'} = $redirect_url; $param->{'bypass'} = 'extreme'; $session->set_cookie($param->{'cookie_domain'},'session'); #$session->set_cookie('localhost','session'); print "Location: $param->{'redirect_to'}\n\n"; } }elsif (defined (my $sso_id = $Conf{'generic_sso_id'}{$robot}{$in{'auth_service_name'}})) { ## Generic SSO ## If contacted via POST, then redirect the user to the URL for the access control to apply if ($ENV{'REQUEST_METHOD'} eq 'POST') { my $path = ''; my $service; if ($param->{'nomenu'}) { $path = "/nomenu"; } &wwslog('info', 'do_sso_login(): POST request processing'); if ($in{'subaction'} eq 'validateemail') { $path .= "/validateemail/$in{'email'}"; }elsif ($in{'subaction'} eq 'confirmemail') { $path .= "/confirmemail/$in{'email'}/$in{'ticket'}"; }else { $path .= "/init"; } my $service = "$param->{'base_url'}$param->{'path_cgi'}/sso_login/$in{'auth_service_name'}".$path; &wwslog('info', 'do_sso_login: redirect user to %s', $service); $in{'action'} = 'redirect'; $param->{'redirect_to'} = $service; $param->{'bypass'} = 'extreme'; print "Location: $param->{'redirect_to'}\n\n"; return 1; } my $email; ## We need to collect/verify the user's email address if (defined $Conf{'auth_services'}{$robot}[$sso_id]{'force_email_verify'}) { my $email_is_trusted = 0; ## the subactions order is : init, requestemail, validateemail, sendssopasswd, confirmemail ## get email from NetiD table if (defined $Conf{'auth_services'}{$robot}[$sso_id]{'internal_email_by_netid'}) { &wwslog('debug', 'do_sso_login(): lookup email internal: %s', $sso_id); if ($email = &Auth::get_email_by_net_id($robot, $sso_id, \%ENV)) { $email_is_trusted = 1; } } ## get email from authN module if (defined $Conf{'auth_services'}{$robot}[$sso_id]{'email_http_header'} && ! $email_is_trusted) { my @email_list = split(/$Conf{'auth_services'}{$robot}[$sso_id]{'http_header_value_separator'}/, lc($ENV{$Conf{'auth_services'}{$robot}[$sso_id]{'email_http_header'}})); $email = $email_list[0]; ## Only get the first occurrence if multi-valued } ## Start the email validation process if ($in{'subaction'} eq 'init' && ($email_is_trusted == 0 || ! $email)) { &wwslog('info', 'do_sso_login(): return request email'); $session->{'auth'} = 'generic_sso'; $param->{'server'}{'key'} = $in{'auth_service_name'}; $param->{'subaction'} = 'requestemail'; $param->{'init_email'} = $email; return 1; } if (defined($in{'email'}) and !($in{'subaction'} eq 'init')) { $email = $in{'email'}; } ## Send a confirmation email and request it on the web interface if ($in{'subaction'} eq 'validateemail') { $session->{'auth'} = 'generic_sso'; $param->{'server'}{'key'} = $in{'auth_service_name'}; $param->{'init_email'} = $email; ## Replace sendpassword with one time ticket $param->{'one_time_ticket'} = &Auth::create_one_time_ticket($in{'email'},$robot,'sso_login/confirmemail?auth_service_name='.$in{'auth_service_name'},$ip); unless (&sendssopasswd($email)) { &report::reject_report_web('user','incorrect_email',{'email' => $email},$param->{'action'}); $param->{'subaction'} = 'requestemail'; return 1; } $param->{'subaction'} = 'validateemail'; return 1; } if ($in{'subaction'} eq 'confirmemail') { $session->{'auth'} = 'generic_sso' ; $param->{'server'}{'key'} = $in{'auth_service_name'}; $param->{'init_email'} = $email; $in{'email'} = $email; # # Check input parameters and verify ticket for email, stolen from do_login # unless ($in{'email'}) { &report::reject_report_web('user','no_email',{},$param->{'action'}); &wwslog('info','confirmemail: no email'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'no_email'}); $param->{'subaction'} = 'validateemail'; return 1; } unless ($in{'ticket'}) { $in{'init_email'} = $in{'email'}; $param->{'init_email'} = $in{'email'}; $param->{'escaped_init_email'} = &tools::escape_chars($in{'email'}); &report::reject_report_web('user','missing_arg',{'argument' => 'ticket'},$param->{'action'}); &wwslog('info','do_sso_login: confirmemail: missing parameter ticket'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); $param->{'subaction'} = 'validateemail'; return 1; } ## Validate the ticket my $ticket_output = &Auth::get_one_time_ticket($in{'ticket'}, $ip ); unless ($ticket_output->{'result'} eq 'success'){ &report::reject_report_web('user','auth_failed',{},$param->{'action'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'authentication'}); &wwslog('err', "Authentication failed\n"); $param->{'subaction'} = 'validateemail'; return 1; } &wwslog('info', 'do_sso_login: confirmemail: email validation succeeded'); # need to create netid to email map entry $email = $in{'email'}; # everything is ok to proceed to with possible sympa account created and traddional sso login ## TODO : netidmap_table should also be used when no confirmation is performed if (defined $Conf{'auth_services'}{$robot}[$sso_id]{'internal_email_by_netid'}) { my $netid = $ENV{$Conf{'auth_services'}{$robot}[$sso_id]{'netid_http_header'}}; my $idpname = $Conf{'auth_services'}{$robot}[$sso_id]{'service_id'}; unless(&List::set_netidtoemail_db($robot, $netid, $idpname, $in{'email'})) { &report::reject_report_web('intern','db_update_failed',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err', 'error update netid map'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return &Conf::get_robot_conf($robot, 'default_home'); } }else { &wwslog('info', 'do_sso_login: confirmemail: validation failed'); $param->{'subaction'} = 'validateemail'; return 1; } } }else { ## if (defined $Conf{'auth_services'}{$robot}[$sso_id]{'email_http_header'}) { my @email_list = split($Conf{'auth_services'}{$robot}[$sso_id]{'http_header_value_separator'}, lc($ENV{$Conf{'auth_services'}{$robot}[$sso_id]{'email_http_header'}})); $email = $email_list[0]; ## Only get the first occurrence if multi-valued }else { unless (defined $Conf{'auth_services'}{$robot}[$sso_id]{'ldap_host'} && defined $Conf{'auth_services'}{$robot}[$sso_id]{'ldap_get_email_by_uid_filter'}) { &report::reject_report_web('intern','auth_conf_no_identified_user',{},$param->{'action'},'','',$robot); &wwslog('err','do_sso_login: auth.conf error : either email_http_header or ldap_host/ldap_get_email_by_uid_filter entries should be defined'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return 'home'; } $email = &Auth::get_email_by_net_id($robot, $sso_id, \%ENV); } } unless ($email) { &report::reject_report_web('intern','no_identified_user',{},$param->{'action'},'','',$robot); &wwslog('err','do_sso_login: user could not be identified, no %s HTTP header set', $Conf{'auth_services'}{$robot}[$sso_id]{'email_http_header'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'status' => 'error', 'error_type' => 'no_email'}); return 'home'; } $param->{'user'}{'email'} = $email; $session->{'email'} = $email; $session->{'auth'} = 'generic_sso' ; &wwslog('notice', 'User identified as %s', $email); ## There are two ways to list the attributes that Sympa will cache for the user ## Either with a defined header prefix (http_header_prefix) ## Or with an explicit list of header fields (http_header_list) my @sso_attr; if ($Conf{'auth_services'}{$robot}[$sso_id]{'http_header_list'}) { my $list_of_headers = $Conf{'auth_services'}{$robot}[$sso_id]{'http_header_list'}; foreach my $field (split(/,/, $list_of_headers)) { if (defined $ENV{$field}) { push @sso_attr, $field.'__PAIRS_SEP__'.$ENV{$field}; } } }elsif ($Conf{'auth_services'}{$robot}[$sso_id]{'http_header_prefix'}) { my $prefix = $Conf{'auth_services'}{$robot}[$sso_id]{'http_header_prefix'}; foreach my $k (keys %ENV) { if ($k =~ /^$prefix/) { push @sso_attr, $k.'__PAIRS_SEP__'.$ENV{$k}; } } } my $all_sso_attr = join '__ATT_SEP__', @sso_attr; ## Create user entry if required unless (&List::is_user_db($email)) { unless (&List::add_user_db({'email' => $email})) { &report::reject_report_web('intern','add_user_db_failed',{'user'=>$email},$param->{'action'},'',$email,$robot); &wwslog('info','do_sso_login: add failed'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } unless (&List::update_user_db($email, {'attributes' => $all_sso_attr })) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$email},$param->{'action'},'',$email,$robot); &wwslog('info','do_sso_login: update failed'); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &report::notice_report_web('you_have_been_authenticated',{},$param->{'action'}); ## Keep track of the SSO used to login ## Required to provide logout feature if available $session->{'sso_id'} = $in{'auth_service_name'}; &do_redirect ($session->{'redirect_url'}); return ; }else{ ## Unknown SSO service &report::reject_report_web('intern','unknown_authentication_service',{'name'=> $in{'auth_service_name'}},$param->{'action'},'','',$robot); &wwslog('err','do_sso_login: unknown authentication service %s', $in{'auth_service_name'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return 'home'; } &web_db_log({'parameters' => $in{'auth_service_name'}, 'target_email' => $in{'email'}, 'status' => 'success'}); return 1; } sub do_sso_login_succeeded { &wwslog('info', 'do_sso_login_succeeded(%s)', $in{'auth_service_name'}); if (defined $param->{'user'} && $param->{'user'}{'email'}) { &report::notice_report_web('you_have_been_authenticated',{},$param->{'action'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'status' => 'success'}); }else{ &report::reject_report_web('user','auth_failed',{},$param->{'action'}); &web_db_log({'parameters' => $in{'auth_service_name'}, 'status' => 'error', 'error_type' => 'authentication'}); } ## We should refresh the main window if ($param->{'nomenu'}) { $param->{'back_to_mom'} = 1; return 1; }else{ &do_redirect ($session->{'redirect_url'}); return; } } sub is_ldap_user { my $auth = shift; ## User email or UID &wwslog('debug2',"is_ldap_user ($auth)"); unless (&tools::get_filename('etc',{}, 'auth.conf', $robot)) { return undef; } ## List all LDAP servers first my @ldap_servers; foreach my $ldap (@{$Conf{'auth_services'}{$robot}}){ next unless ($ldap->{'auth_type'} eq 'ldap'); push @ldap_servers, $ldap; } unless ($#ldap_servers >= 0) { return undef; } unless (eval "require Net::LDAP") { &wwslog ('err',"Unable to use LDAP library, Net::LDAP required,install perl-ldap (CPAN) first"); return undef; } require Net::LDAP; my ($ldap_anonymous,$filter); foreach my $ldap (@ldap_servers){ # skip ldap auth service if the user id or email do not match regexp auth service parameter next unless ($auth =~ /$ldap->{'regexp'}/i); my $param = &tools::dup_var($ldap); my $ds = new Datasource('LDAP', $param); unless (defined $ds && ($ldap_anonymous = $ds->connect())) { &do_log('err',"Unable to connect to the LDAP server '%s'", $ldap->{'ldap_host'}); next; } my @alternative_conf = split(/,/,$ldap->{'alternative_email_attribute'}); my $attrs = $ldap->{'email_attribute'}; if (&tools::valid_email($auth)){ $filter = $ldap->{'get_dn_by_email_filter'}; }else{ $filter = $ldap->{'get_dn_by_uid_filter'}; } $filter =~ s/\[sender\]/$auth/ig; ## !! une fonction get_dn_by_email/uid my $mesg = $ldap_anonymous->search(base => $ldap->{'suffix'} , filter => "$filter", scope => $ldap->{'scope'}, timeout => $ldap->{'timeout'} ); unless($mesg->count() != 0) { &wwslog('notice','No entry in the Ldap Directory Tree of %s for %s',$ldap->{'host'},$auth); $ds->disconnect(); last; } $ds->disconnect(); my $redirect = $ldap->{'authentication_info_url'}; return $redirect || 1; next unless ($ldap_anonymous); } } ## send back login form sub do_loginrequest { &wwslog('info','do_loginrequest'); if ($param->{'user'}{'email'}) { &report::reject_report_web('user','already_login',{'email' => $param->{'user'}{'email'}},$param->{'action'}); &wwslog('info','do_loginrequest: already logged in as %s', $param->{'user'}{'email'}); return undef; } if ($in{'init_email'}) { $param->{'init_email'} = $in{'init_email'}; } if ($in{'previous_action'} eq 'referer') { $param->{'referer'} = &tools::escape_chars($ENV{'HTTP_REFERER'}); }elsif (! $param->{'previous_action'}) { $param->{'previous_action'} = 'loginrequest'; } $param->{'title'} = 'Login' if ($param->{'nomenu'}); return 1; } ## Help / about WWSympa sub do_help { &wwslog('info','do_help(%s)', $in{'help_topic'}); ## Contextual help if ($in{'help_topic'}) { if ($in{'help_topic'} eq 'editlist') { foreach my $pname (sort List::by_order keys %{$pinfo}) { next if ($pname =~ /^(comment|defaults)$/); if ($pinfo->{$pname}{'gettext_id'}) { $param->{'param'}{$pname}{'title'} = gettext($pinfo->{$pname}{'gettext_id'}); } else { $param->{'param'}{$pname}{'title'} = $pinfo->{$pname}{'title'}{$param->{'lang'}}; } $param->{'param'}{$pname}{'comment'} = $pinfo->{$pname}{'comment'}{$param->{'lang'}}; } } $param->{'help_topic'} = $in{'help_topic'}; } return 1; } # update session cookie and redirect the client to redirect_to parameter or glob var; sub do_redirect { my $redirect_to = shift; &wwslog('info','do_redirect(%s)', $redirect_to); $redirect_to ||= $param->{'redirect_to'}; # because of some bug Sympa did redirection to un empty URL. Next line should prevent it. $redirect_to ||= $param->{'base_url'}.$param->{'path_cgi'}; #$session->set_cookie('localhost','session'); $session->set_cookie($param->{'cookie_domain'},'session'); print "Location: $redirect_to\n\n"; $param->{'bypass'} = 'extreme'; return 1; } ## Logout from WWSympa sub do_logout { &wwslog('info','do_logout(%s)', $param->{'user'}{'email'}); delete $param->{'user'}; $session->{'email'} = 'nobody' ; # no reason to alter the lang because user perform logout # $param->{'lang'} = $param->{'cookie_lang'} = &cookielib::check_lang_cookie($ENV{'HTTP_COOKIE'}) || $list->{'admin'}{'lang'} || &Conf::get_robot_conf($robot, 'lang'); if (defined $session->{'cas_server'} && (defined $Conf{'auth_services'}{$robot}[$session->{'cas_server'}])) { # this user was logged using CAS my $cas_server = $Conf{'auth_services'}{$robot}[$session->{'cas_server'}]{'cas_server'}; $in{'action'} = 'redirect'; my $return_url = &wwslib::get_my_url(); $return_url =~ s/\/logout//; $param->{'redirect_to'} = $cas_server->getServerLogoutURL($return_url); delete $session->{'cas_server'}; return 'redirect'; } elsif (defined $session->{'sso_id'}) { # this user was logged using a generic_sso ## Check if logout_url is known for this SSO my $sso; unless ($sso = &Conf::get_sso_by_id(robot => $robot, service_id => $session->{'sso_id'})) { &wwslog('err',"unknown SSO service_id '%s'", $session->{'sso_id'}); return undef ; } ## Remove sso_id delete $session->{'sso_id'}; if ($sso->{'logout_url'}) { $in{'action'} = 'redirect'; $param->{'redirect_to'} = $sso->{'logout_url'}; return 'redirect'; } } &wwslog('info','do_logout: logout performed'); &web_db_log({'parameters' => $param->{'user'}{'email'}, 'target_email' => $in{'email'}, 'status' => 'success'}); if ($in{'previous_action'} eq 'referer') { $param->{'referer'} = &tools::escape_chars($in{'previous_list'}); } return &Conf::get_robot_conf($robot, 'default_home'); } sub sendssopasswd { my $email = shift; do_log('info', 'sendssopasswd(%s)', $email); my ($passwd, $user); unless ($email) { &report::reject_report_web('user','no_email',{},$param->{'action'}); &wwslog('info','do_sendssopasswd: no email'); &web_db_log({'parameters' => $email, 'target_email' => $email, 'status' => 'error', 'error_type' => "no_email"}); return 'requestemail'; } unless (&tools::valid_email($email)) { &report::reject_report_web('user','incorrect_email',{'email' => $email},$param->{'action'}); &wwslog('info','do_sendssopasswd: incorrect email %s', $email); &web_db_log({'parameters' => $email, 'target_email' => $email, 'status' => 'error', 'error_type' => "incorrect_email"}); return 'requestemail'; } my $url_redirect; if ($param->{'newuser'} = &List::get_user_db($email)) { ## Create a password if none unless ($param->{'newuser'}{'password'}) { unless ( &List::update_user_db($email, {'password' => &tools::tmp_passwd($email) })) { &report::reject_report_web('intern','db_update_failed',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','sendssopasswd: update failed'); &web_db_log({'parameters' => $email, 'target_email' => $email, 'status' => 'error', 'error_type' => "internal"}); return undef; } $param->{'newuser'}{'password'} = &tools::tmp_passwd($email); } $param->{'newuser'}{'escaped_email'} = &tools::escape_chars($param->{'newuser'}{'email'}); }else { $param->{'newuser'} = {'email' => $email, 'escaped_email' => &tools::escape_chars($email), 'password' => &tools::tmp_passwd($email) }; } $param->{'init_passwd'} = 1 if ($param->{'user'}{'password'} =~ /^init/); &List::send_global_file('sendssopasswd', $email, $robot, $param); $param->{'email'} = $email; &web_db_log({'parameters' => $email, 'target_email' => $email, 'status' => 'success'}); return 'validateemail'; } sub do_firstpasswd { &wwslog('info', 'do_firstpasswd(%s)', $in{'email'}); $param->{'requestpasswd_context'} = 'firstpasswd'; return 'renewpasswd'; } ## send a ticket for choosing a new password sub do_renewpasswd { &wwslog('info', 'do_renewpasswd(%s)', $in{'email'}); my $url_redirect; if($in{'email'}){ if($url_redirect = &is_ldap_user($in{'email'})){ $param->{'redirect_to'} = $url_redirect if ($url_redirect && ($url_redirect != 1)); }elsif (! &tools::valid_email($in{'email'})) { &report::reject_report_web('user','incorrect_email',{'email' => $in{'email'}},$param->{'action'}); &wwslog('info','do_renewpasswd: incorrect email \"%s\"', $in{'email'}); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'incorrect_email'}); return undef; } } $param->{'email'} = $in{'email'}; &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'success', }); if ($in{'previous_action'} eq 'referer') { $param->{'referer'} = &tools::escape_chars($in{'previous_list'}); } return 1; } #################################################### # do_requestpasswd #################################################### # Sends a message to the user containing user password. # # IN : - # # OUT : 'renewpasswd' | 1 | 'loginrequest' | undef # #################################################### sub do_requestpasswd { &wwslog('info', 'do_requestpasswd(%s)', $in{'email'}); my ($passwd, $user); $param->{'account_creation'} = 1; my $url_redirect; if($url_redirect = &is_ldap_user($in{'email'})){ ## There might be no authentication_info_url URL defined in auth.conf if ($url_redirect == 1) { &report::reject_report_web('user','ldap_user',{},$param->{'action'}); &wwslog('info','do_requestpasswd: LDAP user %s, cannot remind password', $in{'email'}); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return 'home'; }else{ $param->{'redirect_to'} = $url_redirect if ($url_redirect && ($url_redirect != 1)); return 1; } } ## Check auth.conf before creating/sending a password unless (&Auth::may_use_sympa_native_auth($robot, $in{'email'})) { ## TODO: Error handling &report::reject_report_web('user','passwd_reminder_not_allowed',{},$param->{'action'}); return undef } &wwslog('debug','do_requestpasswd: sending one tile ticket for %s', $in{'email'}); $param->{'one_time_ticket'} = &Auth::create_one_time_ticket($in{'email'},$robot,'choosepasswd',$ip); $param->{'request_from_host'} = $ip; unless ($param->{'newuser'} = &List::get_user_db($in{'email'})) { $param->{'newuser'} = { 'email' => &tools::clean_email($in{'email'}) }; $param->{'newuser'}{'escaped_email'} = &tools::escape_chars($param->{'newuser'}{'email'}); } if ($param->{'one_time_ticket'}) { $param->{'login_error'}='ticket_sent'; unless (&List::send_global_file('sendpasswd', $in{'email'}, $robot, $param)) { &wwslog('notice',"Unable to send template 'sendpasswd' to $in{'email'}"); $param->{'login_error'}='unable_to_send_ticket'; } }else{ &wwslog('notice',"Unable to create_one_time_ticket"); &report::reject_report_web('user','passwd_reminder_error',{},$param->{'action'}); $param->{'login_error'}='unable_to_create_ticket'; } return 1 unless ($param->{'previous_action'}) ; return $param->{'previous_action'}; } ## Which list the user is subscribed to ## TODO (pour listmaster, toutes les listes) sub do_which { my $which = {}; &wwslog('info', 'do_which'); $param->{'get_which'} = undef ; $param->{'which'} = undef ; foreach my $role ('member','owner','editor') { foreach my $list ( &List::get_which($param->{'user'}{'email'}, $robot, $role) ){ my $l = $list->{'name'}; my $result = $list->check_list_authz('visibility', $param->{'auth_method'}, {'sender' =>$param->{'user'}{'email'} , 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); next unless ($r_action =~ /do_it/); $param->{'which'}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{'which'}{$l}{'host'} = $list->{'admin'}{'host'}; if ($role eq 'member') { push @{$param->{'get_which'}}, $list; } if ($role eq 'owner' || $role eq 'editor') { $param->{'which'}{$l}{'admin'} = 1; } ## For compatibility concerns (3.0) ## To be deleted one of these day $param->{$role}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{$role}{$l}{'host'} = $list->{'admin'}{'host'}; } } # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'which','',$robot,'','done'); return 1; } ## The list of list sub do_lists { my @lists; &wwslog('info', 'do_lists(%s,%s)', $in{'topic'}, $in{'subtopic'}); my %topics = &List::load_topics($robot); if ($in{'topic'}) { $param->{'topic'} = $in{'topic'}; if ($in{'subtopic'}) { $param->{'subtopic'} = $in{'subtopic'}; $param->{'subtitle'} = sprintf "%s / %s", $topics{$in{'topic'}}{'current_title'}, $topics{$in{'topic'}}{'sub'}{$in{'subtopic'}}{'current_title'}; $param->{'subtitle'} ||= "$in{'topic'} / $in{'subtopic'}"; }else { $param->{'subtitle'} = $topics{$in{'topic'}}{'current_title'} || $in{'topic'}; } } my $all_lists = &List::get_lists($robot); foreach my $list ( @$all_lists ) { my $sender = $param->{'user'}{'email'} || 'nobody'; my $result = $list->check_list_authz('visibility',$param->{'auth_method'}, {'sender' => $sender, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'options' => {'dont_reload_scenario' => 1}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); next unless ($r_action eq 'do_it'); my $list_info = {}; $list_info->{'subject'} = $list->{'admin'}{'subject'}; $list_info->{'host'} = $list->{'admin'}{'host'}; $list_info->{'date_epoch'} = $list->{'admin'}{'creation'}{'date_epoch'}; $list_info->{'date'} = $list->{'admin'}{'creation'}{'date'}; $list_info->{'topics'} = $list->{'admin'}{'topics'}; if ($param->{'user'}{'email'} && ($list->am_i('owner',$param->{'user'}{'email'}) || $list->am_i('editor',$param->{'user'}{'email'})) ) { $list_info->{'admin'} = 1; } if ($param->{'user'}{'email'} && $list->is_user($param->{'user'}{'email'})) { $list_info->{'is_subscriber'} = 1; } ## Filter lists by topic. ## topic argument 'topicsless' or 'other' means 'lists with topic ## "other" or without topics'. ## no topic argument; List all lists if (! $in{'topic'}) { $param->{'which'}{$list->{'name'}} = $list_info; }elsif ($list->{'admin'}{'topics'}) { foreach my $topic (@{$list->{'admin'}{'topics'}}) { my @tree = split '/', $topic; if (($in{'topic'} eq 'other' and ! $in{'subtopic'}) or $in{'topic'} eq 'topicsless') { $param->{'which'}{$list->{'name'}} = $list_info if $tree[0] eq 'other' and ! $tree[1]; next; } next if (($in{'topic'}) && ($tree[0] ne $in{'topic'})); next if (($in{'subtopic'}) && ($tree[1] ne $in{'subtopic'})); $param->{'which'}{$list->{'name'}} = $list_info; } } elsif (($in{'topic'} eq 'other' and ! $in{'subtopic'}) or $in{'topic'} eq 'topicsless') { $param->{'which'}{$list->{'name'}} = $list_info; } } foreach my $listname (sort keys %{$param->{'which'}}) { if ($listname =~ /^([a-z])/){ push @{$param->{'orderedlist'}{$1}}, $listname ; }else{ push @{$param->{'orderedlist'}{'others'}}, $listname ; } } return 1; } ## The list of latest created lists sub do_latest_lists { &wwslog('info', "do_latest_lists($in{'for'}, $in{'count'},$in{'topic'}, $in{'subtopic'})"); unless (&do_lists()) { &wwslog('err','do_latest_lists: error while calling do_lists'); return undef; } my $today = time; my $oldest_day; if (defined $in{'for'}) { $oldest_day = $today - (3600 * 24 * ($in{'for'})); $param->{'for'} = $in{'for'}; unless ($oldest_day >= 0){ &report::reject_report_web('user','nb_days_to_much',{'nb_days' => $in{'for'} },$param->{'action'}); &wwslog('err','do_latest_lists: parameter "for" is too big"'); } } my $nb_lists = 0; my @date_lists; foreach my $listname (keys (%{$param->{'which'}})) { if ($param->{'which'}{$listname}{'date_epoch'} < $oldest_day) { delete $param->{'which'}{$listname}; next; } $nb_lists++; } if (defined $in{'count'}) { $param->{'count'} = $in{'count'}; unless ($in{'count'}) { $param->{'which'} = undef; } } my $count_lists = 0; foreach my $l ( sort {$param->{'which'}{$b}{'date_epoch'} <=> $param->{'which'}{$a}{'date_epoch'}} (keys (%{$param->{'which'}}))) { $count_lists++; if ($in{'count'}) { if ($count_lists > $in{'count'}){ last; } } $param->{'which'}{$l}{'name'} = $l; push @{$param->{'latest_lists'}} , $param->{'which'}{$l}; } $param->{'which'} = undef; return 1; } ## The list of the most active lists sub do_active_lists { &wwslog('info', "do_active_lists($in{'for'}, $in{'count'},$in{'topic'}, $in{'subtopic'})"); unless (&do_lists()) { &wwslog('err','do_active_lists: error while calling do_lists'); return undef; } ## oldest interesting day my $oldest_day = 0; if (defined $in{'for'}) { $oldest_day = int(time/86400) - $in{'for'}; unless ($oldest_day >= 0){ &report::reject_report_web('user','nb_days_to_much',{'nb_days' => $in{'for'} },$param->{'action'}); &wwslog('err','do_latest_lists: parameter "for" is too big"'); return undef; } } ## get msg count for each list foreach my $l (keys (%{$param->{'which'}})) { my $list = new List ($l, $robot); my $file = "$list->{'dir'}/msg_count"; my %count ; if (open(MSG_COUNT, $file)) { while (){ if ($_ =~ /^(\d+)\s(\d+)$/) { $count{$1} = $2; } } close MSG_COUNT ; $param->{'which'}{$l}{'msg_count'} = &count_total_msg_since($oldest_day,\%count); if ($in{'for'}) { my $average = $param->{'which'}{$l}{'msg_count'} / $in{'for'}; ## nb msg by day $average = int($average * 10); $param->{'which'}{$l}{'average'} = $average /10; ## one digit } } else { $param->{'which'}{$l}{'msg_count'} = 0; } } my $nb_lists = 0; ## get "count" lists foreach my $l ( sort {$param->{'which'}{$b}{'msg_count'} <=> $param->{'which'}{$a}{'msg_count'}} (keys (%{$param->{'which'}}))) { if (defined $in{'count'}) { $nb_lists++; if ($nb_lists > $in{'count'}) { last; } } $param->{'which'}{$l}{'name'} = $l; push @{$param->{'active_lists'}} , $param->{'which'}{$l}; } if (defined $in{'count'}) { $param->{'count'} = $in{'count'}; } if (defined $in{'for'}) { $param->{'for'} = $in{'for'}; } $param->{'which'} = undef; return 1; } sub count_total_msg_since { my $oldest_day = shift; my $count = shift; my $total = 0; foreach my $d (sort {$b <=> $a} (keys %$count)) { if ($d < $oldest_day) { last; } $total = $total + $count->{$d}; } return $total; } ## List information page sub do_info { &wwslog('info', 'do_info'); ## Access control unless (defined &check_authz('do_info', 'info')) { delete $param->{'list'}; return undef; } ## Get List Description if (-r $list->{'dir'}.'/homepage') { my $file_path = $list->{'dir'}.'/homepage'; unless (open FILE, "<", $file_path) { &report::reject_report_web('intern','cannot_open_file',{'file' => $file_path},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_info: failed to open file %s: %s', $file_path,$!); &web_db_log({'parameters' => $file_path, 'status' => 'error', 'error_type' => 'internal'}); return undef; } while () { Encode::from_to($_, $Conf{'filesystem_encoding'}, 'utf8'); $param->{'homepage_content'} .= $_; } close FILE; ## Used by previous templates $param->{'homepage'} = 1; }elsif (-r $list->{'dir'}.'/info') { my $file_path = $list->{'dir'}.'/info'; unless (open FILE, "<", $file_path) { &report::reject_report_web('intern','cannot_open_file',{'file' => $file_path},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_info: failed to open file %s: %s', $file_path,$!); &web_db_log({'parameters' => $file_path, 'status' => 'error', 'error_type' => 'internal'}); return undef; } while () { Encode::from_to($_, $Conf{'filesystem_encoding'}, 'utf8'); $param->{'info_content'} .= $_; } close FILE; $param->{'info_content'} =~ s/\n/\/g; } &tt2::add_include_path($list->{'dir'}); return 1; } ## List subcriber count page sub do_subscriber_count { &wwslog('info', 'do_subscriber_count'); unless (&do_info()) { &wwslog('info','do_subscriber_count: error while calling do_info'); return undef; } print "Content-type: text/plain\n\n"; print $list->get_total()."\n"; $param->{'bypass'} = 'extreme'; return 1; } ## Subscribers' list sub do_review { &wwslog('info', 'do_review(%s)', $in{'page'}); my $record; my @users; my $size ; my $sortby = $in{'sortby'} || 'email'; my %sources; ## Access control return undef unless (defined &check_authz('do_review', 'review')); if($in{'size'}){ $size = $in{'size'}; $session->{'review_page_size'} = $in{'size'} ; if ($param->{'user'}{'prefs'}{'review_page_size'} ne $in{'size'}) { # update user pref as soon as connected user change page size $param->{'user'}{'prefs'}{'review_page_size'} = $in{'size'}; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } }else{ $size = $param->{'user'}{'prefs'}{'review_page_size'} || $session->{'review_page_size'} || $wwsconf->{'review_page_size'}; } $param->{'review_page_size'} = $size; unless ($param->{'total'}) { &wwslog('info','do_review: no subscriber'); # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'review',$param->{'list'},$robot,'','no subscriber'); return 1; } ## Owner $param->{'page'} = $in{'page'} || 1; $param->{'total_page'} = int ($param->{'total'} / $size); $param->{'total_page'} ++ if ($param->{'total'} % $size); if ($param->{'total_page'} > 0 and ($param->{'page'} > $param->{'total_page'})) { &report::reject_report_web('user','no_page',{'page' => $param->{'page'}},$param->{'action'},$list); # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'review',$param->{'list'},$robot,'','out of pages'); &wwslog('info','do_review: no page %d', $param->{'page'}); return undef; } my $offset; if ($param->{'page'} > 1) { $offset = (($param->{'page'} - 1) * $size); }else { $offset = 0; } ## We might not use LIMIT clause my ($limit_not_used, $count); unless (($Conf{'db_type'} =~ /^(Pg|mysql$)/)) { $limit_not_used = 1; } ## Additional DB fields my @additional_fields = split ',', $Conf{'db_additional_subscriber_fields'}; ## Members list synchronization if list has included data sources. if ($list->has_include_data_sources()) { if ($list->on_the_fly_sync_include('use_ttl'=>1)) { &report::notice_report_web('subscribers_updated',{},$param->{'action'}); }else { &report::reject_report_web('intern','sync_include_failed',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); } } ## Members list $count = -1; for (my $i = $list->get_first_user({'sortby' => $sortby, 'offset' => $offset, 'rows' => $size}); $i; $i = $list->get_next_user()) { ## some review pages may be empty while viewed by subscribers next if (($i->{'visibility'} eq 'conceal') and (! $param->{'is_priv'}) ); if ($limit_not_used) { $count++; next unless (($count >= $offset) && ($count <= $offset+$size)); } ## Add user &_prepare_subscriber($i, \@additional_fields, \%sources); push @{$param->{'members'}}, $i; } if ($param->{'page'} > 1) { $param->{'prev_page'} = $param->{'page'} - 1; } unless (($offset + $size) >= $param->{'total'}) { $param->{'next_page'} = $param->{'page'} + 1; } $param->{'size'} = $size; $param->{'sortby'} = $sortby; ###################### if($in{'exclude'} eq '1'){ $param->{'exclude_opt'} = 0; }else{ $param->{'exclude_opt'} = 1; } ####################### ## additional DB fields $param->{'additional_fields'} = $Conf{'db_additional_subscriber_fields'}; # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'review',$param->{'list'},$robot,'','done'); ## msg_topics if ($list->is_there_msg_topic()) { foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if (defined $top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } } return 1; } ## Show the table of exclude sub do_show_exclude { &wwslog('info', 'do_exclude()'); # Get the emails of the exclude about a list and the date of their insertion my $data_exclu = &List::get_exclusion($list->{'name'},$list->{'domain'}); my $excluded; my $key = 0; while (($data_exclu->{emails}->[$key]) && ($data_exclu->{date}->[$key])){ my $email = $data_exclu->{'emails'}->[$key]; my $date = gettext_strftime "%d %b %Y", localtime($data_exclu->{'date'}->[$key]); $excluded = {'email' => $email, 'since' => $date}; push @{$param->{'exclude_users'}}, $excluded; $key = $key + 1; } return 1; } ## Search in subscribers and in exclude sub do_search { &wwslog('info', 'do_search(%s)', $in{'filter'}); my %sources; ## Additional DB fields my @additional_fields = split ',', $Conf{'db_additional_subscriber_fields'}; ## Access control return undef unless (defined &check_authz('do_search', 'review')); ## Regexp $param->{'filter'} = $in{'filter'}; ## Decode regexp to Unicode for case-insensitive match. my $regexp = &tools::escape_regexp(Encode::decode_utf8($param->{'filter'} || '')); my $record = 0; ## Maximum size of selection my $max_select = 50; ## Members list for (my $i = $list->get_first_user({'sortby' => 'email'}); $i; $i = $list->get_next_user()) { ## Decode gecos to Unicode for case-insensitive match. my $gecos = Encode::decode_utf8($i->{'gecos'} || ''); ## Search filter next if ($i->{'email'} !~ /$regexp/i && $gecos !~ /$regexp/i); next if (($i->{'visibility'} eq 'conceal') and (! $param->{'is_owner'}) ); ## Add user &_prepare_subscriber($i, \@additional_fields, \%sources); $record++; push @{$param->{'members'}}, $i; } my $data_exclu = &List::get_exclusion($list->{'name'},$list->{'domain'}); my $key = 0; ## Exclude users are searched too while (($data_exclu->{emails}->[$key]) && ($data_exclu->{date}->[$key])){ my $email = $data_exclu->{'emails'}->[$key]; my $date = gettext_strftime "%d %b %Y", localtime($data_exclu->{'date'}->[$key]); $key = $key + 1; ## Search filter next if ($email !~ /$regexp/i); next if (!$param->{'is_owner'}); my $excluded = {'email' => $email, 'since' => $date}; push @{$param->{'exclude_users'}}, $excluded; $record++; } if ($record > $max_select) { undef $param->{'members'}; $param->{'too_many_select'} = 1; } $param->{'similar_subscribers'} = &List::get_ressembling_subscribers_no_object({'name'=>$list->{'name'},'domain'=>$robot,'email'=>$in{'filter'}}); foreach my $u (@{$param->{'similar_subscribers'} || []}) { $u->{'date'} = gettext_strftime "%d %b %Y", localtime($u->{'date'}); $u->{'update_date'} = gettext_strftime "%d %b %Y", localtime($u->{'update_date'}); } $param->{'similar_subscribers_occurrence'} =$#{$param->{'similar_subscribers'}}+1; ## for misspelling in 6.1.20 or earlier. $param->{'similar_subscribers_occurence'} = $param->{'similar_subscribers_occurrence'}; $param->{'occurrence'} = $record; return 1; } ## Access to user preferences sub do_pref { &wwslog('info', 'do_pref'); ## Find nearest expiration period my $selected = 0; foreach my $p (sort {$b <=> $a} keys %wwslib::cookie_period) { my $entry = {'value' => $p}; ## Set description from NLS $entry->{'desc'} = gettext($wwslib::cookie_period{$p}{'gettext_id'}); ## Choose nearest delay if ((! $selected) && $param->{'user'}{'cookie_delay'} >= $p) { $entry->{'selected'} = 'selected="selected"'; $selected = 1; } unshift @{$param->{'cookie_periods'}}, $entry; } $param->{'previous_list'} = $in{'previous_list'}; $param->{'previous_action'} = $in{'previous_action'}; return 1; } ## Set the initial password sub do_choosepasswd { &wwslog('info', 'do_choosepasswd'); if($session->{'auth'} eq 'ldap'){ &report::reject_report_web('auth','',{'login'=> $param->{'need_login'}},$param->{'action'}); &wwslog('notice', "do_choosepasswd : user not authorized\n"); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'authorization'}); } unless ($param->{'user'}{'email'}) { unless ($in{'email'} && $in{'passwd'}) { &report::reject_report_web('user','no_user',{},$param->{'action'}); &wwslog('info','do_pref: no user'); &web_db_log({'parameters' => $in{'email'}, 'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'no_user'}); $param->{'previous_action'} = 'choosepasswd'; return 'loginrequest'; } $in{'previous_action'} = 'choosepasswd'; return 'login'; } &web_db_log({'parameters' => "$in{'email'}", 'target_email' => $in{'email'} || $param->{'user'}{'email'}, 'status' => 'success', }); $param->{'init_passwd'} = 1 if ($param->{'user'}{'password'} =~ /^INIT/i); return 1; } #################################################### # do_set #################################################### # Changes subscription parameter (reception or visibility) # # IN : - # # OUT :'loginrequest'|'info' | undef sub do_set { &wwslog('info', 'do_set(%s, %s)', $in{'reception'}, $in{'visibility'}); my ($reception, $visibility) = ($in{'reception'}, $in{'visibility'}); my $email; my $xml_custom_attribute; if ($in{custom_attribute}){ return undef if ( &check_custom_attribute() != 1) ; my $xml = &List::createXMLCustomAttribute($in{custom_attribute}); $xml_custom_attribute = $xml ; } if ($in{'email'}) { unless ($param->{'is_owner'}) { &report::reject_report_web('auth','action_owner',{},$param->{'action'},$list); &wwslog('info','do_set: not owner'); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'error', 'error_type' => 'authorization'}); return undef; } $email = &tools::unescape_chars($in{'email'}); }else { unless ($param->{'user'}{'email'}) { &report::reject_report_web('user','no_user',{},$param->{'action'}); &wwslog('info','do_set: no user'); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'error', 'error_type' => 'no_user'}); return 'loginrequest'; } $email = $param->{'user'}{'email'}; } unless ($list->is_user($email)) { &report::reject_report_web('user','not_subscriber',{'list'=> $param->{'list'}},$param->{'action'},$list); &wwslog('info','do_set: %s not subscriber of list %s', $email, $param->{'list'}); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'error', 'error_type' => 'not_subscriber'}); return undef; } # Verify that the mode is allowed if (! $list->is_available_reception_mode($reception)) { &report::reject_report_web('user','not_available_reception_mode',{'recpetion_mode'=> $reception},$param->{'action'},$list); return undef; } $reception = '' if $reception eq 'mail'; $visibility = '' if $visibility eq 'noconceal'; my $update = {'reception' => $reception, 'visibility' => $visibility, 'update_date' => time}; ## Lower-case new email address $in{'new_email'} = lc( $in{'new_email'}); if ($in{'new_email'} && ($in{'email'} ne $in{'new_email'})) { unless ($in{'new_email'} && &tools::valid_email($in{'new_email'})) { &wwslog('notice', "do_set:incorrect email %s",$in{'new_email'}); &report::reject_report_web('user','incorrect_email',{'email' => $in{'new_email'}},$param->{'action'}); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'error', 'error_type' => 'incorrect_email'}); return undef; } ## Check if new email is already subscribed if ($list->is_user($in{'new_email'})) { &report::reject_report_web('user','already_subscriber', {'list' => $list->{'name'}},$param->{'action'},$list); &wwslog('info','do_set: %s already subscriber', $in{'new_email'}); &web_db_log({'parameters' => $in{'new_email'}, 'status' => 'error', 'error_type' => 'already subscriber'}); return undef; } ## Duplicate entry in user_table unless (&List::is_user_db($in{'new_email'})) { my $user_pref = &List::get_user_db($in{'email'}); $user_pref->{'email'} = $in{'new_email'}; &List::add_user_db($user_pref); } $update->{'email'} = $in{'new_email'}; } ## message topic subscription if ($list->is_there_msg_topic()) { my @user_topics; if ($in{'no_topic'}) { $update->{'topics'} = undef; } else { foreach my $msg_topic (@{$list->{'admin'}{'msg_topic'}}) { my $var_name = "topic_"."$msg_topic->{'name'}"; if ($in{"$var_name"}) { push @user_topics, $msg_topic->{'name'}; } } if ($in{"topic_other"}) { push @user_topics, 'other'; } $update->{'topics'} = join(',',@user_topics); } } if ($reception =~ /^(digest|digestplain|nomail|summary)$/i) { $update->{'topics'} = ''; } ## Get additional DB fields foreach my $v (keys %in) { if ($v =~ /^additional_field_(\w+)$/) { $update->{$1} = $in{$v}; } } if ($in{'gecos'}) { $update->{'gecos'} = $in{'gecos'}; }else{ $update->{'gecos'} = undef; } $update->{'custom_attribute'} = $xml_custom_attribute if $xml_custom_attribute; unless ( $list->update_user($email, $update) ) { &report::reject_report_web('intern','update_subscriber_db_failed',{'sub'=>$email},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info', 'do_set: set failed'); &web_db_log({'parameters' => "$email,$update", 'status' => 'error', 'error_type' => 'internal'}); return undef; } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'success', }); return $in{'previous_action'} || 'info'; } ## checks if each element of the custom attribute is conform to the list's ## definition sub check_custom_attribute { my @custom_attributes = @{$list->{'admin'}{'custom_attribute'}} ; my $isOK = 1 ; foreach my $ca (@custom_attributes){ my $value = $in{custom_attribute}{$ca->{id}}{value} ; if ($ca->{optional} eq 'required' && $value eq '') { &report::reject_report_web('user','missing_arg',{'argument' => "\"$ca->{name}\" is required"},$param->{'action'}); &wwslog('info','do_set: missing parameter'); &web_db_log({'parameters' => "$in{'reception'},$in{'visibility'}", 'status' => 'error', 'error_type' => 'missing_parameter'}); $isOK = undef; next ; } ## No further checking if attribute if empty next if ($value =~ /^$/); my @values = split(/,/ , $ca->{'enum_values'}) if (defined $ca->{'enum_values'}); ## Check that the parameter has the correct format unless (($ca->{'type'} eq 'enum' && grep(/^$value$/, @values)) || ($ca->{'type'} eq 'integer' && $value =~ /^\d+$/) || ($ca->{'type'} eq 'string' && $value =~ /^.+$/) || ($ca->{'type'} eq 'text' && $value =~ /^.+$/m) ) { &report::reject_report_web('user','syntax_errors',{'params' => $ca->{name}},$param->{'action'}); &wwslog('info','do_set: syntax error'); &web_db_log({'parameters' => $ca->{name}, 'status' => 'error', 'error_type' => 'missing_parameter'}); $isOK = undef; next ; } } return $isOK ; } ## Update of user preferences sub do_setpref { &wwslog('info', 'do_setpref'); my $changes = {}; foreach my $p ('gecos','lang','cookie_delay') { $changes->{$p} = $in{$p} if (defined($in{$p})); } ## Set session language and user language to new value $session->{'lang'} = $in{'lang'} ; $param->{'lang'} = $in{'lang'}; $param->{'lang_tag'} = Language::LanguageTag($param->{'lang'}); if (&List::is_user_db($param->{'user'}{'email'})) { unless (&List::update_user_db($param->{'user'}{'email'}, $changes)) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_pref: update failed'); &web_db_log({'parameters' => "$in{'gecos'},$in{'lang'},$in{'cookie_delay'}", 'status' => 'error', 'error_type' => 'internal'}); return undef; } }else { $changes->{'email'} = $param->{'user'}{'email'}; unless (&List::add_user_db($changes)) { &report::reject_report_web('intern','add_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_pref: add failed'); &web_db_log({'parameters' => "$in{'gecos'},$in{'lang'},$in{'cookie_delay'}", 'status' => 'error', 'error_type' => 'internal'}); return undef; } } foreach my $p ('gecos','lang','cookie_delay') { $param->{'user'}{$p} = $in{$p}; } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'parameters' => "$in{'gecos'},$in{'lang'},$in{'cookie_delay'}", 'status' => 'success', }); if ($in{'previous_action'}) { $in{'list'} = $in{'previous_list'}; return $in{'previous_action'}; }else { return 'pref'; } } ## Prendre en compte les défauts sub do_viewfile { &wwslog('info', 'do_viewfile'); unless (defined $wwslib::filenames{$in{'file'}}) { &report::reject_report_web('user','file_not_editable',{'file' => $in{'file'}},$param->{'action'}); &wwslog('info','do_viewfile: file %s not editable', $in{'file'}); return undef; } $param->{'file'} = $in{'file'}; $param->{'filepath'} = $list->{'dir'}.'/'.$in{'file'}; if ((-e $param->{'filepath'}) and (! -r $param->{'filepath'})) { &report::reject_report_web('intern','cannot_read',{'filepath' => $param->{'filepath'}},$param->{'action'},'','',$robot); &wwslog('info','do_viewfile: cannot read %s', $param->{'filepath'}); return undef; } return 1; } #################################################### # do_subscribe #################################################### # Subscribes a user to the list # # IN : - # # OUT :'subrequest'|'login'|'info'|$in{'previous_action'} # | undef #################################################### ## TOTO: accepter nouveaux users sub do_subscribe { &wwslog('info', 'do_subscribe(%s)', $in{'email'}); return undef if (purely_closed('subscribe')); if (defined $param->{'user'} && $param->{'user'}{'email'}) { my $xml_custom_attribute; if ($list->{'admin'}{'custom_attribute'} ) { ## This variable is set in the subrequest form ## If not set, it means that the user has not been prompted to provide custom_attributes unless ($in{'via_subrequest'}) { &wwslog('notice', 'Returning subrequest form'); return "subrequest"; } if (&check_custom_attribute() != 1) { &wwslog('notice', "Missing required custom attributes") ; return 'subrequest'; } my $xml = &List::createXMLCustomAttribute($in{custom_attribute}); $xml_custom_attribute = $xml ; } if ($param->{'is_subscriber'}) { &report::reject_report_web('user','already_subscriber', {'list' => $list->{'name'}},$param->{'action'},$list); &wwslog('info','do_subscribe: %s already subscriber', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'email'}, 'status' => 'error', 'error_type' => 'already_subscriber'}); return undef; } my $result = $list->check_list_authz('subscribe',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $sub_is; my $reason; if (ref($result) eq 'HASH') { $sub_is = $result->{'action'}; $reason = $result->{'reason'}; } if ($sub_is =~ /reject/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info', 'do_subscribe: subscribe closed'); &web_db_log({'parameters' => $in{'email'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } $param->{'may_subscribe'} = 1; if ($sub_is =~ /owner/) { unless ($list->send_notify_to_owner('subrequest',{'who' => $param->{'user'}{'email'}, 'keyauth' => $list->compute_auth($param->{'user'}{'email'}, 'add'), 'replyto' => &Conf::get_robot_conf($robot, 'sympa'), 'custom_attribute' => $in{custom_attribute}, 'gecos' => $param->{'user'}{'gecos'}, 'ip'=>$ip})) { &wwslog('notice',"Unable to send notify 'subrequest' to $list->{'name'} listowner"); } $list->store_subscription_request($param->{'user'}{'email'}, "", $xml_custom_attribute); &report::notice_report_web('sent_to_owner',{},$param->{'action'}); &wwslog('info', 'do_subscribe: subscribe sent to owners'); return 'info'; }elsif ($sub_is =~ /do_it/) { if ($param->{'is_subscriber'}) { unless ($list->update_user($param->{'user'}{'email'}, {'subscribed' => 1, 'update_date' => time})) { &report::reject_report_web('intern','update_subscriber_db_failed',{'sub'=>$param->{'user'}{'email'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info', 'do_subscribe: update failed'); &web_db_log({'parameters' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } }else { my $defaults = $list->get_default_user_options(); my $u; %{$u} = %{$defaults}; $u->{'email'} = $param->{'user'}{'email'}; $u->{'gecos'} = $param->{'user'}{'gecos'} || $in{'gecos'}; $u->{'date'} = $u->{'update_date'} = time; $u->{'password'} = $param->{'user'}{'password'}; if (my $reason = &tools::password_validation($u->{'password'})) { &report::reject_report_web('user','passwd_validation',{'reason' => $reason},$param->{'action'}); &wwslog('info','do_setpasswd: password validation'); &web_db_log({ 'status' => 'error', 'error_type' => 'bad_parameter' }); return undef; } $u->{'custom_attribute'} = $xml_custom_attribute if (defined $xml_custom_attribute); $u->{'lang'} = $param->{'user'}{'lang'} || $param->{'lang'}; unless ($list->add_user($u)) { &report::reject_report_web('intern','add_subscriber_db_failed',{'sub'=>$param->{'user'}{'email'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info', 'do_subscribe: subscribe failed'); &web_db_log({'parameters' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } unless ($sub_is =~ /quiet/i ) { unless ($list->send_file('welcome', $param->{'user'}{'email'}, $robot,{})) { &wwslog('notice',"Unable to send template 'welcome' to $param->{'user'}{'email'}"); } } if ($sub_is =~ /notify/) { unless ($list->send_notify_to_owner('notice',{'who' => $param->{'user'}{'email'}, 'gecos' => $param->{'user'}{'gecos'}, 'command' => 'subscribe'})) { &wwslog('notice','Unable to send notify "notice" to listmaster'); } } } }else{ # user is not autenticated if ($in{'email'} && $in{'passwd'}) { $in{'previous_action'} = 'subscribe'; $in{'previous_list'} = $param->{'list'}; return 'login'; }else{ return 'subrequest'; } } ## perform which to update your_subscriptions cookie ; @{$param->{'get_which'}} = &List::get_which($param->{'user'}{'email'},$robot,'member') ; &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'parameters' => $in{'email'},'status' => 'success'}); if ($in{'previous_action'}) { return $in{'previous_action'}; } # return 'suboptions'; return 'info'; } #################################################### # do_multiple_subscribe #################################################### # Subscribes a user to each lists # # IN : lists a array of lists # # OUT :'subrequest'|'login'|'info'|$in{'previous_action'} # | undef #################################################### sub do_multiple_subscribe { &wwslog('info', 'do_multiple_subscribe(%s)', $in{'email'}); ## Not authenticated unless (defined $param->{'user'} && $param->{'user'}{'email'}) { ## no email unless ($in{'email'}) { return 'lists'; } } my @lists = split /\0/, $in{'lists'}; my $total; my %results ; foreach my $requested_list (@lists) { my $param->{'list'} = new List ($requested_list, $robot); $results{'requested_list'} = &do_subscribe(); } } ## Subscription request (user not authenticated) sub do_suboptions { &wwslog('info', 'do_suboptions()'); unless($param->{'is_subscriber'} ) { &report::reject_report_web('user','not_subscriber',{'list'=> $list->{'name'}},$param->{'action'},$list); &wwslog('info','do_suboptions: %s not subscribed to %s',$param->{'user'}{'email'}, $param->{'list'} ); return undef; } my ($s, $m); unless($s = $list->get_subscriber($param->{'user'}{'email'})) { &report::reject_report_web('intern','subscriber_not_found',{'email' => $param->{'user'}{'email'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info', 'do_sub_options: subscriber %s not found', $param->{'user'}{'email'}); return undef; } $s->{'reception'} ||= 'mail'; $s->{'visibility'} ||= 'noconceal'; $s->{'date'} = gettext_strftime "%d %b %Y", localtime($s->{'date'}); $s->{'update_date'} = gettext_strftime "%d %b %Y", localtime($s->{'update_date'}); foreach $m ($list->available_reception_mode) { $param->{'reception'}{$m}{'description'} = $list->get_option_title($m, 'reception'); if ($s->{'reception'} eq $m) { $param->{'reception'}{$m}{'selected'} = ' selected'; if ($m =~ /^(mail|notice|not_me|txt|html|urlize)$/i) { $param->{'possible_topic'} = 1; } }else { $param->{'reception'}{$m}{'selected'} = ''; } } foreach $m (qw(conceal noconceal)) { $param->{'visibility'}{$m}{'description'} = $list->get_option_title($m, 'visibility'); if ($s->{'visibility'} eq $m) { $param->{'visibility'}{$m}{'selected'} = ' selected'; }else { $param->{'visibility'}{$m}{'selected'} = ''; } } $param->{'subscriber'} = $s; #msg_topic $param->{'sub_user_topic'} = 0; foreach my $user_topic (split (/,/,$s->{'topics'})) { $param->{'topic_checked'}{$user_topic} = 1; $param->{'sub_user_topic'}++; } if ($list->is_there_msg_topic()) { foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if (defined $top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } } return 1; } ## Subscription request (user not authenticated) sub do_subrequest { &wwslog('info', 'do_subrequest(%s,%s)', $in{'email'},$in{'custom_attribute'}); return undef if (purely_closed('subscribe')); if (defined $in{'custom_attribute'}) { $param->{'custom_attribute'} = $in{'custom_attribute'}; } ## Auth ? if ($param->{'user'}{'email'}) { ## Subscriber ? if ($param->{'is_subscriber'}) { &report::reject_report_web('user','already_subscriber', {'list' => $list->{'name'}},$param->{'action'},$list); &wwslog('info','%s already subscriber', $param->{'user'}{'email'}); &web_db_log({'status' => 'error', 'error_type' => 'already_subscriber'}); return undef; } $param->{'status'} = 'auth'; }else { ## Provided email parameter ? unless ($in{'email'}) { $param->{'status'} = 'notauth_noemail'; return 1; } ## valid email address? unless (&tools::valid_email($in{'email'})) { &report::reject_report_web('user','incorrect_email',{'email' => $in{'email'}},$param->{'action'},$list); &wwslog('info','do_subrequest: incorrect email %s', $in{'email'}); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'incorrect_email'}); $param->{'status'} = 'notauth_noemail'; return 1; } ## Subscriber ? if ($list->is_user($in{'email'})) { $param->{'status'} = 'notauth_subscriber'; return 1; } my $user; $user = &List::get_user_db($in{'email'}) if &List::is_user_db($in{'email'}); ## Need to send a password by email $param->{'one_time_ticket'} = &Auth::create_one_time_ticket($in{'email'},$robot,'subscribe/'.$list->{'name'},$ip); $param->{'login_error'}='ticket_sent'; $param->{'request_from_host'} = $ip; unless ($param->{'newuser'} = &List::get_user_db($in{'email'})) { $param->{'newuser'} = { 'email' => &tools::clean_email($in{'email'}) }; $param->{'newuser'}{'escaped_email'} = &tools::escape_chars($param->{'newuser'}{'email'}); } unless (&List::send_global_file('sendpasswd', $in{'email'}, $robot, $param)) { &wwslog('notice',"Unable to send template 'sendpasswd' to $in{'email'}"); $param->{'login_error'}='unable_to_send_ticket'; } # &do_requestpasswd(); $param->{'status'} = 'notauth_passwordsent'; return 1; } return 1; } sub do_auto_signoff { &wwslog('info', 'do_auto_signoff'); ## If the URL isn't valid, then go to home page. No need to guide the user: this function is supposed to be used by clicking on autocreated URL only. return &Conf::get_robot_conf($robot, 'default_home') unless $in{'email'}; ## If unsubscribe is forbidden, reject the request. Other my $result = $list->check_list_authz('unsubscribe',$param->{'auth_method'}, {'sender' => $in{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $sig_is; my $reason; if (ref($result) eq 'HASH') { $sig_is = $result->{'action'}; $reason = $result->{'reason'}; } if ($sig_is =~ /reject/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info', 'do_signoff: %s may not signoff from %s' , $in{'email'}, $param->{'list'}); &web_db_log({'status' => 'error', 'error_type' => 'authorization'}); return undef; } ## Send the confirmation email to the user. if ($list->is_user($in{'email'})) { my $ticket = &Auth::create_one_time_ticket($in{'email'},$robot,'signoff/'.$list->{'name'},$ip); my $tt2_param = { 'list' => $list, 'type' => 'ticket_to_signoff', 'one_time_ticket' => $ticket, 'email' => $in{'email'}, 'context' => 'auto_signoff', 'ip' => $ip, }; unless (&List::send_global_file('user_notification', $in{'email'}, $robot, $tt2_param)) { &do_log('notice',"Unable to send template 'user_notification' to $in{'email'}"); return undef; } }else{ return &Conf::get_robot_conf($robot, 'default_home'); } $param->{'signing_off_email'} = $in {'email'}; ## If OK, return the page displaying the informations to the user. return 1; } sub do_family_signoff_request { &wwslog('info', 'do_family_signoff_request'); ## If the URL isn't valid, then go to home page. No need to guide the user: this function is supposed to be used by clicking on autocreated URL only. return &Conf::get_robot_conf($robot, 'default_home') unless $in{'email'}; my $ticket = &Auth::create_one_time_ticket($in{'email'},$robot,'family_signoff/'.$in{'family'}.'/'.$in{'email'},$ip); my $tt2_param = { 'family' => $in{'family'}, 'type' => 'ticket_to_family_signoff', 'one_time_ticket' => $ticket, 'email' => $in{'email'}, 'context' => 'family_signoff', 'ip' => $ip, }; unless (&List::send_global_file('user_notification', $in{'email'}, $robot, $tt2_param)) { &do_log('notice',"Unable to send template 'user_notification' to $in{'email'}"); return undef; } $param->{'signing_off_email'} = $in {'email'}; $param->{'family'} = $in {'family'}; ## If OK, return the page displaying the informations to the user. return 1; } sub do_family_signoff { &wwslog('info', 'do_family_signoff'); $param->{'signing_off_email'} = $in {'email'}; $param->{'family'} = $in {'family'}; unless ($in{'email'} eq $session->{'email'}) { &report::reject_report_web('user','cannot_do_signoff'); &wwslog('err','do_signoff: user %s tried to unsubscribe address %s from family %s',$session->{'email'}, $in{'email'},$in {'family'}); return undef; } unless (List::insert_delete_exclusion($in{'email'},&SympaSession::get_random(),$robot,'insert',$in{'family'})) { &report::reject_report_web('user','cannot_do_signoff'); &wwslog('err','do_signoff: Unsubscription of address %s from family %s failed.',$in{'email'},$in {'family'}); return undef; } return 1; } #################################################### # do_signoff #################################################### # Unsubcribes a user from a list # # IN : - # # OUT : 'sigrequest' | 'login' | 'info' # #################################################### ## Unsubscribe from list sub do_signoff { &wwslog('info', 'do_signoff'); return undef if (purely_closed('unsubscribe')); my $authenticated_email_address = $param->{'user'}{'email'}; unless ($authenticated_email_address) { unless ($in{'email'}) { return 'sigrequest'; } if ($in{'fingerprint'}) { unless(&tools::get_fingerprint($in{'email'}, $in{'fingerprint'})){ &report::reject_report_web('user','cannot_do_signoff'); &wwslog('err','do_signoff: failed to unsubscribe user %s', $in{'email'}); return undef; } ## We don't set $param->{'user'}{'email'} because we don't want the user to be authenticated ## to prevent the cookie from being set $authenticated_email_address = $in{'email'}; }else { ## Perform login first if ($in{'passwd'}) { $in{'previous_action'} = 'signoff'; $in{'previous_list'} = $param->{'list'}; return 'login'; } if ( &List::is_user_db($in{'email'}) ) { &report::reject_report_web('user','no_user',{},$param->{'action'}); &wwslog('info','do_signoff: need auth for user %s', $in{'email'}); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'authentication'}); return undef; } ## No passwd wwslib::init_passwd($robot, $in{'email'}, {'lang' => $param->{'lang'}}); $param->{'user'}{'email'} = $in{'email'}; $authenticated_email_address = $in{'email'}; } } my %result = &unsubscribe($authenticated_email_address, $list); if ($result{'success'} == 1) { &report::notice_report_web($result{'details'},{},$param->{'action'}); $param->{'is_subscriber'} = 0; $param->{'may_signoff'} = 0; }else{ &report::reject_report_web($result{'category_error'},$result{'reason_error'},{%{$result{'reason_error'}},'list'=>$list->{'name'}},$param->{'action'},$list); } return &Conf::get_robot_conf($robot, 'default_home'); } ## Unsubscribe current user from a list. sub unsubscribe { my $authenticated_email_address = shift; my $list = shift; my %report = ('success',1,'details',''); unless ($list->is_user($authenticated_email_address)) { &wwslog('info','do_signoff: %s not subscribed to %s',$authenticated_email_address, $param->{'list'} ); &web_db_log({'status' => 'error', 'error_type' => 'not_subscriber'}); $report{'success'} = 0; $report{'category_error'} = 'user'; $report{'reason_error'} = 'not_subscribed'; $report{'details_error'} = {}; return %report; } my $result = $list->check_list_authz('unsubscribe',$param->{'auth_method'}, {'sender' => $authenticated_email_address, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $sig_is; my $reason; if (ref($result) eq 'HASH') { $sig_is = $result->{'action'}; $reason = $result->{'reason'}; } $param->{'may_signoff'} = 1 if ($sig_is =~ /do_it|owner/); if ($sig_is =~ /reject/) { &wwslog('info', 'do_signoff: %s may not signoff from %s' , $authenticated_email_address, $param->{'list'}); &web_db_log({'status' => 'error', 'error_type' => 'authorization'}); $report{'success'} = 0; $report{'category_error'} = 'auth'; $report{'reason_error'} = $reason; $report{'details_error'} = {}; return %report; }elsif ($sig_is =~ /owner/) { unless ($list->send_notify_to_owner('sigrequest',{'who' => $authenticated_email_address, 'keyauth' => $list->compute_auth($authenticated_email_address, 'del')})) { &wwslog('notice',"Unable to send notify 'sigrequest' to $list->{'name'} list owner"); } &wwslog('info', 'do_signoff: signoff sent to owner'); $report{'success'} = 1; $report{'details'} = 'sent_to_owner'; return %report; }else { unless ($list->delete_user('users' => [$authenticated_email_address], 'exclude' =>' 1')) { &wwslog('info', 'do_signoff: signoff failed'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); $report{'success'} = 0; $report{'category_error'} = 'intern'; $report{'reason_error'} = 'delete_subscriber_db_failed'; $report{'details_error'} = {'sub'=>$authenticated_email_address}; return %report; } if ($sig_is =~ /notify/) { unless ($list->send_notify_to_owner('notice',{'who' => $authenticated_email_address, 'gecos' => '', 'command' => 'signoff'})) { &wwslog('notice',"Unable to send notify 'notice' to $list->{'name'} list owner"); } } ## perform which to update your_subscribtions cookie ; @{$param->{'get_which'}} = &List::get_which($authenticated_email_address,$robot,'member') ; unless ($list->send_file('bye', $authenticated_email_address, $robot, {})) { &wwslog('notice',"Unable to send template 'bye' to $authenticated_email_address"); } } &web_db_log({'status' => 'success'}); $report{'success'} = 1; $report{'details'} = 'performed'; return %report; } ## Unsubscription request (user not authenticated) sub do_sigrequest { &wwslog('info', 'do_sigrequest(%s)', $in{'email'}); return undef if (purely_closed('unsubscribe')); ## If user is authenticated then redirect him to the signoff action but ## get a confirmation (via the sigrequest web page) first if ($param->{'user'}{'email'}) { return 1; } ## Not auth & no email => return the sigrequest web form to get the user email unless ($in{'email'}) { return 1; } ## valid email address? unless (&tools::valid_email($in{'email'})) { &report::reject_report_web('user','incorrect_email',{'email' => $in{'email'}},$param->{'action'},$list); &wwslog('info','do_sigrequest: incorrect email %s', $in{'email'}); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'incorrect_email'}); return 1; } if ($list->is_user($in{'email'})) { my $ticket = &Auth::create_one_time_ticket($in{'email'},$robot,'signoff/'.$list->{'name'},$ip); my $tt2_param = {'type' => 'ticket_to_signoff', 'list' => $list, 'one_time_ticket' => $ticket, 'email' => $in{'email'}}; unless (&List::send_global_file('user_notification', $in{'email'}, $robot, $tt2_param)) { &do_log('notice',"Unable to send template 'user_notification' to $in{'email'}"); return undef; } }else { $param->{'not_subscriber'} = 1; } $param->{'email'} = $in{'email'}; return 1; } ## Update of password sub do_setpasswd { &wwslog('info', 'do_setpasswd'); my $user; if ($in{'newpasswd1'} =~ /^\s+$/ ) { &report::reject_report_web('user','no_passwd',{},$param->{'action'}); &wwslog('info','do_setpasswd: no newpasswd1'); &web_db_log({'status' => 'error', 'error_type' => 'missing_parameter'}); return undef; } unless ($in{'newpasswd1'} eq $in{'newpasswd2'}) { &report::reject_report_web('user','diff_passwd',{},$param->{'action'}); &wwslog('info','do_setpasswd: different newpasswds'); &web_db_log({'status' => 'error', 'error_type' => 'bad_parameter'}); return undef; } if (my $reason = &tools::password_validation($in{'newpasswd1'})) { &report::reject_report_web('user','passwd_validation',{'reason' => $reason},$param->{'action'}); &wwslog('info','do_setpasswd: password validation'); &web_db_log({'status' => 'error', 'error_type' => 'bad_parameter'}); return undef; } if (&List::is_user_db($param->{'user'}{'email'})) { unless ( &List::update_user_db($param->{'user'}{'email'}, {'password' => $in{'newpasswd1'},'wrong_login_count' => 0} )) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_setpasswd: update failed'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } }else { unless ( &List::add_user_db({'email' => $param->{'user'}{'email'}, 'password' => $in{'newpasswd1'}, 'wrong_login_count' => 0} )) { &report::reject_report_web('intern','add_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_setpasswd: update failed'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } } $param->{'user'}{'password'} = $in{'newpasswd1'}; &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'status' => 'success'}); if ($in{'previous_action'}) { $in{'list'} = $in{'previous_list'}; return $in{'previous_action'}; }else { return 'pref'; } } ## List admin page sub do_admin { &wwslog('info', 'do_admin'); ## Messages edition foreach my $f ('info','homepage','welcome.tt2','bye.tt2','removed.tt2','message.footer','message.header','remind.tt2','invite.tt2','reject.tt2') { next unless ($list->may_edit($f, $param->{'user'}{'email'}) eq 'write'); if ($wwslib::filenames{$f}{'gettext_id'}) { $param->{'files'}{$f}{'complete'} = gettext($wwslib::filenames{$f}{'gettext_id'}); }else { $param->{'files'}{$f}{'complete'} = $f; } $param->{'files'}{$f}{'selected'} = ''; } $param->{'files'}{'info'}{'selected'} = 'selected="selected"'; # my %mode; # $mode{'edit'} = 1; # my %access = &d_access_control(\%mode,$path); return 1; } ## Server admin page sub do_serveradmin { &wwslog('info', 'do_serveradmin'); my $f; ## Lists Default files foreach my $f ('welcome.tt2','bye.tt2','removed.tt2','message.footer','message.header','remind.tt2','invite.tt2','reject.tt2','your_infected_msg.tt2') { if ($wwslib::filenames{$f}{'gettext_id'}){ $param->{'lists_default_files'}{$f}{'complete'} = gettext($wwslib::filenames{$f}{'gettext_id'}); }else { $param->{'lists_default_files'}{$f}{'complete'} = $f; } $param->{'lists_default_files'}{$f}{'selected'} = ''; } ## Checking families and other virtual hosts. &get_server_details(); ## Server files foreach my $f ('helpfile.tt2','lists.tt2','global_remind.tt2','summary.tt2','create_list_request.tt2','list_created.tt2','list_aliases.tt2') { $param->{'server_files'}{$f}{'complete'} = gettext($wwslib::filenames{$f}{'gettext_id'}); $param->{'server_files'}{$f}{'selected'} = ''; } $param->{'server_files'}{'helpfile.tt2'}{'selected'} = 'selected="selected"'; $param->{'log_level'} = $session->{'log_level'} ; $param->{'subaction'} = $in{'subaction'} ; return 1; } sub do_edit_config { my @editable_params = @confdef::params ; &get_server_details; unless ($param->{'main_robot'}) { &report::reject_report_web('auth','super lismaster feature only','{}',$param->{'action'}); &wwslog('info','check_authz: access denied in edit_config for %s because not super listmaster', $param->{'user'}{'email'}); } for my $i ( 0 .. $#editable_params ) { if ($editable_params[$i]->{'name'}) { $editable_params[$i]->{'current_value'} = &Conf::get_robot_conf($robot, $editable_params[$i]->{'name'}); $editable_params[$i]->{'current_value'} = join( ",", @{$editable_params[$i]->{'current_value'}}) if (ref($editable_params[$i]->{'current_value'}) eq 'ARRAY'); } } if ($in{'conf_new_value'}) { my $editable; my $i; for $i ( 0 .. $#editable_params ) { # if the parameter is editable and if the is a change next unless ($editable_params[$i]->{'name'} eq $in{'conf_parameter_name'}); if ($editable_params[$i]->{'edit'} ne '1'){ do_log ('err','Ignoring change of parameter %s (value %s) because not editable', $in{'conf_parameter_name'}, $in{'conf_new_value'}); last; } if ($in{'conf_new_value'} eq $editable_params[$i]->{'current_value'} ){ do_log ('notice','Ignoring change of parameter %s (value %s) because inchanged', $in{'conf_parameter_name'}, $in{'conf_new_value'}); last; }else{ $editable_params[$i]->{'current_value'} = $in{'conf_new_value'}; &Conf::set_robot_conf($robot, $in{'conf_parameter_name'}, $in{'conf_new_value'}); do_log ('notice','setting parameter %s to value %s', $in{'conf_parameter_name'}, $in{'conf_new_value'}); last; } } } $param->{'editable_params'} = \@editable_params ; return 1; } ## Change log_level for the current session sub do_set_loglevel { &wwslog('info', 'do_set_loglevel'); $session->{'log_level'} = $in{'log_level'}; return 'serveradmin'; } ## activate dump var feature sub do_set_dumpvars { &wwslog('info', 'do_set_dumpvars'); $session->{'dumpvars'} = 'true' ; $param->{'dumpavars'} = $session->{'dumpvars'} ; $param->{'redirect_to'} = $param->{'base_url'}.$param->{'path_cgi'}.'/serveradmin'; return '1'; } ## un-activate dump var feature sub do_unset_dumpvars { &wwslog('info', 'do_unset_dumpvars'); $session->{'dumpvars'} = '' ; $param->{'dumpavars'} = ''; $param->{'redirect_to'} = $param->{'base_url'}.$param->{'path_cgi'}.'/serveradmin'; return '1'; } ## un-activate dump var feature sub do_show_sessions { &wwslog('info', 'do_show_sessions'); $in{'session_delay'} = 10 unless ($in{'session_delay'}); my $delay = 60 * $in{'session_delay'}; $param->{'sessions'} = &SympaSession::list_sessions($delay,$robot,$in{'connected_only'}); return '1'; } ## Change user email sub do_set_session_email { &wwslog('info', 'do_set_session_email'); my $email_regexp = &tools::get_regexp('email'); unless ($in{'email'} =~ /^\s*$email_regexp\s*$/){ &report::reject_report_web('user','Invalid email provided.',{},$param->{'action'},$list); return 'serveradmin'; }; if ($session){ $session->{'restore_email'} = $param->{'user'}{'email'}; $session->{'email'} = $in{'email'}; $param->{'redirect_to'} = $param->{'base_url'}.$param->{'path_cgi'}; return '1'; }else{ &report::reject_report_web('user','No active session',{},$param->{'action'},$list); return 'serveradmin'; } } ## Change user email sub do_restore_email { &wwslog('info', 'do_restore_email'); &wwslog('debug2', 'do_restore_email from %s to %s',$session->{'email'},$session->{'restore_email'} ); if ($param->{'restore_email'}){ $session->{'email'} = $session->{'restore_email'} ; $param->{'restore_email'}= $session->{'restore_email'} = '' ; $param->{'redirect_to'} = $param->{'base_url'}.$param->{'path_cgi'}; }else{ &wwslog('info','do_restore_email from %s no restore_email attached to current session', $param->{'user'}{'email'}); &report::reject_report_web('user','wrong_param',{},$param->{'action'},$list); } return 'home'; } ## list available templates sub do_ls_templates { &wwslog('info', 'do_ls_templates'); $in{'webormail'} ||= 'web'; if (defined $list) { $param->{'templates'} = &tools::get_templates_list($in{'webormail'},$robot,$list); }else{ $param->{'templates'} = &tools::get_templates_list($in{'webormail'},$robot, undef); } ## List of lang per type foreach my $level ('site','robot','list') { $param->{'lang_per_level'}{$level}{'default'} = 1; } foreach my $file (keys %{$param->{'templates'}}) { foreach my $level (keys %{$param->{'templates'}{$file}}) { foreach my $lang (keys %{$param->{'templates'}{$file}{$level}}) { $param->{'lang_per_level'}{$level}{$lang} = { 'title' => (Language::GetLangName($lang) || $lang), 'lang_tag' => Language::LanguageTag($lang), }; } } } ## Colspan per level foreach my $level (keys %{$param->{'lang_per_level'}}) { foreach my $lang (keys %{$param->{'lang_per_level'}{$level}}) { $param->{'colspan_per_level'}{$level}++; foreach my $file (keys %{$param->{'templates'}}) { $param->{'templates'}{$file}{$level}{$lang} ||= ''; } } } $param->{'webormail'} = $in{'webormail'}; return 1; } # show a template, used by copy_template and edit_emplate sub do_remove_template { &wwslog('info', 'do_remove_template'); my $template_path; if ($in{'scope'} eq 'list' and ref $list ne 'List') { report::reject_report_web('user', 'missing_arg', {'argument' => 'list'}, $param->{'action'}); wwslog('err', 'do_remove_template: missing parameter list'); web_db_log({'parameters' => $in{'webormail'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return 1; } $template_path = tools::get_template_path($in{'webormail'}, $robot, $in{'scope'}, $in{'template_name'}, $in{'tpl_lang'}, $list); my $template_old_path = &tools::shift_file($template_path,10); unless ($template_old_path) { &report::reject_report_web('intern','remove_failed',{'path'=>$template_path},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"remove_template: could not remove $template_path"); &web_db_log({'parameters' => $in{'webormail'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &report::notice_report_web('file_renamed',{'orig_file'=>$template_path,'new_file'=>$template_old_path}, $param->{'action'}); &web_db_log({'parameters' => $in{'webormail'}, 'status' => 'status'}); $param->{'webormail'} = $in{'webormail'}; $param->{'scope'} = $in{'scope'}; $param->{'template_name'} = $in{'template_name'}; $param->{'tpl_lang'} = $in{'tpl_lang'}; return 'ls_templates'; } # show a template, used by copy_template and edit_emplate sub do_view_template { &wwslog('info', "do_view_template(type=$in{'webormail'},template-name=$in{'template_name'},listname=$in{'list'},path=$in{'template_path'},scope=$in{'scope'},lang=$in{'tpl_lang'})"); my $template_path; if ($in{'scope'} eq 'list' and ref $list ne 'List') { report::reject_report_web('user', 'missing_arg', {'argument' => 'list'}, $param->{'action'}); wwslog('err', 'do_view_template: missing parameter webormail'); web_db_log({'parameters' => $in{'webormail'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return 1; } $template_path = tools::get_template_path($in{'webormail'}, $robot, $in{'scope'}, $in{'template_name'}, $in{'tpl_lang'}, $list); unless (open (TPL,$template_path)) { &report::reject_report_web('intern','cannot_open_file',{'path' => $in{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"view_template: can't open file %s",$template_path); return undef; } $param->{'rows'} = 5; # minimum size of 5 rows; $param->{'template_content'} = ''; # init content while() {$param->{'template_content'}.= $_; $param->{'rows'}++;} $param->{'template_content'} = &tools::escape_html($param->{'template_content'}); close TPL; $param->{'webormail'} = $in{'webormail'}; $param->{'template_name'} = $in{'template_name'}; $param->{'template_path'} = $template_path; $param->{'scope'} = $in{'scope'}; my $tpl_lang = $in{'tpl_lang'} || 'default'; $param->{'tpl_lang'} = $tpl_lang; unless ($tpl_lang eq 'default') { $param->{'tpl_lang_title'} = Language::GetLangName($tpl_lang) || $tpl_lang; $param->{'tpl_lang_tag'} = Language::LanguageTag($tpl_lang); } return 1; } ## template copy sub do_copy_template { &wwslog('info', 'do_copy_template'); ## Load original template &do_view_template(); ## Return form unless ($in{'scope_out'}) { return 1; } # one of theses parameters is commint from the form submission if ($in{'scope_out'} eq 'list') { if ($in{'list_out'}) { my $list_out; unless ($list_out = new List $in{'list_out'}, $robot) { &report::reject_report_web('user','unknown_list',{'list' => $in{'list_out'}},$param->{'action'},''); &wwslog('info','do_copy_template: unknown list %s', $in{'list_out'}); &web_db_log({'parameters' => $in{'list_out'}, 'status' => 'error', 'error_type' => 'unknown_list'}); return undef; } $param->{'template_path_out'} = tools::get_template_path($in{'webormail'}, $robot, 'list', $in{'template_name_out'}, $in{'tpl_lang_out'}, $list_out); }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('err','do_copy_template: missing parameter webormail'); &web_db_log({'parameters' => $in{'webormail'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return 1; } }else{ $param->{'template_path_out'} = tools::get_template_path($in{'webormail'}, $robot, $in{'scope_out'}, $in{'template_name_out'}, $in{'tpl_lang_out'}); } unless (&tools::mk_parent_dir($param->{'template_path_out'})) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path_out'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't create parent directory for %s : %s", $param->{'template_path_out'}, $!); &web_db_log({'parameters' => $param->{'template_name_out'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (open (TPLOUT,'>'.$param->{'template_path_out'})) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path_out'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't open file %s : %s", $param->{'template_path_out'}, $!); &web_db_log({'parameters' => $param->{'template_name_out'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } print TPLOUT &tools::unescape_html($param->{'template_content'}); close TPLOUT; if ($in{'list_out'}) {$param->{'list'} = $in{'list'} = $in{'list_out'} ;} $param->{'webormail'} = $in{'webormail'}; my $tpl_lang = $in{'tpl_lang_out'} || 'default'; $param->{'tpl_lang'} = $in{'tpl_lang'} = $tpl_lang; unless ($tpl_lang eq 'default') { $param->{'tpl_lang_title'} = Language::GetLangName($tpl_lang) || $tpl_lang; $param->{'tpl_lang_tag'} = Language::LanguageTag($tpl_lang); } $param->{'scope'} = $in{'scope'} = $in{'scope_out'} ; $param->{'template_path'} = $in{'template_path'} = $param->{'template_path_out'}; $param->{'template_name'} = $in{'template_name'} = $in{'template_name_out'}; &web_db_log({'parameters' => $param->{'template_name_out'}, 'status' => 'success'}); return ('edit_template'); } ## manage the rejection templates sub do_manage_template { &wwslog('info', '(%s,%s)', $in{'subaction'}, $in{'message_template'}); my $file; $in{'message_template'} =~ s/^reject_//; if ($in{'message_template'}) { my $escaped_template_path = $in{'message_template'}; $escaped_template_path =~ s/\s/_/g ; $param->{'template_path'} = tools::get_template_path('mail', $robot, 'list', 'reject_'.$escaped_template_path.'.tt2', '', $list); } my $default_file = tools::get_filename('etc', {}, 'mail_tt2/reject.tt2', $robot, $list); if ($in{'subaction'} eq 'save') { ## create the parent directory if it doesn't already exist unless (&tools::mk_parent_dir($param->{'template_path'})) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't create parent directory for %s : %s", $param->{'template_path'}, $!); &web_db_log({'parameters' => $param->{'template_name'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## open the template unless (open (TPLOUT ,'>' ,$param->{'template_path'})) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't open file %s : %s", $param->{'template_path'}, $!); &web_db_log({'parameters' => $in{'template_name'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## save template contents print TPLOUT $in{'template_content'}; close TPLOUT; &report::notice_report_web('performed',{},$in{'subaction'}); }elsif($in{'subaction'} eq 'create_new') { $in{'template_new'} = $in{'new_template_name'} ; unless ($in{'new_template_name'}) { &report::reject_report_web('user','missing template name',{'path' => ''},$param->{'action'},'',$param->{'user'}{'email'},$robot); return undef; } my $escaped_template_path = $in{'new_template_name'};$escaped_template_path =~ s/\s/_/g ; my $new_template_path = tools::get_template_path('mail', $robot, 'list', 'reject_'.$escaped_template_path.'.tt2', '', $list); if (-f $new_template_path) { &report::reject_report_web('intern','template already exist',{'path' => $new_template_path},$param->{'action'},'',$param->{'user'}{'email'},$robot); return undef; } ## create the parent directory if it doesn't already exist unless (&tools::mk_parent_dir($new_template_path)) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't create parent directory for %s : %s", $param->{'template_path'}, $!); &web_db_log({'parameters' => $param->{'template_name'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } my $default_file = tools::get_filename('etc', {}, 'mail_tt2/reject.tt2', $robot, $list); unless(open (DEFAULT, $default_file)){ &report::reject_report_web('intern','cannot_open_file',{'path' => $default_file},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't open file %s : %s", $default_file, $!); return undef; } unless(open (TPL, '> '.$new_template_path)){ &report::reject_report_web('intern','cannot_open_file',{'path' => $new_template_path},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't open file %s : %s", $new_template_path, $!); return undef; } while (){ print TPL $_; } close DEFAULT; close TPL; $in{'subaction'}='modify'; $in{'message_template'} = $in{'new_template_name'}; return 'manage_template'; }elsif ($in{'subaction'} eq 'modify') { unless(open (FILE, $param->{'template_path'})){ &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"can't open file MODIFY %s : %s", $param->{'template_path'}, $!); &web_db_log({'parameters' => $param->{'template_path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } while (){ $param->{'content'} .= $_; } $param->{'content'} = &tools::escape_html($param->{'content'}); close FILE; $param->{'message_template'} = $in{'message_template'}; }elsif($in{'subaction'} eq 'setdefault') { # replace existing reject.tt2 file by a symlink to reject_default.tt2 for compatibility with version older than 6.0 my $base = $list->{'dir'}.'/mail_tt2/'; $in{'new_default'} =~ s/\s/_/g ; my $absolute_file = $base.'reject_'.$in{'new_default'}.'.tt2'; &do_log('info','Change default by linking %s 2 %s',$base.'reject.tt2',$absolute_file); if (-l $base.'reject.tt2') { unless (unlink ($base.'reject.tt2')){ &wwslog('err','Could not unlink %s',$base.'reject.tt2'); } } unless (symlink ($absolute_file,$base.'reject.tt2')){ &wwslog('err','Could not symlink %s,%s',$absolute_file,$base.'reject.tt2'); } }elsif ($in{'subaction'} eq 'delete') { unless(unlink $param->{'template_path'}) { &report::reject_report_web('intern','cannot_delete',{'file_del' => $param->{'template_path'}},'','','',$robot); &wwslog('err',"can't open file %s : %s", $param->{'template_path'}, $!); &web_db_log({'parameters' => $param->{'template_path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &report::notice_report_web('performed',{},$in{'subaction'}); } ## Build the list of available templates my $available_files = &tools::get_templates_list('mail','',$list, {'ignore_global' => 1}); foreach $file (keys %$available_files) { if ($file eq 'reject.tt2') { my $base = $list->{'dir'}.'/mail_tt2/'; my $absolute_file = $base.'reject.tt2'; if (-l $absolute_file){ my $default = readlink ($absolute_file); if ((-f $default )||( -f $base.$default )) { $default =~ s/^.*reject_//; $default =~ s/.tt2$//; $default =~ s/_/ /g; $param->{'default_reject_template'} = $default; }else{ # link to no existing file. remove link &wwslog('err','Link %s point to un no existing file (%s)', $base.'reject.tt2',$default); unless (unlink ($absolute_file)){ &wwslog('err','do_modindex: could not unlink %s',$base.'reject.tt2'); } } }elsif(-f $absolute_file){ # replace existing reject.tt2 file by a symlink to reject_default.tt2 for compatibility with version older than 6.0 unless (rename ($absolute_file,$base.'reject_default.tt2')){ &wwslog('err','Could not rename %,%s',$base.'reject.tt2',$base.'reject_default.tt2'); } unless (symlink ($base.'reject_default.tt2',$absolute_file)){ &wwslog('err','Could not symlink %s,%s',$base.'reject_default.tt2',$absolute_file); } $param->{'default_reject_template'} = 'default'; push (@{$param->{'available_files'}},'default'); } }else{ next unless($file =~ /^reject_/); $file =~ s/^reject_//; $file =~ s/.tt2$//; $file =~ s/_/ /g; push (@{$param->{'available_files'}},$file); } } return 1; } ## online template edition sub do_edit_template { $in{'subdir'} ||= 'default'; &wwslog('info', "do_edit_template(type=$in{'webormail'},template-name=$in{'template_name'},listname=$in{'list'},path=$in{'template_path'},scope=$in{'scope'},lang=$in{'tpl_lang'})"); ## Load original template &do_view_template; unless ($in{'content'}) { return 1; } if ($in{'scope'} eq 'list' and ref $list ne 'List') { report::reject_report_web('user', 'listname_needed', {}, $param->{'action'}); wwslog('info', "edit_template : no output lisname while output scope is list"); web_db_log({'parameters' => $in{'template_name'}, 'status' => 'error', 'error_type' => 'no_list'}); return undef; } $param->{'template_path'} = tools::get_template_path($in{'webormail'}, $robot, $in{'scope'}, $in{'template_name'}, $in{'tpl_lang'}, $list); unless (open (TPLOUT,'>'.$param->{'template_path'})) { &report::reject_report_web('intern','cannot_open_file',{'path' => $param->{'template_path'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err',"edit_template: can't open file %s", $param->{'template_path'}); &web_db_log({'parameters' => $in{'template_name'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } print TPLOUT &tools::unescape_html($in{'content'}); close TPLOUT; $param->{'saved'} = 1; $param->{'template_content'} = $in{'content'}; $param->{'webormail'} = $in{'webormail'}; $param->{'template_name'} = $in{'template_name'}; $param->{'list'} = $in{'list'}; $param->{'scope'} = $in{'scope'}; $param->{'template_path'} = $in{'template_path'}; $param->{'tpl_lang'} = $in{'tpl_lang'}; &web_db_log({'parameters' => $in{'template_name'}, 'status' => 'success'}); return 'ls_templates'; } ## Server show colors, and install static css in future edit colors etc ## Server show colors, and install static css in futur edit colors etc sub do_skinsedit { &wwslog('info', 'do_skinsedit'); my $f; my $dir = &Conf::get_robot_conf($robot, 'css_path'); my $css_url = &Conf::get_robot_conf($robot, 'css_url'); ## Checking families and other virtual hosts. &get_server_details(); $param->{'css_warning'} = "parameter css_url seems strange, it must be the url of a directory not a css file" if ($css_url =~ /\.css$/); if(($in{'editcolors'})&&($in{'subaction'} eq 'reset')){ delete $session->{'custom_css'}; delete $param ->{'session'}{'custom_css'}; delete $param->{'custom_css'}; foreach my $colornumber (0..15){ delete $session->{'color_'.$colornumber} ; delete $param ->{'session'}{'color_'.$colornumber}; } } if ($in{'editcolors'} and $in{'subaction'} eq 'test') { return unless $in{'custom_color_number'} =~ /color_/; $param->{'custom_color_number'} = $in{'custom_color_number'}; $param->{'custom_color_value'} = $in{'custom_color_value'}; $param->{'custom_css'} = $css_url.'/'.$param->{'user'}{'email'}.'.style.css'; $session->{'custom_css'} = $param->{'custom_css'}; $session->{$in{'custom_color_number'}} = $in{'custom_color_value'}; $param->{$in{'custom_color_number'}} = $in{'custom_color_value'}; foreach my $colornumber (0..15){ if ($session->{'color_'.$colornumber} ) { $param->{'color_'.$colornumber} = $session->{'color_'.$colornumber} ; $param->{'session'}{'color_'.$colornumber} = $session->{'color_'.$colornumber} ; } } } if ($in{'subaction'} eq 'install' or $in{'installcss'}) { ## Do not include locale subdirectories. ## The css.tt2 by each locales will override styles in main CSS. my $tt2_include_path = tools::make_tt2_include_path($robot, 'web_tt2', '', ''); my $date= time; my $style_file; # update config foreach my $colornumber (0..15){ &Conf::set_robot_conf($robot, 'color_'.$colornumber, $session->{'color_'.$colornumber}) if ($session->{'color_'.$colornumber}); } $param->{'conf'}=$Conf::Conf; foreach my $css ('style.css','print.css','fullPage.css','print-preview.css') { $param->{'css'} = $css; my $css_file; # if user use editcolor form we must generate a static CSS that used custom colors. if($in{'subaction_test'}){ $css_file = "$dir/$param->{'user'}{'email'}.$css"; }else{ $css_file = "$dir/$css"; } unless (-d $dir) { unless (mkdir $dir, 0775) { &report::reject_report_web('intern',"mkdir_failed",{'path' => $dir}, $param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','skinsedit : failed to create directory %s : %s',$dir, $!); return undef; } chmod 0775, $dir; &wwslog('notice','skinsedit : created missing directory %s',$dir); } ## Keep a copy of the previous CSS (only if this is not a custom css). if ((-f "$css_file")&&!($in{'editcolors'})) { unless (rename "$css_file", "$css_file.$date") { &report::reject_report_web('intern','cannot_rename_file',{'path' => "$css_file.$date"},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','skinsedit : can\'t open file %s.%s',$css_file,$date); return undef; } } if ($in{'subaction_install'}) { foreach my $colornumber (0..15){ $param->{'color_'.$colornumber} = $session->{'color_'.$colornumber} if ($session->{'color_'.$colornumber}); } } unless (open (CSS,">$css_file")) { &report::reject_report_web('intern','cannot_open_file',{'path' => "$css_file"},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','skinsedit : can\'t open file (write) %s',$css_file); return undef; } unless (&tt2::parse_tt2($param,'css.tt2' ,\*CSS, $tt2_include_path)) { my $error = &tt2::get_error(); $param->{'tt2_error'} = $error; &List::send_notify_to_listmaster('web_tt2_error', $robot,[$error]); &wwslog('info', "do_skinsedit : error while installing $css_file"); } close (CSS) ; ## Make the CSS readable to anyone chmod 0775, "$css_file"; } $param->{'css_result'} = 1 ; } return 1; } ## Multiple add sub do_add_request { &wwslog('info', 'do_add_request(%s)', $in{'email'}); ## Access control return undef unless (defined &check_authz('do_add_request', 'add')); return 1; } #################################################### # do_add #################################################### # Adds a user to a list (requested by an other user) # # IN : - # # OUT : 'loginrequest' # | ($in{'previous_action'} || 'review') # | undef #################################################### ## TODO: vérifier validité email sub do_add { &wwslog('info', 'do_add(%s)', $in{'email'}||$in{'pending_email'}); my $subscriptions = $list->get_subscription_requests(); my %user; ## If a list is not 'open' and allow_subscribe_if_pending has been set to 'off' returns undef. unless (($list->{'admin'}{'status'} eq 'open') || (&Conf::get_robot_conf($robot, 'allow_subscribe_if_pending') eq 'on')) { &report::reject_report_web('user','list_not_open',{'status' => $list->{'admin'}{'status'}},$param->{'action'}); &wwslog('info','list not open'); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'list_not_open'}); return undef; } my $email_regexp = &tools::get_regexp('email'); if ($in{'dump'}) { foreach (split /\n/, $in{'dump'}) { if (/^\s*($email_regexp)(\s+(.*))?\s*$/) { $user{&tools::get_canonical_email($1)} = $5; } } }elsif ($in{'email'} =~ /,/) { foreach my $pair (split /\0/, $in{'email'}) { if ($pair =~ /^($email_regexp)(,(.*))?\s*$/) { $user{&tools::get_canonical_email($1)} = $5; } } }elsif ($in{'email'}) { foreach my $email (split /\0/, $in{'email'}) { $user{&tools::get_canonical_email($email)} = $in{'gecos'}; } }elsif ($in{'pending_email'}) { foreach my $pair (split /\0/, $in{'pending_email'}) { my ($email, $gecos); if ($pair =~ /^($email_regexp)(,(.*))?\s*$/) { ($email, $gecos) = ($1,$5); $user{&tools::get_canonical_email($email)} = $gecos; } } }else { &report::reject_report_web('user','no_email',{},$param->{'action'}); &wwslog('info','do_add: no email'); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'no_email'}); return undef; } my ($total, @new_users, @added_users ); my $comma_emails ; foreach my $email (keys %user) { &wwslog('debug', "do_add subscription \$subscriptions->{$email}{custom_attribute} = $subscriptions->{$email}{'custom_attribute'})" ); if (ref($subscriptions->{$email}{'custom_attribute'}) eq 'HASH') { my $xml = List::createXMLCustomAttribute($subscriptions->{$email}{'custom_attribute'}) ; &wwslog('debug', "do_add subscription XML \$subscriptions->{$email}{custom_attribute} = $xml;"); } my $result = $list->check_list_authz('add',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'email' => $in{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $add_is; my $reason; if (ref($result) eq 'HASH') { $add_is = $result->{'action'}; $reason = $result->{'reason'}; } unless ($add_is =~ /do_it/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info','do_add: %s may not add', $param->{'user'}{'email'}); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'internal'}); next; } unless (&tools::valid_email($email)) { &report::reject_report_web('user','incorrect_email',{'email' => $email},$param->{'action'},$list); &wwslog('info','do_add: incorrect email %s', $email); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'incorrect_email'}); next; } my $user_entry = $list->get_subscriber($email); if (defined($user_entry)) { &report::reject_report_web('user','user_already_subscriber', {'list' => $list->{'name'},'email' => $email},$param->{'action'},$list); &wwslog('info','do_add: %s already subscriber', $email); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'already_subscriber'}); next; } my $u2 = &List::get_user_db($email); my $defaults = $list->get_default_user_options(); my $u; %{$u} = %{$defaults}; $u->{'email'} = $email; $u->{'gecos'} = $user{$email} || $u2->{'gecos'}; $u->{'date'} = $u->{'update_date'} = time; $u->{'password'} = $u2->{'password'} || &tools::tmp_passwd($email) ; if (my $reason = &tools::password_validation($u->{'password'})) { &report::reject_report_web('user','passwd_validation',{'reason' => $reason},$param->{'action'}); &wwslog('info','do_setpasswd: password validation'); &web_db_log({'status' => 'error', 'error_type' => 'bad_parameter'}); return undef; } $u->{'lang'} = $u2->{'lang'} || $list->{'admin'}{'lang'}; if ($comma_emails) { $comma_emails = $comma_emails .','. $email; }else{ $comma_emails = $email; } ## push @new_users, $u; push @added_users, $email; ## List only email addresses ; used later to remove pending subrequests unless ($in{'quiet'} || $add_is =~ /quiet/i) { unless ($list->send_file('welcome', $email, $robot,{})) { &wwslog('err',"Unable to send template 'welcome' to $email"); } } } $total = $list->add_user(@new_users); unless( defined $total) { &report::reject_report_web('intern','add_subscriber_db_failed',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_add: failed adding'); &web_db_log({'target_email' => $in{'email'}||$in{'pending_email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Delete subscription request if any $list->delete_subscription_request(@added_users); &report::notice_report_web('add_performed', {'total' => $total},$param->{'action'}); foreach my $email (@added_users) { &web_db_log({'target_email' => $email, 'status' => 'success'}); } $in{'list'} = $in{'previous_list'} if ($in{'previous_list'}); return $in{'previous_action'} || 'review'; } #################################################### # do_del #################################################### # Deletes a user from a list (requested by an other user) # # IN : - # # OUT : 'loginrequest' # | ($in{'previous_action'} || 'review') | undef # #################################################### ## TODO: vérifier validité email sub do_del { &wwslog('info', 'do_del()'); $in{'email'} = &tools::unescape_chars($in{'email'}); my $result = $list->check_list_authz('del',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'email' => $in{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $del_is; my $reason; if (ref($result) eq 'HASH') { $del_is = $result->{'action'}; $reason = $result->{'reason'}; } unless ( $del_is =~ /do_it/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'del',$param->{'list'},$robot,$in{'email'},'may not'); &wwslog('info','do_del: %s may not del', $param->{'user'}{'email'}); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } my @emails = split /\0/, $in{'email'}; my ($total, @removed_users); foreach my $email (@emails) { my $escaped_email = &tools::escape_chars($email); my $user_entry = $list->get_subscriber($email); unless (defined($user_entry)) { &report::reject_report_web('user','not_subscriber',{'email' => $email},$param->{'action'},$list); # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'del',$param->{'list'},$robot,$email,'not subscriber'); &wwslog('info','do_del: %s not subscribed', $email); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'not_subscriber'}); next; } push @removed_users, $email; my $bounce_dir = $list->get_bounce_dir(); if (-f $bounce_dir.'/'.$escaped_email) { unless (unlink $bounce_dir.'/'.$escaped_email) { &wwslog('info','do_resetbounce: failed deleting %s', $bounce_dir.'/'.$escaped_email); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); next; } } &wwslog('info','do_del: subscriber %s deleted from list %s', $email, $param->{'list'}); unless ($in{'quiet'}) { unless ($list->send_file('removed', $email, $robot,{})) { &wwslog('notice',"Unable to send template 'removed' to $email"); } } &web_db_log({'target_email' => $email, 'status' => 'success'}); } $total = $list->delete_user('users' => \@removed_users, 'exclude' =>'1'); unless( defined $total) { &report::reject_report_web('intern','delete_subscriber_db_failed',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_del: failed'); &web_db_log({'target_email' => $in{'email'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &report::notice_report_web('del_performed',{'total' => $total},$param->{'action'}); $param->{'is_subscriber'} = 1; $param->{'may_signoff'} = 1; ## Skip search because we don't have the expression anymore delete $in{'previous_action'} if ($in{'previous_action'} eq 'search'); return $in{'previous_action'} || 'review'; } #################################################### # do_modindex #################################################### # Web page for an editor to moderate documents and # and/or to tag message in message topic context # # IN : - # # OUT : 'loginrequest' | 'admin' | '1' | undef # ####################################################### sub do_modindex { &wwslog('info', 'do_modindex'); my $msg; my $doc; ## Loads message list unless (opendir SPOOL, $Conf{'queuemod'}) { &report::reject_report_web('intern','cannot_open_spool',{'spool'=>$Conf{'queuemod'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_modindex: unable to read spool'); return 'admin'; } my $list_name = $list->{'name'}; my $list_id = $list->get_list_id(); foreach $msg ( sort grep(!/^\./, readdir SPOOL )) { next unless ($msg =~ /^$list_id\_(\w+)$/ || $msg =~ /^$list_name\_(\w+)$/); my $id = $1; ## Load msg my $mail = new Message("$Conf{'queuemod'}/$msg"); unless (defined $mail) { &report::reject_report_web('intern','cannot_get_msg',{'msg'=>"$Conf{'queuemod'}/$msg"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_modindex: unable to parse msg %s', $msg); closedir SPOOL; next; } $param->{'spool'}{$id}{'size'} = int( (-s "$Conf{'queuemod'}/$msg") / 1024 + 0.5); $param->{'spool'}{$id}{'subject'} = &tools::decode_header($mail, 'Subject'); $param->{'spool'}{$id}{'subject'} ||= 'no_subject'; $param->{'spool'}{$id}{'date'} = &tools::decode_header($mail, 'Date'); $param->{'spool'}{$id}{'from'} = &tools::decode_header($mail, 'From'); $param->{'spool'}{$id}{'spam_status'} = $mail->{'spam_status'}; foreach my $field ('subject','date','from') { $param->{'spool'}{$id}{$field} =~ s/&/&/g; $param->{'spool'}{$id}{$field} =~ s/{'spool'}{$id}{$field} =~ s/>/>/g; } } closedir SPOOL; if ($list->is_there_msg_topic()) { $param->{'request_topic'} = 1; foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if ($top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } $param->{'topic_required'} = $list->is_msg_topic_tagging_required(); } my $available_files = &tools::get_templates_list('mail','',$list, {'ignore_global' => 1}); foreach my $file (keys %$available_files) { if ($file eq 'reject.tt2') { my $base = $list->{'dir'}.'/mail_tt2/'; my $absolute_file = $base.'reject.tt2'; if (-l $absolute_file){ my $default = readlink ($absolute_file); if ((-f $default )||( -f $base.$default )) { $default =~ s/^.*reject_//; $default =~ s/.tt2$//; $param->{'default_reject_template'} = $default; }else{ # link to no existing file. remove link &wwslog('err','do_modindex: link %s point to un no existing file (%s)', $base.'reject.tt2',$default); unless (unlink ($absolute_file)){ &wwslog('err','do_modindex: could not unlink %s',$base.'reject.tt2'); } } }elsif(-f $absolute_file){ # replace existing reject.tt2 file by a symlink to reject_default.tt2 for compatibility with version older than 6.0 unless (rename ($absolute_file,$base.'reject_default.tt2')){ &wwslog('err','do_modindex: could not rename %,%s',$base.'reject.tt2',$base.'reject_default.tt2'); } unless (symlink ($base.'reject_default.tt2',$absolute_file)){ &wwslog('err','do_modindex: could not symlink %s,%s',$base.'reject_default.tt2',$absolute_file); } $param->{'default_reject_template'} = 'default'; push (@{$param->{'available_files'}},'default'); } }else{ next unless($file =~ /^reject_/); $file =~ s/^reject_//; $file =~ s/.tt2$//; push (@{$param->{'available_files'}},$file); } } ## shared documents awaiting moderation foreach my $d (@{$param->{'doc_mod_list'}}) { $d =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; my $long_path = $1; # path without the filename my $fname = $3; # the filename with .moderate my $path = $long_path; $path =~ s/^.*\/shared//; #the path for the user, without the filename my $visible_fname = &make_visible_path($fname); # the filename without .moderate my $visible_path = $path; $visible_path = &make_visible_path($visible_path); my %desc_hash; if ($d && (-e "$long_path.desc.$fname")){ %desc_hash = &get_desc_file("$long_path.desc.$fname"); } my @info = stat $d; my $doc = {}; $doc->{'visible_path'} = $visible_path; $doc->{'visible_fname'} = $visible_fname; $doc->{'escaped_fname'} = &tools::escape_docname($fname, '/'); $doc->{'escaped_path'} = &tools::escape_docname($path, '/'); $doc->{'fname'} = $fname; $doc->{'size'} = (-s $d)/1000; $doc->{'date'} = gettext_strftime "%d %b %Y", localtime($info[9]); $doc->{'author'} = $desc_hash{'email'}; $doc->{'path'} = $path; push(@{$param->{'info_doc_mod'}},$doc) } unless (($param->{'spool'}) || ($param->{'mod_total_shared'} > 0)) { &report::notice_report_web('no_msg_document', {'list' => $in{'list'}},$param->{'action'}); &wwslog('err','do_modindex: no message and no document'); } return 1; } ### installation of moderated documents of shared sub do_d_install_shared { &wwslog('info', 'do_d_install_shared(%s)', $in{'id'}); if ($in{'mode_cancel'}) { return 'modindex'; } my $shareddir = $list->{'dir'}.'/shared'; my $file; my $slash_path; my $fname; my $visible_fname; # list of file already existing my @list_file_exist; unless($in{'mode_confirm'} || $in{'mode_cancel'}) { # file already exists ? foreach my $id (split /\0/, $in{'id'}) { $file = "$shareddir$id"; $id =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $slash_path = $1; $fname = $3; $visible_fname = &make_visible_path($fname); if (-e "$file") { if (-e "$shareddir$slash_path$visible_fname") { push(@list_file_exist,"$slash_path$visible_fname"); } } } if (@list_file_exist) { $param->{'list_file'}=\@list_file_exist; my @id = split(/\0/,$in{'id'}); $param->{'id'} = \@id; return 1; } } # install the file(s) selected foreach my $id (split /\0/, $in{'id'}) { $file = "$shareddir$id"; $id =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $slash_path = $1; $fname = $3; my $new_fname; ## new filename without the .moderate extension if ($fname =~ /^\.(.+)\.moderate$/) { $new_fname = $1; } my $visible_path = &make_visible_path($slash_path); $visible_fname = &make_visible_path($fname); if (-e "$file") { # rename the old file in .old if exists if (-e "$shareddir$slash_path$new_fname") { unless (rename "$shareddir$slash_path$new_fname","$shareddir$slash_path$new_fname.old"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir$slash_path$new_fname", 'new'=>"$shareddir$slash_path$new_fname.old" }, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_install_shared : Failed to rename $shareddir$slash_path$new_fname to .old : %s",$!); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (rename "$shareddir$slash_path.desc.$new_fname","$shareddir$slash_path.desc.$new_fname.old"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir$slash_path.desc.$new_fname", 'new'=>"$shareddir$slash_path.desc.$new_fname.old"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_install_shared : Failed to rename shareddir$slash_path.desc.$new_fname to .old : %s",$!); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } } unless (rename ("$shareddir$id","$shareddir$slash_path$new_fname")){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir$id", 'new'=>"$shareddir$slash_path$new_fname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_install_shared : Failed to rename $file to $shareddir$slash_path$new_fname : $!"); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (rename ("$shareddir$slash_path.desc.$fname","$shareddir$slash_path.desc.$new_fname")){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir$slash_path.desc.$fname", 'new'=>"$shareddir$slash_path.desc.$new_fname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_install_shared : Failed to rename $file to $shareddir$slash_path$new_fname : $!"); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } # send a message to the author my %context; $context{'installed_by'} = $param->{'user'}{'email'}; $context{'filename'} = "$visible_path$visible_fname"; my %desc_hash; if ($id && (-e "$shareddir$slash_path.desc.$visible_fname")){ %desc_hash = &get_desc_file("$shareddir$slash_path.desc.$visible_fname"); } my $sender = $desc_hash{'email'}; unless ($list->send_file('d_install_shared', $sender, $robot, \%context)) { &wwslog('notice',"Unable to send template 'd_install_shared' to $sender"); } } } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'modindex'; } ### reject moderated documents of shared sub do_d_reject_shared { &wwslog('info', 'do_d_reject_shared(%s)', $in{'id'}); my $shareddir = $list->{'dir'}.'/shared'; my $file; my $slash_path; my $fname; my $visible_fname; foreach my $id (split /\0/, $in{'id'}) { $file = "$shareddir$id"; $id =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $slash_path = $1; $fname = $3; $visible_fname = &make_visible_path($fname); my $visible_path = &make_visible_path($slash_path); unless ($in{'quiet'}) { my %context; my $sender; $context{'rejected_by'} = $param->{'user'}{'email'}; $context{'filename'} = "$visible_path$visible_fname"; my %desc_hash; if ($id && (-e "$shareddir$slash_path.desc.$fname")){ %desc_hash = &get_desc_file("$shareddir$slash_path.desc.$fname"); } $sender = $desc_hash{'email'}; unless ($list->send_file('d_reject_shared', $sender, $robot, \%context)) { &wwslog('notice',"Unable to send template 'd_reject_shared' to $sender"); } } unless (unlink($file)) { &report::reject_report_web('intern','erase_file',{'file' => $file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_d_reject_shared: failed to erase %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (unlink("$shareddir$slash_path.desc.$fname")) { &report::reject_report_web('intern','erase_file',{'file' => "$shareddir$slash_path.desc.$fname"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_reject_shared: failed to erase $shareddir$slash_path.desc.$fname"); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'parameters' => $in{'id'}, 'status' => 'success'}); return 'modindex'; } #################################################### # do_reject #################################################### # Moderation of messages : rejects messages and notifies # their senders. If in{'blacklist'} add sender to list blacklist # # IN : - # # OUT : 'loginrequest' | 'modindex' | undef # #################################################### sub do_reject { # toggle selection javascript have a distinction of spam and ham base on the checkbox name . It is not useful here so join id list and idspam list. $in{'id'} .= ','.$in{'idspam'} if ($in{'idspam'}); $in{'id'} =~ s/^,//; $in{'id'} =~ s/\0/,/g; $in{'message_template'}; ## The quiet information might either be provided by the 'quiet' variable ## or by the 'quiet' value of the 'message_template' variable if ($in{'message_template'} eq 'quiet') { $in{'quiet'} = 1; delete $in{'message_template'}; } if ($in{'blacklist'}) { $in{'quiet'} = 1; } &wwslog('info', 'do_reject(%s)', $in{'id'}); my ($msg, $file); $param->{'blacklist_added'} = 0; $param->{'blacklist_ignored'} = 0; foreach my $id (split (/,/, $in{'id'})) { ## For compatibility concerns foreach my $list_id ($list->get_list_id(),$list->{'name'}) { $file = $Conf{'queuemod'}.'/'.$list_id.'_'.$id; last if (-f $file); } ## Open the file unless (open(IN, $file)) { &report::reject_report_web('user','already_moderated',{},$param->{'action'}); &wwslog('err','do_reject: Unable to open %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); next; } # extract sender address is needed to report reject to sender and in case the sender is to be added to the blacklist if (($in{'quiet'} ne '1')||($in{'blacklist'})) { my $msg; my $parser = new MIME::Parser; $parser->output_to_core(1); unless ($msg = $parser->read(\*IN)) { &wwslog('err', 'Unable to parse message %s', $file); next; } my @sender_hdr = Mail::Address->parse($msg->head->get('From')); unless ($#sender_hdr == -1) { my $rejected_sender = $sender_hdr[0]->address; unless ($in{'quiet'}) { my %context; $context{'subject'} = &tools::decode_header($msg, 'Subject'); $context{'rejected_by'} = $param->{'user'}{'email'}; $context{'template_used'} = $in{'message_template'}; unless ($list->send_file($in{'message_template'}, $rejected_sender, $robot, \%context)) { &wwslog('notice',"Unable to send template $in{'message_template'} to $rejected_sender"); } } if ($in{'blacklist'}) { if (&tools::add_in_blacklist($rejected_sender,$robot,$list)) { $param->{'blacklist_added'} += 1; &wwslog('info',"added $rejected_sender to $list->{'name'} blacklist"); }else{ &wwslog('notice',"Unable to add $rejected_sender to $list->{'name'} blacklist"); $param->{'blacklist_ignored'} += 0; } } } } close(IN); unless (unlink($file)) { &report::reject_report_web('intern','erase_file',{'file' => $file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_reject: failed to erase %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } &web_db_log({'parameters' => $in{'id'}, 'status' => 'success'}); &report::notice_report_web('performed',{},$param->{'action'}); return 'modindex'; } #################################################### # do_distribute #################################################### # Moderation of messages : distributes moderated # messages and tag it in message moderation context # # IN : - id of message to distribute. This value can also be in idspam parameter # # OUT : 'loginrequest' | 'modindex' | undef # ###################################################### sub do_distribute { $in{'id'} .= ','.$in{'idspam'} if ( $in{'idspam'}); $in{'id'} =~ s/^,//; $in{'id'} =~ s/\0/,/g; &wwslog('info', 'do_distribute(%s)', $in{'id'}); my ($msg, $file); my $time = time; my $data = {'headers' => {'Message-ID' => &tools::get_message_id($robot), 'X-Sympa-NoWrap' => 'yes'}, 'from'=> $param->{'user'}{'email'}}; ## msg topics my @msg_topics; foreach my $msg_topic (@{$list->{'admin'}{'msg_topic'}}) { my $var_name = "topic_"."$msg_topic->{'name'}"; if ($in{"$var_name"}) { push @msg_topics, $msg_topic->{'name'}; } } my $list_topics = join(',',@msg_topics); if (!$list_topics && $list->is_msg_topic_tagging_required()) { &report::reject_report_web('user','msg_topic_missing',{},$param->{'action'}); &wwslog('info','do_distribute: message(s) without topic but in a required list'); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'no_topic'}); return undef; } ## messages foreach my $id (split (/,/, $in{'id'})) { # QUIET DISTRIBUTE &do_log('notice', 'Moderator approves message: %s %s', $list->{'name'},$id); my $mail_command = sprintf ("QUIET DISTRIBUTE %s %s\n",$list->{'name'},$id); $data->{'body'} .= $mail_command; ## For compatibility concerns foreach my $list_id ($list->get_list_id(),$list->{'name'}) { $file = $Conf{'queuemod'}.'/'.$list_id.'_'.$id; last if (-f $file); } unless (-f $file) { &report::reject_report_web('user','already_moderated',{},$param->{'action'}); &wwslog('err','do_distribute: Unable to open %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); next; } ## TAG if ($list_topics) { my $parser = new MIME::Parser; $parser->output_to_core(1); unless (open FILE, "$file") { &wwslog('notice', 'do_distribute: Cannot open file %s', $file); &report::reject_report_web('intern','cannot_open_file',{'file' => $file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } my $msg = $parser->parse(\*FILE); my $head = $msg->head(); my $filetopic = $list->tag_topic(&tools::clean_msg_id($head->get('Message-Id')),$list_topics,'editor'); } unless (rename($file,"$file.distribute")) { &report::reject_report_web('intern','rename_file',{'old'=>$file, 'new'=>"$file.distribute"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_distribute: failed to rename %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); } } $data->{'not_auto_submitted'} = 1; unless (&mail::mail_file('',&Conf::get_robot_conf($robot, 'sympa'), $data, $robot)) { &report::reject_report_web('intern','cannot_send_distribute',{'from' => $param->{'user'}{'email'},'listname'=>$list->{'name'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_distribute: failed to send message for file %s', $file); &web_db_log({'parameters' => $in{'id'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &web_db_log({'parameters' => $in{'id'}, 'status' => 'success'}); &report::notice_report_web('performed_soon',{},$param->{'action'}); return 'modindex'; } #################################################### # do_viewmod #################################################### # Web page for an editor to moderate a mail and/or # to tag it in message topic context # # IN : - # # OUT : 'login,request' | '1' | undef # #################################################### sub do_viewmod { &wwslog('info', 'do_viewmod(%s,%s)', $in{'id'},$in{'file'}); my $msg; my $tmp_dir; my $available_files = &tools::get_templates_list('mail','',$list, {'ignore_global' => 1}); foreach my $file (keys %$available_files) { next unless($file =~ /^reject_/); $file =~ s/^reject_//; $file =~ s/.tt2$//; push (@{$param->{'available_files'}},$file); } ## For compatibility concerns foreach my $list_id ($list->get_list_id(),$list->{'name'}) { $tmp_dir = $Conf{'queuemod'}.'/.'.$list_id.'_'.$in{'id'}; if (-d $tmp_dir) { last; } } unless (-d $tmp_dir) { &report::reject_report_web('intern','no_html_message_available',{'dir' => $tmp_dir},$param->{'action'}); &wwslog('err','do_viewmod: no HTML version of the message available in %s', $tmp_dir); return undef; } if ($in{'file'}) { $in{'file'} =~ /\.(\w+)$/; $param->{'file_extension'} = $1; $param->{'file'} = $tmp_dir.'/'.$in{'file'}; $param->{'bypass'} = 1; }else { &tt2::add_include_path($tmp_dir) ; } $param->{'base'} = sprintf "%s/viewmod/%s/%s/", &Conf::get_robot_conf($robot, 'wwsympa_url'), $param->{'list'}, $in{'id'}; $param->{'id'} = $in{'id'}; if ($list->is_there_msg_topic()) { $param->{'request_topic'} = 1; foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if ($top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } $param->{'topic_required'} = $list->is_msg_topic_tagging_required(); } return 1; } ## Edition of list/sympa files ## No list -> sympa files (helpfile,...) ## TODO : upload ## TODO : edit family file ??? sub do_editfile { &wwslog('info', 'do_editfile(%s)', $in{'file'}); $param->{'subtitle'} = sprintf $param->{'subtitle'}, $in{'file'}; unless ($in{'file'}) { ## Messages edition foreach my $f ('info','homepage','welcome.tt2','bye.tt2','removed.tt2','message.footer','message.header','remind.tt2','invite.tt2','reject.tt2','your_infected_msg.tt2') { my $filename_for_auth = $f; $filename_for_auth = 'info.file' if ($filename_for_auth eq 'info'); next unless ($list->may_edit($filename_for_auth, $param->{'user'}{'email'}) eq 'write'); if ($wwslib::filenames{$f}{'gettext_id'}) { $param->{'files'}{$f}{'complete'} = gettext($wwslib::filenames{$f}{'gettext_id'}); }else { $param->{'files'}{$f}{'complete'} = $f; } $param->{'files'}{$f}{'selected'} = ''; } return 1; } unless (defined $wwslib::filenames{$in{'file'}}) { &report::reject_report_web('user','file_not_editable',{'file' => $in{'file'}},$param->{'action'}); &wwslog('err','do_editfile: file %s not editable', $in{'file'}); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } $param->{'file'} = $in{'file'}; $param->{'complete'} = gettext($wwslib::filenames{$in{'file'}}{'gettext_id'}); my $subdir = ''; if ($in{'file'} =~ /\.tt2$/) { $subdir = 'mail_tt2/'; } if ($param->{'list'}) { my $filename_for_auth = $in{'file'}; $filename_for_auth = 'info.file' if ($filename_for_auth eq 'info'); my ($role,$right) = $list->may_edit($filename_for_auth, $param->{'user'}{'email'}); unless ($right eq 'write') { &report::reject_report_web('auth','edit_right',{'role'=>$role, 'right' => $right},$param->{'action'},$list); &wwslog('err','do_editfile: not allowed'); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } ## Add list lang to tpl filename my $file = $in{'file'}; #$file =~ s/\.tpl$/\.$list->{'admin'}{'lang'}\.tpl/; ## Look for the template $param->{'filepath'} = &tools::get_filename('etc',{},$subdir.$file,$robot, $list); ## There might be no matching file if default template not provided with Sympa if (defined $param->{'filepath'}) { ## open file and provide filecontent to the parser ## It allows to us the correct file encoding unless (open FILE, "<", $param->{'filepath'}) { &report::reject_report_web('intern','cannot_open_file',{'file' => $param->{'filepath'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_editfile: failed to open file %s: %s', $param->{'filepath'},$!); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } while () { Encode::from_to($_, $Conf{'filesystem_encoding'}, 'utf8'); $param->{'filecontent'} .= $_; } close FILE; }else { $param->{'filepath'} = $list->{'dir'}.'/'.$subdir.$file; } ## Default for 'homepage' is 'info' if (($in{'file'} eq 'homepage') && ! $param->{'filepath'}) { $param->{'filepath'} = &tools::get_filename('etc',{},$subdir.'info',$robot, $list); } }else { unless (&List::is_listmaster($param->{'user'}{'email'},$robot)) { &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('err','do_editfile: no list'); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'no_list'}); return undef; } my $file = $in{'file'}; ## Look for the template if ($file eq 'list_aliases.tt2') { $param->{'filepath'} = &tools::get_filename('etc',{},$file,$robot,$list); }else { #my $lang = &Conf::get_robot_conf($robot, 'lang'); #$file =~ s/\.tpl$/\.$lang\.tpl/; $param->{'filepath'} = &tools::get_filename('etc',{},$subdir.$file,$robot,$list); } } if (-f $param->{'filepath'} && (! -r $param->{'filepath'})) { &report::reject_report_web('intern','cannot_read',{'filepath' => $param->{'filepath'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','do_editfile: cannot read %s', $param->{'filepath'}); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &web_db_log({'parameters' => $in{'file'}, 'status' => 'success'}); $allow_absolute_path = 1; return 1; } ##################################################################################### ## Saving of list files sub do_savefile { &wwslog('info', 'do_savefile(%s)', $in{'file'}); $param->{'subtitle'} = sprintf $param->{'subtitle'}, $in{'file'}; if ($param->{'list'}) { unless ($list->am_i('owner', $param->{'user'}{'email'})) { &report::reject_report_web('auth','action_owner',{},$param->{'action'},$list); &wwslog('err','do_savefile: not allowed'); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } if ($in{'file'} =~ /\.tt2$/) { $param->{'filepath'} = $list->{'dir'}.'/mail_tt2/'.$in{'file'}; }else { $param->{'filepath'} = $list->{'dir'}.'/'.$in{'file'}; if (defined $list->{'admin'}{'family_name'}) { unless ($list->update_config_changes('file',$in{'file'})) { &report::reject_report_web('intern','update_config_changes',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_savefile: cannot write in config_changes for file %s', $param->{'filepath'}); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } } }else { unless (&List::is_listmaster($param->{'user'}{'email'}),$robot) { &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('err','do_savefile: no list'); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'no_list'}); return undef; } if ($robot ne $Conf{'domain'}) { if ($in{'file'} eq 'list_aliases.tt2') { $param->{'filepath'} = "$Conf{'etc'}/$robot/$in{'file'}"; }else { $param->{'filepath'} = "$Conf{'etc'}/$robot/mail_tt2/$in{'file'}"; } }else { if ($in{'file'} eq 'list_aliases.tt2') { $param->{'filepath'} = "$Conf{'etc'}/$in{'file'}"; }else { $param->{'filepath'} = "$Conf{'etc'}/mail_tt2/$in{'file'}"; } } } unless ((! -e $param->{'filepath'}) or (-w $param->{'filepath'})) { &report::reject_report_web('intern','cannot_write',{'filepath' => $param->{'filepath'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_savefile: cannot write %s', $param->{'filepath'}); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Keep the old file if (-e $param->{'filepath'}) { rename($param->{'filepath'}, "$param->{'filepath'}.orig"); } ## Not empty if ($in{'content'} && ($in{'content'} !~ /^\s*$/)) { ## Remove DOS linefeeds (^M) that cause problems with Outlook 98, AOL, and EIMS: $in{'content'} =~ s/\r\n|\r/\n/g; ## Create directory if required my $dir = $param->{'filepath'}; $dir =~ s/\/[^\/]+$//; unless (-d $dir) { unless (mkdir $dir, 0777) { &report::reject_report_web('intern','cannot_mkdir',{'dir' => $dir},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_savefile: failed to create directory %s: %s', $dir,$!); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } ## Save new file unless (open FILE, ">", $param->{'filepath'}) { &report::reject_report_web('intern','cannot_open_file',{'file' => $param->{'filepath'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_savefile: failed to save file %s: %s', $param->{'filepath'},$!); &web_db_log({'parameters' => $in{'file'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } my $e = $in{'content'}; Encode::from_to($e, 'utf8', $Conf{'filesystem_encoding'}); print FILE $e; close FILE; }elsif (-f $param->{'filepath'}) { &wwslog('info', 'do_savefile: deleting %s', $param->{'filepath'}); unlink $param->{'filepath'}; } &web_db_log({'parameters' => $in{'file'}, 'status' => 'success'}); &report::notice_report_web('performed',{},$param->{'action'}); # undef $in{'file'}; # undef $param->{'file'}; return 'editfile'; } ## Access to web archives sub do_arc { &wwslog('info', 'do_arc(%s, %s)', $in{'month'}, $in{'arc_file'}); my $latest; my $index = $session->{'arc_mode'} || $wwsconf->{'archive_default_index'}; $index = 'thrd' unless $index and $index =~ /^(thrd|mail)$/; ## Clean arc_file if ($in{'arc_file'} eq '/') { delete $in{'arc_file'}; } ## Access control unless (defined &check_authz('do_arc', 'web_archive.access')) { $param->{'previous_action'} = 'arc'; $param->{'previous_list'} = $list->{'name'}; return undef; } $session->{'archive_sniffer'} = 'false' if ($param->{'user'}{'email'} or $in{'not_a_sniffer'}) ; if ($list->{'admin'}{'web_archive_spam_protection'} eq 'cookie'){ return 'arc_protect' unless ($session->{'archive_sniffer'} eq 'false') ; } my $arc_path = $wwsconf->{'arc_path'}.'/'.$list->get_list_id(); ## Calendar unless (opendir ARC, $arc_path) { &report::reject_report_web('user','empty_archives',{},$param->{'action'},$list); &wwslog('err','do_arc: no directory %s', $arc_path); return undef; } foreach my $dir (sort grep(!/^\./,readdir ARC)) { if ($dir =~ /^(\d{4})-(\d{2})$/ && -d $arc_path.'/'.$dir.'/arctxt') { $param->{'calendar'}{$1}{$2} = '???'; if (open(IDX, $arc_path.'/'.$dir.'/index')) { my ($msgs) = ; chomp ($msgs); close IDX; $param->{'calendar'}{$1}{$2} = $msgs if ($msgs); } $latest = $dir; } } closedir ARC; ## Read html file $in{'month'} ||= $latest; my $arc_month_path = $arc_path.'/'.$in{'month'}; unless ($in{'arc_file'}) { undef $latest; unless (opendir ARC, $arc_month_path) { &wwslog('err',"unable to readdir $arc_month_path"); &report::reject_report_web('user','month_not_found',{'month' => $in{'month'}, 'dir' => $arc_month_path, 'listname' => $param->{'list'}}, $param->{'action'}, $list,$param->{'user'}{'email'}, $robot); } foreach my $file (grep(/^$index/,readdir ARC)) { if ($file =~ /^$index(\d+)\.html$/) { $latest = $1 if ($latest < $1); } } closedir ARC; $in{'arc_file'} = $index.$latest.".html"; } ## File exist ? my $arc_file_path = $arc_month_path.'/'.$in{'arc_file'}; unless (-r $arc_file_path) { &wwslog('err',"unable to read $arc_file_path"); &report::reject_report_web('user','arc_not_found',{'arc_file' => $in{'arc_file'}, 'path' => $arc_file_path, 'listname' => $param->{'list'}}, $param->{'action'}, $list,$param->{'user'}{'email'}, $robot); return undef; } ## File type if ($in{'arc_file'} =~ /^(mail\d+|msg\d+|thrd\d+)\.html$/) { if ($in{'arc_file'} =~/^(thrd|mail)\d+\.html/) { $session->{'arc_mode'} = $1; } if ($param->{'user'}{'email'}){ if ($param->{'user'}{'prefs'}{'arc_mode'} ne $session->{'arc_mode'}) { # update user pref as soon as connected user change the way he consult archives $param->{'user'}{'prefs'}{'arc_mode'} = $session->{'arc_mode'}; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } } if ($in{'arc_file'} =~ /^(msg\d+)\.html$/) { ## If the file is a message, load the metadata to find out who is the author of the message my $metadata = &Archive::load_html_message('file_path' => $arc_file_path); $param->{'include_picture'} = &tools::make_pictures_url('email' => $metadata->{'X-From'}, 'list' => $list); $param->{'subtitle'} = $metadata->{'X-Subject'}; } ## Provide a filehandle to the TT2 parser (instead of a filename previously) ## It allows to set the appropriate utf8 binmode on the FH open $param->{'file_handle'}, "<", $arc_file_path; &tt2::add_include_path($arc_month_path); }else { if ($in{'arc_file'} =~ /\.(\w+)$/) { $param->{'file_extension'} = $1; } $param->{'bypass'} = 1; $param->{'file'} = $arc_file_path; } my @stat = stat ($arc_file_path); $param->{'date'} = $stat[9]; # send page as static if client is a bot. That's prevent crawling all archices every weeks by google, yahoo and others bots if ($session->{'is_a_crawler'}) { $param->{'header_date'} = $stat[9]; } $param->{'base'} = sprintf "%s%s/arc/%s/%s/%s", $param->{'base_url'}, $param->{'path_cgi'}, $param->{'list'}, $in{'month'}, $in{'arc_file'}; $param->{'archive_name'} = $in{'month'}; return 1; } ## Access to latest web archives sub do_latest_arc { &wwslog('info', 'do_latest_arc(%s,%s,%s)', $in{'list'}, $in{'for'}, $in{'count'}); ## Access control return undef unless (defined &check_authz('do_latest_arc', 'web_archive.access')); ## parameters of the query my $today = time; my $oldest_day; if (defined $in{'for'}) { $oldest_day = $today - (86400 * ($in{'for'})); $param->{'for'} = $in{'for'}; unless ($oldest_day >= 0){ &report::reject_report_web('user','nb_days_to_much',{'nb_days' => $in{'for'} },$param->{'action'},$list); &wwslog('err','do_latest_lists: parameter "for" is too big"'); } } my $nb_arc; my $NB_ARC_MAX = 100; if (defined $in{'count'}) { if ($in{'count'} > $NB_ARC_MAX) { $in{'count'} = $NB_ARC_MAX; } $param->{'count'} = $in{'count'}; $nb_arc = $in{'count'}; } else { $nb_arc = $NB_ARC_MAX; } my $arc_path = $wwsconf->{'arc_path'}.'/'.$list->get_list_id(); unless (opendir ARC_DIR, $arc_path) { &report::reject_report_web('user','empty_archives',{},$param->{'action'},$list); &wwslog('err','do_latest_arc: no directory %s', $arc_path); return undef; } my @months; my $latest; foreach my $dir (sort grep(!/^\./,readdir ARC_DIR)) { if ($dir =~ /^(\d{4})-(\d{2})$/) { push @months, $dir; $latest = $dir; } } closedir ARC_DIR; @months = reverse @months; my $stop_search; my @archives; ## year-month directory foreach my $year_month (@months) { if ($nb_arc <= 0) { last; } last if $stop_search; my $arc_month_path = $arc_path.'/'.$year_month.'/arctxt'; unless (opendir MONTH, $arc_month_path) { &report::reject_report_web('intern','inaccessible_archive',{'path' => $arc_month_path, 'listname' => $list->{'name'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_latest_arc: unable to open directory %s', $arc_month_path); next; } ## mails in the year-month directory foreach my $arc (sort {$b <=> $a} grep(!/^\./,readdir MONTH)) { last if ($nb_arc <= 0); if ($arc =~ /^(\d)+$/) { my %msg_info; use MIME::Parser; my $parser = new MIME::Parser; $parser->output_to_core(1); my $arc_file = $arc_month_path.'/'.$arc; unless (open (FILE, $arc_file)) { &wwslog('err', 'Unable to open file %s', $arc_file); } my $message; unless ($message = $parser->read(\*FILE)) { &wwslog('err', 'Unable to parse message %s', $arc_file); next; } use Mail::Header; my $hdr = $message->head; unless (defined $hdr) { &wwslog('err', 'Unable to parse header of message %s', $arc_file); next; } foreach my $field ('message-id','subject','from') { my $var = $field; $var =~ s/-/_/g; $msg_info{$var} = $hdr->get($field); if (ref $msg_info{$var} eq 'ARRAY') { $msg_info{$var} = $msg_info{$var}->[0]; } ## Hide full email address if ($field eq 'from') { if ($msg_info{$var} =~ /(.+)\<.+\>/) { $msg_info{$var} = $1; }else { my @email = split /\@/, $msg_info{$var}; $msg_info{$var} = $email[0]; } } if ($field eq 'message-id') { $msg_info{$var} = &tools::clean_msg_id($msg_info{'message_id'}); $msg_info{$var} = &tools::escape_chars($msg_info{$var}); $msg_info{'year_month'} = $year_month; }else { $msg_info{$var} = &MIME::EncWords::decode_mimewords($msg_info{$var}, Charset=>'utf8'); $msg_info{$var} = &tools::escape_html($msg_info{$var}); } } my $date = $hdr->get('Date'); unless (defined $date) { &wwslog('err', 'No date found in message %s', $arc_file); next; } my @array_date = &time_utils::parse_date($date); $msg_info{'date_smtp'} = $date; $msg_info{'date_epoch'} = &get_timelocal_from_date(@array_date[1..$#array_date]); $msg_info{'date'} = gettext_strftime "%d %b %Y", localtime($msg_info{'date_epoch'}); if ($msg_info{'date_epoch'} < $oldest_day) { $stop_search = 1; last; } foreach my $key (keys %msg_info) { chomp($msg_info{$key}); } push @archives,\%msg_info; $nb_arc--; } } closedir MONTH; } @{$param->{'archives'}} = sort ({$b->{'date_epoch'} <=> $a->{'date_epoch'}} @archives); return 1; } sub get_timelocal_from_date { my($mday, $mon, $yr, $hr, $min, $sec, $zone) = @_; my($time) = 0; $yr -= 1900 if $yr >= 1900; # if given full 4 digit year $yr += 100 if $yr <= 37; # in case of 2 digit years if (($yr < 70) || ($yr > 137)) { warn "Warning: Bad year (", $yr+1900, ") using current\n"; $yr = (localtime(time))[5]; } $time = &timelocal($sec,$min,$hr,$mday,$mon,$yr); return $time } #################################################### # do_remove_arc #################################################### # # request by list owner or message sender to remove message from archive # Create in the outgoing spool a file containing the message-id of mesage to be removed # # IN : list@host yyyy month and a tab of msgid # # OUT : 1 | undef # #################################################### sub do_remove_arc { &wwslog('info', 'do_remove_arc : list %s, yyyy %s, mm %s, #message %s', $in{'list'}, $in{'yyyy'}, $in{'month'}); my $arcpath = $wwsconf->{'arc_path'}.'/'.$list->get_list_id().'/'.$in{'yyyy'}.'-'.$in{'month'}; ## Access control # $in{'msgid'} = &tools::unescape_chars($in{'msgid'}); my @msgids = split /\0/, $in{'msgid'}; if ($#msgids == -1) { &report::reject_report_web('user','may_not_remove_arc',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','remove_arc: no message id found'); &web_db_log({'parameters' => $in{'msgid'}, 'msg_id' => $in{'msgid'}, 'status' => 'error', 'error_type' => 'no_msgid'}); $param->{'status'} = 'no_msgid'; return undef; } my $file = $Conf{'queueoutgoing'}.'/.remove.'.$list->get_list_id().'.'.$in{'yyyy'}.'-'.$in{'month'}.'.'.time; unless (open REBUILD, ">$file") { &report::reject_report_web('intern','cannot_open_file',{'file' => $file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_remove: cannot create %s', $file); &web_db_log({'parameters' => $in{'msgid'}, 'msg_id' => $in{'msgid'}, 'status' => 'error', 'error_type' => 'internal'}); closedir ARC; return undef; } foreach my $msgid (@msgids) { chomp $msgid ; printf REBUILD ('%s||%s',$msgid,$param->{'user'}{'email'}) ; printf REBUILD "\n"; } close REBUILD; &wwslog('info', 'do_remove_arc %d messages marked to be removed by archived', $#msgids+1); &web_db_log({'parameters' => $in{'msgid'}, 'msg_id' => $in{'msgid'}, 'status' => 'success'}); $param->{'status'} = 'done'; return 1; } #################################################### # do_send_me #################################################### # Sends a web archive message to a # requesting user # It uses mail::mail_forward() to do it. # # IN : - # # OUT : 'arc' | 1 | undef # #################################################### sub do_send_me { &wwslog('info', 'do_send_me(%s, %s, %s, %s', $in{'list'}, $in{'yyyy'}, $in{'month'}, $in{'msgid'}); if (! $in{'msgid'} || $in{'msgid'} =~ /NO-ID-FOUND\.mhonarc\.org/) { &report::reject_report_web('intern','may_not_send_me',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','send_me: no message id found'); $param->{'status'} = 'no_msgid'; return undef; } ## my $arcpath = $wwsconf->{'arc_path'}.'/'.$list->get_list_id().'/'.$in{'yyyy'}.'-'.$in{'month'}; opendir ARC, "$arcpath/arctxt"; my $msgfile; foreach my $file (grep (!/\./,readdir ARC)) { &wwslog('debug','send_me: scanning %s', $file); next unless (open MAIL,"$arcpath/arctxt/$file") ; while () { last if /^$/ ; my $idline = $_; if (/^Message-id\s*:\s*$/i) { chomp $idline; $idline .= ; } if ($idline =~ /^Message-id\s*:\s*\s]+)>?\s?/i ) { my $id = $1; if ($id eq $in{'msgid'}) { $msgfile = $file ; } last ; } } close MAIL ; } if ($msgfile) { unless (open MSG, "$arcpath/arctxt/$msgfile") { $param->{'status'} = 'message_err'; &wwslog('info', 'do_send_me : could not read file %s',"$arcpath/arctxt/$msgfile"); } my $msg_string; while (){ $msg_string .= $_ ; } close MSG; unless (&mail::mail_forward($msg_string,&Conf::get_robot_conf($robot, 'sympa'),\$param->{'user'}{'email'},$robot)) { $param->{'status'} = 'message_err'; &wwslog('err',"do_send_me : impossible to send archive file to %s",$param->{'user'}{'email'}); return undef; } &wwslog('info', 'do_send_me message %s spooled for %s', "$arcpath/arctxt/$msgfile", $param->{'user'}{'email'} ); &report::notice_report_web('performed',{},$param->{'action'}); $in{'month'} = $in{'yyyy'}."-".$in{'month'}; return 'arc'; }else{ &wwslog('info', 'do_send_me : no file match msgid'); $param->{'status'} = 'not_found'; return undef; } return 1; } #################################################### # do_view_source #################################################### # Display message as text/plain in archives # # IN : - # # OUT : 'arc' | 1 | undef # #################################################### sub do_view_source { &wwslog('info', 'do_view_source(%s, %s, %s, %s', $in{'list'}, $in{'yyyy'}, $in{'month'}, $in{'msgid'}); ## Access control unless (defined &check_authz('do_arc', 'web_archive.access')) { $param->{'previous_action'} = 'arc'; $param->{'previous_list'} = $list->{'name'}; return undef; } if (! $in{'msgid'} || $in{'msgid'} =~ /NO-ID-FOUND\.mhonarc\.org/) { &report::reject_report_web('intern','may_not_view_source',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','view_source: no message id found'); $param->{'status'} = 'no_msgid'; return undef; } ## my $arcpath = $wwsconf->{'arc_path'}.'/'.$list->get_list_id().'/'.$in{'yyyy'}.'-'.$in{'month'}; opendir ARC, "$arcpath/arctxt"; my $msgfile; foreach my $file (grep (!/\./,readdir ARC)) { &wwslog('debug','view_source: scanning %s', $file); next unless (open MAIL,"$arcpath/arctxt/$file") ; while () { last if /^$/ ; my $idline = $_; if (/^Message-id\s*:\s*$/i) { chomp $idline; $idline .= ; } if ($idline =~ /^Message-id\s*:\s*\s]+)>?\s?/i ) { my $id = $1; if ($id eq $in{'msgid'}) { $msgfile = $file ; } last ; } } close MAIL ; } if ($msgfile) { unless (open MSG, "$arcpath/arctxt/$msgfile") { $param->{'status'} = 'message_err'; &wwslog('info', 'do_view_source : could not read file %s',"$arcpath/arctxt/$msgfile"); } $param->{'bypass'} = 'extreme'; print "Content-Type: text/plain\n\n"; while (){ print $_; } close MSG; }else{ &wwslog('info', 'do_view_source : no file match msgid'); $param->{'status'} = 'not_found'; return undef; } return 1; } ## Output an initial form to search in web archives sub do_arcsearch_form { &wwslog('info', 'do_arcsearch_form(%s)', $param->{'list'}); ## Access control return undef unless (defined &check_authz('do_arcsearch_form', 'web_archive.access')); my $search_base = $wwsconf->{'arc_path'}.'/'.$list->get_list_id(); opendir ARC, "$search_base"; foreach my $dir (sort {$b cmp $a} grep(!/^\./,readdir ARC)) { if ($dir =~ /^(\d{4})-(\d{2})$/) { push @{$param->{'yyyymm'}}, $dir; } } closedir ARC; $param->{'key_word'} = $in{'key_word'}; $param->{'archive_name'} = $in{'archive_name'}; return 1; } ## Search in web archives sub do_arcsearch { &wwslog('info', 'do_arcsearch(%s)', $param->{'list'}); ## Access control return undef unless (defined &check_authz('do_arcsearch', 'web_archive.access')); use Marc::Search; my $search = new Marc::Search; $search->search_base ($wwsconf->{'arc_path'} . '/' . $list->get_list_id()); $search->base_href (&Conf::get_robot_conf($robot, 'wwsympa_url') . '/arc/' . $param->{'list'}); $search->archive_name ($in{'archive_name'}); unless (defined($in{'directories'})) { # by default search in current month and in the previous non-empty one my $archive_name = $in{'archive_name'} || ''; $archive_name = POSIX::strftime '%Y-%m', localtime(time) unless $archive_name =~ /^\d{4}-\d{2}$/; my @directories = (); my $search_base = $wwsconf->{'arc_path'}.'/'.$list->get_list_id(); opendir ARC, "$search_base"; foreach my $dir (sort {$b cmp $a} grep(!/^\./,readdir ARC)) { if ($dir =~ /^(\d{4})-(\d{2})$/) { if ($archive_name) { push @directories, $dir if $dir le $archive_name; $archive_name = '' if $dir lt $archive_name; } push @{$param->{'yyyymm'}}, $dir; } } closedir ARC; $in{'directories'} = join "\0", @directories; } if (defined($in{'directories'})) { $search->directories ($in{'directories'}); foreach my $dir (split/\0/, $in{'directories'}) { push @{$param->{'directories'}}, $dir; } } if (defined $in{'previous'}) { $search->body_count ($in{'body_count'}); $search->date_count ($in{'date_count'}); $search->from_count ($in{'from_count'}); $search->subj_count ($in{'subj_count'}); $search->previous ($in{'previous'}); } ## User didn't enter any search terms if ($in{'key_word'} =~ /^\s*$/) { &report::reject_report_web('user','missing_arg',{'argument' => 'key_word'},$param->{'action'}); &wwslog('info','do_arcsearch: no search term'); return undef; } $param->{'key_word'} = $in{'key_word'}; $search->limit ($in{'limit'}); $search->age (1) if ($in{'age'} eq 'new'); $search->match (1) if (($in{'match'} eq 'partial') or ($in{'match'} eq '1')); $search->clean_words ($in{'key_word'}); my @clean_words = split(/\s+/, $in{'key_word'}); my @words = @clean_words; foreach my $w (@words) { $w = &tools::escape_regexp($w); $w =~ s,/,\\/,g; $w = '\b' . $w . '\b' if $in{'match'} eq 'exact'; } $search->words(\@words); $search->key_word (join('|',@words)); if ($in{'case'} eq 'off') { $search->case(1); $search->key_word ('(?i)' . $search->key_word); } if ($in{'how'} eq 'any') { $search->function2 ($search->match_any(@words)); $search->how ('any'); }elsif ($in{'how'} eq 'all') { $search->function1 ($search->body_match_all(@clean_words,@words)); $search->function2 ($search->match_all(@words)); $search->how ('all'); }else { $search->function2 ($search->match_this(@words)); $search->how ('phrase'); } $search->subj (defined($in{'subj'})); $search->from (defined($in{'from'})); $search->date (defined($in{'date'})); $search->body (defined($in{'body'})); $search->body (1) if ( not ($search->subj) and not ($search->from) and not ($search->body) and not ($search->date)); my $searched = $search->search; if (defined($search->error)) { &wwslog('info','do_arcsearch_search_error : %s', $search->error); } $search->searched($searched); if ($searched < $search->file_count) { $param->{'continue'} = 1; } foreach my $field ('list','archive_name','age','body','case','date','from','how','limit','match','subj') { $param->{$field} = $in{$field}; } $param->{'body_count'} = $search->body_count; $param->{'clean_words'} = $search->clean_words; $param->{'date_count'} = $search->date_count; $param->{'from_count'} = $search->from_count; $param->{'subj_count'} = $search->subj_count; $param->{'num'} = $search->file_count + 1; $param->{'searched'} = $search->searched; $param->{'res'} = $search->res; return 1; } ## Search message-id in web archives sub do_arcsearch_id { &wwslog('info', 'do_arcsearch_id(%s,%s,%s)', $param->{'list'},$in{'archive_name'},$in{'msgid'}); ## Access control return undef unless (defined &check_authz('do_arcsearch_id', 'web_archive.access')); use Marc::Search; my $search = new Marc::Search; $search->search_base ($wwsconf->{'arc_path'} . '/' . $list->get_list_id()); $search->base_href (&Conf::get_robot_conf($robot, 'wwsympa_url') . '/arc/' . $param->{'list'}); $search->archive_name ($in{'archive_name'}); # search in current month and in the previous none empty one my $search_base = $search->search_base; my $previous_active_dir ; opendir ARC, "$search_base"; foreach my $dir (sort {$b cmp $a} grep(!/^\./,readdir ARC)) { if (($dir =~ /^(\d{4})-(\d{2})$/) && ($dir lt $search->archive_name)) { $previous_active_dir = $dir; last; } } closedir ARC; $in{'archive_name'} = $search->archive_name."\0".$previous_active_dir ; $search->directories ($in{'archive_name'}); # $search->directories ($search->archive_name); ## User didn't enter any search terms if ($in{'msgid'} =~ /^\s*$/) { &report::reject_report_web('user','missing_arg',{'argument' => 'msgid'},$param->{'action'}); &wwslog('info','do_arcsearch_id: no search term'); return undef; } $in{'msgid'} = &tools::unescape_chars($in{'msgid'}); $param->{'msgid'} = $in{'msgid'}; $search->limit (1); $search->clean_words ($in{'msgid'}); my @words = split(/\s+/, $in{'msgid'}); foreach my $w (@words) { $w = &tools::escape_regexp($w); $w =~ s,/,\\/,g; } $search->words (\@words); $search->key_word (join('|',@words)); $search->function2 ($search->match_this(@words)); $search->id (1); my $searched = $search->search; if (defined($search->error)) { &wwslog('info','do_arcsearch_id_search_error : %s', $search->error); } $search->searched($searched); $param->{'res'} = $search->res; unless ($#{$param->{'res'}} >= 0) { &report::reject_report_web('intern_quiet','archive_not_found',{'msgid'=> $in{'msgid'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','No message found in archives matching Message-ID %s', $in{'msgid'}); return 'arc'; } $param->{'redirect_to'} = $param->{'res'}[0]{'file'}; return 1; } # get pendings lists sub do_get_pending_lists { &wwslog('info', 'get_pending_lists'); ## Checking families and other virtual hosts. &get_server_details(); my $all_lists = &List::get_lists($robot, { 'filter_query' => [ 'status' => 'pending' ] }); foreach my $list ( @$all_lists ) { $param->{'pending'}{$list->{'name'}}{'subject'} = $list->{'admin'}{'subject'}; $param->{'pending'}{$list->{'name'}}{'by'} = $list->{'admin'}{'creation'}{'email'}; $param->{'pending'}{$list->{'name'}}{'date'} = gettext_strftime "%d %b %y %H:%M", localtime($list->{'admin'}{'creation'}{'date_epoch'}); } return 1; } # get closed lists sub do_get_closed_lists { &wwslog('info', 'get_closed_lists'); ## Checking families and other virtual hosts. &get_server_details(); my $all_lists = &List::get_lists($robot, { 'filter_query' => [ 'status' => 'closed|family_closed' ] }); foreach my $list ( @$all_lists ) { $param->{'closed'}{$list->{'name'}}{'subject'} = $list->{'admin'}{'subject'}; $param->{'closed'}{$list->{'name'}}{'by'} = $list->{'admin'}{'creation'}{'email'}; } return 1; } # get ordered latest lists sub do_get_latest_lists { &wwslog('info', 'get_latest_lists'); ## Checking families and other virtual hosts. &get_server_details(); my @unordered_lists; my $all_lists = &List::get_lists($robot); foreach my $list ( @$all_lists ) { push @unordered_lists, {'name' => $list->{'name'}, 'subject' => $list->{'admin'}{'subject'}, 'creation_date' => $list->{'admin'}{'creation'}{'date_epoch'}}; } foreach my $l (sort {$b->{'creation_date'} <=> $a->{'creation_date'}} @unordered_lists) { push @{$param->{'latest_lists'}}, $l; $l->{'creation_date'} = gettext_strftime "%d %b %Y", localtime($l->{'creation_date'}); } return 1; } # get inactive lists sub do_get_inactive_lists { &wwslog('info', 'get_inactive_lists'); ## Checking families and other virtual hosts. &get_server_details(); my @unordered_lists; my $all_lists = &List::get_lists($robot); foreach my $list ( @$all_lists ) { ## skip closed lists if ($list->{'admin'}{'status'} eq 'closed') { next; } my $last_message = 0; if (open COUNT, $list->{'dir'}.'/msg_count') { while () { $last_message = $1 if (/^(\d+)\s/ && ($1 > $last_message)); } close COUNT; }else { &wwslog('info', 'Could not open file %s', $list->{'dir'}.'/msg_count'); } push @unordered_lists, { 'name' => $list->{'name'}, 'creator' =>$list->{'admin'}{'creation'}{'email'}, 'send_scenario' =>$list->{'admin'}{'send'}{'name'}, 'owners' => join(", ", map {$_->{'email'}} @{$list->{'admin'}{'owner'}}), 'editors' => join(", ", map {$_->{'email'}} @{$list->{'admin'}{'editor'}}), 'subscribers_count' => $list->get_total('nocache'), 'subject' => $list->{'admin'}{'subject'}, 'msg_count' => $list->get_msg_count(), 'last_message_epoch' => $last_message, 'last_message_date' => (gettext_strftime "%d %b %Y", localtime($last_message*86400)), 'creation_date_epoch' => $list->{'admin'}{'creation'}{'date_epoch'}, 'creation_date' => (gettext_strftime "%d %b %Y", localtime($list->{'admin'}{'creation'}{'date_epoch'})), }; } foreach my $l (sort {$a->{'last_message_epoch'} <=> $b->{'last_message_epoch'}} @unordered_lists) { push @{$param->{'inactive_lists'}}, $l; } return 1; } ## show a list parameters sub do_set_pending_list_request { &wwslog('info', 'set_pending_list(%s)',$in{'list'}); my $list_dir = $list->{'dir'}; $param->{'list_config'} = $list_dir.'/config'; if (-f $list_dir.'/info'){ $param->{'list_info_file_exists'} = 1; } $param->{'list_info'} = $list_dir.'/info'; $param->{'list_subject'} = $list->{'admin'}{'subject'}; $param->{'list_request_by'} = $list->{'admin'}{'creation'}{'email'}; $param->{'list_request_date'} = $list->{'admin'}{'creation'}{'date'}; $param->{'list_serial'} = $list->{'admin'}{'serial'}; $param->{'list_status'} = $list->{'admin'}{'status'}; &tt2::add_include_path($list->{'dir'}); return 1; } ## show a list parameters sub do_install_pending_list { &wwslog('info', 'do_install_pending_list(%s,%s,%s)',$in{'list'},$in{'status'},$in{'notify'}); unless ($in{'status'} && (($in{'status'} eq 'open') || ($in{'status'} eq 'closed'))) { &report::reject_report_web('user','missing_arg',{'argument' => 'status'},$param->{'action'}); &wwslog('info', 'Missing status parameter',); &web_db_log({'parameters' => "$in{'status'},$in{'notify'}", 'status' => 'error', 'error_type' => 'missing_parameter'}); return undef; } if ($list->{'admin'}{'status'} eq $in{'status'}) { &report::reject_report_web('user','didnt_change_anything',{},$param->{'action'}); &wwslog('info','view_pending_list: didn t change really the status, nothing to do'); &web_db_log({'parameters' => "$in{'status'},$in{'notify'}", 'status' => 'error', 'error_type' => 'didnt_change_anything'}); return undef ; } $list->{'admin'}{'status'} = $in{'status'}; # open TMP, ">/tmp/dump1"; # &tools::dump_var ($list->{'admin'}, 0, \*TMP); # close TMP; unless ($list->save_config($param->{'user'}{'email'})) { &report::reject_report_web('intern','cannot_save_config',{'listname'=> $list->{'name'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','_create_list: Cannot save config file'); &web_db_log({'parameters' => "$in{'status'},$in{'notify'}", 'status' => 'error', 'error_type' => 'internal'}); return undef; } # open TMP, ">/tmp/dump2"; # &tools::dump_var ($list->{'admin'}, 0, \*TMP); # close TMP; ## create the aliases if ($in{'status'} eq 'open') { my $aliases = &admin::install_aliases($list,$robot); if ($aliases == 1) { $param->{'auto_aliases'} = 1; }else { &report::reject_report_web('intern','failed_to_install_aliases',{'listname'=> $list->{'name'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','Failed to install list aliases'); } } ## Notify listmasters my @listmasters = split /\s*,\s*/, &Conf::get_robot_conf($robot, 'listmaster'); if ($in{'status'} eq 'open') { unless ($list->send_file('list_created', \@listmasters, $robot, {})) { &wwslog('notice',"Unable to send template 'list_created' to listmaster"); } }elsif ($in{'status'} eq 'closed') { unless ($list->send_file('list_rejected', \@listmasters, $robot, {})) { &wwslog('notice',"Unable to send template 'list_rejected' to listmaster"); } } if ($in{'notify'}) { my $owners = $list->get_owners(); foreach my $i (@{$owners}) { ## Notify all listowners, even if reception is nomail next unless ($i->{'email'}); if ($in{'status'} eq 'open') { unless ($list->send_file('list_created', $i->{'email'}, $robot,{})) { &wwslog('notice',"Unable to send template 'list_created' to $i->{'email'}"); } }elsif ($in{'status'} eq 'closed') { unless ($list->send_file('list_rejected', $i->{'email'}, $robot,{})) { &wwslog('notice',"Unable to send template 'list_rejected' to $i->{'email'}"); } } } } $param->{'status'} = $in{'status'}; $list = $param->{'list'} = $in{'list'} = undef; return 'get_pending_lists'; &web_db_log({'parameters' => "$in{'status'},$in{'notify'}", 'status' => 'success'}); return 1; } =pod =head2 sub do_create_list Creates a list using a list template =head3 Arguments =over =item * I =back =head3 Return =over =item * I<1>, if no problem is encountered =item * I, if anything goes wrong =item * I<'loginrequest'> if no user is logged in at the time the function is called. =back =head3 Calls =over =item * web_db_log =item * wwslog =item * admin::create_list_old =item * check_param_in =item * List::send_notify_to_listmaster =item * report::reject_report_web =back =cut ## create a liste using a list template. sub do_create_list { &wwslog('info', 'do_create_list(%s,%s,%s)',$in{'listname'},$in{'subject'},$in{'template'}); ## Check that all the needed arguments are present. ## This is checked here because it requires to return the incomplete form to the user foreach my $arg ('listname','subject','template','info','topics') { unless ($in{$arg}) { &report::reject_report_web('user','missing_arg',{'argument' => $arg},$param->{'action'}); &wwslog('info','do_create_list: missing param %s', $arg); &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return 'create_list_request'; } } ## Lowercase listname if required if ($in{'listname'} =~ /[A-Z]/) { $in{'listname'} = lc($in{'listname'}); &report::notice_report_web('listname_lowercased',{},$param->{'action'}); } my $result = &Scenario::request_action ('create_list', $param->{'auth_method'}, $robot, { 'sender' => $param->{'user'}{'email'}, 'candidate_listname' => $in{'listname'}, 'candidate_subject' => $in{'subject'}, 'candidate_template' => $in{'template'}, 'candidate_info' => $in{'info'}, 'candidate_topics' => $in{'topics'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'} } ); if (ref($result) eq 'HASH') { $param->{'create_action'} = $result->{'action'}; $param->{'reason'} = $result->{'reason'}; } &wwslog('info',"do_create_list, get action : $param->{'create_action'} "); ## If the action is forbidden, stop here. if ($param->{'create_action'} =~ /reject/) { &report::reject_report_web('auth',$param->{'reason'},{},$param->{'action'},$list); &wwslog('info','do_create_list: not allowed'); &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'error', 'error_type' => 'authorization'}); return 'home'; ## If the action is reserved to listmaster, note that it will have to be moderated }elsif ($param->{'create_action'} =~ /listmaster/i) { $param->{'status'} = 'pending' ; ## If the action is plainly authorized, note that it will be executed. }elsif ($param->{'create_action'} =~ /do_it/i) { $param->{'status'} = 'open' ; ## If the action hasn't an authorization status, stop here. }else{ &report::reject_report_web('intern','internal_scenario_error_create_list',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_create_list: internal error in scenario create_list'); &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'error', 'error_type' => 'internal'}); return 'create_list_request'; } ## 'other' topic means no topic $in{'topics'} = undef if ($in{'topics'} eq 'other'); ## Store creation parameters. my %owner; $owner{'email'} = $param->{'user'}{'email'}; $owner{'gecos'} = $param->{'user'}{'gecos'}; my $parameters; push @{$parameters->{'owner'}},\%owner; $parameters->{'listname'} = $in{'listname'}; $parameters->{'subject'} = $in{'subject'}; $parameters->{'creation_email'} = $param->{'user'}{'email'}; $parameters->{'lang'} = $param->{'lang'}; $parameters->{'status'} = $param->{'status'}; $parameters->{'topics'} = $in{'topics'}; $parameters->{'description'} = $in{'info'}; $parameters->{'custom_input'} = $in{'custom_input'}; ## create liste if (my $testlist = new List($in{'listname'},$robot)){ &report::reject_report_web('user','list_already_exists',{'new_listname'=> $in{'listname'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_create_list: requested list %s already exist (from %s)',$in{'listname'},$param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'error', 'error_type' => 'user'}); return 'create_list_request'; } my $resul = &admin::create_list_old($parameters,$in{'template'},$robot,"web"); unless(defined $resul) { &report::reject_report_web('intern','create_list',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_create_list: unable to create list %s for %s',$in{'listname'},$param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'error', 'error_type' => 'internal'}); return 'create_list_request'; } ## Create list object $in{'list'} = $in{'listname'}; &check_param_in(); if ($param->{'create_action'} =~ /do_it/i) { if ($resul->{'aliases'} == 1) { $param->{'auto_aliases'} = 1; }else { &report::reject_report_web('intern','failed_to_install_aliases',{'listname'=> $in{'listname'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','Failed to install list aliases'); } } ## notify listmaster my $list = List->new($in{'listname'}, $robot); unless (defined $list) { &wwslog('info',"failed to create list object for list '%s'",$in{'listname'}); &report::reject_report_web('intern','create_list',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); return 'create_list_request'; } if ($param->{'create_action'} =~ /notify/) { &wwslog('info','notify listmaster'); unless (&List::send_notify_to_listmaster('request_list_creation',$robot, {'list' => $list, 'email' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'request_list_creation' to listmaster"); } } &web_db_log({'parameters' => $in{'listname'}, 'list' => $in{'listname'}, 'status' => 'success'}); $in{'list'} = $resul->{'list'}{'name'}; &check_param_in(); $param->{'listname'} = $resul->{'list'}{'name'}; return 1; } =pod =head2 sub do_create_list_request Sends back the list creation edition form. =head3 Arguments =over =item * I =back =head3 Return =over =item * I<1>, if no problem is encountered =item * I, if anything goes wrong =item * I<'loginrequest'> if no user is logged in at the time the function is called. =back =head3 Calls =over =item * wwslog =item * _prepare_edit_form =item * List::request_action =item * List::load_topics =item * tools::get_list_list_tpl =item * report::reject_report_web =back =cut ## Return the creation form sub do_create_list_request { &wwslog('info', 'do_create_list_request()'); my $result = &Scenario::request_action('create_list',$param->{'auth_method'},$robot, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } $param->{'create_action'} = $r_action; ## Initialize the form ## When returning to the form foreach my $p ('listname','template','subject','topics','info') { $param->{'saved'}{$p} = $in{$p}; } if ($param->{'create_action'} =~ /reject/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info','do_create_list: not allowed'); return undef; } my %topics; unless (%topics = &List::load_topics($robot)) { &report::reject_report_web('intern','unable_to_load_list_of_topics',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); } $param->{'list_of_topics'} = \%topics; $param->{'list_of_topics'}{$in{'topics'}}{'selected'} = 1 if ($in{'topics'}); unless ($param->{'list_list_tpl'} = &tools::get_list_list_tpl($robot)) { &report::reject_report_web('intern','unable_to_load_create_list_templates',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); } $allow_absolute_path = 1; foreach my $template (keys %{$param->{'list_list_tpl'}}){ $param->{'tpl_count'} ++ ; } $param->{'list_list_tpl'}{$in{'template'}}{'selected'} = 1 if ($in{'template'}); return 1 ; } ## WWSympa Home-Page sub do_home { &wwslog('info', 'do_home'); return 1; } sub do_editsubscriber { &wwslog('info', 'do_editsubscriber(%s)', $in{'email'}); my $subscriber; $in{'email'} = &tools::unescape_chars($in{'email'}); unless($subscriber = $list->get_subscriber($in{'email'})) { &report::reject_report_web('intern','subscriber_not_found',{'email' => $in{'email'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_editsubscriber: subscriber %s not found', $in{'email'}); return undef; } $param->{'current_subscriber'} = $subscriber; $param->{'current_subscriber'}{'escaped_email'} = &tools::escape_html($param->{'current_subscriber'}{'email'}); $param->{'current_subscriber'}{'escaped_bounce_address'} = &tools::escape_html($param->{'current_subscriber'}{'bounce_address'}); $param->{'current_subscriber'}{'date'} = gettext_strftime "%d %b %Y", localtime($subscriber->{'date'}); $param->{'current_subscriber'}{'update_date'} = gettext_strftime "%d %b %Y", localtime($subscriber->{'update_date'}); $param->{'current_subscriber'}{'pictures_url'} = &tools::make_pictures_url('email' => $subscriber->{'email'}, 'list' => $list); ## Prefs $param->{'current_subscriber'}{'reception'} ||= 'mail'; $param->{'current_subscriber'}{'visibility'} ||= 'noconceal'; ## Get language from user_table my $user = &List::get_user_db($in{'email'}); $param->{'current_subscriber'}{'lang'} = &Language::GetLangName($user->{'lang'}); foreach my $m ($list->available_reception_mode) { $param->{'reception'}{$m}{'description'} = $list->get_option_title($m, 'reception'); if ($param->{'current_subscriber'}{'reception'} eq $m) { $param->{'reception'}{$m}{'selected'} = ' selected'; }else { $param->{'reception'}{$m}{'selected'} = ''; } } foreach my $m (qw(conceal noconceal)) { $param->{'visibility'}{$m}{'description'} = $list->get_option_title($m, 'visibility'); if ($param->{'current_subscriber'}{'visibility'} eq $m) { $param->{'visibility'}{$m}{'selected'} = ' selected'; }else { $param->{'visibility'}{$m}{'selected'} = ''; } } ## Bounces if ($subscriber->{'bounce'} =~ /^(\d+)\s+(\d+)\s+(\d+)(\s+(.*))?$/) { my @bounce = ($1, $2, $3, $5); $param->{'current_subscriber'}{'first_bounce'} = gettext_strftime "%d %b %Y", localtime($bounce[0]); $param->{'current_subscriber'}{'last_bounce'} = gettext_strftime "%d %b %Y", localtime($bounce[1]); $param->{'current_subscriber'}{'bounce_count'} = $bounce[2]; if ($bounce[3] =~ /^(\d+\.(\d+\.\d+))$/) { $subscriber->{'bounce_code'} = $1; $subscriber->{'bounce_status'} = $wwslib::bounce_status{$2}; } $param->{'previous_action'} = $in{'previous_action'}; } ## Additional DB fields if ($Conf{'db_additional_subscriber_fields'}) { my @additional_fields = split ',', $Conf{'db_additional_subscriber_fields'}; my %data; foreach my $field (@additional_fields) { ## Is the Database defined unless ($Conf{'db_name'}) { &wwslog('info', 'No db_name defined in configuration file'); return undef; } ## Check field type (enum or not) with MySQL $data{$field}{'type'} = &List::get_db_field_type('subscriber_table', $field); if ($data{$field}{'type'} =~ /^enum\((\S+)\)$/) { my @enum = split /,/,$1; foreach my $e (@enum) { $e =~ s/^\'([^\']+)\'$/$1/; $data{$field}{'enum'}{$e} = ''; } $data{$field}{'type'} = 'enum'; $data{$field}{'enum'}{$subscriber->{$field}} = 'selected="selected"' if (defined $subscriber->{$field}); }else { $data{$field}{'type'} = 'string'; $data{$field}{'value'} = $subscriber->{$field}; } } $param->{'additional_fields'} = \%data; } $param->{'previous_action'} = $in{'previous_action'}; return 1; } sub do_viewbounce { &wwslog('info', 'do_viewbounce(%s)', $in{'email'}); my $escaped_email = &tools::escape_chars($in{'email'}); $param->{'lastbounce_path'} = $list->get_bounce_dir().'/'.$escaped_email; unless (-r $param->{'lastbounce_path'}) { &report::reject_report_web('user','no_bounce_user',{'email'=>$in{'email'}},$param->{'action'},$list); &wwslog('info','do_viewbounce: no bounce %s', $param->{'lastbounce_path'}); return undef; } $allow_absolute_path = 1; return 1; } ## some help for listmaster and developpers sub do_scenario_test { &wwslog('info', 'do_scenario_test'); ## List available scenarii unless (opendir SCENARI, Sympa::Constants::DEFAULTDIR.'/scenari/'){ &report::reject_report_web('intern','cannot_open_dir',{'dir' => Sympa::Constants::DEFAULTDIR.'/scenari/'},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"do_scenario_test : unable to open %s/scenari", Sympa::Constants::DEFAULTDIR); return undef; } foreach my $scfile (readdir SCENARI) { if ($scfile =~ /^(\w+)\.(\w+)/ ) { $param->{'scenario'}{$1}{'defined'}=1 ; } } closedir SCENARI; my $all_lists = &List::get_lists('*'); foreach my $list ( @$all_lists ) { $param->{'listname'}{$list->{'name'}}{'defined'}=1 ; } foreach my $a ('smtp','md5','smime') { #$param->{'auth_method'}{$a}{'define'}=1 ; $param->{'authmethod'}{$a}{'defined'}=1 ; } $param->{'scenario'}{$in{'scenario'}}{'selected'} = 'selected="selected"' if $in{'scenario'}; $param->{'listname'}{$in{'listname'}}{'selected'} = 'selected="selected"' if $in{'listname'}; $param->{'authmethod'}{$in{'auth_method'}}{'selected'} = 'selected="selected"' if $in{'auth_method'}; $param->{'email'} = $in{'email'}; if ($in{'scenario'}) { my $operation = $in{'scenario'}; &wwslog('debug3', 'do_scenario_test: perform scenario_test'); my $result = &Scenario::request_action ($operation,$in{'auth_method'},$robot, {'listname' => $in{'listname'}, 'sender' => $in{'sender'}, 'email' => $in{'email'}, 'remote_host' => $in{'remote_host'}, 'remote_addr' => $in{'remote_addr'}},'debug'); if (ref($result) eq 'HASH'){ $param->{'scenario_action'} = $result->{'action'}; $param->{'scenario_condition'} = $result->{'condition'}; $param->{'scenario_auth_method'} = $result->{'auth_method'}; $param->{'scenario_reason'} = $result->{'reason'}; } } return 1; } ## Bouncing addresses review sub do_reviewbouncing { &wwslog('info', 'do_reviewbouncing(%s)', $in{'page'}); my $size = $in{'size'} || $wwsconf->{'review_page_size'}; ## Owner $param->{'page'} = $in{'page'} || 1; if ($size eq 'all') { $param->{'total_page'} = $param->{'bounce_total'}; }else { $param->{'total_page'} = int ( $param->{'bounce_total'} / $size); $param->{'total_page'} ++ if ($param->{'bounce_total'} % $size); } if ($param->{'total_page'} > 0 and ($param->{'page'} > $param->{'total_page'})) { &report::reject_report_web('user','no_page',{'page' => $param->{'page'}},$param->{'action'}); &wwslog('info','do_reviewbouncing: no page %d', $param->{'page'}); return 'admin'; } my @users; ## Members list for (my $i = $list->get_first_bouncing_user(); $i; $i = $list->get_next_bouncing_user()) { $i->{'bounce'} =~ /^(\d+)\s+(\d+)\s+(\d+)(\s+(.*))?$/; $i->{'first_bounce'} = $1; $i->{'last_bounce'} = $2; $i->{'bounce_count'} = $3; if ($5 =~ /^(\d+)\.\d+\.\d+$/) { $i->{'bounce_class'} = $1; } ## Define color in function of bounce_score if ($i->{'bounce_score'} <= $list->{'admin'}{'bouncers_level1'}{'rate'}) { $i->{'bounce_level'} = 0; }elsif ($i->{'bounce_score'} <= $list->{'admin'}{'bouncers_level2'}{'rate'}){ $i->{'bounce_level'} = 1; }else{ $i->{'bounce_level'} = 2; } push @users, $i; } my $record; foreach my $i (sort {($b->{'bounce_score'} <=> $a->{'bounce_score'}) || ($b->{'last_bounce'} <=> $a->{'last_bounce'}) || ($b->{'bounce_class'} <=> $a->{'bounce_class'}) } @users) { $record++; if (($size ne 'all') && ($record > ( $size * ($param->{'page'} ) ) ) ) { $param->{'next_page'} = $param->{'page'} + 1; last; } next if (($size ne 'all') && ($record <= ( ($param->{'page'} - 1) * $size))); $i->{'first_bounce'} = gettext_strftime "%d %b %Y", localtime($i->{'first_bounce'}); $i->{'last_bounce'} = gettext_strftime "%d %b %Y", localtime($i->{'last_bounce'}); ## Escape some weird chars $i->{'escaped_email'} = &tools::escape_chars($i->{'email'}); push @{$param->{'members'}}, $i; } if ($param->{'page'} > 1) { $param->{'prev_page'} = $param->{'page'} - 1; } $param->{'size'} = $size; return 1; } sub do_resetbounce { &wwslog('info', 'do_resetbounce()'); $in{'email'} = &tools::unescape_chars($in{'email'}); my @emails = split /\0/, $in{'email'}; foreach my $email (@emails) { my $escaped_email = &tools::escape_chars($email); unless ( $list->is_user($email) ) { &report::reject_report_web('user','not_subscriber',{'email'=> $email},$param->{'action'},$list); &wwslog('info','do_del: %s not subscribed', $email); &web_db_log({'status' => 'error', 'error_type' => 'not_subscriber'}); return undef; } unless( $list->update_user($email, {'bounce' => 'NULL', 'update_date' => time, 'score' => 0})) { &report::reject_report_web('intern','update_subscriber_db_failed',{'sub'=> $email},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_resetbounce: failed update database for %s', $email); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } my $bounce_dir = $list->get_bounce_dir(); unless (unlink $bounce_dir.'/'.$escaped_email) { &wwslog('info','do_resetbounce: failed deleting %s', $bounce_dir.'/'.$escaped_email); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); } &wwslog('info','do_resetbounce: bounces for %s reset ', $email); &web_db_log({'status' => 'success'}); } return $in{'previous_action'} || 'review'; } ## Rebuild an archive using arctxt/ sub do_rebuildarc { &wwslog('info', 'do_rebuildarc(%s, %s)', $param->{'list'}, $in{'month'}); my $file = $Conf{'queueoutgoing'}.'/.rebuild.'.$list->get_list_id(); unless (open REBUILD, ">$file") { &report::reject_report_web('intern','cannot_open_file',{'file' => $file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_rebuildarc: cannot create %s', $file); &web_db_log({'parameters' => $in{'month'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &wwslog('info', 'File: %s', $file); print REBUILD ' '; close REBUILD; &report::notice_report_web('performed_soon',{},$param->{'action'}); &web_db_log({'parameters' => $in{'month'}, 'status' => 'success'}); return 'admin'; } ## Rebuild all archives using arctxt/ sub do_rebuildallarc { &wwslog('info', 'do_rebuildallarc'); my $all_lists = &List::get_lists($robot); foreach my $list ( @$all_lists ) { next unless (defined $list->{'admin'}{'web_archive'}); my $file = $Conf{'queueoutgoing'}.'/.rebuild.'.$list->get_list_id(); unless (open REBUILD, ">$file") { &report::reject_report_web('intern','cannot_open_file',{'file' => $file},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_rebuildarc: cannot create %s', $file); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } &wwslog('info', 'File: %s', $file); print REBUILD ' '; close REBUILD; } &report::notice_report_web('performed_soon',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'serveradmin'; } ## Search among lists sub do_edit_attributes { &wwslog('info', 'do_edit_attributes(%s)', $in{'filter'}); return 1; } ## Search among lists sub do_search_list { &wwslog('info', 'do_search_list(%s)', $in{'filter'}); unless ($in{'filter'}) { &report::reject_report_web('user','no_filter',{},$param->{'action'}); &wwslog('info','do_search_list: no filter'); return undef; }elsif ($in{'filter'} =~ /[<>\\\*\$]/) { &report::reject_report_web('user','syntax_errors',{'params' => 'filter'},$param->{'action'}); &wwslog('err','do_search_list: syntax error'); return undef; } ## Regexp $param->{'filter'} = $in{'filter'}; $param->{'regexp'} = &tools::escape_regexp($param->{'filter'}); ## Members list my $record = 0; my $regexp = $param->{'regexp'}; my $statement; if ($Conf::Conf{'db_list_cache'} eq 'on') { if ($Conf::Conf{'db_type'} eq 'mysql') { $statement = sprintf "name_list RLIKE '%s' OR subject_list RLIKE '%s'", $regexp, $regexp; }elsif($Conf::Conf{'db_type'} eq 'Oracle') { $statement = sprintf "name_list REGEXP_LIKE '%s' OR subject_list REGEXP_LIKE '%s'", $regexp, $regexp; }elsif($Conf::Conf{'db_type'} eq 'Pg') { $statement = sprintf "name_list ~ '%s' OR subject_list ~ '%s'", $regexp, $regexp; }elsif($Conf::Conf{'db_type'} eq 'Sybase') { $statement = sprintf "name_list REGEXP '%s' OR subject_list REGEXP '%s'", $regexp, $regexp; } } my $all_lists; if ($statement) { my @lists = &List::get_lists_db($statement) || undef; $all_lists = &List::get_lists($robot, undef, @lists); }else{ $all_lists = &List::get_lists($robot); } foreach my $list ( @$all_lists ) { my $is_admin; ## Search filter my $regtest = eval { (($list->{'name'} !~ /$param->{'regexp'}/i) && ($list->{'admin'}{'subject'} !~ /$param->{'regexp'}/i)) }; unless (defined($regtest)) { &report::reject_report_web('user','syntax_errors',{'params' => 'filter'},$param->{'action'}); &wwslog('err','do_search_list: syntax error'); return undef; } next if $regtest; my $result = $list->check_list_authz('visibility',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; $r_action = $result->{'action'} if (ref($result) eq 'HASH'); next unless ($r_action eq 'do_it'); if ($param->{'user'}{'email'} && ($list->am_i('owner',$param->{'user'}{'email'}) || $list->am_i('editor',$param->{'user'}{'email'})) ) { $is_admin = 1; } $record++; $param->{'which'}{$list->{'name'}} = {'host' => $list->{'admin'}{'host'}, 'subject' => $list->{'admin'}{'subject'}, 'admin' => $is_admin, 'export' => 'no'}; } $param->{'occurrence'} = $record; foreach my $listname (sort keys %{$param->{'which'}}) { if ($listname =~ /^([a-z])/){ push @{$param->{'orderedlist'}{$1}}, $listname ; }else{ push @{$param->{'orderedlist'}{'others'}}, $listname ; } } return 1; } sub do_edit_list { &wwslog('info', 'do_edit_list()'); ## Check if the list belong to a family. my $family; if (defined $list->{'admin'}{'family_name'}) { unless ($family = $list->get_family()) { &report::reject_report_web('intern','unable_get_family',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_edit_list : impossible to get list %s\'s family',$list->{'name'}); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } } ## This hash will contain all the data gathered from the edit list form. ## The keys are the parameter names. ## The values are either the parameter value or an array containing this value if this is a multiple values parameter. ## The value can be a scalar or a hash. my $new_admin = {}; ## This hash contains the names of all the parameters sent by the form to the FCGI. ## The keys are the parameters name, the value is always 1. ## Used only to parse the data. my $edited_param = {}; ## Parse all the data sent from the web interface to the FCGI. ## Fills the $new_admin and $edited_param hashes. foreach my $key (sort keys %in) { next unless ($key =~ /^(single_param|multiple_param)\.(\S+)$/); $key =~ /^(single_param|multiple_param)\.(\S+)$/; my ($type, $name) = ($1, $2); ## Tag parameter as present in the form if ($name =~ /^([^\.]+)(\.)/ || $name =~ /^([^\.]+)$/) { $edited_param->{$1} = 1; } ## Parameter value my $value = $in{$key}; next if ($value =~ /^\s*$/); ## If the parameter is a multiple values parameter, store the values into an array. if ($type eq 'multiple_param') { my @values = split /\0/, $value; $value = \@values; } my @token = split (/\./, $name); ## make it an entry in $new_admin my $var = &_shift_var(0, $new_admin, @token); $$var = $value; } ## Check that the serial number sent by the form is the same as the one we expect. ## Avoid modifying a list previously modified by another way. unless ($list->{'admin'}{'serial'} == $in{'serial'}) { &report::reject_report_web('user','config_changed',{'email' => $list->{'admin'}{'update'}{'email'}},$param->{'action'},$list); &wwslog('info','do_edit_list: Config file has been modified(%d => %d) by %s. Cannot apply changes', $in{'single_param.serial'}, $list->{'admin'}{'serial'}, $list->{'admin'}{'update'}{'email'}); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Check changes & check syntax ## %changed stores the names of the parameters whose values differs from the value in the config file. ## %stores the name of parameter for which values have been deleted. The keys are the parameter name, the values are the index of the deleted value. ## @syntax_error stores the list of parameters for which syntax errors wre founs while evaluating the data sent by the form. my (%changed, %delete); my @syntax_error; ## Check family constraints. ## %check_family is a hash whose keys are a parameter name and whose values are the constraints ## defined for this parameter. my %check_family; ## Getting changes about owners or editors ## If changes occurred in the owner or editor definition, these scalars are set to 1. my $owner_update = 0; my $editor_update = 0; ###################################################################### ## Start of the loop parsing the data sent by the edition form. ## ###################################################################### foreach my $pname (sort List::by_order keys %{$edited_param}) { ## $p will contain the values of the current parameter in the previous list config ## $new_p will contain the values sent by the form for the current parameter. my ($p, $new_p); ## Check privileges first next unless ($list->may_edit($pname,$param->{'user'}{'email'}) eq 'write'); ## If the list belongs to a family, gather all the constraints for each edited parameter. if (ref($family) eq 'Family') { if ((ref($::pinfo{$pname}{'format'}) ne 'HASH') && (!ref($pname))) { # simple parameter my $constraint = $family->get_param_constraint($pname); if (ref($constraint) eq 'HASH') { # controlled parameter $check_family{$pname} = $constraint; } elsif ($constraint ne '0') { # fixed parameter (free : no control) next; } } } ## Skip the obsolete parameters. next if $pinfo->{$pname}{'obsolete'}; ## $to_index value will correspond to the number of not empty parameters sent by the form. my $to_index; ####### Validation, step 1: remove empty entries ########### ## If the parameter can have multiple values... if ($pinfo->{$pname}{'occurrence'} =~ /n$/) { ## They were either entries removed by the user or empty entries added by wwsympa ## The loop is going backward so we can remove empty entries my @all = 0..$#{$new_admin->{$pname}}; foreach my $i (reverse @all ) { ## If the parameter has a complex structure if (ref ($pinfo->{$pname}{'format'}) eq 'HASH') { ## Check each component of the complex parameter. foreach my $key (keys %{$pinfo->{$pname}{'format'}}) { ## As soon as a required component is found missing, the whole parameter instance is removed. if ($pinfo->{$pname}{'format'}{$key}{'occurrence'} =~ /^1/ && $new_admin->{$pname}[$i]{$key} =~ /^\s*$/ ) { splice(@{$new_admin->{$pname}}, $i, 1); last; } } ## Else if the parameter has only a scalar value }else { ## Remove if empty if ($new_admin->{$pname}[$i] =~ /^\s*$/) { splice(@{$new_admin->{$pname}}, $i, 1); next; } } } ## Now, %new_admin contains only entries for which all the mandatory values are accounted for. ## $last_index corresponds to the number of remaining instances of this param sent by the form. my $last_index = $#{$new_admin->{$pname}}; ## If a mandatory parameter is missing, issue an error and stop here. if ($pinfo->{$pname}{'occurrence'} =~ /^1/ && !($last_index >= 0)){ delete $new_admin->{$pname}; &wwslog('err','Error: Parameter %s is mandatory.', $pname); &report::reject_report_web('user','mandatory_parameter',{'p_name' => $pname},$param->{'action'},$list); &web_db_log({'status' => 'error', 'error_type' => 'syntax_errors'}); next; } ## If there are less entries in the config file than were sent by the form, ## $to_index must correspond to the number of entries sent. if ($#{$list->{'admin'}{$pname}} < $last_index) { $to_index = $last_index; ## Otherwise, $to_index must correspond to the number of entries in the config file. }else { $to_index = $#{$list->{'admin'}{$pname}}; } $p = $list->{'admin'}{$pname}; $new_p = $new_admin->{$pname}; ## If the parameter can't have multiple values... }else { ## If the parameter has a complex structure if (ref ($pinfo->{$pname}{'format'}) eq 'HASH') { ## Check each component of the complex parameter. foreach my $key (keys %{$pinfo->{$pname}{'format'}}) { ## Remove the full record if a component is emtpy and required if ($pinfo->{$pname}{'format'}{$key}{'occurrence'} =~ /^1/ && $new_admin->{$pname}{$key} =~ /^\s*$/ ) { delete $new_admin->{$pname}; last; } } ## If the parameter contains a simple scalar value. }else { ## Remove if empty if ($new_admin->{$pname} =~ /^\s*$/) { delete $new_admin->{$pname}; } } $p = [$list->{'admin'}{$pname}]; $new_p = [$new_admin->{$pname}]; } ####### Validation, step 2: - check if the parameter was modified. ########### ####### - check that the new values have the right syntax. ########### ####### Note: this step is performed for each occurrence of the parameter. ########### foreach my $i (0..$to_index) { unless (defined $new_p->[$i]) { push @{$delete{$pname}}, $i; $changed{$pname} = 1; next; } ## If the parameter corresponds to a scenario or a task, mark it as changed if its name was changed. ## Example: 'subscribe' if ($pinfo->{$pname}{'scenario'} || $pinfo->{$pname}{'task'} ) { if ($p->[$i]{'name'} ne $new_p->[$i]{'name'}) { $changed{$pname} = 1; next; } ## If the parameter has a complex structure, we need to check all its components. ## Example: 'owner' }elsif (ref ($pinfo->{$pname}{'format'}) eq 'HASH') { ## Check each parameter component. ## Example: 'owner->email' foreach my $key (keys %{$pinfo->{$pname}{'format'}}) { ## Check that the user is allowed to edit this parameter component. next unless ($list->may_edit("$pname.$key",$param->{'user'}{'email'}) eq 'write'); ## If the list belongs to a family, check the possible constraints on this parameter component. if (ref($family) eq 'Family') { ## Test constraints only if the parameter component is not a complex structure. if (!ref($key)) { my $constraint = $family->get_param_constraint("$pname.$key"); if (ref($constraint) eq 'HASH') { # controlled parameter $check_family{$pname}{$key} = $constraint; } elsif ($constraint ne '0') { # fixed parameter next; # Go to the next parameter component. } } } ## If the parameter component corresponds to a task or a scenario, mark it as changed if its name was changed. if ($pinfo->{$pname}{'format'}{$key}{'scenario'} || $pinfo->{$pname}{'format'}{$key}{'task'} ) { if ($p->[$i]{$key}{'name'} ne $new_p->[$i]{$key}{'name'}) { $changed{$pname} = 1; next; # Mark as changed and go to the next parameter component. } ## If the parameter component doesn't correspond to a task or a scenario, we must check its content. }else{ ## Parameter component check, case 1: this parameter component can have multiple occurrence. ## Example: 'digest->days' if ($pinfo->{$pname}{'format'}{$key}{'occurrence'} =~ /n$/) { ## If the new value differs from the previous value, mark as changed and go to the next parameter component. if ($#{$p->[$i]{$key}} != $#{$new_p->[$i]{$key}}) { $changed{$pname} = 1; next; } ## For each occurrence of this parameter component, check value foreach my $index (0..$#{$p->[$i]{$key}}) { my $format = $pinfo->{$pname}{'format'}{$key}{'format'}; ## If the format has a complex structure, it is the description of a file format. if (ref ($format)) { $format = $pinfo->{$pname}{'format'}{$key}{'file_format'}; } ## If this occurrence of the parameter component differs from the corresponding one in the config ## check the syntax and mark as changed. if ($p->[$i]{$key}[$index] ne $new_p->[$i]{$key}[$index]) { if (defined($new_p->[$i]{$key}[$index]) && $new_p->[$i]{$key}[$index] !~ /^$format$/i) { &wwslog('err', "Syntax error : $pname/$i/$key/$index = $new_p->[$i]{$key}[$index]"); push @syntax_error, $pname; } $changed{$pname} = 1; next; # Mark as changed and go to the next parameter component. } } ## Parameter component check, case 2: this component is limited to one occurence. ## Example: 'owner->email' }else { ## If the parameter component value differs from the corresponding one in the config, go on. if ($p->[$i]{$key} ne $new_p->[$i]{$key}) { my $format = $pinfo->{$pname}{'format'}{$key}{'format'}; ## If the format has a complex structure, it is the description of a file format. if (ref ($format)) { $format = $pinfo->{$pname}{'format'}{$key}{'file_format'}; } ## Check the syntax and mark as changed if the syntax is correct. if (defined($new_p->[$i]{$key}) && $new_p->[$i]{$key} !~ /^$format$/i) { &wwslog('err', "Syntax error : $pname/$i/$key = $new_p->[$i]{$key}"); push @syntax_error, $pname; } $changed{$pname} = 1; next; # Mark as changed and go to the next parameter component. } } } } ## If the parameter has just a scalar value, just check its value. ## Example: 'max_size' }else { ## If the value differs from the one in the config file, mark parameter as changed if the syntax is correct. if ($p->[$i] ne $new_p->[$i]) { unless ($new_p->[$i] =~ /^$pinfo->{$pname}{'file_format'}$/) { &wwslog('err', "Syntax error : $pname/$i = $new_p->[$i]"); push @syntax_error, $pname; } $changed{$pname} = 1; } } } } ###################################################################### ## Validation of the form finished. Start of valid data treatments ## ###################################################################### ## Error if no parameter was edited unless (keys %changed) { &report::reject_report_web('user','no_parameter_edited',{},$param->{'action'},$list); &wwslog('info','No parameter was edited by user'); return 'edit_list_request'; } ## Syntax errors if ($#syntax_error > -1) { &report::reject_report_web('user','syntax_errors',{'params' => 'filter'},$param->{'action'},$list); &wwslog('info','do_edit_list: Syntax errors for parameters %s', join(',', @syntax_error)); &web_db_log({'status' => 'error', 'error_type' => 'syntax_errors'}); return undef; } ## Checking no topic named "other" foreach my $msg_topic (@{$new_admin->{'msg_topic'}}) { if ($msg_topic->{'name'} =~ /^other$/i) { $msg_topic->{'name'} = undef; $msg_topic->{'title'} = undef; &report::reject_report_web('user','topic_other',{},$param->{'action'},$list); &wwslog('notice',"do_edit_list: topic other is a reserved word"); &web_db_log({'status' => 'error', 'error_type' => 'syntax_errors'}); return undef; } } ## For changed msg_topic.name if (defined $new_admin->{'msg_topic'} && $list->modifying_msg_topic_for_subscribers($new_admin->{'msg_topic'})) { &report::notice_report_web('subscribers_noticed_deleted_topics',{},$param->{'action'}); } ## Checking that list owner address is not set to one of the special ## addresses: if (exists $changed{'owner'}) { my $listname = $list->{'name'}; my $host = $list->{'admin'}{'host'}; my @special = (); push @special, map { sprintf '%s%s@%s', $listname, $_, $host } (Conf::get_robot_conf($robot, 'return_path_suffix'), '-request', '-editor', '-subscribe', '-unsubscribe'); push @special, map { sprintf '%s-%s@%s', $listname, lc $_, $host } split /[,\s]+/, Conf::get_robot_conf($robot, 'list_check_suffixes'); my $bounce_email_re = quotemeta(Conf::get_robot_conf($robot, 'bounce_email_prefix')) . "\\\+.*\\\@" . quotemeta($list->{'domain'}); foreach my $owner (@{$new_admin->{'owner'}}) { my $owner_email = lc $owner->{'email'}; if (grep { $owner_email eq $_ } @special or $owner_email =~ /^$bounce_email_re$/) { ## generate an error and return &report::reject_report_web('user', 'incorrect_email', {'email' => $owner->{'email'}}, $param->{'action'}, $list); &wwslog('info', 'do_edit_list: Reserved email address %s', $owner->{'email'}); &web_db_log( { 'status' => 'error', 'error_type' => 'incorrect_email', 'parameters' => $owner->{'email'} }); return undef; } } } ## Delete selected params foreach my $p (keys %delete) { if (($p eq 'owner') || ($p eq 'owner_include')) { $owner_update = 1; } if (($p eq 'editor') || ($p eq 'editor_include')) { $editor_update = 1; } ## Delete ALL entries unless (ref ($delete{$p})) { undef $new_admin->{$p}; next; } ## Delete selected entries foreach my $k (reverse @{$delete{$p}}) { splice @{$new_admin->{$p}}, $k, 1; } if (defined $check_family{$p}) { # $p is family controlled if ($#{$new_admin->{$p}} < 0) { &report::reject_report_web('user','p_family_controlled',{'param' => $p},$param->{'action'},$list); &wwslog('info','do_edit_list : parameter %s must have values (family context)',$p); &web_db_log({'status' => 'error', 'error_type' => 'missing_parameter'}); return undef; } } } # updating config_changes for deleted parameters if (ref($family)) { my @array_delete = keys %delete; unless ($list->update_config_changes('param',\@array_delete)) { &report::reject_report_web('intern','update_config_changes',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_edit_list: cannot write in config_changes for deleted parameters from list %s', $list->{'name'}); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } } ## Update config in memory my $data_source_updated; foreach my $parameter (keys %changed) { my $pname; if ($parameter =~ /^([\w-]+)\.([\w-]+)$/) { $pname = $1; } else{ $pname = $parameter; } ## If new owners/editors have been added, then notify them foreach my $admin_type ('owner','editor') { my (%previous_emails, %new_emails); ## Check previous entries foreach my $entry (@{$list->{'admin'}{$admin_type}}) { $previous_emails{$entry->{'email'}} = 1; } ## Compare with new entries foreach my $entry (@{$new_admin->{$admin_type}}) { unless ($previous_emails{$entry->{'email'}}) { ## Notify the new list owner/editor $list->send_notify_to_user('added_as_listadmin', $entry->{'email'},{'admin_type' => $admin_type, 'delegator' => $param->{'user'}{'email'}}); &report::notice_report_web('user_notified',{'notified_user' => $entry->{'email'}},$param->{'action'}); } } } if (defined $check_family{$pname}) { # $pname is CONTROLLED &_check_new_values(\%check_family,$pname,$new_admin); } ## If datasource config changed if ($pname =~ /^(include_.*|ttl)$/) { $data_source_updated = 1; } $list->{'admin'}{$pname} = $new_admin->{$pname}; if (defined $new_admin->{$pname} || $pinfo->{$pname}{'internal'}) { delete $list->{'admin'}{'defaults'}{$pname}; }else { $list->{'admin'}{'defaults'}{$pname} = 1; } if (($pname eq 'owner') || ($pname eq 'owner_include')){ $owner_update = 1; } if (($pname eq 'editor') || ($pname eq 'editor_include')){ $editor_update = 1; } # updating config_changes for changed parameters if (ref($family)) { my @array_changed = keys %changed; unless ($list->update_config_changes('param',\@array_changed)) { &report::reject_report_web('intern','update_config_changes',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_edit_file: cannot write in config_changes for changed parameters from list %s', $list->{'name'}); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } } } ## Save config file unless ($list->save_config($param->{'user'}{'email'})) { &report::reject_report_web('intern','cannot_save_config',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_edit_list: Cannot save config file'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Reload config to clean some empty entries in $list->{'admin'} $list = new List $list->{'name'}, $robot, {'reload_config' => 1}; unless (defined $list) { &report::reject_report_web('intern','list_reload',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_edit_list: error in list reloading'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } ## If list has included data sources, update them and delete sync_include task. if ($data_source_updated) { if ($list->on_the_fly_sync_include('use_ttl'=>0)) { &report::notice_report_web('subscribers_updated',{},$param->{'action'}); }else { &report::reject_report_web('intern','sync_include_failed',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); } } ## call sync_include_admin if there are changes about owners or editors and we're in mode include2 unless ($list->sync_include_admin()) { &report::reject_report_web('intern', 'sync_include_admin_failed', {}, $param->{'action'}, $list, $param->{'user'}{'email'}, $robot); &wwslog('info','do_edit_list: sync_include_admin() failed'); &web_db_log({'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Tag changed parameters foreach my $pname (keys %changed) { $::changed_params{$pname} = 1; } ## Save stats $list->savestats(); # print "Content-type: text/plain\n\n"; # &tools::dump_var($list->{'admin'}{'msg_topic'},0); # &tools::dump_var($param->{'param'},0); &report::notice_report_web('list_config_updated',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'edit_list_request'; } ## Shift tokens to get a reference to the desired ## entry in $var (recursive) sub _shift_var { my ($i, $var, @tokens) = @_; &wwslog('debug3','shift_var(%s,%s,%s)',$i, $var, join('.',@tokens)); my $newvar; my $token = shift @tokens; if ($token =~ /^\d+$/) { return \$var->[$token] if ($#tokens == -1); if ($tokens[0] =~ /^\d+$/) { unless (ref $var->[$token]) { $var->[$token] = []; } $newvar = $var->[$token]; }else { unless (ref $var->[$token]) { $var->[$token] = {}; } $newvar = $var->[$token]; } }else { return \$var->{$token} if ($#tokens == -1); if ($tokens[0] =~ /^\d+$/) { unless (ref $var->{$token}) { $var->{$token} = []; } $newvar = $var->{$token}; }else { unless (ref $var->{$token}) { $var->{$token} = {}; } $newvar = $var->{$token}; } } if ($#tokens > -1) { $i++; return &_shift_var($i, $newvar, @tokens); } return $newvar; } =pod =head2 sub do_edit_list_request Sends back the list config edition form. =head3 Arguments =over =item * I =back =head3 Return =over =item * I<1>, if no problem is encountered =item * I, if anything goes wrong =item * I<'loginrequest'> if no user is logged in at the time the function is called. =back =head3 Calls =over =item * wwslog =item * _prepare_edit_form =item * report::reject_report_web =back =cut ## Send back the list config edition form sub do_edit_list_request { &wwslog('info', 'do_edit_list_request(%s)', $in{'group'}); if ($in{'group'}) { $param->{'group'} = $in{'group'}; &_prepare_edit_form ($list); } # print "Content-type: text/plain\n\n"; # &tools::dump_var(\%pinfo,0); # &tools::dump_var($list->{'admin'},0); # &tools::dump_var($param->{'param'},0); $param->{'serial'} = $list->{'admin'}{'serial'}; return 1; } sub _check_new_values { my $check_family = shift; my $pname = shift; my $new_admin = shift; &wwslog('debug3', '_check_new_values(%s)',$pname); my $uncompellable_param = &Family::get_uncompellable_param(); if (ref($::pinfo{$pname}{'format'}) eq 'HASH') { #composed parameter foreach my $key (keys %{$check_family->{$pname}}) { my $constraint = $check_family->{$pname}{$key}; my $values = &List::_get_param_value_anywhere($new_admin,"$pname.$key"); my $nb_for = 0; # exception for uncompellable param foreach my $p (keys %{$uncompellable_param}) { if (($pname eq $p) && !($uncompellable_param->{$p})) { return 1; } if (($pname eq $p) && ($key eq $uncompellable_param->{$p})) { return 1; } } foreach my $p_val (@{$values}) { #each element value $nb_for++; if (ref($p_val) eq 'ARRAY') { # multiple values foreach my $p (@{$p_val}) { if (!($constraint->{$p}) && (($nb_for == 1) || ($p ne ''))) { &report::reject_report_web('user','p_family_wrong',{'param' => $pname,'val'=> $p},$param->{'action'}); &wwslog('info', 'do_edit_list : parameter %s has got wrong value : %s (family context)', $pname, $p); return undef; } } } else { # single value if (!($constraint->{$p_val}) && (($nb_for == 1) || ($p_val ne ''))) { &report::reject_report_web('user','p_family_wrong',{'param' => $pname,'val'=> $p_val},$param->{'action'}); &wwslog('info', 'do_edit_list : parameter %s has got wrong value : %s (family context)', $pname, $p_val); return undef; } } } } } else { #simple parameter # exception for uncompellable param foreach my $p (keys %{$uncompellable_param}) { if ($pname eq $p) { return 1; } } my $constraint = $check_family->{$pname}; my $values = &List::_get_param_value_anywhere($new_admin,$pname); my $nb_for = 0; foreach my $p_val (@{$values}) { #each element value $nb_for++; if (ref($p_val) eq 'ARRAY') { # multiple values foreach my $p (@{$p_val}) { if (!($constraint->{$p}) && (($nb_for == 1) || ($p ne ''))) { &report::reject_report_web('user','p_family_wrong',{'param' => $pname,'val'=> $p},$param->{'action'}); &wwslog('info', 'do_edit_list : parameter %s has got wrong value : %s (family context)', $pname, $p); return undef; } } } else { # single value if (!($constraint->{$p_val}) && (($nb_for == 1) || ($p_val ne ''))) { &report::reject_report_web('user','p_family_wrong',{'param' => $pname,'val'=> $p_val},$param->{'action'}); &wwslog('info', 'do_edit_list : parameter %s has got wrong value : %s (family context)', $pname, $p_val); return undef; } } } } } =pod =head2 sub _prepare_edit_form(LIST) Prepares config data to be sent in the edition form. Adds to the parameters array a hash for each parameter to be edited. =head3 Arguments =over =item * I<$list>, a List object =back =head3 Return =over =item * I<1>, if no problem is encountered =item * I, if anything goes wrong =back =head3 Calls =over =item * _prepare_data =item * _restrict_values =item * wwslog =item * List::by_order =item * List::get_family =item * List::load_topics =item * List::may_edit =item * Language::GetLang =item * Language::SetLang =item * report::reject_report_web =item * tools::dup_var =back =cut ## Prepare config data to be sent in the ## edition form sub _prepare_edit_form { my $list = shift; my $list_config = &tools::dup_var($list->{'admin'}); my $family; my $is_form_editable = '0'; ## If the list belongs to a family, check if the said family can be retrieved. if (defined $list_config->{'family_name'}) { unless ($family = $list->get_family()) { &report::reject_report_web('intern','unable_get_family',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','_prepare_edit_form : impossible to get list %s\'s family',$list->{'name'}); return undef; } } ## For each parameter defined in List.pm, retrieve and prepare for editing foreach my $pname (sort List::by_order keys %{$pinfo}) { ## Skip comments and default values. next if ($pname =~ /^(comment|defaults)$/); ## Skip parameters belonging to another group. next if ($in{'group'} && ($pinfo->{$pname}{'group'} ne $in{'group'})); ## Skip obsolete parameters. next if $pinfo->{$pname}{'obsolete'}; ## Check whether the parameter can be edited by the logged user. my $may_edit = $list->may_edit($pname,$param->{'user'}{'email'}); ## Valid form global edit status as soon as at least one editable parameter is found. if ($may_edit eq 'write') { $is_form_editable = '1'; } ## Store in $p a reference to the hash containing the informations relative to the parameter editing. my $p = &_prepare_data($pname, $pinfo->{$pname}, $list_config->{$pname},$may_edit,$family); ## Store if the parameter is still at its default value or not. $p->{'default'} = $list_config->{'defaults'}{$pname}; ## Store the change state of this parameter, taken from the global variable %changed_params. $p->{'changed'} = $::changed_params{$pname}; ## Exceptions...too many if ($pname eq 'topics') { $p->{'type'} = 'enum'; my @topics; foreach my $topic(@{$p->{'value'}}) { push @topics, $topic->{'value'}; } undef $p->{'value'}; my %list_of_topics = &List::load_topics($robot); if (defined $p->{'constraint'}) { &_restrict_values(\%list_of_topics,$p->{'constraint'}); } foreach my $topic (keys %list_of_topics) { $p->{'value'}{$topic}{'selected'} = 0; $p->{'value'}{$topic}{'title'} = $list_of_topics{$topic}{'current_title'}; if ($list_of_topics{$topic}{'sub'}) { foreach my $subtopic (keys %{$list_of_topics{$topic}{'sub'}}) { $p->{'value'}{"$topic/$subtopic"}{'selected'} = 0; $p->{'value'}{"$topic/$subtopic"}{'title'} = "$list_of_topics{$topic}{'current_title'}/$list_of_topics{$topic}{'sub'}{$subtopic}{'current_title'}"; } } } foreach my $selected_topic (@topics) { next unless (defined $selected_topic); $p->{'value'}{$selected_topic}{'selected'} = 1; $p->{'value'}{$selected_topic}{'title'} = sprintf gettext("Unknown (%s)"), $selected_topic unless (defined $p->{'value'}{$selected_topic}{'title'}); } }elsif ($pname eq 'digest') { foreach my $v (@{$p->{'value'}}) { next unless ($v->{'name'} eq 'days'); if (ref($v->{'value'}) eq 'ARRAY'){ &tools::do_log('debug2','Empty digest parameter. Putting a dummy value.'); $v->{'value'} = undef; $v->{'type'} = 'enum'; }else{ foreach my $day (keys %{$v->{'value'}}) { my $t = gettext_strftime "%A", gmtime(0 + ($day +3) * (3600 * 24)); $t = sprintf '%s (%s)', $t, $day if List::is_listmaster($param->{'user'}{'email'}, $robot); $v->{'value'}{$day}{'title'} = $t; } } } }elsif ($pname eq 'lang') { my $saved_lang = &Language::GetLang(); foreach my $lang (keys %{$p->{'value'}}) { &Language::SetLang($lang); my $t = gettext('_language_'); $t = sprintf '%s (%s)', $t, $lang if List::is_listmaster($param->{'user'}{'email'}, $robot); $p->{'value'}{$lang}{'title'} = $t; $p->{'value'}{$lang}{'lang_tag'} = Language::LanguageTag($lang); } &Language::SetLang($saved_lang); } elsif ($pname eq 'available_user_options' or $pname eq 'default_user_options') { foreach my $v (@{$p->{'value'}}) { if ($v->{'name'} eq 'reception') { foreach my $x (keys %{$v->{'value'}}) { $v->{'value'}{$x}{'title'} = $list->get_option_title($x, 'reception', List::is_listmaster($param->{'user'}{'email'}, $robot)); } } elsif ($v->{'name'} eq 'visibility') { foreach my $x (keys %{$v->{'value'}}) { $v->{'value'}{$x}{'title'} = $list->get_option_title($x, 'visibility', List::is_listmaster($param->{'user'}{'email'}, $robot)); } } } } elsif ($pname eq 'status') { foreach my $x (keys %{$p->{'value'}}) { $p->{'value'}{$x}{'title'} = $list->get_option_title($x, 'status', List::is_listmaster($param->{'user'}{'email'}, $robot)); } } push @{$param->{'param'}}, $p; } ## If at least one param was editable, make the update button appear in the form. $param->{'is_form_editable'} = $is_form_editable; return 1; } =pod =head2 sub _prepare_data(STRING $name, HASH_Ref $struct, SCALAR $data, STRING $may_edit, FAMILY $family, STRING $main_p) Returns a reference to a hash containing the data used to edit the parameter (of name $name, corresponding to the structure $struct in pinfo, with the $may_edit editing status) containing the data in the Sympa web interface. =head3 Arguments =over =item * I<$name> (STRING), the name of the parameter processed =item * I<$struct> (HASH_Ref), a ref to the hash describing this parameter in %List::pinfo =item * I<$data> (), the value(s) taken by this parameter in the current list. Can be a reference to a list or the value of a single parameter. =item * I<$may_edit> (STRING), the editing status of this parameter in the current context. =item * I<$family> (FAMILY), the family the list belongs to. =item * I<$main_p> (STRING), the prefix composing the complete name of the parameter. =back =head3 Return =over =item * I<$p_glob>, a reference to a hash containing the data used to edit the parameter. =back =head3 Calls =over =item * _restrict_values =item * _prepare_data =item * load_data_sources_list =item * Family::get_param_constraint =item * List::load_scenario_list =item * List::load_task_list =item * List::may_edit =item * tools::escape_html =back =cut sub _prepare_data { my ($name, $struct,$data,$may_edit,$family,$main_p) = @_; # &wwslog('debug2', '_prepare_data(%s, %s)', $name, $data); # $family and $main_p (recursive call) are optionnal # if $main_p is needed, $family also next if ($struct->{'obsolete'}); ## Prepare data structure for the parser my $p_glob = {'name' => $name, 'comment' => $struct->{'comment'}{$param->{'lang'}} }; ## Check if some family constraint modify the editing rights. my $restrict = 0; my $constraint; if ((ref($family) eq 'Family') && ($may_edit eq 'write')) { if ($main_p && defined $::pinfo{$main_p}) { if (ref($::pinfo{$main_p}{'format'}) eq 'HASH') { # composed parameter $constraint = $family->get_param_constraint("$main_p.$p_glob->{'name'}"); } } else { # simple parameter if (ref($::pinfo{$p_glob->{'name'}}{'format'}) ne 'HASH') { # simple parameter $constraint = $family->get_param_constraint($p_glob->{'name'}); } } if ($constraint eq '0') { # free parameter $p_glob->{'may_edit'} = 'write'; } elsif (ref($constraint) eq 'HASH') { # controlled parameter $p_glob->{'may_edit'} = 'write'; $restrict = 1; } else { # fixed parameter $p_glob->{'may_edit'} = 'read'; } } else { $p_glob->{'may_edit'} = $may_edit; } ## Naming the parameter. if ($struct->{'gettext_id'}) { $p_glob->{'title'} = gettext($struct->{'gettext_id'}); }else { $p_glob->{'title'} = $name; } ## Occurrences : if the parameter can have multiple occurrences, ## its values are transfered into the array pointed by $data2 ## if they were given in arguments (if not, an empty array is created). ## if it is a single occurrence parameter, an array is created with ## its single value. my $data2; if ($struct->{'occurrence'} =~ /n$/) { $p_glob->{'occurrence'} = 'multiple'; if (defined($data)) { $data2 = $data; if ($may_edit eq 'write') { ## Add an empty entry unless (($name eq 'days') || ($name eq 'reception') || ($name eq 'rfc2369_header_fields') || ($name eq 'topics')) { my $empty_entry; ## Structured parameter if (ref($struct->{'format'}) eq 'HASH') { foreach my $sub_parameter (keys %{$struct->{'format'}}) { ## Use default value if defined if ($struct->{'format'}{$sub_parameter}{'default'}) { $empty_entry->{$sub_parameter} = $struct->{'format'}{$sub_parameter}{'default'}; } } ## Simpe parameter }else { $empty_entry = undef; } push @{$data2}, $empty_entry; ## &wwslog('debug2', 'Add 1 %s', $name); } } }else { if ($may_edit eq 'write') { $data2 = [undef]; } } }else { $data2 = [$data]; } my @all_p; ## Foreach occurrence of param foreach my $d (@{$data2}) { my $p = {}; ## Type of data if ($struct->{'scenario'}) { $p_glob->{'type'} = 'scenario'; my $list_of_scenario; my $tmp_list_of_scenario = $list->load_scenario_list($struct->{'scenario'},$robot); ## Only get required scenario attributes foreach my $scenario (keys %{$tmp_list_of_scenario}) { $list_of_scenario->{$scenario} = {'name' => $tmp_list_of_scenario->{$scenario}{'name'}, 'web_title' => $tmp_list_of_scenario->{$scenario}{'web_title'}}; } $list_of_scenario->{$d->{'name'}}{'selected'} = 1; $p->{'value'} = $list_of_scenario; if ($restrict) { &_restrict_values($p->{'value'},$constraint); } }elsif ($struct->{'task'}) { $p_glob->{'type'} = 'task'; my $list_of_task = $list->load_task_list($struct->{'task'}, $robot); $list_of_task->{$d->{'name'}}{'selected'} = 1; $p->{'value'} = $list_of_task; if ($restrict) { &_restrict_values($p->{'value'},$constraint); } }elsif ($struct->{'datasource'}) { $p_glob->{'type'} = 'datasource'; my $list_of_data_sources = $list->load_data_sources_list($robot); $list_of_data_sources->{$d}{'selected'} = 1; $p->{'value'} = $list_of_data_sources; if ($restrict) { &_restrict_values($p->{'value'},$constraint); } }elsif (ref ($struct->{'format'}) eq 'HASH') { $p_glob->{'type'} = 'paragraph'; unless (ref($d) eq 'HASH') { $d = {}; } foreach my $k (sort {$struct->{'format'}{$a}{'order'} <=> $struct->{'format'}{$b}{'order'}} keys %{$struct->{'format'}}) { ## Prepare data recursively my $m_e = $list->may_edit("$name.$k",$param->{'user'}{'email'}); my $v = &_prepare_data($k, $struct->{'format'}{$k}, $d->{$k},$m_e,$family,$name); push @{$p->{'value'}}, $v; } }elsif ((ref ($struct->{'format'}) eq 'ARRAY') || ($restrict && ($main_p eq 'msg_topic' && $name eq 'keywords'))) { $p_glob->{'type'} = 'enum'; unless (defined $p_glob->{'value'}) { ## Initialize foreach my $elt (@{$struct->{'format'}}) { $p_glob->{'value'}{$elt}{'selected'} = 0; } ## Check obsolete values ; they should not be printed if (defined $struct->{'obsolete_values'}) { foreach my $elt (@{$struct->{'obsolete_values'}}) { delete $p_glob->{'value'}{$elt}; } } } if (ref ($d)) { next unless (ref ($d) eq 'ARRAY'); foreach my $v (@{$d}) { $p_glob->{'value'}{$v}{'selected'} = 1; } }else { $p_glob->{'value'}{$d}{'selected'} = 1 if (defined $d); } if ($restrict) { &_restrict_values($p_glob->{'value'},$constraint); } }else { if ($restrict && ($name ne 'topics')) { $p_glob->{'type'} = 'enum'; foreach my $elt (keys %{$constraint}) { $p->{'value'}{&tools::escape_html($elt)}{'selected'} = 0; } $p->{'value'}{&tools::escape_html($d)}{'selected'} = 1; $p->{'length'} = $struct->{'length'}; $p->{'unit'} = gettext($struct->{'gettext_unit'}); } else { $p_glob->{'type'} = 'scalar'; $p->{'value'} = &tools::escape_html($d); $p->{'length'} = $struct->{'length'}; $p->{'field_type'} = $struct->{'field_type'}; my $l = length($p->{'value'}); $p->{'hidden_field'} = '*' x $l; $p->{'unit'} = gettext($struct->{'gettext_unit'}); if ($restrict) { # for topics $p_glob->{'constraint'} = $constraint; } } } push @all_p, $p; } if (($p_glob->{'occurrence'} eq 'multiple') && ($p_glob->{'type'} ne 'enum')) { $p_glob->{'value'} = \@all_p; }else { foreach my $k (keys %{$all_p[0]}) { $p_glob->{$k} = $all_p[0]->{$k}; } } return $p_glob; } ## Restrict allowed values in the hash sub _restrict_values { my $values = shift; #ref on hash of values my $allowed = shift; #ref on hash of allowed values &wwslog('debug3', '_restrict_values()'); foreach my $v (keys %{$values}) { unless (defined $allowed->{$v}) { delete $values->{$v}; } } } ## NOT USED anymore (expect chinese) sub do_close_list_request { &wwslog('info', 'do_close_list_request()'); if ($list->{'admin'}{'status'} eq 'closed') { &report::reject_report_web('user','already_closed',{},$param->{'action'},$list); &wwslog('info','do_close_list_request: already closed'); return undef; } return 1; } # in order to rename a list you must be list owner and you must be allowed to create new list sub do_rename_list_request { &wwslog('info', 'do_rename_list_request()'); my $result = &Scenario::request_action ('create_list',$param->{'auth_method'},$robot, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } unless ($r_action =~ /do_it|listmaster/) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info','do_rename_list_request: not owner'); return undef; } ## Super listmaster can move a list to another robot if (&List::is_listmaster($param->{'user'}{'email'}, $robot)) { foreach (keys %{$Conf{'robots'}}) { if ($_ eq $robot) { $param->{'robots'}{$_} = 'selected="selected"'; }else { $param->{'robots'}{$_} = ''; } } } return '1'; } sub do_copy_list { &wwslog('info', 'do_copy_list(%s,%s)', $in{'new_listname'}, $in{'new_robot'}); &do_rename_list('copy'); } # in order to rename a list you must be list owner and you must be allowed to create new list sub do_rename_list { my $mode = shift; if ($in{'new_listname'} =~ /[A-Z]/) { $in{'new_listname'} = lc($in{'new_listname'}); &report::notice_report_web('listname_lowercased',{},$param->{'action'}); } &wwslog('info', 'do_rename_list(%s,%s, mode = %s)', $in{'new_listname'}, $in{'new_robot'},$mode); my $result = &admin::rename_list(list => $list, new_listname =>$in{'new_listname'}, new_robot => $in{'new_robot'}, mode => $mode, auth_method => $param->{'auth_method'}, user_email => $param->{'user'}{'email'}, remote_host => $param->{'remote_host'}, remote_addr => $param->{'remote_addr'}, aliases => $param->{'aliases'}, status => $param->{'status'}, ); if ($result eq 'incorrect_listname') { &report::reject_report_web('user','incorrect_listname', {'bad_listname' => $in{'new_listname'}},$param->{'action'},$list); &wwslog('info','do_rename_list: incorrect listname %s', $in{'new_listname'}); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'incorrect_listname'}); return 'rename_list_request'; }elsif ($result eq 'authorization') { &report::reject_report_web('auth','authorization',{},$param->{'action'},$list); &wwslog('info','do_rename_list: not owner'); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'authorization'}); return undef; }elsif ($result eq 'internal') { &report::reject_report_web('intern','unable_to_rename_list',{'new_listname' => $in{'new_listname'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err', "can't rename list %s to %s@%s", $list->get_list_address(), $in{'new_listname'}, $in{'new_robot'}); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'internal'}); return undef; }elsif ($result eq 'list_already_exists') { &report::reject_report_web('user','list_already_exists',{'new_listname' => $in{'new_listname'}},$param->{'action'},$list); &wwslog('info', 'Could not rename list %s for %s: new list %s already existing list', $in{'listname'},$param->{'user'}{'email'},$in{'new_listname'}); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'list_already_exists'}); return undef; }elsif ($result eq 'incorrect_listname') { &report::reject_report_web('user','listname_matches_aliases',{'new_listname' => $in{'new_listname'}},$param->{'action'},$list); &wwslog('info','do_create_list: incorrect listname %s', $in{'new_listname'}); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'incorrect_listname'}); return 'rename_list_request'; }elsif ($result eq 'unknown_robot') { &wwslog('info',"do_rename_list : unknown robot $in{'new_robot'}"); &report::reject_report_web('user','unknown_robot',{'new_robot' => $in{'new_robot'}},$param->{'action'},$list); &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'error', 'error_type' => 'unknown_robot'}); return undef; } ## Were aliases installed? if ($param->{'aliases'} == 1) { $param->{'auto_aliases'} = 1; }else { $param->{'auto_aliases'} = 0; } # set list status to pending if creation list is moderated if ($param->{'status'} eq 'pending') { &report::notice_report_web('pending_list',{},$param->{'action'},$list); } if ($in{'new_robot'} eq '$robot') { $param->{'redirect_to'} = "$param->{'base_url'}$param->{'path_cgi'}/admin/$in{'new_listname'}"; }else { $param->{'redirect_to'} = &Conf::get_robot_conf($in{'new_robot'}, 'wwsympa_url')."/admin/$in{'new_listname'}"; } $param->{'list'} = $in{'new_listname'}; &web_db_log({'parameters' => "$in{'new_listname'},$in{'new_robot'}", 'status' => 'success'}); $list->save_config($param->{'user'}{'email'}); return 1; } sub do_purge_list { &wwslog('info', 'do_purge_list()'); my @lists = split /\0/, $in{'selected_lists'}; foreach my $l (@lists) { my $list = new List ($l, $robot); next unless (defined $list); $list->purge($param->{'user'}{'email'}); } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'parameters' => $in{'selected_lists'}, 'status' => 'success'}); return 'get_closed_lists'; } sub do_close_list { &wwslog('info', "do_close_list($list->{'name'})"); if ($list->{'admin'}{'status'} eq 'closed') { &report::reject_report_web('user','already_closed',{},$param->{'action'},$list); &wwslog('info','do_close_list: already closed'); &web_db_log({'status' => 'error', 'error_type' => 'already_closed'}); return undef; }elsif($list->{'admin'}{'status'} eq 'pending') { &wwslog('info','do_close_list: closing a pending list makes it purged'); $list->purge($param->{'user'}{'email'}); &report::notice_report_web('list_purged',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'home'; }else{ $list->close($param->{'user'}{'email'}); &report::notice_report_web('list_closed',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'admin'; } } sub do_restore_list { &wwslog('info', 'do_restore_list()'); unless ($list->{'admin'}{'status'} eq 'closed') { &report::reject_report_web('user','not_closed',{},$param->{'action'},$list); &wwslog('info','do_restore_list: list not closed'); &web_db_log({'status' => 'error', 'error_type' => 'not_closed'}); return undef; } ## Change status & save config $list->{'admin'}{'status'} = 'open'; $list->save_config($param->{'user'}{'email'}); unless (-f "$list->{'dir'}/subscribers.closed.dump") { &wwslog('notice', 'No subscribers to restore'); &web_db_log({'status' => 'error', 'error_type' => 'no_subscribers'}); } my @users = &List::_load_users_file("$list->{'dir'}/subscribers.closed.dump"); ## Insert users in database foreach my $user (@users) { $list->add_user($user); } $list->savestats(); my $aliases = &admin::install_aliases($list,$robot); if ($aliases == 1) { $param->{'auto_aliases'} = 1; }else { $param->{'aliases'} = $aliases; $param->{'auto_aliases'} = 0; } &report::notice_report_web('list_restored',{},$param->{'action'}); &web_db_log({'status' => 'success'}); return 'admin'; } sub get_desc_file { my $file = shift; my $ligne; my %hash; open DESC_FILE,"$file"; while ($ligne = ) { if ($ligne =~ /^title\s*$/) { #case title of the document while (($ligne = ) and ($ligne!~/^\s*$/)) { $ligne =~ /^\s*(\S.*\S)\s*/; $hash{'title'} = $hash{'title'}.$1." "; } } if ($ligne =~ /^creation\s*$/) { #case creation of the document while (($ligne = ) and ($ligne!~/^\s*$/)) { if ($ligne =~ /^\s*email\s*(\S*)\s*/) { $hash{'email'} = $1; } if ($ligne =~ /^\s*date_epoch\s*(\d*)\s*/) { $hash{'date'} = $1; } } } if ($ligne =~ /^access\s*$/) { #case access scenari for the document while (($ligne = ) and ($ligne!~/^\s*$/)) { if ($ligne =~ /^\s*read\s*(\S*)\s*/) { $hash{'read'} = $1; } if ($ligne =~ /^\s*edit\s*(\S*)\s*/) { $hash{'edit'} = $1; } } } } close DESC_FILE; return %hash; } sub do_show_cert { return 1; } ## Function synchronize ## Return true if the file in parameter can be overwrited ## false if it has changes since the parameter date_epoch sub synchronize { # args : 'path' , 'date_epoch' my $path = shift; my $date_epoch = shift; my @info = stat $path; return ($date_epoch == $info[9]); } #******************************************* # Function : d_access_control # Description : return a hash with privileges # in read, edit, control # if first parameter require # it #****************************************** ## Regulars # read(/) = default (config list) # edit(/) = default (config list) # control(/) = not defined # read(A/B)= (read(A) && read(B)) || # (author(A) || author(B)) # edit = idem read # control (A/B) : author(A) || author(B) # + (set owner A/B) if (empty directory && # control A) sub d_access_control { # Arguments: # (\%mode,$path) # if mode->{'read'} control access only for read # if mode->{'edit'} control access only for edit # if mode->{'control'} control access only for control # return the hash ( # $result{'may'}{'read'} == $result{'may'}{'edit'} == $result{'may'}{'control'} if is_author else : # $result{'may'}{'read'} = 0 or 1 (right or not) # $result{'may'}{'edit'} = 0(not may edit) or 0.5(may edit with moderation) or 1(may edit ) : it is not a boolean anymore # $result{'may'}{'control'} = 0 or 1 (right or not) # $result{'reason'}{'read'} = string for authorization_reject.tt2 when may_read == 0 # $result{'reason'}{'edit'} = string for authorization_reject.tt2 when may_edit == 0 # $result{'scenario'}{'read'} = scenario name for the document # $result{'scenario'}{'edit'} = scenario name for the document # Result my %result; $result{'reason'} = {}; # Control # Arguments my $mode = shift; my $path = shift; &wwslog('debug', "d_access_control(%s, %s)", join('/',%$mode), $path); my $mode_read = $mode->{'read'}; my $mode_edit = $mode->{'edit'}; my $mode_control = $mode->{'control'}; # Useful parameters my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; # document to read my $doc; if ($path) { # the path must have no slash a its end $path =~ /^(.*[^\/])?(\/*)$/; $path = $1; $doc = $shareddir.'/'.$path; } else { $doc = $shareddir; } # Control for editing my $may_read = 1; my $why_not_read = ''; my $may_edit = 1; my $why_not_edit = ''; my $is_author = 0; # <=> $may_control ## First check privileges on the root shared directory $result{'scenario'}{'read'} = $list->{'admin'}{'shared_doc'}{'d_read'}{'name'}; $result{'scenario'}{'edit'} = $list->{'admin'}{'shared_doc'}{'d_edit'}{'name'}; ## Privileged owner has all privileges if ($param->{'is_privileged_owner'}) { $result{'may'}{'read'} = 1; $result{'may'}{'edit'} = 1; $result{'may'}{'control'} = 1; return %result; } # if not privileged owner if ($mode_read) { my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_read = $result->{'reason'}; } $may_read = ($action =~ /do_it/i); } if ($mode_edit) { my $result = $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_edit = $result->{'reason'}; } #edit = 0, 0.5 or 1 $may_edit = &find_edit_mode($action); $why_not_edit = '' if ($may_edit); } ## Only authenticated users can edit files unless ($param->{'user'}{'email'}) { $may_edit = 0; $why_not_edit = 'not_authenticated'; } # if ($mode_control) { # $result{'may'}{'control'} = 0; # } my $current_path = $path; my $current_document; my %desc_hash; my $user = $param->{'user'}{'email'} || 'nobody'; while ($current_path ne "" && $current_path ne '/') { # no description file found yet my $def_desc_file = 0; my $desc_file; $current_path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $current_document = $3; my $next_path = $1; # opening of the description file appropriated if (-d $shareddir.'/'.$current_path) { # case directory # unless ($slash) { $current_path = $current_path.'/'; # } if (-e "$shareddir/$current_path.desc"){ $desc_file = $shareddir.'/'.$current_path.".desc"; $def_desc_file = 1; } }else { # case file if (-e "$shareddir/$next_path.desc.$3"){ $desc_file = $shareddir.'/'.$next_path.".desc.".$3; $def_desc_file = 1; } } if ($def_desc_file) { # a description file was found # loading of acces information %desc_hash = &get_desc_file($desc_file); if ($mode_read) { my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario'=> $desc_hash{'read'}}); my $action; if (ref($result) eq 'HASH') { $action = $result->{'action'}; $why_not_read = $result->{'reason'}; } $may_read = $may_read && ( $action=~ /do_it/i); $why_not_read = '' if ($may_read); } if ($mode_edit) { my $result = $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario'=> $desc_hash{'edit'}}); my $action_edit; if (ref($result) eq 'HASH') { $action_edit = $result->{'action'}; $why_not_edit = $result->{'reason'}; } # $may_edit = 0, 0.5 or 1 my $may_action_edit = &find_edit_mode($action_edit); $may_edit = &merge_edit($may_edit,$may_action_edit); $why_not_edit = '' if ($may_edit); } ## Only authenticated users can edit files unless ($param->{'user'}{'email'}) { $may_edit = 0; $why_not_edit = 'not_authenticated'; } $is_author = $is_author || ($user eq $desc_hash{'email'}); unless (defined $result{'scenario'}{'read'}) { $result{'scenario'}{'read'} = $desc_hash{'read'}; $result{'scenario'}{'edit'} = $desc_hash{'edit'}; } ## Author has all privileges if ($is_author) { $result{'may'}{'read'} = 1; $result{'may'}{'edit'} = 1; $result{'may'}{'control'} = 1; return %result; } } # truncate the path for the while $current_path = $next_path; } if ($mode_read) { $result{'may'}{'read'} = $may_read; $result{'reason'}{'read'} = $why_not_read; } if ($mode_edit) { $result{'may'}{'edit'} = $may_edit; $result{'reason'}{'edit'} = $why_not_edit; } # if ($mode_control) { # $result{'may'}{'control'} = 0; # } return %result; } ## return the mode of editing included in $action : 0, 0.5 or 1 sub find_edit_mode{ my $action=shift; my $result; if ($action =~ /editor/i){ $result = 0.5; } elsif ($action =~ /do_it/i){ $result = 1; } else { $result = 0; } return $result; } ## return the mode of editing : 0, 0.5 or 1 : # do the merging between 2 args of right access edit : "0" > "0.5" > "1" # instead of a "and" between two booleans : the most restrictive right is # imposed sub merge_edit{ my $arg1=shift; my $arg2=shift; my $result; if ($arg1 == 0 || $arg2 == 0){ $result = 0; }elsif ($arg1 == 0.5 || $arg2 == 0.5){ $result = 0.5; }else { $result = 1; } return $result; } # create the root shared document sub do_d_admin { &wwslog('info', 'do_d_admin(%s,%s)', $in{'list'}, $in{'d_admin'}); my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$in{'path'}); my $dir = $list->{'dir'}; unless ($access{'may'}{'edit'}) { &wwslog('info',"do_d_admin : permission denied for $param->{'user'}{'email'} "); &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } if ($in{'d_admin'} eq 'create') { unless ($list->create_shared()) { &wwslog('info',"do_d_admin : could not create the shared"); &report::reject_report_web('intern','create_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } return 'd_read'; }elsif($in{'d_admin'} eq 'restore') { unless (-e "$dir/pending.shared") { &wwslog('info',"do_d_admin : restore; $dir/pending.shared not found"); &report::reject_report_web('intern','restore_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } if (-e "$dir/shared") { &wwslog('info',"do_d_admin : restore; $dir/shared already exist"); &report::reject_report_web('intern','restore_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (rename ("$dir/pending.shared", "$dir/shared")){ &wwslog('info',"do_d_admin : restore; unable to rename $dir/pending.shared"); &report::reject_report_web('intern','restore_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 'd_read'; }elsif($in{'d_admin'} eq 'delete') { unless (-e "$dir/shared") { &wwslog('info',"do_d_admin : restore; $dir/shared not found"); &report::reject_report_web('intern','delete_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } if (-e "$dir/pending.shared") { &wwslog('info',"do_d_admin : delete ; $dir/pending.shared already exist"); &report::reject_report_web('intern','delete_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (rename ("$dir/shared", "$dir/pending.shared")){ &wwslog('info',"do_d_admin : restore; unable to rename $dir/shared"); &report::reject_report_web('intern','delete_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 'admin'; } # Function which sorts a hash of documents # Sort by various parameters sub by_order { my $order = shift; my $hash = shift; # $order = 'order_by_size'/'order_by_doc'/'order_by_author'/'order_by_date' if ($order eq 'order_by_doc') { $hash->{$a}{'doc'} cmp $hash->{$b}{'doc'} or $hash->{$b}{'date_epoch'} <=> $hash->{$a}{'date_epoch'}; } elsif ($order eq 'order_by_author') { $hash->{$a}{'author'} cmp $hash->{$b}{'author'} or $hash->{$b}{'date_epoch'} <=> $hash->{$a}{'date_epoch'}; } elsif ($order eq 'order_by_size') { $hash->{$a}{'size'} <=> $hash->{$b}{'size'} or $hash->{$b}{'date_epoch'} <=> $hash->{$a}{'date_epoch'}; } elsif ($order eq 'order_by_date') { $hash->{$b}{'date_epoch'} <=> $hash->{$a}{'date_epoch'} or $a cmp $b; } else { $a cmp $b; } } #******************************************* # Function : do_d_read # Description : reads a file or a directory #****************************************** ## ## Function do_d_read sub do_d_read { &wwslog('info', 'do_d_read(%s)', $in{'path'}); ### Useful variables # current list / current shared directory my $list_name = $list->{'name'}; # relative path / directory shared of the document my $path = &no_slash_end($in{'path'}); # moderation my $visible_path = &make_visible_path($path); # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; # document to read my $doc; if ($path) { $doc = $shareddir.'/'.$path; } else { $doc = $shareddir; } ### is list open ? unless ($list->{'admin'}{'status'} eq 'open'){ &report::reject_report_web('user','list_not_open',{'status' => $list->{'admin'}{'status'}},$param->{'action'},$list); &wwslog('err','d_read : access denied for %s because list is not open', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } ### Document exists ? unless (-r "$doc") { &wwslog('err',"do_d_read : unable to read $shareddir/$path : no such file or directory"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ### Document has non-size zero? unless (-s "$doc") { &wwslog('err',"do_d_read : unable to read $shareddir/$path : empty document"); &report::reject_report_web('user','empty_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ### Document isn't a description file unless ($path !~ /\.desc/) { &wwslog('err',"do_d_read : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ### Access control my %mode; $mode{'read'} = 1; $mode{'edit'} = 1; $mode{'control'} = 1; my %access = &d_access_control(\%mode,$path); my $may_read = $access{'may'}{'read'}; unless ($may_read) { &report::reject_report_web('auth',$access{'reason'}{'read'},{},$param->{'action'},$list); &wwslog('err','d_read : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } my $may_edit = $access{'may'}{'edit'}; my $may_control = $access{'may'}{'control'}; ### File or directory ? if (!(-d $doc)) { my @tokens = split /\//,$doc; my $filename = $tokens[$#tokens]; ## Jump to the URL if ($filename =~ /^\..*\.(\w+)\.moderate$/) { $param->{'file_extension'} = $1; }elsif ($filename =~ /^.*\.(\w+)$/) { $param->{'file_extension'} = $1; } if ($param->{'file_extension'} eq 'url') { open DOC, $doc; my $url = ; close DOC; chomp $url; $param->{'redirect_to'} = $url; return 1; }else { # parameters for the template file # view a file $param->{'file'} = $doc; $param->{'bypass'} = 1; return 1; } }else { # directory # verification of the URL (the path must have a slash at its end) # if ($ENV{'PATH_INFO'} !~ /\/$/) { # $param->{'redirect_to'} = "$param->{'base_url'}$param->{'path_cgi'}/d_read/$list_name/"; # return 1; # } ## parameters of the current directory if ($path && (-e "$doc/.desc")) { my %desc_hash = &get_desc_file("$doc/.desc"); $param->{'doc_owner'} = $desc_hash{'email'}; $param->{'doc_title'} = $desc_hash{'title'}; } my @info = stat $doc; $param->{'doc_date'} = gettext_strftime "%d %b %Y", localtime($info[9]); # listing of all the shared documents of the directory unless (opendir DIR, "$doc") { &report::reject_report_web('intern','cannot_open_dir',{'dir' => $doc },$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_read : cannot open $doc : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # array of entry of the directory DIR my @tmpdir = readdir DIR; closedir DIR; my $dir = &get_directory_content(\@tmpdir,$param->{'user'}{'email'},$list,$doc); # empty directory? $param->{'empty'} = ($#{$dir} == -1); # subdirectories hash my %subdirs; # file hash my %files; ## for the exception of index.html # name of the file "index.html" if exists in the directory read my $indexhtml; # boolean : one of the subdirectories or files inside # can be edited -> normal mode of read -> d_read.tt2; my $normal_mode; my $path_doc; my %desc_hash; my $may, my $def_desc; my $user = $param->{'user'}{'email'} || 'nobody'; foreach my $d (@{$dir}) { # current document my $path_doc = "$doc/$d"; #case subdirectory if (-d $path_doc) { # last update my @info = stat $path_doc; if (-e "$path_doc/.desc") { # check access permission for reading %desc_hash = &get_desc_file("$path_doc/.desc"); my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'read'}}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); if (($user eq $desc_hash{'email'}) || ($may_control) || ($action =~ /do_it/i)) { $subdirs{$d}{'date_epoch'} = $info[9]; $subdirs{$d}{'date'} = gettext_strftime "%d %b %Y", localtime($info[9]); # Case read authorized : fill the hash $subdirs{$d}{'icon'} = $icon_table{'folder'}; $subdirs{$d}{'doc'} = &make_visible_path($d); $subdirs{$d}{'escaped_doc'} = &tools::escape_docname($d, '/'); # size of the doc $subdirs{$d}{'size'} = (-s $path_doc)/1000; # description $subdirs{$d}{'title'} = $desc_hash{'title'}; $subdirs{$d}{'escaped_title'}=&tools::escape_html($desc_hash{'title'}); # Author if ($desc_hash{'email'}) { $subdirs{$d}{'author'} = $desc_hash{'email'}; $subdirs{$d}{'author_mailto'} = &mailto($list,$desc_hash{'email'}); $subdirs{$d}{'author_known'} = 1; } # if the file can be read, check for edit access & edit description files access ## only authenticated users can edit a file if ($param->{'user'}{'email'}) { my $result = $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'edit'}}); my $action_edit; $action_edit = $result->{'action'} if (ref($result) eq 'HASH'); #may_action_edit = 0, 0.5 or 1 my $may_action_edit=&find_edit_mode($action_edit); $may_action_edit=&merge_edit($may_action_edit,$may_edit); if ($may_control || ($user eq $desc_hash{'email'})){ $subdirs{$d}{'edit'} = 1;# or = $may_action_edit ? # if index.html, must know if something can be edit in the dir $normal_mode = 1; } elsif ($may_action_edit != 0) { # $may_action_edit = 0.5 or 1 $subdirs{$d}{'edit'} = $may_action_edit; # if index.html, must know if something can be edit in the dir $normal_mode = 1; } } if ($may_control || ($user eq $desc_hash{'email'})) { $subdirs{$d}{'control'} = 1; } } } else { # no description file = no need to check access for read # access for edit and control if ($may_control) { $subdirs{$d}{'edit'} = 1; # or = $may_action_edit ? $normal_mode = 1; } elsif ($may_edit !=0) { # $may_action_edit = 1 or 0.5 $subdirs{$d}{'edit'} = $may_edit; $normal_mode = 1; } if ($may_control) {$subdirs{$d}{'control'} = 1;} } }else { # case file $may = 1; $def_desc = 0; if (-e "$doc/.desc.$d") { # a desc file was found $def_desc = 1; # check access permission %desc_hash = &get_desc_file("$doc/.desc.$d"); my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'read'}}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); unless (($user eq $desc_hash{'email'}) || ($may_control) || ($action =~ /do_it/i)) { $may = 0; } } # if permission or no description file if ($may) { $path_doc =~ /^([^\/]*\/)*([^\/]+)\.([^\/]+)$/; ## Bookmark if (($path_doc =~ /\.url$/) || ($path_doc =~ /\.url\.moderate$/)) { open DOC, $path_doc; my $url = ; close DOC; chomp $url; $files{$d}{'url'} = $url; $files{$d}{'anchor'} = &make_visible_path($d); $files{$d}{'icon'} = $icon_table{'url'}; ## MIME - TYPES : icons for template }elsif (my $type = $mime_types->{$3}) { # type of the file and apache icon $type =~ /^([\w\-]+)\/([\w\-]+)$/; my $mimet = $1; my $subt = $2; if ($subt) { if ($subt =~ /^octet-stream$/) { $mimet = 'octet-stream'; $subt = 'binary'; } $files{$d}{'type'} = "$subt file"; } $files{$d}{'icon'} = $icon_table{$mimet} || $icon_table{'unknown'}; } else { # unknown file type $files{$d}{'icon'} = $icon_table{'unknown'}; } ## case html if ($3 =~ /^html?$/i) { $files{$d}{'html'} = 1; $files{$d}{'type'} = 'html file'; $files{$d}{'icon'} = $icon_table{'text'}; } ## exception of index.html if ($d =~ /^(index\.html?)$/i) { $indexhtml = $1; } ## Access control for edit and control if ($def_desc) { # check access for edit and control the file ## Only authenticated users can edit files if ($param->{'user'}{'email'}) { my $result= $list->check_list_authz('shared_doc.d_edit',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'edit'}}); my $action_edit; $action_edit = $result->{'action'} if (ref($result) eq 'HASH'); #may_action_edit = 0, 0.5 or 1 my $may_action_edit=&find_edit_mode($action_edit); $may_action_edit=&merge_edit($may_action_edit,$may_edit); if ($may_control || ($user eq $desc_hash{'email'})){ $normal_mode = 1; $files{$d}{'edit'} = 1; # or = $may_action_edit ? } elsif ($may_action_edit != 0){ # $may_action_edit = 1 or 0.5 $normal_mode = 1; $files{$d}{'edit'} = $may_action_edit; } if (($user eq $desc_hash{'email'}) || $may_control) { $files{$d}{'control'} = 1; } # fill the file hash # description of the file $files{$d}{'title'} = $desc_hash{'title'}; $files{$d}{'escaped_title'}=&tools::escape_html($desc_hash{'title'}); # author if ($desc_hash{'email'}) { $files{$d}{'author'} = $desc_hash{'email'}; $files{$d}{'author_known'} = 1; $files{$d}{'author_mailto'} = &mailto($list,$desc_hash{'email'}); } } else { if ($may_edit!=0) { $files{$d}{'edit'} = $may_edit ; $normal_mode = 1; } if ($may_control) {$files{$d}{'control'} = 1;} } # name of the file if ($d =~ /^(\.).*(.moderate)$/) { # file not yet moderated can be seen by its author $files{$d}{'doc'} = &make_visible_path($d); $files{$d}{'moderate'} = 1; } else { $files{$d}{'doc'} = &make_visible_path($d); } $files{$d}{'escaped_doc'} = &tools::escape_docname($d, '/'); # last update my @info = stat $path_doc; $files{$d}{'date_epoch'} = $info[9]; $files{$d}{'date'} = gettext_strftime "%d %b %Y", localtime($info[9]); # size $files{$d}{'size'} = (-s $path_doc)/1000; } } } } ### Exception : index.html if ($indexhtml) { unless ($normal_mode) { $param->{'file_extension'} = 'html'; $param->{'bypass'} = 1; $param->{'file'} = "$doc/$indexhtml"; return 1; } } ## to sort subdirs my @sort_subdirs; my $order = $in{'order'} || 'order_by_doc'; $param->{'order_by'} = $order; foreach my $k (sort {by_order($order,\%subdirs)} keys %subdirs) { push @sort_subdirs, $subdirs{$k}; } ## to sort files my @sort_files; foreach my $k (sort {by_order($order,\%files)} keys %files) { push @sort_files, $files{$k}; } # parameters for the template file $param->{'list'} = $list_name; $param->{'may_edit'} = $may_edit; $param->{'may_control'} = $may_control; if ($path) { # building of the parent directory path if ($path =~ /^(([^\/]*\/)*)([^\/]+)$/) { $param->{'father'} = $1; }else { $param->{'father'} = ''; } $param->{'escaped_father'} = &tools::escape_docname($param->{'father'}, '/'); # Parameters for the description if (-e "$doc/.desc") { my @info = stat "$doc/.desc"; $param->{'serial_desc'} = $info[9]; my %desc_hash = &get_desc_file("$doc/.desc"); $param->{'description'} = $desc_hash{'title'}; } $param->{'path'} = $path; $param->{'visible_path'} = $visible_path; $param->{'escaped_path'} = &tools::escape_docname($param->{'path'}, '/'); } if (scalar keys %subdirs) { $param->{'sort_subdirs'} = \@sort_subdirs; } if (scalar keys %files) { $param->{'sort_files'} = \@sort_files; } } $param->{'father_icon'} = $icon_table{'father'}; $param->{'sort_icon'} = $icon_table{'sort'}; ## Show expert commands / user page # for the curent directory if ($may_edit == 0 && $may_control == 0) { $param->{'has_dir_rights'} = 0; } else { $param->{'has_dir_rights'} = 1; if ($may_edit == 1) { # (is_author || ! moderated) $param->{'total_edit'} = 1; } } # set the page mode if ($in{'show_expert_page'} && $param->{'has_dir_rights'}) { $session->{'shared_mode'}='expert'; if ($param->{'user'}{'prefs'}{'shared_mode'} ne 'expert') { # update user pref as soon as connected user change shared mode $param->{'user'}{'prefs'}{'shared_mode'} = 'expert'; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } $param->{'expert_page'} = 1; } elsif ($in{'show_user_page'}) { $session->{'shared_mode'}='basic'; if ($param->{'user'}{'prefs'}{'shared_mode'} ne 'basic') { # update user pref as soon as connected user change shared mode $param->{'user'}{'prefs'}{'shared_mode'} = 'basic'; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } $param->{'expert_page'} = 0; } else { if ( $session->{'shared_mode'} eq 'expert' && $param->{'has_dir_rights'}) { $param->{'expert_page'} = 1; } else { $param->{'expert_page'} = 0; } } #open TMP, ">/tmp/dump1"; #&tools::dump_var($param, 0,\*TMP); #close TMP; &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 1; } ## return a ref on an array of file (or subdirecties) to show to user sub get_directory_content { my $tmpdir = shift; my $user = shift; my $list = shift; my $doc = shift; # array of file not hidden my @dir = grep !/^\./, @$tmpdir; # array with documents not yet moderated my @moderate_dir = grep (/(\.moderate)$/, @$tmpdir); @moderate_dir = grep (!/^\.desc\./, @moderate_dir); # the editor can see file not yet moderated # a user can see file not yet moderated if he is th owner of these files if ($list->am_i('editor',$user)) { push(@dir,@moderate_dir); }else { my @privatedir = &select_my_files($user,$doc,\@moderate_dir); push(@dir,@privatedir); } return \@dir; } ## return an array that contains only file from @$refdir that belongs to $user sub select_my_files { my ($user,$path,$refdir)=@_; my @new_dir; foreach my $d (@$refdir) { if (-e "$path/.desc.$d") { my %desc_hash = &get_desc_file("$path/.desc.$d"); if ($user eq $desc_hash{'email'}){ $new_dir[$#new_dir+1]=$d; } } } return @new_dir; } ## Useful function to get off the slash at the end of the path ## at its end sub no_slash_end { my $path = shift; ## supress ending '/' $path =~ s/\/+$//; return $path; } ## return a visible path from a moderated file or not sub make_visible_path { my $path = shift; my $visible_path = $path; if ($path =~ /\.url(\.moderate)?$/){ if ($path =~ /^([^\/]*\/)*([^\/]+)\.([^\/]+)$/) { $visible_path =~ s/\.moderate$//; $visible_path =~ s/^\.//; $visible_path =~ s/\.url$//; } }elsif ($path =~ /\.moderate$/){ if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { my $name = $3; $name =~ s/^\.//; $name =~ s/\.moderate//; $visible_path = "$2"."$name"; } } ## Qdecode the visible path return &tools::qdecode_filename($visible_path); } ## Access to latest shared documents sub do_latest_d_read { &wwslog('info', 'do_latest_d_read(%s,%s,%s)', $in{'list'}, $in{'for'}, $in{'count'}); ### is list open ? unless ($list->{'admin'}{'status'} eq 'open'){ &report::reject_report_web('user','list_not_open',{'status' => $list->{'admin'}{'status'}},$param->{'action'},$list); &wwslog('err','d_latest_d_read : access denied for %s because list is not open', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } ### shared exist ? my $shareddir = $list->{'dir'}.'/shared'; unless (-r "$shareddir") { &wwslog('err',"do_latest_d_read : unable to read $shareddir : no such file or directory"); &report::reject_report_web('user','no_shared',{},$param->{'action'},$list); return undef; } ### Document has non-size zero? unless (-s "$shareddir") { &wwslog('err',"do_latest_d_read : unable to read $shareddir : empty document"); &report::reject_report_web('user','shared_empty',{},$param->{'action'},$list); return undef; } ### Access control my %mode; $mode{'read'} = 1; $mode{'control'} = 1; my %access = &d_access_control(\%mode,$shareddir); unless ($access{'may'}{'read'}) { &report::reject_report_web('auth',$access{'reason'}{'read'},{},$param->{'action'},$list); &wwslog('err','latest_d_read : access denied for %s', $param->{'user'}{'email'}); return undef; } ## parameters of the query my $today = time; my $oldest_day; if (defined $in{'for'}) { $oldest_day = $today - (86400 * ($in{'for'})); $param->{'for'} = $in{'for'}; unless ($oldest_day >= 0){ &report::reject_report_web('user','nb_days_to_much',{'nb_days' => $in{'for'} },$param->{'action'},$list); &wwslog('err','do_latest_d_read: parameter "for" is too big"'); } } my $nb_doc; my $NB_DOC_MAX = 100; if (defined $in{'count'}) { if ($in{'count'} > $NB_DOC_MAX) { $in{'count'} = $NB_DOC_MAX; } $param->{'count'} = $in{'count'}; $nb_doc = $in{'count'}; } else { $nb_doc = $NB_DOC_MAX; } my $documents; unless ($documents = &directory_browsing('',$oldest_day,$access{'may'}{'control'})) { &wwslog('err',"do_d_latest_d_read($list) : impossible to browse shared"); &report::reject_report_web('intern','browse_shared',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); return undef; } @$documents = sort ({$b->{'date_epoch'} <=> $a->{'date_epoch'}} @$documents); @{$param->{'documents'}} = splice(@$documents,0,$nb_doc); return 1; } ## browse a directory recursively and return documents younger than $oldest_day sub directory_browsing { my ($dir,$oldest_day,$may_control) = @_; &wwslog('debug2',"directory_browsing($dir,$oldest_day)"); my @result; my $shareddir = $list->{'dir'}.'/shared'; my $path_dir = "$shareddir/$dir"; ## listing of all the shared documents of the directory unless (opendir DIR, "$path_dir") { &wwslog('err',"directory_browsing($dir) : cannot open the directory : $!"); return undef; } my @tmpdir = readdir DIR; closedir DIR; # array of file not hidden my @directory = grep !/^\./, @tmpdir; my $user = $param->{'user'}{'email'} || 'nobody'; ## browsing foreach my $d (@directory) { my $path_d = "$path_dir/$d"; #case subdirectory if (-d $path_d) { if (-e "$path_d/.desc") { # check access permission for reading my %desc_hash = &get_desc_file("$path_d/.desc"); my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'read'}}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); if (($user eq $desc_hash{'email'}) || ($may_control) || ($action =~ /do_it/i)) { my $content_d; unless($content_d = &directory_browsing("$dir/$d",$oldest_day)) { &wwslog('err',"directory_browsing($dir) : impossible to browse subdirectory $d"); next; } if (ref($content_d) eq "ARRAY") { push @result,@$content_d; } } } #case file } else { my %file_info; ## last update my @info = stat $path_d; $file_info{'date_epoch'} = $info[9]; if ($file_info{'date_epoch'} < $oldest_day) { next; } $file_info{'last_update'} = gettext_strftime "%d %b %Y", localtime($info[9]); ## exception of index.html if ($d =~ /^(index\.html?)$/i) { next; } my $may = 1; my $def_desc = 0; my %desc_hash; if (-e "$path_dir/.desc.$d") { # a desc file was found $def_desc = 1; # check access permission %desc_hash = &get_desc_file("$path_dir/.desc.$d"); my $result = $list->check_list_authz('shared_doc.d_read',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}, 'scenario' => $desc_hash{'read'}}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); unless (($user eq $desc_hash{'email'}) || ($may_control) || ($action =~ /do_it/i)) { $may = 0; } } # if permission or no description file if ($may) { $path_d =~ /^([^\/]*\/)*([^\/]+)\.([^\/]+)$/; ## Bookmark if ($path_d =~ /\.url$/) { open DOC, $path_d; my $url = ; close DOC; chomp $url; $file_info{'url'} = $url; $file_info{'anchor'} = &make_visible_path($d); $file_info{'icon'} = $icon_table{'url'}; ## MIME - TYPES : icons for template }elsif (my $type = $mime_types->{$3}) { # type of the file and apache icon $type =~ /^([\w\-]+)\/([\w\-]+)$/; my $mimet = $1; my $subt = $2; if ($subt) { if ($subt =~ /^octet-stream$/) { $mimet = 'octet-stream'; $subt = 'binary'; } } $file_info{'icon'} = $icon_table{$mimet} || $icon_table{'unknown'}; ## UNKNOWN FILE TYPE } else { $file_info{'icon'} = $icon_table{'unknown'}; } ## case html if ($3 =~ /^html?$/i) { $file_info{'html'} = 1; $file_info{'icon'} = $icon_table{'text'}; } ## name of the file $file_info{'name'} = &make_visible_path($d); $file_info{'escaped_name'} = &tools::escape_docname($d, '/'); ## content_directory if ($dir) { $file_info{'content_dir'} = &make_visible_path($dir); } else { $file_info{'content_dir'} = "/"; } $file_info{'escaped_content_dir'} = &tools::escape_docname($dir,'/'); if ($def_desc) { ## description $file_info{'title'} = $desc_hash{'title'}; $file_info{'escaped_title'}=&tools::escape_html($desc_hash{'title'}); ## author if ($desc_hash{'email'}) { $file_info{'author'} = $desc_hash{'email'}; } } push @result,\%file_info; } } # else (file) } # foreach return \@result; } #******************************************* # Function : do_d_editfile # Description : prepares the parameters to # edit a file #******************************************* sub do_d_editfile { &wwslog('info', 'do_d_editfile(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; my $visible_path = &make_visible_path($path); $param->{'directory'} = -d "$shareddir/$path"; # Control unless ($path) { &report::reject_report_web('user','missing_arg',{'argument' => 'file name'},$param->{'action'}); &wwslog('err','do_d_editfile: no file name'); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return undef; } # Existing document? File? unless (-w "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('err',"d_editfile : Cannot edit $shareddir/$path : not an existing file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef; } ### Document isn't a description file? unless ($path !~ /\.desc/) { &wwslog('err',"do_editfile : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } if (($path =~ /\.url$/) ||($path =~ /^\..+\.url.moderate$/)) { ## Get URL of bookmark open URL, "$shareddir/$path"; my $url = ; close URL; chomp $url; $param->{'url'} = $url; $visible_path =~ s/\.url$//; } ### is list open ? unless ($list->{'admin'}{'status'} eq 'open'){ &report::reject_report_web('user','list_not_open',{'status' => $list->{'admin'}{'status'}},$param->{'action'},$list); &wwslog('err','d_edit : access denied for %s because list is not open', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); my $may_edit = $access{'may'}{'edit'}; unless ($may_edit > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','d_editfile : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'autorization'}); return undef; } ## End of controls $param->{'list'} = $list_name; $param->{'path'} = $path; $param->{'visible_path'} = $visible_path; # test if it's a text file if (-T "$shareddir/$path") { $param->{'textfile'} = 1; $param->{'filepath'} = "$shareddir/$path"; } else { $param->{'textfile'} = 0; } $param->{'use_htmlarea'} = '1' if (($wwsconf->{'htmlarea_url'}) and ($param->{'textfile'}) and ($path =~ /\.html?/)); #Current directory if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { $param->{'father'} = $1; }else { $param->{'father'} = ''; } $param->{'escaped_father'} = &tools::escape_docname($param->{'father'}, '/'); # Description of the file my $descfile; if (-d "$shareddir/$path") { $descfile = "$shareddir/$1$3/.desc"; }else { $descfile = "$shareddir/$1.desc.$3"; } if (-e $descfile) { my %desc_hash = &get_desc_file($descfile); $param->{'desc'} = $desc_hash{'title'}; $param->{'doc_owner'} = $desc_hash{'email'}; ## Synchronization my @info = stat $descfile; $param->{'serial_desc'} = $info[9]; } ## Synchronization my @info = stat "$shareddir/$path"; $param->{'serial_file'} = $info[9]; ## parameters of the current directory $param->{'doc_date'} = gettext_strftime "%d %b %y %H:%M", localtime($info[9]); $allow_absolute_path = 1; $param->{'father_icon'} = $icon_table{'father'}; &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 1; } #******************************************* # Function : do_d_properties # Description : prepares the parameters to # change a file properties #******************************************* sub do_d_properties { &wwslog('info', 'do_d_properties(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; my $visible_path = &make_visible_path($path); $param->{'directory'} = -d "$shareddir/$path"; # Control unless ($path) { &report::reject_report_web('user','missing_arg',{'argument' => 'filename'},$param->{'action'}); &wwslog('err','do_d_properties: no file name'); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef; } # Existing document? File? unless (-w "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('err',"do_d_properties : Cannot edit $shareddir/$path : not an existing file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef; } ### Document isn't a description file? unless ($path !~ /\.desc/) { &wwslog('err',"do_d_properties : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } if ($path =~ /\.url$/) { ## Get URL of bookmark open URL, "$shareddir/$path"; my $url = ; close URL; chomp $url; $param->{'url'} = $url; } # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); my $may_edit = $access{'may'}{'edit'}; unless ($may_edit > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_properties : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } ## End of controls $param->{'list'} = $list_name; $param->{'path'} = $path; $param->{'visible_path'} = $visible_path; # test if it's a text file if (-T "$shareddir/$path") { $param->{'textfile'} = 1; $param->{'filepath'} = "$shareddir/$path"; } else { $param->{'textfile'} = 0; } $param->{'use_htmlarea'} = '1' if (($wwsconf->{'htmlarea_url'}) and ($param->{'textfile'}) and ($path =~ /\.html?/)); #Current directory if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { $param->{'father'} = $1; }else { $param->{'father'} = ''; } $param->{'escaped_father'} = &tools::escape_docname($param->{'father'}, '/'); $param->{'fname'} = &make_visible_path($3); # Description of the file my $descfile; if (-d "$shareddir/$path") { $descfile = "$shareddir/$1$3/.desc"; }else { $descfile = "$shareddir/$1.desc.$3"; } if (-e $descfile) { my %desc_hash = &get_desc_file($descfile); $param->{'desc'} = $desc_hash{'title'}; $param->{'doc_owner'} = $desc_hash{'email'}; ## Synchronization my @info = stat $descfile; $param->{'serial_desc'} = $info[9]; } ## Synchronization my @info = stat "$shareddir/$path"; $param->{'serial_file'} = $info[9]; ## parameters of the current directory $param->{'doc_date'} = gettext_strftime "%d %b %y %H:%M", localtime($info[9]); $allow_absolute_path = 1; $param->{'father_icon'} = $icon_table{'father'}; &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 1; } #******************************************* # Function : do_d_describe # Description : Saves the description of # the file #****************************************** sub do_d_describe { &wwslog('info', 'do_d_describe(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); my $visible_path=&make_visible_path($path); my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; #### Controls ### Document isn't a description file? unless ($path !~ /\.desc/) { &wwslog('info',"do_d_describe : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## the path must not be empty (the description file of the shared directory # doesn't exist) unless ($path) { &report::reject_report_web('intern','cannot_describe_shared_directory',{'path' => $path },$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"d_describe : Cannot describe $shareddir : root directory"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # the file to describe must already exist unless (-e "$shareddir/$path") { &report::reject_report_web('user','no_doc_to_describe',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('info',"d_describe : Unable to describe $shareddir/$path : not an existing document"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef;in{'shortname'} } # Access control # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('info','d_describe : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } ## End of controls if ($in{'content'} !~ /^\s*$/) { # Description file $path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; my $dir = $1; my $file = $3; my $desc_file; if (-d "$shareddir/$path") { $desc_file = "$shareddir/$dir$file/.desc"; } else { $desc_file = "$shareddir/$dir.desc.$file"; } if (-r "$desc_file"){ # if description file already exists : open it and modify it my %desc_hash = &get_desc_file ("$desc_file"); # Synchronization unless (&synchronize($desc_file,$in{'serial'})){ &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('info',"d_describe : Synchronization failed for $desc_file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # fill the description file unless (open DESC,">$desc_file") { &wwslog('info',"do_d_describe : cannot open $desc_file : $!"); &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # information modified print DESC "title\n $in{'content'}\n\n"; # information not modified print DESC "access\n read $desc_hash{'read'}\n edit $desc_hash{'edit'}\n\n"; print DESC "creation\n"; # time print DESC " date_epoch $desc_hash{'date'}\n"; # author print DESC " email $desc_hash{'email'}\n\n"; close DESC; } else { # Creation of a description file unless (open (DESC,">$desc_file")) { &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"d_describe : Cannot create description file $desc_file : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # fill # description print DESC "title\n $in{'content'}\n\n"; # date and author my @info = stat "$shareddir/$path"; print DESC "creation\n date_epoch ".$info[10]."\n email\n\n"; # access rights print DESC "access\n"; print DESC " read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n\n"; close DESC; } $in{'path'} = &no_slash_end($dir); } &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 'd_read'; } #******************************************* # Function : do_d_savefile # Description : Saves a file edited in a # text area #****************************************** sub do_d_savefile { &wwslog('info', 'do_d_savefile(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); if ($in{'url'} && $in{'previous_action'} eq 'd_read') { $path .= '/'.$in{'name_doc'} . '.url'; } my $visible_path = &make_visible_path($path); my $moderated; if ($visible_path ne $path) { $moderated = 1; } #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; #### Controls my $creation = 1 unless (-f "$shareddir/$path"); ### Document isn't a description file unless ($path !~ /\.desc/) { &wwslog('err',"do_d_savefile : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_savefile : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } #### End of controls if (($in{'content'} =~ /^\s*$/) && ($in{'url'} =~ /^\s*$/)) { &report::reject_report_web('user','no_content',{},$param->{'action'},$list); &wwslog('err',"do_d_savefile : Cannot save file $shareddir/$path : no content"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'missing_parameter'}); return undef; } # Synchronization unless ($in{'url'}) { # only for files unless (&synchronize("$shareddir/$path",$in{'serial'})){ &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('err',"do_d_savefile : Synchronization failed for $shareddir/$path"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } } # Renaming of the old file ############""" pas les url ? rename ("$shareddir/$path","$shareddir/$path.old") unless ($creation); my $dir; my $file; if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/){ $dir = $1; $file = $3; } if ($in{'url'}) { ############## # if ($access{'may'}{'edit'} == 0.5) { # open URL, ">$shareddir/$dir.$file.moderate"; # }else { open URL, ">$shareddir/$path"; # } print URL "$in{'url'}\n"; close URL; }else { # Creation of the shared file unless (open FILE, ">$shareddir/$path") { rename("$shareddir/$path.old","$shareddir/$path"); &report::reject_report_web('user','cannot_overwrite', {'reason' => $1, 'path' => $visible_path } ,$param->{'action'},$list); &wwslog('err',"do_d_savefile : Cannot open for replace $shareddir/$path : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } print FILE $in{'content'}; close FILE; } unlink "$shareddir/$path.old"; # Description file if (-e "$shareddir/$dir.desc.$file"){ # if description file already exists : open it and modify it my %desc_hash = &get_desc_file ("$shareddir/$dir.desc.$file"); open DESC,">$shareddir/$dir.desc.$file"; # information not modified print DESC "title\n $desc_hash{'title'}\n\n"; print DESC "access\n read $desc_hash{'read'}\n edit $desc_hash{'edit'}\n\n"; print DESC "creation\n"; # date print DESC ' date_epoch '.$desc_hash{'date'}."\n"; # information modified # author print DESC " email $param->{'user'}{'email'}\n\n"; close DESC; } else { # Creation of a description file if author is known unless (open (DESC,">$shareddir/$dir.desc.$file")) { &wwslog('info',"do_d_savefile: cannot create description file $shareddir/$dir.desc.$file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); } # description print DESC "title\n \n\n"; # date of creation and author my @info = stat "$shareddir/$path"; print DESC "creation\n date_epoch ".$info[10]."\n email $param->{'user'}{'email'}\n\n"; # Access print DESC "access\n"; print DESC " read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n\n"; close DESC; } # shared_moderated ####################### if (($access{'may'}{'edit'} == 0.5) && ($creation)) { unless (rename "$shareddir/$path","$shareddir/$dir.$file.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir/$path", 'new'=>"$shareddir/$dir.$file.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_savefile : Failed to rename $path to $dir.$file.moderate : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); } unless (rename "$shareddir/$dir.desc.$file","$shareddir/$dir.desc..$file.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir/$dir.desc.$file", 'new'=>"$shareddir/$dir.desc..$file.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_savefile : Failed to rename $dir.desc.$file to $dir.desc..$file.moderate : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); } if (!$in{'url'}){ $in{'path'}=$path; $param->{'path'}=$path; }else { $visible_path = $file; $visible_path =~ s/\.url$// } unless ($list->send_notify_to_editor('shared_moderated',{'filename' => $visible_path, 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } &report::notice_report_web('to_moderate', {'path' => $visible_path},$param->{'action'}); } &report::notice_report_web('save_success', {'path' => $visible_path},$param->{'action'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); if ($in{'previous_action'}) { return $in{'previous_action'}; }else { $in{'path'} =~ s/([^\/]+)$//; $param->{'path'} =~ s/([^\/]+)$//; return 'd_read'; } } #******************************************* # Function : do_d_overwrite # Description : Overwrites a file with a # uploaded file #****************************************** sub do_d_overwrite { &wwslog('info', 'do_d_overwrite(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); my $visible_path = &make_visible_path($path); #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; # Parameters of the uploaded file my $fh = $query->upload('uploaded_file'); my $fn = $query->param('uploaded_file'); # name of the file my $fname; if ($fn =~ /([^\/\\]+)$/) { $fname = $1; } ### uploaded file must have a name unless ($fname) { &report::reject_report_web('user','missing_arg',{'argument' => 'file name'},$param->{'action'}); &wwslog('info',"do_d_overwrite : No file specified to overwrite"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef; } ####### Controls ### Document isn't a description file? unless ($path !~ /\.desc/) { &wwslog('err',"do_d_overwrite : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # the path to replace must already exist unless (-e "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('err',"do_d_overwrite : Unable to overwrite $shareddir/$path : not an existing file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'no_file'}); return undef; } # the path must represent a file if (-d "$shareddir/$path") { &report::reject_report_web('user','doc_already_a_dir',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('err',"do_d_overwrite : Unable to create $shareddir/$path : a directory named $path already exists"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'already_exists'}); return undef; } # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_overwrite : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } #### End of controls # Synchronization unless (&synchronize("$shareddir/$path",$in{'serial'})){ &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('err',"do_d_overwrite : Synchronization failed for $shareddir/$path"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # Renaming of the old file rename ("$shareddir/$path","$shareddir/$path.old"); # Creation of the shared file unless (open FILE, ">:bytes", "$shareddir/$path") { &report::reject_report_web('user','cannot_overwrite', {'reason' => $!, 'path' => $visible_path } ,$param->{'action'},$list); &wwslog('err',"d_overwrite : Cannot open for replace $shareddir/$path : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'cannot_overwrite'}); return undef; } while (<$fh>) { print FILE; } close FILE; # Description file my ($dir, $file); if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { $dir = $1; $file = $3; } if (-e "$shareddir/$dir.desc.$file"){ # if description file already exists: open it and modify it my %desc_hash = &get_desc_file ("$shareddir/$dir.desc.$file"); open DESC,">$shareddir/$dir.desc.$file"; # information not modified print DESC "title\n $desc_hash{'title'}\n\n"; print DESC "access\n read $desc_hash{'read'}\n edit $desc_hash{'edit'}\n\n"; print DESC "creation\n"; # time print DESC " date_epoch $desc_hash{'date'}\n"; # information modified # author print DESC " email $param->{'user'}{'email'}\n\n"; close DESC; } else { # Creation of a description file unless (open (DESC,">$shareddir/$dir.desc.$file")) { &wwslog('info',"do_d_overwrite : Cannot create description file $shareddir/$dir.desc.$file"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } # description print DESC "title\n \n\n"; # date of creation and author my @info = stat "$shareddir/$path"; print DESC "creation\n date_epoch ".$info[10]."\n email $param->{'user'}{'email'}\n\n"; # access rights print DESC "access\n"; print DESC " read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n\n"; close DESC; } # shared_moderated if (($access{'may'}{'edit'} == 0.5) && ($path eq $visible_path)) { unless (rename "$shareddir/$path","$shareddir/$dir.$file.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir/$path", 'new'=>"$shareddir/$dir.$file.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_overwrite : Failed to rename $path to $dir.$file.moderate : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); return undef; } unless (rename "$shareddir/$dir.desc.$file","$shareddir/$dir.desc..$file.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir/$dir.desc.$file", 'new'=>"$shareddir/$dir.desc..$file.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_overwrite : Failed to rename $dir.desc.$file to $dir.desc..$file.moderate : $!"); &web_db_log({'parameters' => $in{'path'}, 'status' => 'error', 'error_type' => 'internal'}); } unless ($list->send_notify_to_editor('shared_moderated',{'filename' => $visible_path, 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } $in{'path'}="$dir.$file.moderate"; &report::notice_report_web('to_moderate',{'path' => $visible_path},$param->{'action'}); } # Removing of the old file unlink "$shareddir/$path.old"; $in{'list'} = $list_name; #$in{'path'} = $dir; # message of success &report::notice_report_web('upload_success', {'path' => $visible_path}); &web_db_log({'parameters' => $in{'path'}, 'status' => 'success'}); return 'd_editfile'; } #******************************************* # Function : do_d_upload # Description : Creates a new file with a # uploaded file #****************************************** sub do_d_upload { # Parameters of the uploaded file (from d_read.tt2) my $fn = $in{'uploaded_file'}; # name of the file, without path my ($fname, $visible_fname); if ($fn =~ /([^\/\\]+)$/) { $fname = &tools::qencode_filename($1); $visible_fname = &make_visible_path($fname); } # param from d_upload.tt2 if ($in{'shortname'}){ $fname = $in{'shortname'}; } &wwslog('info', 'do_d_upload(%s/%s)', $in{'path'},$fname); # Variables my $path = &no_slash_end($in{'path'}); my $visible_path = &make_visible_path($path); # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; # name of the file my $longname = "$shareddir/$path/$fname"; $longname =~ s/\/+/\//g; # ## $path must have a slash at its end # $path = &format_path('with_slash',$path); #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; ## Controls # uploaded file must have a name unless ($fname) { &report::reject_report_web('user','no_name',{},$param->{'action'},$list); &wwslog('err',"do_d_upload : No file specified to upload"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## Check quota if ($list->{'admin'}{'shared_doc'}{'quota'}) { if ($list->get_shared_size() >= $list->{'admin'}{'shared_doc'}{'quota'} * 1024){ &report::reject_report_web('user','shared_full',{},$param->{'action'},$list); &wwslog('err',"do_d_upload : Shared Quota exceeded for list $list->{'name'}"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'shared_full','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } # The name of the file must be correct and musn't not be a description file if ($fname =~ /^\./ || $fname =~ /\.desc/ || $fname =~ /[~\#\[\]]$/) { # unless ($fname =~ /^\w/ and # $fname =~ /\w$/ and # $fname =~ /^[\w\-\.]+$/ and # $fname !~ /\.desc/) { &report::reject_report_web('user','incorrect_name',{'name' => $fname},$param->{'action'},$list); &wwslog('err',"do_d_upload : Unable to create file $fname : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # the file must be uploaded in a directory existing unless (-d "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('err',"do_d_upload : $shareddir/$path : not a directory"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control for the directory where there is the uploading my %mode; $mode{'edit'} = 1; $mode{'control'} = 1; # for the exception index.html my %access_dir = &d_access_control(\%mode,$path); if ($access_dir{'may'}{'edit'} == 0) { &report::reject_report_web('auth',$access_dir{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_upload : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Lowercase for file name # $fname = $fname; ## when the file already exists : # the temporary name of the uploaded file : with .duplicate my $tmpname="."."$fname".".duplicate"; my $longtmpname="$shareddir/$path/$tmpname"; $longtmpname =~ s/\/+/\//g; # the temporary desc of the uploaded file : with .duplicate my $tmpdesc=".desc."."$tmpname"; my $longtmpdesc="$shareddir/$path/$tmpdesc"; $longtmpdesc =~ s/\/+/\//g; # if we aren't in mode_delete nor in mode_rename nor in mode_cancel and the file already exists # then we create of a temporary file if ((-e "$longname") && ($in{'mode_delete'} eq undef) && ($in{'mode_rename'} eq undef) && ($in{'mode_cancel'} eq undef)) { #access control for the file already existing my %mode; $mode{'edit'} = 1; my %access_file = &d_access_control(\%mode,"$path/$fname"); unless ($access_file{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access_file{'reason'}{'edit'},{},$param->{'action'},$list); return undef; } if (-e "$longtmpname"){ # if exists a temp file younger than 5 minutes that belongs to another user : upload refused my @info = stat $longtmpname; my $timeold = time - $info[10]; if ($timeold<=300){ my %desc_hash = &get_desc_file($longtmpdesc); unless($desc_hash{'email'} eq $param->{'user'}{'email'}){ &report::reject_report_web('user','cannot_upload',{'path' => "$visible_path/$visible_fname", 'reason' => "file being uploaded by $desc_hash{'email'} at this time" }, $param->{'action'},$list); &wwslog('err',"do_d_upload : Unable to upload $longtmpname : file being uploaded at this time "); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } } &creation_shared_file($shareddir,$path,$tmpname); &creation_desc_file($shareddir,$path,$tmpname,%access_file); my @info = stat $longname; $param->{'serial_file'} = $info[9]; $param->{'path'} = $path; $param->{'shortname'} = $fname; return 1; } # for the moderation my $longmodname = "$shareddir/$path/"."."."$fname".".moderate"; $longmodname =~ s/\/+/\//g; my $longmoddesc="$shareddir/$path/".".desc.."."$fname".".moderate"; $longmoddesc =~ s/\/+/\//g; # when a file is already waiting for moderation my $file_moderated; if (-e "$longmodname"){ my %desc_hash = &get_desc_file("$longmoddesc"); $file_moderated = 1; unless($desc_hash{'email'} eq $param->{'user'}{'email'}){ &report::reject_report_web('user','cannot_upload',{'path' => "$path/$fname", 'reason' => "file already exists but not yet moderated"}, $param->{'action'},$list); &wwslog('err',"do_d_upload : Unable to create $longname : file already exists but not yet moderated"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } ## Exception index.html unless ($fname !~ /^index.html?$/i) { unless ($access_dir{'may'}{'control'}) { &report::reject_report_web('user','index_html',{'dir' => $path, 'reason' => "d_access_control"}, $param->{'action'},$list); &wwslog('err',"do_d_upload : $param->{'user'}{'email'} not authorized to upload a INDEX.HTML file in $path"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } # if we're in mode_delete or mode_rename or mode_cancel, the temp file and his desc file must exist if ($in{'mode_delete'} || $in{'mode_rename'} || $in{'mode_cancel'}) { unless(-e $longtmpname){ &report::reject_report_web('user','no_uploaded_file',{},$param->{'action'},$list); &wwslog('err',"do_d_upload : there isn't any temp file for the uploaded file $fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless(-e $longtmpdesc){ &report::reject_report_web('user','no_uploaded_file',{},$param->{'action'},$list); &wwslog('err',"do_d_upload : there isn't any desc temp file for the uploaded file $fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } ## End of controls # in mode_delete the file is going to be overwritten if ($in{'mode_delete'}) { # Synchronization unless (&synchronize("$longname",$in{'serial'})){ &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('err',"do_d_upload : Synchronization failed for $longname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'synchro_failed','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Renaming the tmp file and the desc file if ($access_dir{'may'}{'edit'} == 1 ){ # Renaming of the old file my $longgoodname="$shareddir/$path/$fname"; $longgoodname =~ s/\/+/\//g; unless (rename "$longgoodname","$longgoodname.old"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longgoodname", 'new'=>"$longgoodname.old"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to .old : %s",$longgoodname, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Renaming of the old desc my $longgooddesc="$shareddir/$path/".".desc."."$fname"; $longgooddesc =~ s/\/+/\//g; unless (rename "$longgooddesc","$longgooddesc.old"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longgooddesc", 'new'=>"$longgooddesc.old"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to .old : %s", $longgooddesc, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } # the tmp file unless (rename "$longtmpname","$longgoodname"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpname", 'new'=>"$longgoodname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpname, $longgoodname, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } # the tmp desc file unless (rename "$longtmpdesc","$longgooddesc"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpdesc", 'new'=>"$longgooddesc"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpdesc, $longgooddesc, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } }elsif ($access_dir{'may'}{'edit'} == 0.5 ){ unless (rename "$longtmpname","$longmodname"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpname", 'new'=>"$longmodname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpname, $longmodname, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless (rename "$longtmpdesc","$longmoddesc"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpdesc", 'new'=>"$longmoddesc"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpdesc, $longmoddesc, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless ($list->send_notify_to_editor('shared_moderated',{'filename' => "$path/$fname", 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } }else { &report::reject_report_web('auth',$access_dir{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_upload : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # $in{'list'} = $list_name; # message of success &report::notice_report_web('upload_success', {'path' => $fname},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'd_read'; } # in mode_rename the file is going to be renamed if ($in{'mode_rename'}) { my $longnewname="$shareddir/$path/$in{'new_name'}"; $longnewname =~ s/\/+/\//g; # Control new document name unless ($in{'new_name'}) { &report::reject_report_web('user','missing_arg',{'argument' => 'new name'},$param->{'action'}); &wwslog('err',"do_d_upload : new name missing to rename the uploaded file"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'missing_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($in{'new_name'} =~ /^\./ || $in{'new_name'} =~ /\.desc/ || $in{'new_name'} =~ /[~\#\[\]\/]$/) { &report::reject_report_web('user','incorrect_name',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_upload : Unable to create file $in{'new_name'} : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if (($fname =~ /\.url$/) && ($in{'new_name'} !~ /\.url$/)) { &report::reject_report_web('user','incorrect_name',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_upload : New file name $in{'new_name'} does not match URL filenames"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if (-e $longnewname){ &report::reject_report_web('user','doc_already_exist',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_upload : $in{'new_name'} is an existing name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # when a file is already waiting for moderation if (-e "$shareddir/$path/.$in{'new_name'}.moderate"){ &report::reject_report_web('user','doc_already_exist',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_upload : $in{'new_name'} is an existing name for a not yet moderated file" ); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # when a file is being uploaded if (-e "$shareddir/$path/.$in{'new_name'}.duplicate"){ &report::reject_report_web('user','doc_already_exist',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_upload : $in{'new_name'} is an existing name for a file being uploaded "); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } # Renaming the tmp file and the desc file if ($access_dir{'may'}{'edit'} == 1 ){ unless (rename "$longtmpname","$longnewname"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpname", 'new'=>"$longnewname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpname, $longnewname, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } my $longnewdesc="$shareddir/$path/.desc.$in{'new_name'}"; $longnewdesc =~ s/\/+/\//g; unless (rename "$longtmpdesc","$longnewdesc"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpdesc", 'new'=>"$longnewdesc"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename %s to %s : %s", $longtmpdesc, $longnewdesc, $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } }elsif ($access_dir{'may'}{'edit'} == 0.5 ){ unless (rename "$longtmpname","$shareddir/$path/.$in{'new_name'}.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpname", 'new'=>"$shareddir/$path/.$in{'new_name'}.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename $longtmpname to $shareddir/$path/.$in{'new_name'}.moderate : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless (rename "$longtmpdesc","$shareddir/$path/.desc..$in{'new_name'}.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$longtmpdesc", 'new'=>"$shareddir/$path/.desc..$in{'new_name'}.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_upload : Failed to rename $longtmpdesc to $shareddir/$path/.desc..$in{'new_name'}.moderate: $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless ($list->send_notify_to_editor('shared_moderated',{'filename' => "$path/$in{'new_name'}", 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } }else { &report::reject_report_web('auth',$access_dir{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_upload : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # $in{'list'} = $list_name; # message of success &report::notice_report_web('upload_success', {'path' => $fname},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'd_read'; } # in mode_cancel, we delete the temp file and his desc if ($in{'mode_cancel'}) { # removing of the temp file unless (unlink($longtmpname)) { &report::reject_report_web('intern','erase_file',{'file' => $longtmpname},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_d_upload: failed to erase the temp file %s', $longtmpname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # removing of the description temp file unless (unlink($longtmpdesc)) { &report::reject_report_web('intern','erase_file',{'file' => $longtmpdesc},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_d_upload: failed to erase the desc temp file %s', $longtmpdesc); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } return 'd_read'; } ## usual case # shared_moderated if ($access_dir{'may'}{'edit'} == 0.5 ) { my $modname="."."$fname".".moderate"; &creation_shared_file($shareddir,$path,$modname); &creation_desc_file($shareddir,$path,$modname,%access_dir); unless ($file_moderated){ unless ($list->send_notify_to_editor('shared_moderated',{'filename' => "$path/$fname", 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } } &report::notice_report_web('to_moderate', {'path' => $fname},$param->{'action'}); } else { &creation_shared_file($shareddir,$path,$fname); &creation_desc_file($shareddir,$path,$fname,%access_dir); } $in{'list'} = $list_name; &report::notice_report_web('upload_success', {'path' => $visible_fname},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$visible_path,$visible_fname",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'd_read'; } ## Creation of a picture file sub creation_picture_file { my($root_dir, $path ,$fname)=@_; unless(-d $root_dir.'/'.$path) { &wwslog('notice',"creation_picture_file : Create dir $root_dir/$path/"); unless (&tools::mkdir_all($root_dir.'/'.$path, 0755)){ &wwslog('err',"creation_picture_file : Unable to create dir $root_dir/$path/"); return undef; } unless (open(FF,">$root_dir".'/'.$path.'/index.html')){ &wwslog('err',"creation_picture_file : Unable to create dir $root_dir/$path/index.html"); } chmod 0755, $root_dir.'/'.$path.'/index.html'; close FF; } my $fh = $query->upload('uploaded_file'); unless (open FILE, ">:bytes", "$root_dir/$path/$fname") { &report::reject_report_web('intern','cannot_upload',{'path' => "$path/$fname"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"creation_picture_file : Cannot open file $root_dir/$path/$fname : $!"); return undef; } while (<$fh>) { print FILE; } close FILE; chmod 0755, "$root_dir/$path/$fname"; } ## Creation of a shared file sub creation_shared_file { my($shareddir,$path,$fname)=@_; unless(-d $shareddir.'/'.$path) { &wwslog('notice',"creation_shared_file : Create dir $shareddir/$path/"); unless (mkdir($shareddir.'/'.$path,0755)){ &wwslog('err',"creation_shared_file : Unable to create dir $shareddir/$path/"); return undef; } } my $fh = $query->upload('uploaded_file'); unless (open FILE, ">:bytes", "$shareddir/$path/$fname") { &report::reject_report_web('intern','cannot_upload',{'path' => "$path/$fname"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"creation_shared_file : Cannot open file $shareddir/$path/$fname : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } while (<$fh>) { print FILE; } close FILE; ## XSS Protection for HTML files. if (lc($fname) =~ /\.html?/) { my $sanitized_file = &tools::sanitize_html_file('robot' => $robot, 'file' => "$shareddir/$path/$fname"); if (defined $sanitized_file) { open HTMLFILE, ">:bytes", "$shareddir/$path/$fname"; print HTMLFILE $sanitized_file; close HTMLFILE; } else { &do_log('err','Unable to sanitize file %s',$fname); } } } ## Creation of the description file sub creation_desc_file { my($shareddir,$path,$fname,%access)=@_; unless (open (DESC,">$shareddir/$path/.desc.$fname")) { &wwslog('err',"creation_desc_file: cannot create description file $shareddir/.desc.$path/$fname"); } print DESC "title\n \n\n"; print DESC "creation\n date_epoch ".time."\n email $param->{'user'}{'email'}\n\n"; print DESC "access\n"; print DESC " read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n"; close DESC; } #******************************************* # Function : do_d_unzip # Description : unzip a file or a tree structure # from an uploaded zip file #****************************************** sub do_d_unzip { # Parameters of the uploaded file (from d_read.tt2) my $fn = $in{'unzipped_file'}; # name of the file, without path my $fname; if ($fn =~ /([^\/\\]+)$/) { $fname = $1; } &wwslog('info', 'do_d_unzip(%s/%s)', $in{'path'},$fname); # Variables my $path = &no_slash_end($in{'path'}); # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; # name of the file my $longname = "$shareddir/$path/$fname"; $longname =~ s/\/+/\//g; ## Controls my $listname = $list->{'name'}; # uploaded file must have a name unless ($fname) { &report::reject_report_web('user','no_name',{},$param->{'action'},$list); &wwslog('err',"do_d_unzip(%s/%s) : No file specified to upload",$path,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # must have .zip extension unless ($fname =~ /^.+\.zip$/) { &report::reject_report_web('user','incorrect_name',{'name' => "$fname", 'reason' => "must have the '.zip' extension"},$param->{'action'},$list); &wwslog('err',"do_d_unzip(%s/%s) : the file must have '.zip' extension",$path,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## Check quota if ($list->{'admin'}{'shared_doc'}{'quota'}) { if ($list->get_shared_size() >= $list->{'admin'}{'shared_doc'}{'quota'} * 1024){ &report::reject_report_web('user','shared_full',{},$param->{'action'},$list); &wwslog('err',"do_d_unzip(%s/%s) : Shared Quota exceeded for list $list->{'name'}",$path,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'shared_full','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } # The name of the file must be correct and must not be a description file if ($fname =~ /^\./ || $fname =~ /\.desc/ || $fname =~ /[~\#\[\]]$/) { &report::reject_report_web('user','incorrect_name',{'name' => "$fname"},$param->{'action'},$list); &wwslog('err',"do_d_unzip(%s/%s) : incorrect name",$path,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # the file must be uploaded in a directory existing unless (-d "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $path},$param->{'action'},$list); &wwslog('err',"do_d_unzip(%s/%s) : $shareddir/$path : not a directory",$path,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control for the directory where there is the uploading # only for (is_author || !moderated) my %mode; $mode{'edit'} = 1; my %access_dir = &d_access_control(\%mode,$path); if ($access_dir{'may'}{'edit'} == 0) { &report::reject_report_web('auth',$access_dir{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_unzip(%s/%s) : access denied for %s',$path,$fname, $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($access_dir{'may'}{'edit'} == 0.5) { &report::reject_report_web('auth','edit_moderated',{},$param->{'action'},$list); &wwslog('err','do_d_unzip(%s/%s) : access denied for %s',$path,$fname, $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## End of control # directory for the uploaded file my $date = time; my $zip_dir_name = $listname.$date.$$; my $zip_abs_dir = $Conf{'tmpdir'}.'/'.$zip_dir_name; unless (mkdir ("$zip_abs_dir",0777)) { &report::reject_report_web('intern','cannot_mkdir',{'dir' => $zip_abs_dir},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_unzip($path/$fname) : Unable to create $zip_abs_dir : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### directory for unzipped files unless (mkdir ("$zip_abs_dir"."/zip",0777)) { &report::reject_report_web('intern','cannot_mkdir',{'dir' => "$zip_abs_dir"."/zip"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_unzip($path/$fname) : Unable to create $zip_abs_dir/zip : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### uploaded of the file.zip my $fh = $query->upload('unzipped_file'); unless (open FILE, ">:bytes", "$zip_abs_dir/$fname") { &report::reject_report_web('intern','cannot_upload',{'path' => "$path/$fname"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_unzip($path/$fname) : Cannot open file $zip_abs_dir/$fname : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } while (<$fh>) { print FILE; } close FILE; ### unzip the file my $status = &d_unzip_shared_file($zip_abs_dir,$fname,$path); unless (defined($status)) { &report::reject_report_web('intern','cannot_unzip',{'path' => "$zip_abs_dir/$fname", 'name' => $fname},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_unzip($path/$fname) : Unable to unzip the file $zip_abs_dir/$fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless ($status) { &report::reject_report_web('intern','cannot_unzip',{'name' => "$fname"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); } ### install the file hierarchy unless (&d_install_file_hierarchy("$zip_abs_dir/zip",$shareddir,$path,\%access_dir)) { &wwslog('err',"do_d_unzip($path/$fname) : unable to install file hierarchy"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## remove tmp directories and files # &tools::remove_dir($zip_abs_dir); $in{'list'} = $listname; &report::notice_report_web('unzip_success', {'path' => $fname},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'd_read' } ## unzip a shared file in the tmp directory sub d_unzip_shared_file { my ($zip_abs_dir,$fname) = @_; &wwslog('info', 'd_unzip_shared_file(%s/%s)', $zip_abs_dir,$fname); my $status = 1; my $zip = Archive::Zip->new(); my $az = $zip->read( "$zip_abs_dir/$fname" ); unless ($az == AZ_OK){ &wwslog('err',"unzip_shared_file : Unable to read the zip file $zip_abs_dir/$fname : $az"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$zip_abs_dir,$fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my @memberNames = $zip->memberNames(); foreach my $name (@memberNames) { my $az = $zip->extractMember($name, $zip_abs_dir.'/zip/'.$name); unless ($az == AZ_OK) { &wwslog('err',"unzip_shared_file : Unable to extract member $name of the zip file $zip_abs_dir/$fname : $az"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$zip_abs_dir,$fname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); $status = 0; } } ## Qencode 8bit filenames afterward ## The suspected charset is the one that is associated to the user's language &tools::qencode_hierarchy($zip_abs_dir.'/zip', &Language::GetCharset()); return $status; } ## Install file hierarchy from $tmp_dir directory to $shareddir/$path directory sub d_install_file_hierarchy { my ($tmp_dir,$shareddir,$path,$access_dir)=@_; &wwslog('debug2', 'd_install_file_hierarchy(%s,%s)',$tmp_dir,$path); $tmp_dir = &no_slash_end($tmp_dir); $shareddir = &no_slash_end($shareddir); $path = &no_slash_end($path); my $fatal_error = 0; unless (opendir DIR,"$tmp_dir") { &report::reject_report_web('intern','cannot_open_dir',{'dir' => $tmp_dir},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','d_install_file_hierarchy(%s) : impossible to open %s directory',$path,$tmp_dir); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$tmp_dir,$shareddir,$path,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my @from_dir = readdir DIR; closedir DIR; foreach my $doc (@from_dir) { next if($doc eq '.' || $doc eq '..'); if (-d "$tmp_dir/$doc") { if ($fatal_error) { &report::reject_report_web('user','directory_no_copied',{'name'=> "$path/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); }else { unless (&d_copy_rec_dir("$tmp_dir","$path","$shareddir/$path",$doc)){ $fatal_error = 1; &report::reject_report_web('user','directory_no_copied',{'name'=> "$path/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); # return undef; } } } else { if ($fatal_error) { &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); }else { unless (&d_copy_file("$tmp_dir","$path","$shareddir/$path",$doc,$access_dir)) { &wwslog('err',"d_install_hierarchy($path) : fatal error from d_copy_file($doc)"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$tmp_dir,$shareddir,$path,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); $fatal_error = 1; &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); } # return undef; } } } if ($fatal_error) { return undef; }else { &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$tmp_dir,$shareddir,$path,$access_dir",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } } ## copy $dname from $from to $list->{shared}/$path if rights are ok sub d_copy_rec_dir { my ($from,$path,$dest_dir,$dname) = @_; &wwslog('debug3', 'd_copy_rec_dir(%s,%s,%s)',$from,$dest_dir,$dname); $from = &no_slash_end($from); $path = &no_slash_end($path); $dest_dir = &no_slash_end($dest_dir); my $fatal_error = 0; # Access control on the directory $path where there is the copy # Copy allowed only for (is_author || !moderate) my %mode; $mode{'edit'} = 1; $mode{'control'} = 1; my %access_dir = &d_access_control(\%mode,$path); unless ($access_dir{'may'}{'edit'} == 1) { &report::reject_report_web('user','directory_no_copied',{'name'=> $dname, 'reason' => "no edition right on father directory"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','d_copy_rec_dir(%s): access denied for %s',$path,$param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } my $may; unless ($may = &d_test_existing_and_rights($path,$dname,$dest_dir)) { &report::reject_report_web('user','directory_no_copied',{'name'=> $dname }, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','d_copy_rec_dir(%s) : error while calling "test_existing_and_rights(%s/%s)"',$dname,$dest_dir,$dname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } unless ($may->{'exists'}) { # The name of the directory must be correct and musn't not be a description file if ($dname =~ /^\./ || $dname =~ /\.desc/ || $dname =~ /[~\#\[\]]$/) { &report::reject_report_web('user','incorrect_name',{'name' => "$dname"},$param->{'action'},$list); &wwslog('err',"d_copy_rec_dir : $dname : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } ## Exception index.html unless ($dname !~ /^index.html?$/i) { &report::reject_report_web('user','index_html',{'dir' => $path},$param->{'action'},$list); &wwslog('err',"d_copy_rec_dir : the directory cannot be called INDEX.HTML "); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } ## directory creation unless (mkdir ("$dest_dir/$dname",0777)) { &report::reject_report_web('user','directory_no_copied',{'name'=> "$dname"},$param->{'action'},$list); &wwslog('err',"d_copy_rec_dir : Unable to create directory $dest_dir/$dname : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } ## desc directory creation unless (open (DESC,">$dest_dir/$dname/.desc")) { &wwslog('err',"d_copy_rec_dir: cannot create description file $dest_dir/$dname/.desc"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } print DESC "title\n \n\n"; print DESC "creation\n date_epoch ".time."\n email $param->{'user'}{'email'}\n\n"; print DESC "access\n"; print DESC " read $access_dir{'scenario'}{'read'}\n"; print DESC " edit $access_dir{'scenario'}{'edit'}\n"; close DESC; } if ($may->{'rights'} || !($may->{'exists'})) { unless (opendir DIR,"$from/$dname") { &report::reject_report_web('user','directory_no_copied',{'name'=> "$dname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','d_copy_rec_dir(%s) : impossible to open %s directory',$dname,$from); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } my @from_dir = readdir DIR; closedir DIR; foreach my $doc (@from_dir) { if ($doc eq '.' || $doc eq '..') { next; } if (-d "$from/$dname/$doc") { if ($fatal_error) { &report::reject_report_web('user','directory_no_copied',{'name'=> "$dname/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); }else { unless (&d_copy_rec_dir("$from/$dname","$path/$dname","$dest_dir/$dname",$doc)){ $fatal_error = 1; &report::reject_report_web('user','directory_no_copied',{'name'=> "$dname/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); # return undef; } } }else { if ($fatal_error) { &report::reject_report_web('user','file_no_copied',{'name'=> "$dname/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); }else { unless (&d_copy_file("$from/$dname","$path/$dname","$dest_dir/$dname",$doc,\%access_dir)){ &wwslog('err',"d_copy_rec_dir($path/$dname) : fatal error from d_copy_file($doc)"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); $fatal_error = 1; &report::reject_report_web('user','file_no_copied',{'name'=> "$dname/$doc", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); } # return undef; } } } }else{ &report::reject_report_web('user','directory_no_copied',{'name'=> $dname, 'reason' => "no edition right on the father directory"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_rec_file : impossible to copy content directory $dname, the user doesn't have edit rights on directory $path"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } if ($fatal_error) { return undef; } else { &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$dname",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } } ## copy $from/$fname to $list->{shared}/$path if rights are ok sub d_copy_file { my ($from,$path,$dest_dir,$fname,$access_dir) = @_; &wwslog('debug3', 'd_copy_file(%s,%s,%s',$from,$dest_dir,$fname); $from = &no_slash_end($from); $path = &no_slash_end($path); $dest_dir = &no_slash_end($dest_dir); my $may; unless ($may = &d_test_existing_and_rights($path,$fname,$dest_dir)) { &report::reject_report_web('user','file_no_copied',{'name'=> "$fname", 'reason' => "quota exceeded"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','d_copy_file(%s) : error while calling "test_existing_and_rights(%s/%s)"',$fname,$dest_dir,$fname); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } if ($may->{'rights'} || !($may->{'exists'})) { # The name of the file must be correct and musn't not be a description file if ($fname =~ /^\./ || $fname =~ /\.desc/ || $fname =~ /[~\#\[\]]$/) { &report::reject_report_web('user','incorrect_name',{'name' => "$fname"},$param->{'action'},$list); &wwslog('err',"d_copy_file : $fname : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } ## Exception index.html unless ($fname !~ /^index.html?$/i) { unless ($access_dir->{'may'}{'control'}) { &report::reject_report_web('user','index_html',{'dir' => $path},$param->{'action'},$list); &wwslog('err',"d_copy_file : the user is not authorized to upload a INDEX.HTML file in $dest_dir"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } } ## Check quota if ($list->{'admin'}{'shared_doc'}{'quota'}) { if ($list->get_shared_size() >= $list->{'admin'}{'shared_doc'}{'quota'} * 1024){ &report::reject_report_web('user','shared_full',{},$param->{'action'},$list); &wwslog('err',"d_copy_file : Shared Quota exceeded for list $list->{'name'} on file $path/$fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'shared_full','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } ## if already existing :delete it unlink ("$dest_dir/$fname") if (-e "$dest_dir/$fname"); unlink ("$dest_dir/.desc.$fname") if (-e "$dest_dir/.desc.$fname"); ## # if exists a temp file younger than 5 minutes that belongs to another user : file copy refused if (-e "$dest_dir/.$fname.duplicate") { my @info = stat "$dest_dir/.$fname.duplicate"; my $timeold = time - $info[10]; if ($timeold <= 300){ my %desc_hash = &get_desc_file("$dest_dir/.desc..$fname.duplicate"); unless($desc_hash{'email'} eq $param->{'user'}{'email'}){ &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$fname", 'reason' => "file being uploading by $desc_hash{'email'} at this time"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_file : unable to copy $path/$fname : file being uploaded at this time "); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } } unlink ("$dest_dir/.$fname.duplicate"); unlink ("$dest_dir/.desc..$fname.duplicate") if (-e "$dest_dir/.desc..$fname.duplicate"); } if (-e "$dest_dir/.$fname.moderate") { my %desc_hash = &get_desc_file("$dest_dir/.$fname.moderate"); unless($desc_hash{'email'} eq $param->{'user'}{'email'}){ &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$fname", 'reason' => "file awaiting for moderation, uploaded by $desc_hash{'email'}"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_file : unable to copy $path/$fname : file awaiting for moderation"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } unlink ("$dest_dir/.$fname.moderate"); unlink ("$dest_dir/.desc..$fname.moderate") if (-e "$dest_dir/.desc..$fname.moderate"); } ## file copy unless (open FROM_FILE,"$from/$fname") { &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$fname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_file : impossible to open $from/$fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } my $visible_fname = &make_visible_path($fname); unless (open DEST_FILE, ">$dest_dir/$fname") { &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$visible_fname"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_file : Cannot create file $dest_dir/$fname : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } while () { print DEST_FILE; } close FROM_FILE; close DEST_FILE; ## XSS Protection for HTML files. if (lc($fname) =~ /\.html?/) { my $sanitized_file = &tools::sanitize_html_file('robot' => $robot, 'file' => "$dest_dir/$fname"); if (defined $sanitized_file) { open HTMLFILE, ">:bytes", "$dest_dir/$fname"; print HTMLFILE $sanitized_file; close HTMLFILE; } else { &do_log('err','Unable to sanitize file %s',$fname); } } ## desc file creation unless (open (DESC,">$dest_dir/.desc.$fname")) { &wwslog('err',"d_copy_file: cannot create description file $dest_dir/.desc.$fname"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } print DESC "title\n \n\n"; print DESC "creation\n date_epoch ".time."\n email $param->{'user'}{'email'}\n\n"; print DESC "access\n"; print DESC " read $access_dir->{'scenario'}{'read'}\n"; print DESC " edit $access_dir->{'scenario'}{'edit'}\n"; close DESC; ## information &report::notice_report_web('file_erased',{'path'=> "$path/$visible_fname"},$param->{'action'}) if ($may->{'exists'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); }else{ &report::reject_report_web('user','file_no_copied',{'name'=> "$path/$fname", 'reason' => "you do not have total edit right on the file"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_copy_file : impossible to copy file $fname, the user doesn't have total edit rights on the file"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$from,$path,$dest_dir,$fname,$access_dir",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_no_copied','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } return 1; } ## return information on file or dir : existing and edit rights for the user in $param sub d_test_existing_and_rights { my ($path,$name,$dest_dir) = @_; $path = &no_slash_end($path); $name = &no_slash_end($name); $dest_dir = &no_slash_end($dest_dir); my $return; $return->{'exists'} = 0; $return->{'rights'} = 0; if ((-e "$dest_dir/$name") || (-e "$dest_dir/.$name.duplicate") || (-e "$dest_dir/.$name.moderate")) { $return->{'exists'} = 1; my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,"$path/$name"); $return->{'rights'} = 1 if $access{'may'}{'edit'} == 1; } return $return; } #******************************************* # Function : do_d_delete # Description : Delete an existing document # (file or directory) #****************************************** sub do_d_delete { &wwslog('info', 'do_d_delete(%s)', $in{'path'}); #useful variables my $path = &no_slash_end($in{'path'}); my $visible_path = &make_visible_path($path); #Current directory and document to delete $path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; my $current_directory = &no_slash_end($1); my $document = $3; # path of the shared directory #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; #### Controls ## must be something to delete unless ($document) { &report::reject_report_web('user','missing_arg',{'argument' => 'doccument'},$param->{'action'}); &wwslog('err',"do_d_delete : no document to delete has been specified"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'missing_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document isn't a description file? unless ($document !~ /^\.desc/) { &wwslog('err',"do_d_delete : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'description_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document exists? unless (-e "$shareddir/$path") { &wwslog('err',"do_d_delete : $shareddir/$path : no such file or directory"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # removing of the document my $doc = "$shareddir/$path"; # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_delete : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## Directory if (-d "$shareddir/$path") { # test of emptiness opendir DIR, "$doc"; my @readdir = readdir DIR; close DIR; # test for "ordinary" files my @test_normal = grep !/^\./, @readdir; my @test_hidden = grep !(/^\.desc$/ | /^\.(\.)?$/ | /^[^\.]/), @readdir; if (($#test_normal != -1) || ($#test_hidden != -1)) { &report::reject_report_web('user','full_directory',{'directory'=> $path},$param->{'action'},$list); &wwslog('err',"do_d_delete : Failed to erase $doc : directory not empty"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # removing of the description file if exists if (-e "$doc/\.desc") { unless (unlink("$doc/.desc")) { &report::reject_report_web('intern','erase_file',{'file' => "$doc/.desc"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_delete : Failed to erase $doc/.desc : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } # removing of the directory rmdir $doc; ## File }else { # removing of the document unless (unlink($doc)) { &report::reject_report_web('intern','erase_file',{'file' => "$doc"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_d_delete: failed to erase %s', $doc); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # removing of the description file if exists if (-e "$shareddir/$current_directory/.desc.$document") { unless (unlink("$shareddir/$current_directory/.desc.$document")) { &wwslog('err',"do_d_delete: failed to erase $shareddir/$current_directory/.desc.$document"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); $in{'list'} = $list_name; $in{'path'} = $current_directory; return 'd_read'; } #******************************************* # Function : do_d_rename # Description : Rename a document # (file or directory) #****************************************** sub do_d_rename { &wwslog('info', 'do_d_rename(%s)', $in{'path'}); #useful variables my $path = &no_slash_end($in{'path'}); #moderation my $visible_path = &make_visible_path($path); my $moderate; if ($path =~ /\.moderate$/) { $moderate=1; } #Current directory and document to delete my $current_directory; if ($path =~ /^(.*)\/([^\/]+)$/) { $current_directory = &no_slash_end($1); }else { $current_directory = '.'; } $path =~ /(^|\/)([^\/]+)$/; my $document = $2; # path of the shared directory my $list_name = $list->{'name'}; my $shareddir = $list->{'dir'}.'/shared'; #### Controls ## must be something to delete unless ($document) { &report::reject_report_web('user','missing_arg',{'argument' => 'document'},$param->{'action'}); &wwslog('err',"do_d_rename : no document to rename has been specified"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document isn't a description file? unless ($document !~ /^\.desc/) { &wwslog('err',"do_d_rename : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_such_document','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document exists? unless (-e "$shareddir/$path") { &wwslog('err',"do_d_rename : $shareddir/$path : no such file or directory"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_such_document','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($in{'new_name'} =~ /^\./ || $in{'new_name'} =~ /\.desc/ || $in{'new_name'} =~ /[~\#\[\]\/]$/) { &report::reject_report_web('user','incorrect_name',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_rename : Unable to create file $in{'new_name'} : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if (($document =~ /\.url$/) && ($in{'new_name'} !~ /\.url$/)) { &report::reject_report_web('user','incorrect_name',{'name' => $in{'new_name'}},$param->{'action'},$list); &wwslog('err',"do_d_rename : New file name $in{'new_name'} does not match URL filenames"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my $doc = "$shareddir/$path"; # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'edit'} > 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_rename : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($moderate){ &do_log('notice', "RENAME: $doc, $shareddir/$current_directory/$in{'new_name'}"); unless (rename $doc, "$shareddir/$current_directory/.$in{'new_name'}.moderate") { &report::reject_report_web('intern','rename_file',{'old'=>$doc, 'new'=>"$shareddir/$current_directory/.$in{'new_name'}.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_rename : Failed to rename %s to %s : %s", $doc, "$shareddir/$current_directory/$in{'new_name'}", $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } }else { &do_log('notice', "RENAME: $doc, $shareddir/$current_directory/$in{'new_name'}"); unless (rename $doc, "$shareddir/$current_directory/$in{'new_name'}") { &report::reject_report_web('intern','rename_file',{'old'=>$doc, 'new'=>"$shareddir/$current_directory/$in{'new_name'}"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_rename : Failed to rename %s to %s : %s", $doc, "$shareddir/$current_directory/$in{'new_name'}", $!); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } ## Rename description file my $desc_file = "$shareddir/$current_directory/.desc.$document"; my $new_desc_file = $desc_file; if (-f $desc_file) { if ($moderate){ $new_desc_file =~ s/\Q$document/\.$in{'new_name'}\.moderate/; }else { $new_desc_file =~ s/\Q$document/$in{'new_name'}/; } unless (rename $desc_file, $new_desc_file) { &report::reject_report_web('intern','rename_file',{'old'=>$desc_file, 'new'=> $new_desc_file}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_rename : Failed to rename $desc_file : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); $in{'list'} = $list_name; if ($current_directory eq '.') { $in{'path'} = ''; } else { $in{'path'} = $current_directory.''; } return 'd_read'; } #******************************************* # Function : do_d_create_dir # Description : Creates a new file / directory #****************************************** sub do_d_create_dir { &wwslog('info', 'do_d_create_dir(%s)', $in{'name_doc'}); #useful variables my $path = &no_slash_end($in{'path'}); #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; my $name_doc = $in{'name_doc'}; $param->{'list'} = $list_name; $param->{'path'} = $path; ## Q-decode file path and names $param->{'decoded_path'} = &tools::qdecode_filename($param->{'path'}); $param->{'decoded_name_doc'} = &tools::qdecode_filename($name_doc); my $type = $in{'type'} || 'directory'; my $desc_file; ### Controls # Must be a directory to create (directory name not empty) unless ($name_doc) { &report::reject_report_web('user','no_name',{},$param->{'action'},$list); &wwslog('err',"do_d_create_dir : Unable to create : no name specified!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'missing_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # The name of the directory must be correct if ($name_doc =~ /^\./ || $name_doc =~ /\.desc/ || $name_doc =~ /[~\#\[\]\/]$/) { &report::reject_report_web('user','incorrect_name',{'name' => $name_doc},$param->{'action'},$list); &wwslog('err',"do_d_create_dir : Unable to create directory $name_doc : incorrect name"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'bad_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control my %mode; $mode{'edit'} = 1; my %access = &d_access_control(\%mode, $path); if ($type eq 'directory') { ## only when (is_author || !moderated) if ($access{'may'}{'edit'} == 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_create_dir : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($access{'may'}{'edit'} == 0.5) { &report::reject_report_web('auth','dir_edit_moderated',{},$param->{'action'},$list); &wwslog('err','do_d_create_dir : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } else { if ($access{'may'}{'edit'} == 0) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('err','do_d_create_dir : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; my $document = "$shareddir/$path/$name_doc"; $param->{'document'} = $document; # the file musn't already exists if (-e $document){ &report::reject_report_web('user','doc_already_exist',{'name' => "$path/$name_doc"},$param->{'action'},$list); &wwslog('err',"do_d_create_dir : cannot create $path/$name_doc : file already exists"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # if the file .moderate exists, only its author can erase it my $doc_moderate = "$shareddir/$path/"."."."$name_doc".".moderate"; my $file_moderated; if (-e "$doc_moderate"){ $file_moderated = 1; my $desc="$shareddir/$path/".".desc.."."$name_doc".".moderate"; $desc =~ s/\/+/\//g; my %desc_hash = &get_desc_file("$desc"); unless($desc_hash{'email'} eq $param->{'user'}{'email'}){ &report::reject_report_web('user','cannot_upload',{'path' => "$path/$name_doc", 'reason' => "file already exists but not yet moderated"},$param->{'action'},$list); &wwslog('err',"do_d_create_dir : Unable to create $doc_moderate : file already exists but not yet moderated"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'file_already_exists','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } ### End of controls if ($type eq 'directory') { # Creation of the new directory unless (mkdir ("$document",0777)) { &report::reject_report_web('intern','cannot_mkdir',{'dir' => $document},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_create_dir : Unable to create $document : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } $desc_file = "$document/.desc"; }else { # Creation of the new file unless (open FILE, ">$document") { &report::reject_report_web('intern','cannot_open_file',{'file' => "$path/$name_doc"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_create_dir : Unable to create $document : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } close FILE; $desc_file = "$shareddir/$path/.desc.$name_doc"; } # Creation of a default description file unless (open (DESC,">$desc_file")) { &report::reject_report_web('intern','cannot_open_file',{'file' => "$desc_file"},$param->{'action'},$list,$param->{'user'}{'email'},$robot); } print DESC "title\n \n\n"; print DESC "creation\n date_epoch ".time."\n email $param->{'user'}{'email'}\n\n"; print DESC "access\n"; print DESC " read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n\n"; close DESC; # moderation if ($access{'may'}{'edit'} == 0.5 && ($type ne 'directory')) { unless (rename "$shareddir/$path/$name_doc","$shareddir/$path/.$name_doc.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>"$shareddir/$path/$name_doc", 'new'=>"$shareddir/$path/.$name_doc.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_create_dir : Failed to rename $path/$name_doc to $path/.$name_doc.moderate : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless (rename "$desc_file","$shareddir/$path/.desc..$name_doc.moderate"){ &report::reject_report_web('intern','rename_file',{'old'=>$desc_file, 'new'=>"$shareddir/$path/.desc..$name_doc.moderate"}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_d_create_dir : Failed to rename $desc_file to $path/.desc..$name_doc.moderate : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } unless ($file_moderated){ unless ($list->send_notify_to_editor('shared_moderated',{'filename' => $param->{'decoded_path'}.'/'.$param->{'decoded_name_doc'}, 'who' => $param->{'user'}{'email'}})) { &wwslog('notice',"Unable to send notify 'shared_moderated' to $list->{'name'} list editor"); } } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); if ($type eq 'directory') { return 'd_read'; } if ($access{'may'}{'edit'} == 0.5) { $in{'path'} = "$path/.$name_doc.moderate"; }else { $in{'path'} = "$path/$name_doc"; } return 'd_editfile'; } ############## Control #******************************************* # Function : do_d_control # Description : prepares the parameters # to edit access for a doc #******************************************* sub do_d_control { &wwslog('info', "do_d_control $in{'path'}"); # Variables my $path = &no_slash_end($in{'path'}); #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; #moderation my $visible_path = &make_visible_path($path); unless ($path) { &report::reject_report_web('user','missing_arg',{'argument' => 'document'},$param->{'action'}); &wwslog('info','do_d_control: no document name'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'missing_parameter','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Existing document? unless (-e "$shareddir/$path") { &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &wwslog('info',"do_d_control : Cannot control $shareddir/$path : not an existing document"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_such_document','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document isn't a description file? unless ($path !~ /\.desc/) { &wwslog('info',"do_d_control : $shareddir/$path : description file"); &report::reject_report_web('user','no_such_document',{'path'=> $visible_path},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_such_document','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control my %mode; $mode{'control'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'control'}) { &report::reject_report_web('auth',$access{'reason'}{'edit'},{},$param->{'action'},$list); &wwslog('info','d_control : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## End of controls #Current directory if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { $param->{'father'} = &no_slash_end($1); }else { $param->{'father'} = ''; } $param->{'escaped_father'} = &tools::escape_docname($param->{'father'}, '/'); my $desc_file; # path of the description file if (-d "$shareddir/$path") { $desc_file = "$shareddir/$1$3/.desc"; } else { $desc_file = "$shareddir/$1.desc.$3"; } # Description of the file my $read; my $edit; if (-e $desc_file) { ## Synchronization my @info = stat "$desc_file"; $param->{'serial_desc'} = $info[9]; my %desc_hash = &get_desc_file("$desc_file"); # rights for read and edit $read = $desc_hash{'read'}; $edit = $desc_hash{'edit'}; # owner of the document $param->{'owner'} = $desc_hash{'email'}; $param->{'doc_title'} = $desc_hash{'title'}; }else { $read = $access{'scenario'}{'read'}; $edit = $access{'scenario'}{'edit'}; } ## other info my @info = stat "$shareddir/$path"; $param->{'doc_date'} = gettext_strftime "%d %b %y %H:%M", localtime($info[9]); # template parameters $param->{'list'} = $list_name; $param->{'path'} = $path; $param->{'visible_path'} = $visible_path; my $lang = $param->{'lang'}; ## Scenario list for READ my $tmp_list_of_scenario = $list->load_scenario_list('d_read',$robot); ## Only get required scenario attributes foreach my $scenario (keys %{$tmp_list_of_scenario}) { $param->{'scenari_read'}{$scenario} = {'name' => $tmp_list_of_scenario->{$scenario}{'name'}, 'web_title' => $tmp_list_of_scenario->{$scenario}{'web_title'}}; } $param->{'scenari_read'}{$read}{'selected'} = 'selected="selected"'; ## Scenario list for EDIT my $tmp_list_of_scenario = $list->load_scenario_list('d_edit',$robot); ## Only get required scenario attributes foreach my $scenario (keys %{$tmp_list_of_scenario}) { $param->{'scenari_edit'}{$scenario} = {'name' => $tmp_list_of_scenario->{$scenario}{'name'}, 'web_title' => $tmp_list_of_scenario->{$scenario}{'web_title'}}; } $param->{'scenari_edit'}{$edit}{'selected'} = 'selected="selected"'; ## father directory if ($path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/) { $param->{'father'} = &no_slash_end($1); }else { $param->{'father'} = ''; } $param->{'escaped_father'} = &tools::escape_docname($param->{'father'}, '/'); $param->{'set_owner'} = 1; $param->{'father_icon'} = $icon_table{'father'}; &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } #******************************************* # Function : do_d_change_access # Description : Saves the description of # the file #****************************************** sub do_d_change_access { &wwslog('info', 'do_d_change_access(%s)', $in{'path'}); # Variables my $path = &no_slash_end($in{'path'}); my $list_name = $list->{'name'}; # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; #### Controls ## the path must not be empty (the description file of the shared directory # doesn't exist) unless ($path) { &report::reject_report_web('intern','cannot_describe_shared_directory',{'path' => $path },$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"do_d_change_access : Cannot change access $shareddir : root directory"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # the document to describe must already exist unless (-e "$shareddir/$path") { &report::reject_report_web('user','no_doc_to_describe',{'path'=> $path},$param->{'action'},$list); &wwslog('info',"d_change_access : Unable to change access $shareddir/$path : no such document"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control my %mode; $mode{'control'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'control'}) { &report::reject_report_web('auth','action_listmaster_or_privileged_owner_or_author',{},$param->{'action'},$list); &wwslog('info','d_change_access : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## End of controls # Description file $path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; my $dir = $1; my $file = $3; my $desc_file; if (-d "$shareddir/$path") { $desc_file = "$shareddir/$1$3/.desc"; } else { $desc_file = "$shareddir/$1.desc.$3"; } if (-e "$desc_file"){ # if description file already exists : open it and modify it my %desc_hash = &get_desc_file ("$desc_file"); # Synchronization unless (&synchronize($desc_file,$in{'serial'})){ &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('info',"d_change_access : Synchronization failed for $desc_file"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'synchro_failed','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless (open DESC,">$desc_file") { &wwslog('info',"d_change_access : cannot open $desc_file : $!"); &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # information not modified print DESC "title\n $desc_hash{'title'}\n\n"; # access rights print DESC "access\n read $in{'read_access'}\n"; print DESC " edit $in{'edit_access'}\n\n"; print DESC "creation\n"; # time print DESC " date_epoch $desc_hash{'date'}\n"; # author print DESC " email $desc_hash{'email'}\n\n"; close DESC; } else { # Creation of a description file unless (open (DESC,">$desc_file")) { &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"d_change_access : Cannot create description file $desc_file : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } print DESC "title\n \n\n"; my @info = stat "$shareddir/$path"; print DESC "creation\n date_epoch ".$info[10]."\n email\n\n"; print DESC "access\n read $in{'read_access'}\n"; print DESC " edit $in{'edit_access'}\n\n"; close DESC; } return 'd_control'; } sub do_d_set_owner { &wwslog('info', 'do_d_set_owner(%s)', $in{'path'}); # Variables my $desc_file; my $path = &no_slash_end($in{'path'}); #moderation my $visible_path = &make_visible_path($path); #my $list_name = $in{'list'}; my $list_name = $list->{'name'}; # path of the shared directory my $shareddir = $list->{'dir'}.'/shared'; #### Controls ## the path must not be empty (the description file of the shared directory # doesn't exist) unless ($path) { &report::reject_report_web('intern','cannot_describe_shared_directory',{'path' => $path },$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"do_d_set_owner : Cannot change access $shareddir : root directory"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # the email must look like an email "somebody@somewhere" unless (&tools::valid_email($in{'content'})) { &report::reject_report_web('user','incorrect_email',{'email' => $in{'content'}},$param->{'action'},$list); &wwslog('info',"d_set_owner : $in{'content'} : incorrect email"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'incorrect_email','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Access control ## father directory $path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; my $dir = $1; my $file = $3; if (-d "$shareddir/$path") { $desc_file = "$shareddir/$dir$file/.desc"; }else { $desc_file = "$shareddir/$dir.desc.$file"; } my %mode; $mode{'control'} = 1; ## must be authorized to control father directory #my %access = &d_access_control(\%mode,$1); my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'control'}) { &report::reject_report_web('auth','action_listmaster_or_privileged_owner_or_author',{},$param->{'action'},$list); &wwslog('info','d_set_owner : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authentication','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my $may_set = 1; unless ($may_set) { &report::reject_report_web('user','full_directory',{'directory'=> $visible_path},$param->{'action'},$list); &wwslog('info',"d_set_owner : cannot set owner of a full directory"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'full_directory','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## End of controls my %desc_hash; if (-e "$desc_file"){ # if description file already exists : open it and modify it %desc_hash = &get_desc_file ("$desc_file"); # Synchronization unless (&synchronize($desc_file,$in{'serial'})) { &report::reject_report_web('user','synchro_failed',{},$param->{'action'},$list); &wwslog('info',"d_set_owner : Synchronization failed for $desc_file"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'synchro_failed','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless (open DESC,">$desc_file") { &wwslog('info',"d_set_owner : cannot open $desc_file : $!"); &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # information not modified print DESC "title\n $desc_hash{'title'}\n\n"; print DESC "access\n read $desc_hash{'read'}\n"; print DESC " edit $desc_hash{'edit'}\n\n"; print DESC "creation\n"; # time print DESC " date_epoch $desc_hash{'date'}\n"; #information modified # author print DESC " email $in{'content'}\n\n"; close DESC; } else { # Creation of a description file unless (open (DESC,">$desc_file")) { &report::reject_report_web('intern','cannot_open_file',{'file' => $desc_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info',"d_set_owner : Cannot create description file $desc_file : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } print DESC "title\n $desc_hash{'title'}\n\n"; my @info = stat "$shareddir/$path"; print DESC "creation\n date_epoch ".$info[10]."\n email $in{'content'}\n\n"; print DESC "access\n read $access{'scenario'}{'read'}\n"; print DESC " edit $access{'scenario'}{'edit'}\n\n"; close DESC; } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'name_doc'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); ## ONLY IF SET_OWNER can be performed even if not control of the father directory $mode{'control'} = 1; my %access = &d_access_control(\%mode,$path); unless ($access{'may'}{'control'}) { ## father directory $path =~ /^(([^\/]*\/)*)([^\/]+)(\/?)$/; $in{'path'} = &no_slash_end($1); return 'd_read'; } ## ELSE return 'd_control'; } ## Protecting archives from Email Sniffers sub do_arc_protect { &wwslog('info', 'do_arc_protect()'); return 1; } #################################################### # do_remind #################################################### # Sends a remind command to sympa.pl. # # IN : - # # OUT : 'loginrequest' | 'admin' | undef # ##################################################### sub do_remind { &wwslog('info', 'do_remind()'); ## Access control return undef unless (defined &check_authz('do_remind', 'remind')); my $extention = time.".".int(rand 9999) ; my $mail_command; ## Sympa will require a confirmation my $result = $list->check_list_authz('remind','smtp', {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } if ($r_action =~ /reject/i) { &report::reject_report_web('auth',$reason,{},$param->{'action'},$list); &wwslog('info','remind : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; }else { $mail_command = sprintf "REMIND %s", $param->{'list'}; } my $time = time; my $data = {'headers' => {'Message-ID' => '<'.$time.'@wwsympa>', 'X-Sympa-NoWrap' => 'yes'}, 'from'=> $param->{'user'}{'email'}, 'body' => $mail_command}; $data->{'not_auto_submitted'} = 1; unless (&mail::mail_file('',&Conf::get_robot_conf($robot, 'sympa'),$data,$robot)) { &report::reject_report_web('intern','cannot_send_remind',{'from' => $param->{'user'}{'email'},'listname'=>$list->{'name'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_remind: failed to send message for command REMIND'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } &report::notice_report_web('performed_soon',{},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'admin'; } ## Load list certificat sub do_load_cert { &wwslog('info','do_load_cert(%s)', $param->{'list'}); my @cert = $list->get_cert('der'); unless (@cert) { &report::reject_report_web('user','missing_cert',{},$param->{'action'},$list); &wwslog('info','do_load_cert: no cert for this list'); return undef; } # don't you just HATE it when every single browser seems to want a # different content-type for certificates? order is important, as # everybody calls themselves "mozilla", and opera identifies as # IE if told so (but Opera doesn't do S/MIME anyways, it seems) my ($ua, $ct) = ($ENV{HTTP_USER_AGENT}, 'application/x-x509-email-cert'); if ($ua =~ /MSIE/) { $ct = 'application/pkix-cert'; } $param->{'bypass'} = 'extreme'; print "Content-type: $ct\n\n"; foreach my $l (@cert) { print "$l"; } return 1; } #******************************************* # Function : do_upload_pictures # Description : Creates a new pictures with a # uploaded file #****************************************** sub do_upload_pictures { # Parameters of the uploaded file (from suboptions.tt2) my $fn = $query->param('uploaded_file'); &wwslog('info', 'do_upload_pictures(%s,%s)',$fn,$param->{'user'}{'email'}); # name of the file, without path my $fname; if ($fn =~ /([^\/\\]+)$/) { $fname = $1; } # type of the file my $filetype; if ($fn =~ /\.(jpg|jpeg|png|gif)$/i) { $filetype = lc $1; } else {$filetype = undef}; my $filename = &tools::md5_fingerprint($param->{'user'}{'email'}); my $fullfilename = $filename.'.'.$filetype; #uploaded file must have a name unless ($fname) { &report::reject_report_web('user','no_name',{},$param->{'action'}); &wwslog('err',"do_upload_pictures : No file specified to upload"); return 'suboptions'; } unless($filetype) { &report::reject_report_web('user','cannot_upload',{'path' => $fullfilename, 'reason' => "your file does not have an authorized format." },$param->{'action'}); &wwslog('err',"do_upload_pictures : unauthorized format"); return 'suboptions'; } my $filetmp; #check if there is not already a file for the user with a different extension foreach my $ext ('.gif','.png','.jpg','.jpeg') { my $file = &Conf::get_robot_conf($robot,'pictures_path').'/'.$in{'list'}.'@'.$robot.'/'.$filename; if(-f $file.$ext) { rename($file.$ext,$file.$ext.'.tmp'); $filetmp = $file.$ext; last; } } unless(&creation_picture_file(&Conf::get_robot_conf($robot,'pictures_path'),$param->{'list'}.'@'.$robot,$fullfilename)) { &report::reject_report_web('user','upload_failed', {'path' => $fullfilename},$param->{'action'}); &wwslog('err','do_upload_pictures : Failed to create file %s/%s@%s%s',&Conf::get_robot_conf($robot,'pictures_path'),$param->{'list'},$robot,$filename); return 'suboptions'; } my $uploadedfile = &Conf::get_robot_conf($robot,'pictures_path').'/'.$in{'list'}.'@'.$robot.'/'.$fullfilename; my @info = stat($uploadedfile); my $size = $info[7]; unless($size <= $Conf{'pictures_max_size'}) { unlink($uploadedfile); rename($filetmp.'.tmp',$filetmp); &report::reject_report_web('user','cannot_upload',{'path' => $fullfilename, 'reason' => "Your file exceeds the authorized size." },$param->{'action'}); &wwslog('err',"do_upload_pictures : Failed to upload pictures"); return 'suboptions'; } # message of success unlink($filetmp.'.tmp'); &wwslog('info',"do_upload_pictures : Upload of the pictures succeeded"); return 'suboptions'; } ## Delete a picture file sub do_delete_pictures { &wwslog('info', 'do_delete_pictures(%s,%s,%s)', $param->{'list'},$robot,$param->{'user'}{'email'}); my $email = $param->{'user'}{'email'}; #deleted file must exist unless(&tools::pictures_filename('email' => $email, 'list' => $list)) { &report::reject_report_web('user','no_name',{},$param->{'action'},$list); &wwslog('err',"do_delete_pictures : No file exists to delete"); return 'suboptions'; } unless($list->delete_user_picture($email)) { &report::reject_report_web('intern','erase_file',{'file' => &tools::pictures_filename('email' => $email, 'list' => $list)},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"do_delete_pictures : Failed to erase ".&tools::pictures_filename('email' => $email, 'list' => $list)); return undef; } else { &wwslog('notice',"do_delete_pictures : File deleted successfull"); return 'suboptions'; } } #################################################### # do_change_email_request #################################################### # Checks a user's new email address and passes it # to 'change_email' # # IN : - # # OUT : '1' | 'change_email' # #################################################### ## Checks a users new email address by sending a ticket to the new email address ## and demanding that they click it to verify. Leads to 'change_email' sub do_change_email_request { &wwslog('info','do_change_email_request(%s)', $in{'new_email'}); unless ($param->{'one_time_ticket'} = &Auth::create_one_time_ticket($in{'new_email'},$robot,'change_email/'.$param->{'user'}{'email'},$ip)){ &do_log('notice',"Unable to create one_time_ticket for $in{'new_email'}, service do_change_email_request"); }else{ &do_log('notice',"ticket : $param->{'one_time_ticket'}"); } $param->{'new_email'} = $in{'new_email'}; my $tt2_param = {'type' => 'ticket_to_send', 'one_time_ticket' => $param->{'one_time_ticket'}, 'to' => $in{'new_email'}, }; unless (&List::send_global_file('user_notification', $in{'new_email'}, $robot, $tt2_param)) { &do_log('notice',"Unable to send template 'user_notification' to $in{'new_email'}"); return undef; } return '1'; } #################################################### # do_change_email #################################################### # Changes a user's email address in Sympa environment # # IN : - # # OUT : '1' | 'pref' | undef # #################################################### ## Change a user's email address in Sympa environment sub do_change_email { &wwslog('info','do_change_email(%s)', $in{'email'}); my ($old_email, $new_email); my $edited_by_listmaster; unless ($in{'email'} || ($in{'old_email'} && $in{'new_email'})) { &report::reject_report_web('user','Missing argument',{},$param->{'action'}); &wwslog('err',"Lacking parameter : $in{'email'} or $in{'old_email'} or $in{'new_email'} "); &web_db_log({'parameters' => $in{'email'},$in{'old_email'},$in{'new_email'}, 'status' => 'error', 'error_type' => 'user'}); } ## There are two ways to access this function 'change_email'. One from the preferences page and one from the serveradmin page ## If the process comes from server admin it needs the variables 'old_email' and 'new_email'. if ($in{'old_email'} && $in{'new_email'}) { ## if variables old_email and new_email are present $edited_by_listmaster is set to one ## so that at the end of the function we can return to the SympaAdmin page ## instead of the preferences page $edited_by_listmaster = 1; unless (&List::is_listmaster ($param->{'user'}{'email'}, $robot)) { &report::reject_report_web('auth','User is not Listmaster',{},$param->{'action'}); &wwslog('err','do_change_email : not listmaster'); &web_db_log({'parameters' => $in{'email'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } $old_email = $in{'old_email'}; $new_email = $in{'new_email'}; }else { $old_email = $in{'email'}; $new_email = $param->{'user'}{'email'}; } my ($password, $newuser); if ($newuser = &List::get_user_db($old_email)) { $password = $newuser->{'password'}; } ## Change email as list MEMBER foreach my $list ( &List::get_which($old_email,$robot, 'member') ) { my $l = $list->{'name'}; my $user_entry = $list->get_subscriber($old_email); if ($user_entry->{'included'} == 1) { ## Notify list owner $list->send_notify_to_owner('failed_to_change_included_member',{'current_email' => $old_email, 'new_email' => $new_email, 'datasource' => $list->get_datasource_name($user_entry->{'id'})}); &report::reject_report_web('user','change_member_email_failed_included',{'listname'=>$list->{'name'}}, $param->{'action'},$list,$old_email,$robot); &wwslog('err', 'could not change member email for list %s because member is included', $l); next; } ## Check if user is already member of the list with his new address ## then we just need to remove the old address if ($list->is_user($new_email)) { unless ($list->delete_user('users' => [$old_email]) ) { &report::reject_report_web('intern','delete_subscriber_db_failed',{'sub'=>$new_email}, $param->{'action'},$list,$old_email,$robot); &wwslog('info', 'do_change_email: could not remove email from list %s', $l); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$new_email",'target_email' => "$new_email",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $old_email,'client' => $ip,'daemon' => $daemon_name}); } }else { unless ($list->update_user($old_email, {'email' => $new_email, 'update_date' => time}) ) { &report::reject_report_web('intern','update_subscriber_db_failed',{'sub'=>$new_email}, 'old_email' => $old_email, $param->{'action'},$list,$old_email,$robot); &wwslog('info', 'do_change_email: could not change email for list %s', $l); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$new_email",'target_email' => "$new_email",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $old_email,'client' => $ip,'daemon' => $daemon_name}); } } } &report::notice_report_web('performed',{},$param->{'action'}); ## Change email as list OWNER/MODERATOR my %updated_lists; foreach my $role ('owner', 'editor') { foreach my $list ( &List::get_which($old_email,$robot, $role) ) { ## Check if admin is include via an external datasource my $admin_user = $list->get_admin_user($role, $old_email); if ($admin_user->{'included'}) { ## Notify listmaster &List::send_notify_to_listmaster('failed_to_change_included_admin',$robot,{'list' => $list, 'current_email' => $old_email, 'new_email' => $new_email, 'datasource' => $list->get_datasource_name($admin_user->{'id'})}); &report::reject_report_web('user','change_admin_email_failed_included',{'listname'=>$list->{'name'}}, $param->{'action'},$list,$old_email,$robot); &wwslog('err', 'could not change %s email for list %s because admin is included', $role, $list->{'name'}); next; } ## Go through owners/editors of the list foreach my $admin (@{$list->{'admin'}{$role}}) { next unless (lc($admin->{'email'}) eq lc($old_email)); ## Update entry with new email address $admin->{'email'} = $new_email; $updated_lists{$list->{'name'}}++; } ## Update Db cache for the list $list->sync_include_admin(); $list->save_config($param->{'session'}{'email'}); } } ## Notify listmasters that list owners/moderators email have changed if (keys %updated_lists) { &List::send_notify_to_listmaster('listowner_email_changed',$robot, {'list' => $list, 'previous_email' => $old_email, 'new_email' => $new_email, 'updated_lists' => keys %updated_lists}) } ## Update User_table and remove existing entry first (to avoid duplicate entries) &List::delete_user_db($new_email,); unless ( &List::update_user_db($old_email, {'email' => $new_email, })) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$new_email, 'old_email' => $old_email}, $param->{'action'},'',$old_email,$robot); &wwslog('info','change_email: update failed'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$new_email",'target_email' => "$new_email",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $old_email,'client' => $ip,'daemon' => $daemon_name}); return undef; } ## Update netidmap_table unless ( &List::update_email_netidmap_db($robot, $old_email, $new_email) ){ &report::reject_report_web('intern','update_netidmap_failed',{'user'=>$new_email, 'old_email' => $old_email}, $param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','change_email: update failed'); &web_db_log({'target_email' => $old_email, 'status' => 'error', 'error_type' => 'internal'}); return undef; } ## Update the data structure that tells which lists the current user is member/owner/editor of unless ($edited_by_listmaster == 1) { @{$param->{'get_which'}} = &List::get_which($param->{'user'}{'email'},$robot,'member'); @{$param->{'get_which_owner'}} = &List::get_which($param->{'user'}{'email'},$robot,'owner'); @{$param->{'get_which_editor'}} = &List::get_which($param->{'user'}{'email'},$robot,'editor'); } if ($edited_by_listmaster == 1) { return 'serveradmin'; } if ($in{'previous_action'}) { $in{'list'} = $in{'previous_list'}; return $in{'previous_action'}; }elsif ($edited_by_listmaster == 1) { return 'serveradmin'; } return 'pref'; } #################################################### # do_suspend_request #################################################### # Suspend a subscription to one or more lists # # for a given period: start date and end date # # (or unlimited). The user may at any time # # stop the suspension. # # # # IN : - # # OUT : 'loginrequest' # # | 'info' | undef # # # #################################################### sub do_suspend_request { &wwslog('info', 'do_suspend_request', $in{'action'}); #Action = suspend_request my $email = $param->{'user'}{'email'}; my $data; ## Sets the date of the field "start date" to "today" my @d_day = localtime(time); $param->{'d_day'} = ($d_day[3])."-".($d_day[4]+1)."-".($d_day[5]+1900); my $display_resume = 0; ## We display in the table the lists of the subscriber and the state in which they are. ## reception : - nomail/digest/mail || ## - . suspended From XX/XX/XXXX To XX/XX/XXXX my @lists = &List::get_which($email, $robot, 'member'); foreach my $list (@lists) { my $member_info = $list->get_subscriber($param->{'user'}{'email'}); if(($member_info->{'enddate'} < time) && ($member_info->{'enddate'})){ ## If end date is < time, update the BDD by deleting the suspending's data &List::restore_suspended_subscription($param->{'user'}{'email'},$list->{'name'},$list->{'domain'}); } my $final_start_date = gettext_strftime "%d %b %Y", localtime($member_info->{'startdate'}); my $final_end_date; if($member_info->{'suspend'} == 1){ $display_resume = 1; } if($member_info->{'enddate'}){ $final_end_date = gettext_strftime "%d %b %Y", localtime($member_info->{'enddate'}); }else{ $final_end_date = undef; } $member_info->{'reception'} ||= 'mail'; $member_info->{'visibility'} ||= 'noconceal'; foreach my $mode ($list->available_reception_mode) { $param->{'reception'}{$list->{'name'}}{$mode}{'description'} = $list->get_option_title($mode, 'reception'); if ($member_info->{'reception'} eq $mode) { $param->{'reception'}{$list->{'name'}}{$mode}{'selected'} = ' selected'; }else { $param->{'reception'}{$list->{'name'}}{$mode}{'selected'} = ''; } } my $subscription = {'listname' => $list->{'name'}, 'listdomain' => $list->{'domain'}, 'listreception' => $member_info->{'reception'}, 'listsuspend' => $member_info->{'suspend'}, 'liststartdate' =>$final_start_date, 'listenddate' => $final_end_date, 'display' => $display_resume, 'visibility' => $member_info->{'visibility'}, 'reception' => $param->{'reception'}{$list->{'name'}}, }; push @{$param->{'suspend_list'}}, $subscription; } return 1; } #################################################### # do_suspend_request_action #################################################### # Suspend a subscription for lists. # # Action from the suspend form. # # # # IN : %in : HASH with the form's values # # OUT : 'pref' : action # # | 'info' | undef # #################################################### sub do_suspend_request_action { &wwslog('info', 'do_suspend_request_action', $in{'action'}); my $day1; my $month1; my $year1; my $day2; my $month2; my $year2; my @lists; my $data; if($in{'sub_action'} eq 'suspendsave'){ # to retrieve the selected list @lists = split /\0/, $in{'listname'}; my @list_selected; foreach my $list (@lists){ unless($list eq ''){ push @list_selected, $list; } } if($list_selected[0] eq ''){ &report::reject_report_web('user','missing_arg',{'argument' => 'must picked one or more list(s) you are subscribed'}, $param->{'action'}); &wwslog('info','suspend_request: must picked one or more list(s) you are subscribed'); return 'suspend_request'; } if ($in{'date_deb'}){ ($day1, $month1, $year1) = split(/\-/, $in{'date_deb'}); $month1 = $month1-1; if (($day1 =~ /([0-9]*)/) && ($month1 =~ /([0-9]*)/) && ($year1 =~ /([0-9]*)/)){ if (((1<=$day1) && ($day1<=31)) && ((0<=$month1) && ($month1<=11)) && (1900<=$year1)){ ## Return an epoch date $data->{'startdate'} = timelocal(0, 0, 0, $day1,$month1,$year1); }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'Start Date doesn\'t exist.'}, $param->{'action'}); &wwslog('info','suspend_request: Date doesn\'t exist.'); return 'suspend_request'; } }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'Start Date doesn\'t exist.'}, $param->{'action'}); &wwslog('info','suspend_request: Date doesn\'t exist.'); return 'suspend_request'; } ## Case 1 : Start date & End date (without indefinite) if (($in{'date_fin'}) && (!$in{'indefinite'})){ ($day2, $month2, $year2) = split(/\-/, $in{'date_fin'}); $month2 = $month2-1; if (($day2 =~ /([0-9]*)/) && ($month2 =~ /([0-9]*)/) && ($year2 =~ /([0-9]*)/)){ if (((1<=$day2) && ($day2<=31)) && ((0<=$month2) && ($month2<=11)) && (1900<=$year2)){ ## Return an epoch date $data->{'enddate'} = timelocal(0, 0, 0, $day2,$month2,$year2); }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'End Date doesn\'t exist.'}, $param->{'action'}); &wwslog('info','suspend_request: Date doesn\'t exist.'); return 'suspend_request'; } }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'End Date doesn\'t exist.'}, $param->{'action'}); &wwslog('info','suspend_request: Date doesn\'t exist.'); return 'suspend_request'; } unless($data->{'startdate'} <= $data->{'enddate'}){ &report::reject_report_web('user','missing_arg',{'argument' => 'The start date must be less than the end date.'}, $param->{'action'}); &wwslog('info','suspend_request: The start date must be less than the end date.'); return 'suspend_request'; } ## Case 2 : Start date & without indefinite (without end date) }elsif((!$in{'date_fin'}) && ($in{'indefinite'})){ $data->{'enddate'} = undef; }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'Choose end date (dd/mm/yyyy) or indefinite end date'}, $param->{'action'}); &wwslog('info','suspend_request: missing argument for the end date or syntax error : dd/mm/yyyy or must choose a end date or indefinite end date'); return 'suspend_request'; } }else{ &report::reject_report_web('user','missing_arg',{'argument' => 'Miss start date (dd/mm/yyyy)'}, $param->{'action'}); &wwslog('info','suspend_request: missing argument for the start date or syntax error : dd/mm/yyyy'); return 'suspend_request'; } ## Suspend subscription foreach my $list (@list_selected){ unless(&List::suspend_subscription($param->{'user'}{'email'}, $list, $data, $robot)){ &wwslog('info','Can\'t do List suspend_subscription'); return 'suspend_request'; } } &report::notice_report_web('performed',{},$in{'sub_action'}); } ## Restore suspended subscription elsif($in{'sub_action'} eq 'suspendstop'){ # to renew membership lists selected @lists = split /\0/, $in{'listname'}; foreach my $line (@lists) { &List::restore_suspended_subscription($param->{'user'}{'email'}, $line, $robot); } if($lists[0] eq ''){ &report::reject_report_web('user','missing_arg',{'argument' => 'must picked one or more list(s)'}, $param->{'action'}); &wwslog('info','suspend_request: must picked one or more list(s)'); return 'suspend_request'; } &report::notice_report_web('performed',{},"Resume the subscription for the list(s)"); } ## Unsubscribe from the selected lists elsif($in{'sub_action'} eq 'signoff'){ # lists selected @lists = split /\0/, $in{'listname'}; my $report = ""; foreach my $line (@lists) { my $unsub_list = new List ($line, $robot); unless ($unsub_list) { &wwslog('info', 'List %s unknown' , $unsub_list); return undef; } my %result = &unsubscribe($param->{'user'}{'email'}, $unsub_list); if ($result{'success'} == 1) { if ($result{'details'} eq 'sent_to_owner') { $report .= sprintf(gettext("Your unsubscription request to list %s was sent to the list owner."),$unsub_list->{'name'}); }else{ $report .= sprintf(gettext("You were successfully unsubscribed from list %s."),$unsub_list->{'name'}); } }else{ if ($result{'category_error'} eq 'auth') { $report .= sprintf(gettext("Unsubscription from list %s denied: Unsubscription from this list is closed."),$unsub_list->{'name'}); }else{ $report .= sprintf(gettext("Unsubscription from list %s failed."),$unsub_list->{'name'}); } } $report .= "\n"; } if($lists[0] eq ''){ &report::reject_report_web('user','missing_arg',{'argument' => 'must picked one or more list(s)'}, $param->{'action'}); &wwslog('info','suspend_request: must picked one or more list(s)'); return 'suspend_request'; } &report::notice_report_web($report,{},''); }else{ &report::reject_report_web('user','unknown_action',{},$in{'sub_action'},$list); &wwslog('info','unknown action %s', $in{'sub_action'}); return undef; } return 'suspend_request'; } #################################################### # do_compose_mail #################################################### sub do_compose_mail { &wwslog('info', 'do_compose_mail', $in{'subaction'}); unless ($param->{'may_post'}) { &report::reject_report_web('auth',$param->{'may_post_reason'},{},$param->{'action'},$list); &wwslog('info','do_compose_mail: may not send message'); return undef; } # Set the subaction to html_news_letter or undef $param->{'subaction'} = $in{'subaction'}; if ($in{'to'}) { # In archive we hide email replacing @ by ' '. Here we must do the reverse transformation $in{'to'} =~ s/ /\@/g; $param->{'to'} = $in{'to'}; }else{ $param->{'to'} = $list->get_list_address(); } foreach my $recipient (split(',',$param->{'to'})) { ($param->{'recipients'}{$recipient}{'local_to'},$param->{'recipients'}{$recipient}{'domain_to'}) = split ('@',$recipient); } $param->{'mailto'}= &mailto($list,$param->{'to'}); # headers will be encoded later. #XXX$param->{'subject'}= &MIME::Words::encode_mimewords($in{'subject'}); $param->{'subject'} = $in{'subject'}; $param->{'in_reply_to'}= '<'.$in{'in_reply_to'}.'>'; $param->{'message_id'} = &tools::get_message_id($robot); if ($list->is_there_msg_topic()) { $param->{'request_topic'} = 1; foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if ($top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } $param->{'topic_required'} = $list->is_msg_topic_tagging_required(); } $param->{'merge_feature'} = $list->{'admin'}{'merge_feature'} eq 'on'; return 1; } #################################################### # do_send_mail #################################################### # Sends a message to a list by the Web interface # or an html page getting its url. # Need MIME::Lite - MIME::Lite::HTML - EMAIL::DATE::FORMAT # It uses mail::mail_file() to do it. # # IN : - # # OUT : 'loginrequest' # | 'info' | undef # #################################################### sub do_send_mail { &wwslog('info', 'do_send_mail'); # Get the sender mail my $from = $param->{'user'}{'email'}; my $to; # Send the message to the list or to the sender as clicking the send to the list or to me. # First if : send to the list if ($in{'sub_action'} eq 'sendmailtolist'){ # In archive we hide email replacing @ by ' '. Here we must do the reverse transformation $in{'to'} =~ s/ /\@/g; $to = $in{'to'}; unless ($in{'to'}) { unless ($param->{'list'}) { &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('info','do_send_mail: no list'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_list','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless ($param->{'may_post'}) { &report::reject_report_web('auth',$param->{'may_post_reason'},{},$param->{'action'},$list); &wwslog('info','do_send_mail: may not send message'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } $to = $list->get_list_address(); } } # Send the mail to the sender. To test his message # Second if : send to the sender "send to me" if($in{'sub_action'} eq 'sendmailtome') { #Set the sender mail to the addressee $to = $from; } if (defined $param->{'subscriber'}) { $from = &tools::addrencode($from, $param->{'subscriber'}{'gecos'}, &Language::GetCharset()); } ##--------------- TOPICS -------------------- my $list_topics; if ($list->is_there_msg_topic()) { my @msg_topics; foreach my $msg_topic (@{$list->{'admin'}{'msg_topic'}}) { my $var_name = "topic_"."$msg_topic->{'name'}"; if ($in{"$var_name"}) { push @msg_topics, $msg_topic->{'name'}; } } $list_topics = join(',',@msg_topics); } if (!$list_topics && $list->is_msg_topic_tagging_required()) { &report::reject_report_web('user','msg_topic_missing',{},$param->{'action'}); &wwslog('info','do_send_mail: message(s) without topic but in a required list'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_topic','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } if ($list_topics) { my $filetopic = $list->tag_topic($in{'message_id'},$list_topics,'sender'); } ##--------------- send an html page or a message -------------------- if ($in{'html_news_letter'}) { # url should not be empty -> missing argument if ($in{'url'} =~ /^\s*$/) { &report::reject_report_web('user','missing_arg',{'argument' => 'url'},$param->{'action'}); ($Log::log_level >= 0) && &wwslog('info','Missing url'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_url','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } # Generate a newsletter from an HTML URL and send it to a list by the Web interface. # Else you must use parse routine of MIME::Lite::HTML and send of MIME::Lite. my $mailHTML = new MIME::Lite::HTML( { From => $from, To => $to, Headers => {'In-Reply-To' => $in{'in_reply_to'}, 'Message-ID' => $in{'message_id'}}, 'return_path' => &Conf::get_robot_conf($robot, 'sympa'), Subject => $in{'subject'}, HTMLCharset => 'utf-8', TextCharset => 'utf-8', TextEncoding => '8bit', HTMLEncoding => '8bit', remove_jscript => '1', #delete the scripts in the html } ); my $pages_url; $pages_url = $in{'url'}; # parse return the MIME::Lite part to send $mailHTML->{_AGENT}->protocols_allowed(['http', 'https', 'ftp', 'nntp']); my $MIMEmail = eval { $mailHTML->parse($pages_url) }; if ($MIMEmail) { $in{'body'} = $MIMEmail->as_string; } else { report::reject_report_web('user', 'wrong_value', {'argument' => 'url'}, $param->{'action'}); return undef; } }else{ ## Message body should not be empty if ($in{'body'} =~ /^\s*$/) { &report::reject_report_web('user','missing_arg',{'argument' => 'body'},$param->{'action'}); ($Log::log_level >= 0) && &wwslog('info','Missing body'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_body','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } $in{'body'} = "\n".$in{'body'}; } my $data = {'headers' => {'Message-ID' => $in{'message_id'}}, 'subject' => $in{'subject'}, 'return_path' => &Conf::get_robot_conf($robot, 'sympa'), 'to' => $to, 'body' => "From: $from\n" . $in{'body'}, 'sign_mode' => '', 'header_possible' => '1'}; $data->{'headers'}{'In-Reply-To'} = $in{'in_reply_to'} if (($in{'in_reply_to'}) && $in{'in_reply_to'} ne '<>'); $data->{'not_auto_submitted'} = 1; unless (&mail::mail_file('', $to, $data, $robot)) { &report::reject_report_web('intern','cannot_send_mail',{'from' => $param->{'user'}{'email'},'listname'=>$list->{'name'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_send_mail: failed to send message for $to list'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'info'; } #################################################### # do_request_topic #################################################### # Web page for a sender to tag his mail in message # topic context. # # IN : - # # OUT : '1' | 'loginrequest' | undef # #################################################### sub do_request_topic { &wwslog('info', 'do_request_topic(%s)', $in{'authkey'}); unless ($list->is_there_msg_topic()) { &report::reject_report_web('user','no_topic',{},$param->{'action'},$list); &wwslog('info','do_request_topic: list without topic message'); return undef; } foreach my $top (@{$list->{'admin'}{'msg_topic'}}) { if ($top->{'name'}) { push (@{$param->{'available_topics'}},$top); } } $param->{'to'} = $list->get_list_address(); $param->{'mailto'}= &mailto($list,$param->{'to'}); $param->{'authkey'} = $in{'authkey'}; my $listname = $list->{'name'}; my $authqueue = &Conf::get_robot_conf($robot,'queueauth'); my $filename = "$authqueue\/$listname\_$in{'authkey'}"; ## For compatibility concerns foreach my $list_id ($list->get_list_id(),$list->{'name'}) { $filename = $authqueue.'/'.$list_id.'_'.$in{'authkey'}; last if (-f $filename); } my $parser; unless ($parser = new MIME::Parser) { &report::reject_report_web('intern','cannot_parse_message',{'file' => $filename},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('notice', 'Cannot parse message %s', $filename); return undef; } $parser->output_to_core(1); unless (open FILE, "$filename") { &report::reject_report_web('intern','cannot_open_file',{'file' => $filename},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('notice', 'Cannot open file %s', $filename); return undef; } my $msg = $parser->parse(\*FILE); my $head = $msg->head(); # headers will be encoded later. $param->{'subject'} = tools::decode_header($head, 'Subject'); $param->{'subject'} = &tools::escape_html($param->{'subject'}); $param->{'from'} = tools::decode_header($head, 'From'); $param->{'from'} = &tools::escape_html($param->{'from'}); $param->{'date'} = tools::decode_header($head, 'Date'); $param->{'date'} = &tools::escape_html($param->{'date'}); $param->{'message_id'} = &tools::clean_msg_id($head->get('Message-Id')); my $body = $msg->bodyhandle(); if ($body) { $param->{'body'} = $body->as_string(); }else{ $param->{'body'} = ''; } $param->{'topic_required'} = $list->is_msg_topic_tagging_required(); return 1; } #################################################### # do_tag_topic_by_sender #################################################### # Tag a mail by its sender : tag the mail and # send a command CONFIRM for it # # IN : - # # OUT : 'loginrequest' | 'info' | undef # #################################################### sub do_tag_topic_by_sender { &wwslog('info', 'do_tag_topic_by_sender'); my $parser; my $listname = $list->{'name'}; my $authqueue = &Conf::get_robot_conf($robot,'queueauth'); my $filename = "$authqueue\/$listname".'@'."$robot\_$in{'authkey'}"; my $mail ; unless($mail = new Message($filename,'noxsympato')) { &report::reject_report_web('intern','cannot_parse_message',{'file' => $filename},$param->{'action'}); &wwslog('info','do_tag_topic_by_sender: cannot parse message %s',$filename); return undef; } my $sender = $mail->{'sender'}; unless ($list->is_there_msg_topic()) { &report::reject_report_web('user','no_topic',{},$param->{'action'},$list); &wwslog('info','do_tag_topic_by_sender: list without topic message'); return undef; } my @msg_topics; foreach my $msg_topic (@{$list->{'admin'}{'msg_topic'}}) { my $var_name = "topic_"."$msg_topic->{'name'}"; if ($in{"$var_name"}) { push @msg_topics, $msg_topic->{'name'}; } } my $list_topics = join(',',@msg_topics); if (!$list_topics && $list->is_msg_topic_tagging_required()) { &report::reject_report_web('user','msg_topic_missing',{},$param->{'action'},$list); &wwslog('info','do_tag_topic_by_sender: message without topic but in a required list'); return undef; } ## TAG my $filetopic = $list->tag_topic($in{'message_id'},$list_topics,'sender'); ## CONFIRM my $time = time; my $data = {'headers' => {'Message-ID' => '<'.$time.'@wwsympa>', 'X-Sympa-NoWrap' => 'yes'}, 'from'=> $sender}; $data->{'body'} = sprintf ("QUIET CONFIRM %s\n",$in{'authkey'}); my $queueauth = &Conf::get_robot_conf($robot, 'queueauth'); my $filemsg = "$queueauth/$list->{'name'}_$in{'authkey'}"; ## For compatibility concerns foreach my $list_id ($list->get_list_id(),$list->{'name'}) { $filemsg = $queueauth.'/'.$list_id.'_'.$in{'authkey'}; last if (-f $filemsg); } unless ($filemsg && (-r $filemsg)) { &report::reject_report_web('intern','tag_topic_by_sender_failed',{'key' => $in{'authkey'}},$param->{'action'},$robot); &wwslog('err', 'do_tag_topic_by_sender: Unable to find message %s from %s, auth failed', $in{'authkey'},$param->{'user'}{'email'}); return undef; } $data->{'not_auto_submitted'} = 1; unless (&mail::mail_file('',&Conf::get_robot_conf($robot, 'sympa'),$data,$robot)) { &report::reject_report_web('intern','cannot_send_mail',{'from' => $param->{'user'}{'email'},'listname'=>$list->{'name'}}, $param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err','do_tag_topic_by_sender: failed to send message for file %s', $filemsg); return undef; } &report::notice_report_web('performed_soon',{},$param->{'action'}); return 'info'; } sub do_search_user { &wwslog('info', 'do_search_user'); if ($in{'email'} =~ /[<>\\\*\$]/) { &report::reject_report_web('user','syntax_errors',{'params' => 'email'},$param->{'action'}); &wwslog('err','do_search_user: syntax error'); return undef; } foreach my $role ('member','owner','editor') { foreach my $list ( &List::get_which($in{'email'},$robot, $role) ) { my $l = $list->{'name'}; next unless (defined $list); $param->{'which'}{$l}{'subject'} = $list->{'admin'}{'subject'}; $param->{'which'}{$l}{'host'} = $list->{'admin'}{'host'}; # show the requestor role not the requested one if ( ($list->am_i('owner',$param->{'user'}{'email'}) || $list->am_i('editor',$param->{'user'}{'email'})) ) { $param->{'which'}{$l}{'admin'} = 1; } if ($role eq 'member') { $param->{'which'}{$l}{'is_member'} = 1; $param->{'which'}{$l}{'reception'} = $list->{'user'}{'reception'}; $param->{'which'}{$l}{'include_source'} = $list->{'user'}{'include_source'}; $param->{'which'}{$l}{'bounce'} = $list->{'user'}{'bounce'} ; $param->{'which'}{$l}{'topic'} = $list->{'user'}{'topic'} ; $param->{'which'}{$l}{'included'} = $list->{'user'}{'included'} if ($list->{'user'}{'included'} == 1) ; $param->{'which'}{$l}{'subscribed'} = $list->{'user'}{'subscribed'} if ($list->{'user'}{'subscribed'} == 1); my $un = $list->{'user'}{'subscribed'}; # $param->{'which'}{$l}{'subscribed'} = 1; }elsif ($role eq 'owner') { $param->{'which'}{$l}{'is_owner'} = 1; }elsif ($role eq 'editor') { $param->{'which'}{$l}{'is_editor'} = 1; } } } $param->{'email'} = $in{'email'}; unless (defined $param->{'which'}) { &report::reject_report_web('user','no_entry',{'email' => $in{'email'}},$param->{'action'}); &wwslog('info','do_search_user: no entry for %s', $in{'email'}); return 'serveradmin'; } return 1; } ## Set language sub do_set_lang { &wwslog('info', 'do_set_lang(%s)', $in{'lang'}); $session->{'lang'} = $in{'lang'} ; $param->{'lang'} = $in{'lang'}; $param->{'lang_tag'} = Language::LanguageTag($param->{'lang'}); if ($param->{'user'}{'email'}) { if (&List::is_user_db($param->{'user'}{'email'})) { unless (&List::update_user_db($param->{'user'}{'email'}, {'lang' => $in{'lang'}})) { &report::reject_report_web('intern','update_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_set_lang: update failed'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'lang'}",'target_email' => "$param->{'user'}{'email'}",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } }else { unless (&List::add_user_db({'email' => $param->{'user'}{'email'}, 'lang' => $in{'lang'}})) { &report::reject_report_web('intern','add_user_db_failed',{'user'=>$param->{'user'}{'email'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_set_lang: update failed'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'lang'}",'target_email' => "$param->{'user'}{'email'}",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } } if ($in{'previous_action'}) { ## Some actions don't make sense with GET method, redirecting to other functions if ($in{'previous_action'} eq 'arcsearch') { $in{'previous_action'} = 'arc'; } $in{'list'} = $in{'previous_list'}; return $in{'previous_action'}; } return &Conf::get_robot_conf($robot, 'default_home'); } ## Function do_attach sub do_attach { &wwslog('info', 'do_attach(%s,%s)', $in{'dir'},$in{'file'}); ### Useful variables # current list / current shared directory my $list_name = $list->{'name'}; # path of the urlized directory my $urlizeddir = $list->{'dir'}.'/urlized'; # document to read my $doc = $urlizeddir.'/'.$in{'dir'}.'/'.$in{'file'}; ### Document exist ? unless (-e "$doc") { &wwslog('info',"do_attach : unable to read $doc : no such file or directory"); &report::reject_report_web('user','no_such_document',{'path' => $in{'dir'}.'/'.$in{'file'}},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'dir'},$in{'file'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ### Document has non-size zero? unless (-s "$doc") { &wwslog('info',"do_attach : unable to read $doc : empty document"); &report::reject_report_web('user','empty_document',{'path' => $in{'dir'}.'/'.$in{'file'}},$param->{'action'},$list); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'dir'},$in{'file'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'empty_file','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ## Access control return undef unless (defined &check_authz('do_attach', 'web_archive.access')); # parameters for the template file # view a file $param->{'file'} = $doc; $param->{'bypass'} = 'asis'; ## File type if ($in{'file'} =~ /\.(\w+)$/) { $param->{'file_extension'} = $1; } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'dir'},$in{'file'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } sub do_subindex { &wwslog('info', 'do_subindex'); my $subscriptions = $list->get_subscription_requests(); foreach my $sub (keys %{$subscriptions}) { $subscriptions->{$sub}{'date'} = gettext_strftime "%d %b %Y", localtime($subscriptions->{$sub}{'date'}); } $param->{'subscriptions'} = $subscriptions; &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } sub do_ignoresub { &wwslog('info', 'do_ignoresub'); my @users; foreach my $pair (split /\0/, $in{'pending_email'}) { if ($pair =~ /,/) { push @users, $`; } } foreach my $u (@users) { unless ($list->delete_subscription_request($u)) { &report::reject_report_web('intern','del_sub_request',{'sub'=>$u},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('info','do_ignoresub: delete_subscription_request(%s) failed', $u); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'subindex'; } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'subindex'; } sub do_stats { &wwslog('info', 'do_stats'); $param->{'shared_size'} = int (($list->get_shared_size + 512)/1024); $param->{'arc_size'} = int (($list->get_arc_size($wwsconf->{'arc_path'}) + 512)/1024); return 1; } ## setting the topics list for templates sub export_topics { my $robot = shift; wwslog ('debug2',"export_topics($robot)"); my %topics = &List::load_topics($robot); unless (%topics) { &wwslog('err','No topics defined'); return undef; } ## Remove existing topics $param->{'topics'} = undef; my $total = 0; foreach my $t (sort {$topics{$a}{'order'} <=> $topics{$b}{'order'}} keys %topics) { my $result = &Scenario::request_action ('topics_visibility', $param->{'auth_method'},$robot, {'topicname' => $t, 'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $action; $action = $result->{'action'} if (ref($result) eq 'HASH'); next unless ($action =~ /do_it/); my $current = $topics{$t}; $current->{'id'} = $t; ## For compatibility reasons $current->{'mod'} = $total % 3; $current->{'mod2'} = $total % 2; push @{$param->{'topics'}}, $current; $total++; } push @{$param->{'topics'}}, {'id' => 'topicsless', 'mod' => $total, 'sub' => {} }; $param->{'topics'}[int($total / 2)]{'next'} = 1; } # manage blacklist sub do_blacklist { &wwslog('info', 'do_blacklist(%s)', $param->{'list'}); unless ($param->{'list'}){ &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('info','do_blacklist: no list'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'no_list','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } unless($param->{'is_owner'}|| $param->{'is_editor'} || $param->{'is_listmaster'}) { &wwslog('info','do_blacklist : not listmaster or list owner or list editor'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my $file = $list->{'dir'}.'/search_filters/blacklist.txt'; $param->{'rows'} = 0 ; if (defined $in{'blacklist'}){ &wwslog('info','do_blacklist : submit blacklist update'); my $dir = $list->{'dir'}.'/search_filters'; unless ((-d $dir) || mkdir ($dir, 0755)) { &report::reject_report_web('intern','unable to create dir'); &wwslog('info','do_blacklist : unable to create dir %s',$dir); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } my $file = $dir.'/blacklist.txt'; unless (open BLACKLIST, "> $file"){ &report::reject_report_web('intern','unable to create file'); &wwslog('info','do_blacklist : unable to create file %s',$file); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } my @lines = split(/\r\n|\r|\n/, $in{'blacklist'}); $param->{'ignored'} = 0; my $count = 0; # count utils lines in order to remove empty blacklist file foreach my $line (@lines) { if ($line =~ /\*.*\*/) { $param->{'ignored_linest'} .= $line."\n"; $param->{'ignored'} += 1; }else{ print BLACKLIST "$line\n"; $param->{'blacklist'} .= $line."\n"; $param->{'rows'} += 1; $count += 1 unless ($line =~ /^\s*$/o || /^[\#\;]/o); } } close BLACKLIST; if ($count == 0) { unless (unlink $file) { &report::reject_report_web('intern','unable to remove empty blacklist file'); &wwslog('info','do_blacklist : unable to remove empty blacklist file %s',$file); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } &wwslog('info','do_blacklist : removed empty blacklist file %s',$file); } }else{ if (-f $file) { unless (open BLACKLIST, $file) { &report::reject_report_web('intern','unable to open file',{'file' => $file,$param->{'action'},'',$param->{'user'}{'email'}},$robot); &wwslog('err','unable to read %s',$file); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } while () { $param->{'blacklist'} .= $_ ; $param->{'rows'} += 1; } close BLACKLIST; } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$param->{'list'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } # output in text/plain format a scenario sub do_dump_scenario { &wwslog('info', "do_dump_scenario($param->{'list'}), $in{'pname'}"); my $scenario = new Scenario ('function' => $in{'pname'}, 'robot' => $robot, 'name' => $list->{'admin'}{$in{'pname'}}{'name'}, 'directory' => $list->{'dir'}); unless (defined $scenario) { &report::reject_report_web('intern','cannot_open_file',{},$param->{'action'},$list); &wwslog('info','failed to load scenario'); return undef; } ($param->{'dumped_scenario'}, $param->{'scenario_path'}) = ($scenario->{'data'}, $scenario->{'file_path'}); $param->{'pname'} = $in{'pname'}; $param->{'scenario_name'} = $list->{'admin'}{$in{'pname'}}{'name'}; if ($in{'new_scenario_name'}) { # in this case it's a submit. my $scenario_dir = $list->{'dir'}.'/scenari/'; my $scenario_file = $scenario_dir.$in{'pname'}.'.'.$in{'new_scenario_name'} ; if ($param->{'dumped_scenario'} eq $in{'new_scenario_content'}){ &wwslog('info','do_dump_scenario: scenario unchanged'); $param->{'result'} = 'unchanged'; return 1; } unless (-d $scenario_dir) { unless (mkdir ($scenario_dir, 0777)) { &do_log('err',"do_dump_scenario: cannot_create_dir %s : %s ", $scenario_dir, $!); &report::reject_report_web('intern','cannot_create_dir',{'file' => $scenario_dir,$param->{'action'},'',$param->{'user'}{'email'}},$robot); return undef; } } unless (open SCENARIO , ">$scenario_file") { &wwslog('info','do_dump_scenario: cannot_open_file %s', $scenario_file); &report::reject_report_web('intern','cannot_open_file',{'file' => $scenario_file,$param->{'action'},'',$param->{'user'}{'email'}},$robot); return undef; } print SCENARIO $in{'new_scenario_content'}; close SCENARIO; # load the new scenario in the list config. if ($in{'new_scenario_name'} eq $in{'scenario_name'}) { $param->{'result'} = 'success'; }else{ $param->{'result'} = 'success_new_name'; } } return 1 ; } ## Subscribers' list sub do_dump { &wwslog('info', "do_dump($param->{'list'})"); ## Whatever the action return, it must never send a complex html page $param->{'bypass'} = 1; $param->{'content_type'} = "text/plain"; $param->{'file'} = undef ; ## Access control unless (defined &check_authz('do_dump', 'review')) { undef $param->{'bypass'}; return undef; } $list->dump(); $param->{'file'} = $list->{'dir'}.'/subscribers.db.dump'; if ($in{'format'} eq 'light') { unless (open (DUMP,$param->{'file'} )) { &report::reject_report_web('intern','cannot_open_file',{'file' => $param->{'file'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog ('info', 'unable to open file %s\n',$param->{'file'} ); return undef; } unless (open (LIGHTDUMP,">$param->{'file'}.light")) { &report::reject_report_web('intern','cannot_open_file',{'file' => "$param->{'file'}.light"},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','unable to create file %s.light\n',$param->{'file'} ); return undef; } while (){ next unless ($_ =~ /^email\s(.*)/); print LIGHTDUMP "$1\n"; } close LIGHTDUMP; close DUMP; $param->{'file'} = "$list->{'dir'}/subscribers.db.dump.light"; } else { $param->{'file'} = "$list->{'dir'}/select.dump"; &wwslog('info','opening %s',$param->{'file'}); unless (open (DUMP,">$param->{'file'}")) { &report::reject_report_web('intern','file_update_failed',{},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err','unable to create file %s\n',$param->{'file'} ); return undef; } if ($in{'format'} eq 'bounce') { $in{'size'} = 'all'; do_reviewbouncing(); print DUMP "# Exported bouncing subscribers\n"; print DUMP "# Email\t\tName\tBounce score\tBounce count\tFirst bounce\tLast bounce\n"; foreach my $user (@{$param->{'members'}}){ print DUMP "$user->{'email'}\t$user->{'gecos'}\t$user->{'bounce_score'}\t$user->{'bounce_count'}\t$user->{'first_bounce'}\t$user->{'last_bounce'}\n"; } } else { $in{'filter'} = $in{'format'}; do_search(); print DUMP "# Exported subscribers with search filter \"$in{'format'}\"\n"; foreach my $user (@{$param->{'members'}}){ print DUMP "$user->{'email'}\t$user->{'gecos'}\n"; } } close DUMP; } return 1; } ## returns a mailto according to list spam protection parameter sub mailto { my $list = shift; my $email = shift; my $gecos = shift; my $next_one; my $mailto = ''; my @addresses; my %recipients; @addresses = split (',',$email); $gecos = $email unless ($gecos); $gecos =~ s/&/&/g; $gecos =~ s//>/g; foreach my $address (@addresses) { ($recipients{$address}{'local'},$recipients{$address}{'domain'}) = split ('@',$address); } if ($list->{'admin'}{'spam_protection'} eq 'none') { $mailto .= "$gecos"; }elsif($list->{'admin'}{'spam_protection'} eq 'javascript') { if ($gecos =~ /\@/) { $gecos =~ s/@/\" + \"@\" + \"/; } $mailto .= ""; }elsif($list->{'admin'}{'spam_protection'} eq 'at') { foreach my $address (@addresses) { $mailto .= " AND " if ($next_one); $mailto .= "$recipients{$address}{'local'} AT $recipients{$address}{'domain'}"; $next_one = 1; } } return $mailto; } ## Returns a spam-protected form of email address sub get_protected_email_address { my ($local_part, $domain_part) = @_; if($list->{'admin'}{'spam_protection'} eq 'javascript') { my $return = ""; return ($return); }elsif($list->{'admin'}{'spam_protection'} eq 'at') { return ("$local_part AT $domain_part"); }else { return($local_part.'@'.$domain_part); } } ## view logs stored in RDBMS ## this function as been writen in order to allow list owner and listmater to views logs ## of there robot or there is real problems with privacy policy and law in such services. ## sub do_viewlogs { &wwslog('info', 'do_viewlogs(%s)', $in{'page'}); $param->{'page'} = int($in{'page'}) || 1; $param->{'size'} = int($in{'size'}) || $wwsconf->{'viewlogs_page_size'}; $param->{'sortby'} = $in{'sortby'} || 'email'; $param->{'total_results'} = 0; my @date = &Log::get_log_date(); $param->{'date_from_formated'} = gettext_strftime "%Y-%m-%d-%H-%M-%S", localtime($date[0]); $param->{'date_to_formated'} = gettext_strftime "%Y-%m-%d-%H-%M-%S", localtime($date[1]); #display and search parameters preparation my $select = {}; $select->{'robot'} = $robot; $select->{'list'} = $param->{'list'}; foreach my $p ('target_type','target','date_from','date_to','type','ip') { $param->{$p} = $in{$p}; $select->{$p} = $in{$p}; } if($in{'target_type'} or $in{'page'} or $in{'size'}) { #sending of search parameters for the query my $line = &Log::get_first_db_log($select); while(defined $line->{'date'}) { $line->{'date'} = gettext_strftime "%d %b %Y %H:%M:%S", localtime($line->{'date'}); # can be wrapped $line->{'parameters'} =~ s/,(?!\s)/, /g if $line->{'parameters'}; push @{$param->{'log_entries'}}, $line; $line = &Log::get_next_db_log(); } #display the number of rows of the query. $param->{'total_results'} = $#{$param->{'log_entries'}} + 1; $param->{'total_results'} = &Log::return_rows_nb() if(&Log::return_rows_nb() != 0); unless($param->{'total_results'}) { &report::reject_report_web('user','no_logs',{},$param->{'action'}); &wwslog('info','do_viewlogs: no results'); return 1; } $param->{'total_page'} = int($param->{'total_results'} / $param->{'size'}); $param->{'total_page'}++ if($param->{'total_results'} % $param->{'size'}); if($param->{'page'} > $param->{'total_page'}) { &report::reject_report_web('user','no_page',{'page' => $param->{'page'}},$param->{'action'}); # &List::db_log('wwsympa',$param->{'user'}{'email'},$param->{'auth_method'},$ip,'review',$param->{'list'},$robot,'','out of pages'); &wwslog('info','do_viewlogs: no page %d', $param->{'page'}); return undef; } my $offset = 0; if($param->{'page'} > 1) { $offset = (($param->{'page'} - 1) * $param->{'size'}); $param->{'prev_page'} = $param->{'page'} - 1; } unless(($offset + $param->{'size'}) >= $param->{'total_results'}) { $param->{'next_page'} = $param->{'page'} + 1; } @{$param->{'log_entries'}} = sort { lc $a->{$param->{'sortby'}} cmp lc $b->{$param->{'sortby'}} } @{$param->{'log_entries'}}; my $last = $offset + $param->{'size'}; $last = $param->{'total_results'} - 1 if($last >= $param->{'total_results'}); @{$param->{'log_entries'}} = @{$param->{'log_entries'}}[$offset..$last]; } return 1; } sub do_arc_manage { &wwslog('info', "do_arc_manage ($in{'list'})"); ## Access control unless (defined &check_authz('do_arc', 'web_archive.access')) { return undef; } my $search_base = $wwsconf->{'arc_path'}.'/'.$list->get_list_id(); opendir ARC, "$search_base"; foreach my $dir (sort {$b cmp $a} grep(!/^\./,readdir ARC)) { if ($dir =~ /^(\d{4})-(\d{2})$/) { push @{$param->{'yyyymm'}}, $dir; } } closedir ARC; return 1; } ## create a zip file with archives from (list,month) sub do_arc_download { &wwslog('info', "do_arc_download ($in{'list'})"); ## Access control unless (defined &check_authz('do_arc', 'web_archive.access')) { return undef; } ##zip file name:listname_archives.zip my $zip_file_name = $in{'list'}.'_archives.zip'; my $zip_abs_file = $Conf{'tmpdir'}.'/'.$zip_file_name; my $zip = Archive::Zip->new(); #Search for months to put in zip unless (defined($in{'directories'})) { &report::reject_report_web('user','select_month',{},$param->{'action'}); &wwslog('info','do_arc_download : no archives specified'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'select_month','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'arc_manage'; } #for each selected month foreach my $dir (split/\0/, $in{'directories'}) { ## Tainted vars problem if ($dir =~ /^(\d+\-\d+)$/) { $dir = $1; } my $abs_dir = $wwsconf->{'arc_path'}.'/'.$list->get_list_id().'/'.$dir.'/arctxt'; ##check arc directory unless (-d $abs_dir) { &report::reject_report_web('intern','arc_not_found',{'arc_file' => $dir, 'listname' => $in{'list'}, 'path' => $abs_dir}, $param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','archive %s not found',$dir); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); next; } $zip->addDirectory($abs_dir, $in{'list'}.'_'.$dir); unless (opendir SPOOL, $abs_dir) { &report::reject_report_web('intern','cannot_open_dir',{'dir' =>$abs_dir },$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_arc_download: unable to open %s', $abs_dir); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } foreach my $msg (sort grep(!/^\./, readdir SPOOL)) { unless ($zip->addFile ($abs_dir.'/'.$msg, $in{'list'}.'_'.$dir.'/'.$msg)) { &report::reject_report_web('intern','add_file_zip',{'file' => "$abs_dir/$msg"},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','do_arc_download: failed to add %s file to archive', $abs_dir.'/'.$msg); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } } closedir SPOOL; ## create and fill a new folder in zip #$zip->addTree ($abs_dir, $in{'list'}.'_'.$dir); } ## check if zip isn't empty if ($zip->numberOfMembers()== 0) { &report::reject_report_web('intern','inaccessible_archive',{'listname' => $in{'list'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('info','Error : empty directories'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ##writing zip file unless ($zip->writeToFileNamed($zip_abs_file) == AZ_OK){ &report::reject_report_web('intern','write_file_zip',{'zipfile'=>$zip_abs_file},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog ('info', 'Error while writing Zip File %s\n',$zip_file_name); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } ##Sending Zip to browser $param->{'bypass'} ='extreme'; printf("Content-Type: application/zip;\nContent-disposition: attachment; filename=\"%s\";\n\n",$zip_file_name); ##MIME Header unless (open (ZIP,$zip_abs_file)) { &report::reject_report_web('intern','cannot_open_file',{'file' => $zip_abs_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog ('info', 'Error while reading Zip File %s\n',$zip_abs_file); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } print ; close ZIP ; ## remove zip file from server disk unless (unlink ($zip_abs_file)){ &report::reject_report_web('intern','erase_file',{'file' => $zip_abs_file},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog ('info', 'Error while unlinking File %s\n',$zip_abs_file); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } sub do_arc_delete { my @abs_dirs; &wwslog('info', "do_arc_delete ($in{'list'})"); ## Access control unless (defined &check_authz('do_arc', 'web_archive.access')) { return undef; } unless (defined $in{'directories'}){ &report::reject_report_web('user','select_month',{},$param->{'action'}); &wwslog('info','No Archives months selected'); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'select_month','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'arc_manage'; } ## if user want to download archives before delete &wwslog('notice', "ZIP: $in{'zip'}"); if ($in{'zip'} == 1) { &do_arc_download(); } foreach my $dir (split/\0/, $in{'directories'}) { push(@abs_dirs ,$wwsconf->{'arc_path'}.'/'.$list->get_list_id().'/'.$dir); } unless (tools::remove_dir(@abs_dirs)) { &wwslog('info','Error while Calling tools::remove_dir'); } &report::notice_report_web('performed',{},$param->{'action'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'list'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 'arc_manage'; } sub do_css { &wwslog('debug', 'do_css(%s)', $in{'file'}); $param->{'bypass'} = 'extreme'; print "Content-type: text/css; charset=utf-8\n\n"; $param->{'css'} = $in{'file'}; ## Do not include locale subdirectories. ## The css.tt2 by each locales will override styles in main CSS. my $tt2_include_path = tools::make_tt2_include_path($robot, 'web_tt2', '', ''); unless (&tt2::parse_tt2($param, 'css.tt2' ,\*STDOUT, $tt2_include_path)) { my $error = &tt2::get_error(); $param->{'tt2_error'} = $error; &List::send_notify_to_listmaster('web_tt2_error', $robot, [$error]); &wwslog('info', "do_css/$in{'file'} : error"); } return; } sub do_rss_request { &wwslog('info', "do_rss_request"); my $result = $list->check_list_authz('visibility',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $sub_is; my $reason; if (ref($result) eq 'HASH') { $sub_is = $result->{'action'}; $reason = $result->{'reason'}; } if ($sub_is =~ /reject/) { &wwslog('info', 'RSS not accessible because list %s is not visible to user %s',$list->get_list_id,$param->{'user'}{'email'}); &web_db_log({'parameters' => $param->{'user'}{'email'}, 'status' => 'error', 'error_type' => 'authorization'}); return undef; } my $args ; $in{'count'} ||= 20; $in{'for'} ||= 10; $args = 'count='.$in{'count'}.'&' if ($in{'count'}) ; $args .= 'for='.$in{'for'} if ($in{'for'}); if ($list ) { $param->{'latest_arc_url'} = &Conf::get_robot_conf($robot, 'wwsympa_url')."/rss/latest_arc/".$list->{'name'}."?".$args; $param->{'latest_d_read_url'} = &Conf::get_robot_conf($robot, 'wwsympa_url')."/rss/latest_d_read/".$list->{'name'}."?".$args; } $param->{'active_lists_url'} = &Conf::get_robot_conf($robot, 'wwsympa_url')."/rss/active_lists?".$args; $param->{'latest_lists_url'} = &Conf::get_robot_conf($robot, 'wwsympa_url')."/rss/latest_lists?".$args; $param->{'output'} = 1; return 1; } sub do_wsdl { &wwslog('info', "do_wsdl ()"); my $sympawsdl = &tools::get_filename('etc',{}, 'sympa.wsdl', $robot); unless (-r $sympawsdl){ &report::reject_report_web('intern','err_404',{},$param->{'action'}); &wwslog('err','could not find $sympawsdl'); return undef; } my $soap_url= &Conf::get_robot_conf($robot,'soap_url'); unless (defined $soap_url) { &report::reject_report_web('user','no_soap_service',{},$param->{'action'}); &wwslog('err','No SOAP service was defined in sympa.conf (soap_url parameter)'); return undef; } $param->{'bypass'} = 'extreme'; print "Content-type: text/xml\n\n"; $param->{'conf'}{'soap_url'} = $soap_url; ## Get the directory path, without the file name my $wsdl_path = $sympawsdl; $wsdl_path =~ s/\/sympa.wsdl//; &tt2::parse_tt2($param, 'sympa.wsdl' , \*STDOUT, [$wsdl_path]); # unless (open (WSDL,$sympawsdl)) { # &error_message('404'); # &wwslog('info','could not open $sympawsdl'); # return undef; # } # print ; # close WSDL; return 1; } ## Synchronize list members with data sources sub do_sync_include { &wwslog('info', "do_sync_include($in{'list'})"); unless ($list->sync_include()) { &report::reject_report_web('intern','sync_include_failed',{},$param->{'action'},$list,$param->{'user'}{'email'},$robot); return undef; } &report::notice_report_web('subscribers_updated',{},$param->{'action'}); return 'review'; } ## Review lists from a family sub do_review_family { &wwslog('info', 'do_review_family'); my $family = Family->new($in{'family_name'}, $robot); unless (defined $family) { &report::reject_report_web('user','unknown_family',{'family'=>$in{'family_name'}},$param->{'action'},'',$param->{'user'}{'email'},$robot); &wwslog('err', 'do_review_family: incorrect family %s', $in{'family_name'}); return undef; } my $all_lists = $family->get_family_lists(); foreach my $flist (@{$all_lists}) { unless (defined $flist) { wwslog('err', 'incorrect list'); next; } push @{$param->{'family_lists'}}, { 'name' => $flist->{'name'}, 'status' => $flist->{'admin'}{'status'}, 'instantiation_date' => (gettext_strftime "%d %b %Y at %H:%M:%S", localtime $flist->{'admin'}{'latest_instantiation'}{'date_epoch'}), 'subject' => $flist->{'admin'}{'subject'}, }; } return 1; } ################################################################ ## do_ca : executes a custom action ## ## IN: ## - 'custom_action': ther name of the custom action (and subsequent tt2 file to use, see below) ## - '@cap': an array of parameters. ## ## Custom actions are used to display user defined templates. ## To use it, follow these steps: ## 1- create a new file "your_action.tt2" and put it in the relevant dir (either etc or expl) ## 2- in this file, add the HTML fragment you want to insert to the web interface. You don't need the section or the tag. ## 3- you can type your action URL: http://your-sympa-server-root-url/ca/your_action/param2/param2/param3/... ## ## the HTML code in 'your_action.tt2' can make use of the parameters this way: [% cap.1 %] for param1, [% cap.2 %] for param, and so on. ############################################################### sub do_ca { &wwslog('info', 'custom action: %s (robot %s) with params: (%s, %s, %s, %s, %s)',$in{'custom_action'},$robot,$in{'cap'}); $param->{'custom_action'} = $in{'custom_action'}; $param->{'cap'} = [split '/',$in{'cap'}]; return 1; } ################################################################ ## do_ca : executes a custom action in list context ## ## IN: ## - 'custom_action': ther name of the custom action (and subsequent tt2 file to use, see below) ## - 'list': the nalme of the list (without the '@robot' part) in the context of which the action is executed. ## - '@lcap': an array of parameters. ## ## Custom actions are used to display user defined templates. ## To use it, follow these steps: ## 1- create a new file "your_action.tt2" and put it in the relevant dir (either etc or expl) ## 2- in this file, add the HTML fragment you want to insert to the web interface. You don't need the section or the tag. ## 3- you can type your action URL: http://your-sympa-server-root-url/ca/your_action/param2/param2/param3/... ## ## the HTML code in 'your_action.tt2' can make use of the parameters this way: [% lcap.1 %] for param1, [% lcap.2 %] for param, and so on. ############################################################### sub do_lca { &wwslog('info', 'List custom action: %s for list %s (robot %s) with params: (%s, %s, %s, %s, %s)',$in{'custom_action'},$in{'list'},$robot,$in{'lcap'}); $param->{'custom_action'} = $in{'custom_action'}; $param->{'cap'} = [split '/',$in{'cap'}]; return 1; } ## Prepare subscriber data to be prompted on the web interface ## Used by review, search,... sub _prepare_subscriber { my $user = shift; my $additional_fields = shift; my $sources = shift; ## Add user $user->{'date'} = gettext_strftime "%d %b %Y", localtime($user->{'date'}); $user->{'update_date'} = gettext_strftime "%d %b %Y", localtime($user->{'update_date'}); ## Reception mode and topics $user->{'reception'} ||= 'mail'; if (($user->{'reception'} eq 'mail') && $user->{'topics'}) { $user->{'reception'} = sprintf gettext("topic (%s)"), $user->{'topics'}; } $user->{'email'} =~ /\@(.+)$/; $user->{'domain'} = $1; $user->{'pictures_url'} = &tools::make_pictures_url('email' => $user->{'email'}, 'list' => $list); ## Escape some weird chars $user->{'escaped_email'} = &tools::escape_chars($user->{'email'}); ## Check data sources $user->{'sources'} = $list->get_datasource_name($user->{'id'}) if ($user->{'id'}); if (@{$additional_fields}) { my @fields; foreach my $f (@{$additional_fields}) { push @fields, $user->{$f}; } $user->{'additional'} = join ',', @fields; } return 1; } ## New d_read function using SharedDocument module ## The following features should be tested : ## * inheritance on privileges ## X moderation ## * escaping special chars sub new_d_read { &wwslog('info', 'new_d_read(%s)', $in{'path'}); ### action relative to a list ? unless ($param->{'list'}) { &report::reject_report_web('user','missing_arg',{'argument' => 'list'},$param->{'action'}); &wwslog('err','do_d_read: no list'); return undef; } # current list / current shared directory my $list_name = $list->{'name'}; my $document = new SharedDocument ($list, $in{'path'}, $param); unless (defined $document) { &report::reject_report_web('intern','new_document_failed',{'path'=>$in{'path'}},$param->{'action'},$list,$param->{'user'}{'email'},$robot); &wwslog('err',"d_read : cannot open $document->{'absolute_path'} : $!"); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'internal','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my $path = $document->{'path'}; my $visible_path = $document->{'visible_path'}; my $shareddir = $document->{'shared_dir'}; my $doc = $document->{'absolute_path'}; my $ref_access = $document->{'access'}; my %access = %{$ref_access}; $param->{'doc_owner'} = $document->{'owner'}; $param->{'doc_title'} = $document->{'title'}; $param->{'doc_date'} = $document->{'date'}; ### Access control unless ($access{'may'}{'read'}) { &report::reject_report_web('auth',$access{'reason'}{'read'},{},$param->{'action'},$list); &wwslog('err','d_read : access denied for %s', $param->{'user'}{'email'}); &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'error','error_type' => 'authorization','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return undef; } my $may_edit = $access{'may'}{'edit'}; my $may_control = $access{'may'}{'control'}; $param->{'may_edit'} = $may_edit; $param->{'may_control'} = $may_control; ### File or directory ? if ($document->{'type'} eq 'url') { $param->{'file_extension'} = $document->{'file_extension'}; $param->{'redirect_to'} = $document->{'url'}; return 1; }elsif ($document->{'type'} eq 'file') { $param->{'file'} = $document->{'absolute_path'}; $param->{'bypass'} = 1; return 1; }else { # directory $param->{'empty'} = $#{$document->{'subdir'}} == -1; # subdirectories hash my %subdirs; # file hash my %files; ## for the exception of index.html # name of the file "index.html" if exists in the directory read my $indexhtml; # boolean : one of the subdirectories or files inside # can be edited -> normal mode of read -> d_read.tt2; my $normal_mode; my $path_doc; my %desc_hash; my $may, my $def_desc; foreach my $subdocument (@{$document->{'subdir'}}) { my $d = $subdocument->{'filename'}; my $path_doc = $subdocument->{'path'}; ## Subdir if ($subdocument->{'type'} eq 'directory') { if ($subdocument->{'access'}{'may'}{'read'}) { $subdirs{$d} = $subdocument->dup(); $subdirs{$d}{'doc'} = $subdocument->{'visible_filename'}; $subdirs{$d}{'escaped_doc'} = $subdocument->{'escaped_filename'}; if ($param->{'user'}{'email'}) { if ($subdocument->{'access'}{'may'}{'control'} == 1) { $subdirs{$d}{'edit'} = 1; # or = $may_action_edit ? # if index.html, must know if something can be edit in the dir $normal_mode = 1; }elsif ($subdocument->{'access'}{'may'}{'edit'} != 0) { # $may_action_edit = 0.5 or 1 $subdirs{$d}{'edit'} = $subdocument->{'access'}{'may'}{'edit'}; # if index.html, must know if something can be edit in the dir $normal_mode = 1; } if ($subdocument->{'access'}{'may'}{'control'}) { $subdirs{$d}{'control'} = 1; } } } }else { # case file if ($subdocument->{'access'}{'may'}{'read'}) { $files{$d} = $subdocument->dup(); $files{$d}{'doc'} = $subdocument->{'visible_filename'}; $files{$d}{'escaped_doc'} = $subdocument->{'escaped_filename'}; ## exception of index.html if ($d =~ /^(index\.html?)$/i) { $indexhtml = $1; } if ($param->{'user'}{'email'}) { if ($subdocument->{'access'}{'may'}{'edit'} == 1) { $normal_mode = 1; $files{$d}{'edit'} = 1; # or = $may_action_edit ? } elsif ($subdocument->{'access'}{'may'}{'edit'} != 0){ # $may_action_edit = 1 or 0.5 $normal_mode = 1; $files{$d}{'edit'} = $subdocument->{'access'}{'may'}{'edit'}; } if ($subdocument->{'access'}{'may'}{'control'}) { $files{$d}{'control'} = 1; } } } } } ### Exception : index.html if ($indexhtml) { unless ($normal_mode) { $param->{'file_extension'} = 'html'; $param->{'bypass'} = 1; $param->{'file'} = $document->{'absolute_path'}; return 1; } } ## to sort subdirs my @sort_subdirs; my $order = $in{'order'} || 'order_by_doc'; $param->{'order_by'} = $order; foreach my $k (sort {by_order($order,\%subdirs)} keys %subdirs) { push @sort_subdirs, $subdirs{$k}; } ## to sort files my @sort_files; foreach my $k (sort {by_order($order,\%files)} keys %files) { push @sort_files, $files{$k}; } # parameters for the template file $param->{'list'} = $list_name; $param->{'father'} = $document->{'father_path'}; $param->{'escaped_father'} = $document->{'escaped_father_path'} ; $param->{'description'} = $document->{'title'}; $param->{'serial_desc'} = $document->{'serial_desc'}; $param->{'path'} = $document->{'path'}; $param->{'visible_path'} = $document->{'visible_path'}; $param->{'escaped_path'} = $document->{'escaped_path'}; if (scalar keys %subdirs) { $param->{'sort_subdirs'} = \@sort_subdirs; } if (scalar keys %files) { $param->{'sort_files'} = \@sort_files; } } $param->{'father_icon'} = $icon_table{'father'}; $param->{'sort_icon'} = $icon_table{'sort'}; ## Show expert commands / user page # for the curent directory if ($may_edit == 0 && $may_control == 0) { $param->{'has_dir_rights'} = 0; } else { $param->{'has_dir_rights'} = 1; if ($may_edit == 1) { # (is_author || ! moderated) $param->{'total_edit'} = 1; } } # set the page mode if ($in{'show_expert_page'} && $param->{'has_dir_rights'}) { $session->{'shared_mode'}='expert'; if ($param->{'user'}{'prefs'}{'shared_mode'} ne 'expert') { # update user pref as soon as connected user change shared mode $param->{'user'}{'prefs'}{'shared_mode'} = 'expert'; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } $param->{'expert_page'} = 1; } elsif ($in{'show_user_page'}) { $session->{'shared_mode'}='basic'; if ($param->{'user'}{'prefs'}{'shared_mode'} ne 'basic') { # update user pref as soon as connected user change shared mode $param->{'user'}{'prefs'}{'shared_mode'} = 'basic'; &List::update_user_db($param->{'user'}{'email'},{data=>&tools::hash_2_string($param->{'user'}{'prefs'})}) ; } $param->{'expert_page'} = 0; } else { if ($session->{'shared_mode'} eq 'expert' && $param->{'has_dir_rights'}) { $param->{'expert_page'} = 1; } else { $param->{'expert_page'} = 0; } } &web_db_log({'robot' => $robot,'list' => $list->{'name'},'action' => $param->{'action'},'parameters' => "$in{'path'}",'target_email' => "",'msg_id' => '','status' => 'success','error_type' => '','user_email' => $param->{'user'}{'email'},'client' => $ip,'daemon' => $daemon_name}); return 1; } ## Check authorizations to the current action ## used in common cases where actions fails unless result is 'do_it' ## It does not apply to actions that can be moderated sub check_authz { my ($subname, $action) = @_; my $result = $list->check_list_authz($action,$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'} || 'nobody', 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $r_action; my $reason; if (ref($result) eq 'HASH') { $r_action = $result->{'action'}; $reason = $result->{'reason'}; } unless ($r_action =~ /do_it/i) { unless(prevent_visibility_bypass()) { &report::reject_report_web('auth',$reason,{'login'=> $param->{'need_login'}},$param->{'action'}); } &wwslog('info','check_authz: access denied in %s for %s', $subname, $param->{'user'}{'email'}); return undef; } return 1; } sub get_server_details { ## All Robots are shown to super listmaster if (&List::is_listmaster($param->{'user'}{'email'})) { $param->{'main_robot'} = 1; $param->{'robots'} = $Conf{'robots'}; } ## Families my @families = &Family::get_available_families($robot); if (@families) { $param->{'families'} = \@families; } } sub get_icon { my $type = shift; return $icon_table{$type}; } sub get_mime_type { my $type = shift; return $mime_types->{$type}; } sub do_maintenance { &wwslog('notice', 'do_maintenance()'); return 1; } sub do_automatic_lists_management_request { &wwslog('notice', 'Starting'); $param->{'automatic_lists_description'} = &Conf::load_automatic_lists_description(); return 1; } sub do_automatic_lists_management { &wwslog('notice', 'Starting'); return 1; } sub do_automatic_lists_request { &wwslog('notice', 'Starting'); # check authorization my $family; unless ($family = new Family($in{'family'},$robot)) { &wwslog('err', "Failed to instantiate family %s. This family does not exist.",$in{'family'}); &List::send_notify_to_listmaster('automatic_list_creation_failed',$robot,["Failed to instantiate family $in{'family'}. This family does not exist."]); return undef; } unless ($family->is_allowed_to_create_automatic_lists(('auth_level' => 'md5', 'sender' => $session->{'email'}, 'message' => undef, 'listname' => ''))){ &report::reject_report_web('auth',"You are not allowed to create list in this family",{},$param->{'action'}); &wwslog('err', 'Access to automatic list creation form denied to user %s.', $session->{'email'}); return undef; } $param->{'family'} = $family; return 1; } sub do_automatic_lists { &wwslog('notice', 'Starting'); my $family_name = $in{'family'}; my $family; my @list_name_parts; my $families_config = &Conf::get_robot_conf($robot,'automatic_list_families'); my $family_config = $families_config->{$family_name}; my $listname = $family_config->{'prefix'}.$family_config->{'prefix_separator'}; foreach my $input (keys %in) { next unless ($input =~ /automatic_list_part_(\d+)/); $list_name_parts[$1] = $in{$input}; } foreach my $list_name_part (@list_name_parts) { $listname .= "$list_name_part$family_config->{'classes_separator'}"; } my $sep = $family_config->{'classes_separator'}.'$'; if ($listname =~ /(.*)($sep)/) { $listname = $1; } $list = new List ($listname, $robot); unless (defined $list) { ## Automatic creation of a mailing list, based on a family unless ($family = new Family($family_name,$robot)) { &Log::do_log('err', "Failed to create the dynamic list $listname: family $family_name does not exist."); &List::send_notify_to_listmaster('automatic_list_creation_failed',$robot,["Failed to create the dynamic list $listname: family $family_name does not exist."]); return undef; } unless ($list = $family->create_automatic_list(('listname' => $listname,'auth_level' => 'md5','sender'=>$session->{'email'}))) { &Log::do_log('err', "Failed to create the dynamic list $listname."); &List::send_notify_to_listmaster('automatic_list_creation_failed',$robot,["Failed to create the dynamic list $listname."]); return undef; } } $in{'list'} = $listname; return 'compose_mail'; } sub prevent_visibility_bypass { &wwslog('debug2', 'Starting'); if (defined $list && ref $list eq 'List') { my $result = $list->check_list_authz('visibility',$param->{'auth_method'}, {'sender' => $param->{'user'}{'email'}, 'remote_host' => $param->{'remote_host'}, 'remote_addr' => $param->{'remote_addr'}}); my $sub_is; my $reason; if (ref($result) eq 'HASH') { $sub_is = $result->{'action'}; $reason = $result->{'reason'}; } if ($sub_is =~ /reject/) { &wwslog('info', 'visibility: List must remain hidden. Returning "home" to prevent visibility bypass'); return "home"; }else { return undef; } } return undef; } sub purely_closed { my $action = shift; my $scenario = new Scenario ('robot' => $robot, 'directory' => $list->{'dir'}, 'file_path' => $list->{'admin'}{$action}{'file_path'}, 'options' => undef); return $scenario->is_purely_closed; } =pod =head1 AUTHORS =over =item * Serge Aumont =item * Olivier Salaun =back =cut